aboutsummaryrefslogtreecommitdiff
path: root/gcc/d
diff options
context:
space:
mode:
authorIain Buclaw <ibuclaw@gdcproject.org>2019-06-18 20:42:10 +0200
committerIain Buclaw <ibuclaw@gdcproject.org>2021-11-30 16:53:28 +0100
commit5fee5ec362f7a243f459e6378fd49dfc89dc9fb5 (patch)
tree61d1bdbca854a903c0860406f457f06b2040be7a /gcc/d
parentb3f60112edcb85b459e60f66c44a55138b1cef49 (diff)
downloadgcc-5fee5ec362f7a243f459e6378fd49dfc89dc9fb5.zip
gcc-5fee5ec362f7a243f459e6378fd49dfc89dc9fb5.tar.gz
gcc-5fee5ec362f7a243f459e6378fd49dfc89dc9fb5.tar.bz2
d: Import dmd b8384668f, druntime e6caaab9, phobos 5ab9ad256 (v2.098.0-beta.1)
The D front-end is now itself written in D, in order to build GDC, you will need a working GDC compiler (GCC version 9.1 or later). GCC changes: - Add support for bootstrapping the D front-end. These add the required components in order to have a D front-end written in D itself. Because the compiler front-end only depends on the core runtime modules, only libdruntime is built for the bootstrap stages. D front-end changes: - Import dmd v2.098.0-beta.1. Druntime changes: - Import druntime v2.098.0-beta.1. Phobos changes: - Import phobos v2.098.0-beta.1. The jump from v2.076.1 to v2.098.0 covers nearly 4 years worth of development on the D programming language and run-time libraries. ChangeLog: * Makefile.def: Add bootstrap to libbacktrace, libphobos, zlib, and libatomic. * Makefile.in: Regenerate. * Makefile.tpl (POSTSTAGE1_HOST_EXPORTS): Fix command for GDC. (STAGE1_CONFIGURE_FLAGS): Add --with-libphobos-druntime-only if target-libphobos-bootstrap. (STAGE2_CONFIGURE_FLAGS): Likewise. * configure: Regenerate. * configure.ac: Add support for bootstrapping D front-end. config/ChangeLog: * acx.m4 (ACX_PROG_GDC): New m4 function. gcc/ChangeLog: * Makefile.in (GDC): New variable. (GDCFLAGS): New variable. * configure: Regenerate. * configure.ac: Add call to ACX_PROG_GDC. Substitute GDCFLAGS. gcc/d/ChangeLog: * dmd/MERGE: Merge upstream dmd b8384668f. * Make-lang.in (d-warn): Use strict warnings. (DMD_WARN_CXXFLAGS): Remove. (DMD_COMPILE): Remove. (CHECKING_DFLAGS): Define. (WARN_DFLAGS): Define. (ALL_DFLAGS): Define. (DCOMPILE.base): Define. (DCOMPILE): Define. (DPOSTCOMPILE): Define. (DLINKER): Define. (DLLINKER): Define. (D_FRONTEND_OBJS): Add new dmd front-end objects. (D_GENERATED_SRCS): Remove. (D_GENERATED_OBJS): Remove. (D_ALL_OBJS): Remove D_GENERATED_OBJS. (d21$(exeext)): Build using DLLINKER and -static-libphobos. (d.tags): Remove dmd/*.c and dmd/root/*.c. (d.mostlyclean): Remove D_GENERATED_SRCS, d/idgen$(build_exeext), d/impcnvgen$(build_exeext). (D_INCLUDES): Include $(srcdir)/d/dmd/res. (CFLAGS-d/id.o): Remove. (CFLAGS-d/impcnvtab.o): Remove. (d/%.o): Build using DCOMPILE and DPOSTCOMPILE. Update dependencies from d/dmd/%.c to d/dmd/%.d. (d/idgen$(build_exeext)): Remove. (d/impcnvgen$(build_exeext)): Remove. (d/id.c): Remove. (d/id.h): Remove. (d/impcnvtab.c): Remove. (d/%.dmdgen.o): Remove. (D_SYSTEM_H): Remove. (d/idgen.dmdgen.o): Remove. (d/impcnvgen.dmdgen.o): Remove. * config-lang.in (boot_language): New variable. * d-attribs.cc: Include dmd/expression.h. * d-builtins.cc: Include d-frontend.h. (build_frontend_type): Update for new front-end interface. (d_eval_constant_expression): Likewise. (d_build_builtins_module): Likewise. (maybe_set_builtin_1): Likewise. (d_build_d_type_nodes): Likewise. * d-codegen.cc (d_decl_context): Likewise. (declaration_reference_p): Likewise. (declaration_type): Likewise. (parameter_reference_p): Likewise. (parameter_type): Likewise. (get_array_length): Likewise. (build_delegate_cst): Likewise. (build_typeof_null_value): Likewise. (identity_compare_p): Likewise. (lower_struct_comparison): Likewise. (build_filename_from_loc): Likewise. (build_assert_call): Remove LIBCALL_SWITCH_ERROR. (build_bounds_index_condition): Call LIBCALL_ARRAYBOUNDS_INDEXP on bounds error. (build_bounds_slice_condition): Call LIBCALL_ARRAYBOUNDS_SLICEP on bounds error. (array_bounds_check): Update for new front-end interface. (checkaction_trap_p): Handle CHECKACTION_context. (get_function_type): Update for new front-end interface. (d_build_call): Likewise. * d-compiler.cc: Remove include of dmd/scope.h. (Compiler::genCmain): Remove. (Compiler::paintAsType): Update for new front-end interface. (Compiler::onParseModule): Likewise. * d-convert.cc (convert_expr): Remove call to LIBCALL_ARRAYCAST. (convert_for_rvalue): Update for new front-end interface. (convert_for_assignment): Likewise. (convert_for_condition): Likewise. (d_array_convert): Likewise. * d-diagnostic.cc (error): Remove. (errorSupplemental): Remove. (warning): Remove. (warningSupplemental): Remove. (deprecation): Remove. (deprecationSupplemental): Remove. (message): Remove. (vtip): New. * d-frontend.cc (global): Remove. (Global::_init): Remove. (Global::startGagging): Remove. (Global::endGagging): Remove. (Global::increaseErrorCount): Remove. (Loc::Loc): Remove. (Loc::toChars): Remove. (Loc::equals): Remove. (isBuiltin): Update for new front-end interface. (eval_builtin): Likewise. (getTypeInfoType): Likewise. (inlineCopy): Remove. * d-incpath.cc: Include d-frontend.h. (add_globalpaths): Call d_gc_malloc to allocate Strings. (add_filepaths): Likewise. * d-lang.cc: Include dmd/id.h, dmd/root/file.h, d-frontend.h. Remove include of dmd/mars.h, id.h. (entrypoint_module): Remove. (entrypoint_root_module): Remove. (deps_write_string): Update for new front-end interface. (deps_write): Likewise. (d_init_options): Call rt_init. Remove setting global params that are default initialized by the front-end. (d_handle_option): Handle OPT_fcheckaction_, OPT_fdump_c___spec_, OPT_fdump_c___spec_verbose, OPT_fextern_std_, OPT_fpreview, OPT_revert, OPT_fsave_mixins_, and OPT_ftransition. (d_post_options): Propagate dip1021 and dip1000 preview flags to dip25, and flag_diagnostics_show_caret to printErrorContext. (d_add_entrypoint_module): Remove. (d_parse_file): Update for new front-end interface. (d_type_promotes_to): Likewise. (d_types_compatible_p): Likewise. * d-longdouble.cc (CTFloat::zero): Remove. (CTFloat::one): Remove. (CTFloat::minusone): Remove. (CTFloat::half): Remove. * d-system.h (POSIX): Remove. (realpath): Remove. (isalpha): Remove. (isalnum): Remove. (isdigit): Remove. (islower): Remove. (isprint): Remove. (isspace): Remove. (isupper): Remove. (isxdigit): Remove. (tolower): Remove. (_mkdir): Remove. (INT32_MAX): Remove. (INT32_MIN): Remove. (INT64_MIN): Remove. (UINT32_MAX): Remove. (UINT64_MAX): Remove. * d-target.cc: Include calls.h. (target): Remove. (define_float_constants): Remove initialization of snan. (Target::_init): Update for new front-end interface. (Target::isVectorTypeSupported): Likewise. (Target::isVectorOpSupported): Remove cases for unordered operators. (TargetCPP::typeMangle): Update for new front-end interface. (TargetCPP::parameterType): Likewise. (Target::systemLinkage): Likewise. (Target::isReturnOnStack): Likewise. (Target::isCalleeDestroyingArgs): Define. (Target::preferPassByRef): Define. * d-tree.h (d_add_entrypoint_module): Remove. * decl.cc (gcc_attribute_p): Update for new front-end interface. (apply_pragma_crt): Define. (DeclVisitor::visit(PragmaDeclaration *)): Handle pragmas crt_constructor and crt_destructor. (DeclVisitor::visit(TemplateDeclaration *)): Update for new front-end interface. (DeclVisitor::visit): Likewise. (DeclVisitor::finish_vtable): Likewise. (get_symbol_decl): Error if template has more than one nesting context. Update for new front-end interface. (make_thunk): Update for new front-end interface. (get_vtable_decl): Likewise. * expr.cc (ExprVisitor::visit): Likewise. (build_return_dtor): Likewise. * imports.cc (ImportVisitor::visit): Likewise. * intrinsics.cc: Include dmd/expression.h. Remove include of dmd/mangle.h. (maybe_set_intrinsic): Update for new front-end interface. * intrinsics.def (INTRINSIC_ROL): Update intrinsic signature. (INTRINSIC_ROR): Likewise. (INTRINSIC_ROR_TIARG): Likewise. (INTRINSIC_TOPREC): Likewise. (INTRINSIC_TOPRECL): Likewise. (INTRINSIC_TAN): Update intrinsic module and signature. (INTRINSIC_ISNAN): Likewise. (INTRINSIC_ISFINITE): Likewise. (INTRINSIC_COPYSIGN): Define intrinsic. (INTRINSIC_COPYSIGNI): Define intrinsic. (INTRINSIC_EXP): Update intrinsic module. (INTRINSIC_EXPM1): Likewise. (INTRINSIC_EXP2): Likewise. (INTRINSIC_LOG): Likewise. (INTRINSIC_LOG2): Likewise. (INTRINSIC_LOG10): Likewise. (INTRINSIC_POW): Likewise. (INTRINSIC_ROUND): Likewise. (INTRINSIC_FLOORF): Likewise. (INTRINSIC_FLOOR): Likewise. (INTRINSIC_FLOORL): Likewise. (INTRINSIC_CEILF): Likewise. (INTRINSIC_CEIL): Likewise. (INTRINSIC_CEILL): Likewise. (INTRINSIC_TRUNC): Likewise. (INTRINSIC_FMIN): Likewise. (INTRINSIC_FMAX): Likewise. (INTRINSIC_FMA): Likewise. (INTRINSIC_VA_ARG): Update intrinsic signature. (INTRINSIC_VASTART): Likewise. * lang.opt (fcheck=): Add alternate aliases for contract switches. (fcheckaction=): New option. (check_action): New Enum and EnumValue entries. (fdump-c++-spec-verbose): New option. (fdump-c++-spec=): New option. (fextern-std=): New option. (extern_stdcpp): New Enum and EnumValue entries (fpreview=): New options. (frevert=): New options. (fsave-mixins): New option. (ftransition=): Update options. * modules.cc (get_internal_fn): Replace Prot with Visibility. (build_internal_fn): Likewise. (build_dso_cdtor_fn): Likewise. (build_module_tree): Remove check for __entrypoint module. * runtime.def (P5): Define. (ARRAYBOUNDS_SLICEP): Define. (ARRAYBOUNDS_INDEXP): Define. (NEWTHROW): Define. (ADCMP2): Remove. (ARRAYCAST): Remove. (SWITCH_STRING): Remove. (SWITCH_USTRING): Remove. (SWITCH_DSTRING): Remove. (SWITCH_ERROR): Remove. * toir.cc (IRVisitor::visit): Update for new front-end interface. (IRVisitor::check_previous_goto): Remove checks for case and default statements. (IRVisitor::visit(SwitchStatement *)): Remove handling of string switch conditions. * typeinfo.cc: Include d-frontend.h. (get_typeinfo_kind): Update for new front-end interface. (make_frontend_typeinfo): Likewise. (TypeInfoVisitor::visit): Likewise. (builtin_typeinfo_p): Likewise. (get_typeinfo_decl): Likewise. (build_typeinfo): Likewise. * types.cc (valist_array_p): Likewise. (make_array_type): Likewise. (merge_aggregate_types): Likewise. (TypeVisitor::visit(TypeBasic *)): Likewise. (TypeVisitor::visit(TypeFunction *)): Likewise. (TypeVisitor::visit(TypeStruct *)): Update comment. * verstr.h: Removed. * d-frontend.h: New file. gcc/po/ChangeLog: * EXCLUDES: Remove d/dmd sources from list. gcc/testsuite/ChangeLog: * gdc.dg/Wcastresult2.d: Update test. * gdc.dg/asm1.d: Likewise. * gdc.dg/asm2.d: Likewise. * gdc.dg/asm3.d: Likewise. * gdc.dg/gdc282.d: Likewise. * gdc.dg/imports/gdc170.d: Likewise. * gdc.dg/intrinsics.d: Likewise. * gdc.dg/pr101672.d: Likewise. * gdc.dg/pr90650a.d: Likewise. * gdc.dg/pr90650b.d: Likewise. * gdc.dg/pr94777a.d: Likewise. * gdc.dg/pr95250.d: Likewise. * gdc.dg/pr96869.d: Likewise. * gdc.dg/pr98277.d: Likewise. * gdc.dg/pr98457.d: Likewise. * gdc.dg/simd1.d: Likewise. * gdc.dg/simd2a.d: Likewise. * gdc.dg/simd2b.d: Likewise. * gdc.dg/simd2c.d: Likewise. * gdc.dg/simd2d.d: Likewise. * gdc.dg/simd2e.d: Likewise. * gdc.dg/simd2f.d: Likewise. * gdc.dg/simd2g.d: Likewise. * gdc.dg/simd2h.d: Likewise. * gdc.dg/simd2i.d: Likewise. * gdc.dg/simd2j.d: Likewise. * gdc.dg/simd7951.d: Likewise. * gdc.dg/torture/gdc309.d: Likewise. * gdc.dg/torture/pr94424.d: Likewise. * gdc.dg/torture/pr94777b.d: Likewise. * lib/gdc-utils.exp (gdc-convert-args): Handle new compiler options. (gdc-convert-test): Handle CXXFLAGS, EXTRA_OBJC_SOURCES, and ARG_SETS test directives. (gdc-do-test): Only import modules in the test run directory. * gdc.dg/pr94777c.d: New test. * gdc.dg/pr96156b.d: New test. * gdc.dg/pr96157c.d: New test. * gdc.dg/simd_ctfe.d: New test. * gdc.dg/torture/simd17344.d: New test. * gdc.dg/torture/simd20052.d: New test. * gdc.dg/torture/simd6.d: New test. * gdc.dg/torture/simd7.d: New test. libphobos/ChangeLog: * libdruntime/MERGE: Merge upstream druntime e6caaab9. * libdruntime/Makefile.am (D_EXTRA_FLAGS): Build libdruntime with -fpreview=dip1000, -fpreview=fieldwise, and -fpreview=dtorfields. (ALL_DRUNTIME_SOURCES): Add DRUNTIME_DSOURCES_STDCXX. (DRUNTIME_DSOURCES): Update list of C binding modules. (DRUNTIME_DSOURCES_STDCXX): Likewise. (DRUNTIME_DSOURCES_LINUX): Likewise. (DRUNTIME_DSOURCES_OPENBSD): Likewise. (DRUNTIME_DISOURCES): Remove __entrypoint.di. * libdruntime/Makefile.in: Regenerated. * libdruntime/__entrypoint.di: Removed. * libdruntime/gcc/deh.d (_d_isbaseof): Update signature. (_d_createTrace): Likewise. (__gdc_begin_catch): Remove reference to the exception. (_d_throw): Increment reference count of thrown object before unwind. (__gdc_personality): Chain exceptions with Throwable.chainTogether. * libdruntime/gcc/emutls.d: Update imports. * libdruntime/gcc/sections/elf.d: Update imports. (DSO.moduleGroup): Update signature. * libdruntime/gcc/sections/macho.d: Update imports. (DSO.moduleGroup): Update signature. * libdruntime/gcc/sections/pecoff.d: Update imports. (DSO.moduleGroup): Update signature. * src/MERGE: Merge upstream phobos 5ab9ad256. * src/Makefile.am (D_EXTRA_DFLAGS): Add -fpreview=dip1000 and -fpreview=dtorfields flags. (PHOBOS_DSOURCES): Update list of std modules. * src/Makefile.in: Regenerate. * testsuite/lib/libphobos.exp (libphobos-dg-test): Handle assembly compile types. (dg-test): Override. (additional_prunes): Define. (libphobos-dg-prune): Filter any additional_prunes set by tests. * testsuite/libphobos.aa/test_aa.d: Update test. * testsuite/libphobos.druntime/druntime.exp (version_flags): Add -fversion=CoreUnittest. * testsuite/libphobos.druntime_shared/druntime_shared.exp (version_flags): Add -fversion=CoreUnittest -fversion=Shared. * testsuite/libphobos.exceptions/unknown_gc.d: Update test. * testsuite/libphobos.hash/test_hash.d: Update test. * testsuite/libphobos.phobos/phobos.exp (version_flags): Add -fversion=StdUnittest * testsuite/libphobos.phobos_shared/phobos_shared.exp (version_flags): Likewise. * testsuite/libphobos.shared/host.c: Update test. * testsuite/libphobos.shared/load.d: Update test. * testsuite/libphobos.shared/load_13414.d: Update test. * testsuite/libphobos.thread/fiber_guard_page.d: Update test. * testsuite/libphobos.thread/tlsgc_sections.d: Update test. * testsuite/testsuite_flags.in: Add -fpreview=dip1000 to --gdcflags. * testsuite/libphobos.shared/link_mod_collision.d: Removed. * testsuite/libphobos.shared/load_mod_collision.d: Removed. * testsuite/libphobos.betterc/betterc.exp: New test. * testsuite/libphobos.config/config.exp: New test. * testsuite/libphobos.gc/gc.exp: New test. * testsuite/libphobos.imports/imports.exp: New test. * testsuite/libphobos.lifetime/lifetime.exp: New test. * testsuite/libphobos.unittest/unittest.exp: New test.
Diffstat (limited to 'gcc/d')
-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
269 files changed, 167411 insertions, 122328 deletions
diff --git a/gcc/d/Make-lang.in b/gcc/d/Make-lang.in
index 1ed8134..4c0a032 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 0568b8d..00a71d6 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 b79cf96..04b9791 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 33347a1..ab3a950 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 e633650..403e3c7 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 512ef4b..3df40073 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 3b47902..25fd603 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 1982bd9..947b6e2 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 30fc6d43..522095f 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 0000000..3edf812
--- /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 d863929..9a65622 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 be6330f..d20370e 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 471544e..27c8d17 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 a6a9fcc..d7a0079 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 1488bce..21417dd 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 9b90ca5..328b6b8 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 9c9205f..e28a581 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 2568993..129050b 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 0000000..720d256
--- /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 0000000..64ce79a
--- /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 11b26c5..0000000
--- 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 0000000..944c9d3
--- /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 0000000..cff4b9f
--- /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 4935e6a..f8d2f45 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 458416f..0000000
--- 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 0000000..81e0d7e
--- /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 15905e4..de93a8e 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 8a727ae..0000000
--- 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 0000000..ab427e8
--- /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 52d596b..0000000
--- 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 0000000..66be73e
--- /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 0000000..b1f8d86
--- /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 0ecccf1..602d890 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 0000000..82d62a0
--- /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 0000000..d40f836
--- /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 0000000..df88bb9
--- /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 a808b8a..0000000
--- 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 0000000..ae8f65b
--- /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 174d3c1..e63c80b 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 1895d36..0000000
--- 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 0000000..1fd9005
--- /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 0000000..b99f690
--- /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 5d180f5..0000000
--- 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 0000000..ed05af6
--- /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 a4a97c9..0000000
--- 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 0000000..97adc5a
--- /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 eb09076..0000000
--- 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 0000000..d300617
--- /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 0000000..28f9ba6
--- /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 e7ef5a4..27e87b6 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 0000000..84bf5e97
--- /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 7f17460..3359171 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 6c7dc9e..0000000
--- 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 0000000..d4a8b13
--- /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 593a98d..4f26116 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 8cfeac5..0000000
--- 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 0000000..1dada60
--- /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 0000000..bb40649
--- /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 baf64c5..0000000
--- 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 0000000..0381f9a
--- /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 359739e..242dd55 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 a8e9783..0000000
--- 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 0000000..22633a8
--- /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 0000000..c8b61be
--- /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 d84ab7f..0000000
--- 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 0000000..4c70565
--- /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 3f33014..0000000
--- 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 0000000..b065251
--- /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 a9394dc..0000000
--- 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 0000000..0f40c11
--- /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 55c8142..1c56def 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 b3019aa..0000000
--- 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 0000000..07c1bbd
--- /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 b00eaa0..0000000
--- 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 0000000..54467d8
--- /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 7b63a18..0000000
--- 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 0000000..8cd4364
--- /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 ab9d88c..0000000
--- 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 0000000..541fac7
--- /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 91cbe50..0000000
--- 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 0000000..ddfee2c
--- /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 83f4c18..0000000
--- 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 0000000..71b8c7a
--- /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 472b2b9..0000000
--- 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 0000000..768eaa0
--- /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 5d2da1c..0000000
--- 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 0000000..9b4329b
--- /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 6d13ab1..a144417 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 e56f393..0000000
--- 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 0000000..638fc7e
--- /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 9862159..0000000
--- 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 0000000..80ecd36
--- /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 f0c1cf6..0000000
--- 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 0000000..3a6dff2
--- /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 ce0ce45..f43bc83 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 7a44ed2..0000000
--- 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 0000000..eac2095
--- /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 20036f2..0000000
--- 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 0000000..c3503bb
--- /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 0000000..28054d0
--- /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 269d924..0000000
--- 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 0000000..0dff754
--- /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 cac901d..0000000
--- 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 0000000..f22dfdb
--- /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 ae5ea21..76c1235 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 0000000..91a5c77
--- /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 a92ae2a..6d9587d 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 cd0382b..0000000
--- 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 0000000..d502f80
--- /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 18aa6aa..0000000
--- 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 0000000..0d6fa1e
--- /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 9413ad9..dec3713 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 5ae5fe6..0000000
--- 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 0000000..3b604af
--- /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 0000000..9579ac7
--- /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 b8e1e31..0000000
--- 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 0000000..7f0b0bb
--- /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 0000000..9b65d02
--- /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 d9d59d6..6e79474 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 0000000..debb9ca
--- /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 9397b1e..0000000
--- 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 0000000..8c31590
--- /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 6822aaf4..531d5d8 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 fc58a3c..0000000
--- 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 0000000..df8d1c9
--- /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 e3940a8..0000000
--- 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 0000000..e61fb23
--- /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 0000000..1f04dcf
--- /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 0000000..8066747
--- /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 197d288..0000000
--- 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 0000000..43a1435
--- /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 278ce9b..790d5a0 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 0740653..0000000
--- 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 d7c27ea..0000000
--- 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 0000000..db09f0c
--- /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 239cb07..0000000
--- 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 0000000..e1919a6
--- /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 07fb32a..34c5a05 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 d18e054..0000000
--- 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 0000000..45e101b
--- /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 4ba18d6..23204b8 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 c7d1dfe..0000000
--- 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 0000000..ae8bde2
--- /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 0000000..cfd619a
--- /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 36af8da..0000000
--- 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 0000000..9b70f49
--- /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 832e559..0000000
--- 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 0000000..bfd31bc
--- /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 d680001..1311fb3 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 0000000..d29bdc1
--- /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 3ea932c..0000000
--- 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 0000000..afffc2d
--- /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 544f77b..670cf4d 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 1664492..969290c 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 6cccf40..0000000
--- 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 0000000..80e4791
--- /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 3687053..cdf221f 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 12c8b49..0000000
--- 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 0000000..4bb2907
--- /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 95cfb6f..0000000
--- 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 0000000..215f259
--- /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 71dafb2..43d36e9 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 0000000..7719ccf
--- /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 3199a01..0000000
--- 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 0000000..85e371e
--- /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 f3da5a3..483e501 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 0aff8b4..0000000
--- 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 0000000..4ef55f3
--- /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 44dedd8..0000000
--- 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 0000000..3ae3061
--- /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 e1f1321..0000000
--- 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 0000000..21042dd
--- /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 0000000..d3e3086
--- /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 0000000..5d7f3fc
--- /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 0000000..3f12b17
--- /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 a9a31af..0000000
--- 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 0000000..7ae0db8
--- /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 0000000..539b940
--- /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 992a117..0000000
--- 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 0000000..92b58ba
--- /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 0000000..ed925c8
--- /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 f7cb0c7..f573dca 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 0000000..f912961
--- /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 004c43c..e773711 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 af7b56f..0000000
--- 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 0000000..9b98742
--- /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 0a829f3..1221b82 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 9fd176e..88f2095 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 314b5b5..0000000
--- 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 0000000..ef6056c
--- /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 5135818..ee0d51e 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 0c5138b..0000000
--- 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 0000000..1e4ccb5
--- /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 52cd963..9f773b5 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 0000000..f484819
--- /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 0000000..a845c97
--- /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 d5e3b2b..fb367bc 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 7fbbfe5..0000000
--- 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 0000000..e756917
--- /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 186fbb7..b635373 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 0000000..1bafa20
--- /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 94651cd..08cf66c 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 0000000..50689fe
--- /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 768b75d..0000000
--- 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 0000000..198f3d0
--- /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 1f603b8..04d9e3f 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 d998d95..667ce67 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 7fee0d7..0000000
--- 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 0000000..854ec1a
--- /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 3957c11..0000000
--- 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 0000000..543005b
--- /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 0000000..73fe562
--- /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 fe14807..0000000
--- 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 0000000..42b26e2
--- /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 7d83dd1..0000000
--- 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 0000000..35734b2
--- /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 ce08926..0000000
--- 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 0000000..018b046
--- /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 ea3061b..4d8c0bb 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 194a3fb..0000000
--- 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 0000000..7b2fa5e
--- /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 6bd9a6d..0000000
--- 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 0000000..ac2b239
--- /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 661bd43..0000000
--- 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 0000000..d238150
--- /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 1f8e512..0000000
--- 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 0000000..b49c903
--- /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 c64e51a..7825762 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 0000000..2f6605c
--- /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 24e534e..0000000
--- 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 0000000..f067c91
--- /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 c2d0f5b..0000000
--- 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 0000000..984dc42
--- /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 6d43cb7..8f88080 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 ef0a35f..0000000
--- 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 0000000..2f27414
--- /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 0000000..bb13d7c
--- /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 0000000..d5b3de2
--- /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 f8f977c..83281a6 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 086ec72..08ce9ac 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 d3e9b23..0000000
--- 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 0000000..620492f
--- /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 eb06bee..0000000
--- 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 0000000..7680fb8
--- /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 f79d841..0fd6634 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 5a9f58b..0000000
--- 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 0000000..8f968ed
--- /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 0000000..0479d5a
--- /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 31e93c2..0000000
--- 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 0000000..ace4e42
--- /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 0000000..d8160f0
--- /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 f6b5435..0000000
--- 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 0000000..1125c21
--- /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 c9e6322..0000000
--- 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 0000000..600521f
--- /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 33811ee..6c5e2f0 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 0000000..8b9c012
--- /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 09ba202..e61f16d 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 ea21bd5..3168056 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 2288843..740a020 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 539dc0c..b14b0ca 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 f5af2a5..1f350f3 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 ded218f..f7f90fb 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 8786344..06eb5ae 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 0296233..fdff6db 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 1c9da10..55d63f8 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 fd8c746..065f6b3 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 fc8a133..db500ee 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 0dd41ee..0000000
--- a/gcc/d/verstr.h
+++ /dev/null
@@ -1 +0,0 @@
-"2.076.1"