]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
d: Merge upstream dmd, druntime 24a41073c2, phobos 24a41073c2.
authorIain Buclaw <ibuclaw@gdcproject.org>
Sun, 1 Feb 2026 15:02:20 +0000 (16:02 +0100)
committerIain Buclaw <ibuclaw@gdcproject.org>
Tue, 3 Feb 2026 22:09:53 +0000 (23:09 +0100)
D front-end changes:

- Import dmd v2.112.0.
- Bitfields feature is now enabled by default.
- The compiler now accepts `-std=d2024' and `-std=d202y'.
- An error is now issued for dangling `else' statements.
- `finally' statements are no longer rewritten to a sequence if
  no `Exception' was thrown.
- Some forms of `printf' calls are now treated as `@safe'.
- Implicit integer conversions in `int op= float` assignments
  has been deprecated.

D runtime changes:

- Import druntime v2.112.0.
- Added `filterCaughtThrowable' in `core.thread.ThreadBase'.

Phobos changes:

- Import phobos v2.112.0.

gcc/d/ChangeLog:

* dmd/VERSION: Bump version to v2.112.0.
* dmd/MERGE: Merge upstream dmd 24a41073c2.
* d-attribs.cc (build_attributes): Update for new front-end interface.
* d-builtins.cc (build_frontend_type): Likewise.
(matches_builtin_type): Likewise.
(d_init_versions): Predefine D_Profile when compiling with profile
enabled.
* d-codegen.cc (get_array_length): Update for new front-end interface.
(lower_struct_comparison): Likewise.
(build_array_from_val): Likewise.
(get_function_type): Likewise.
(get_frameinfo): Likewise.
* d-compiler.cc (Compiler::paintAsType): Likewise.
* d-convert.cc (convert_expr): Likewise.
(convert_for_rvalue): Likewise.
(convert_for_assignment): Likewise.
(d_array_convert): Likewise.
* d-diagnostic.cc (verrorReport): Rename to ...
(vreportDiagnostic): ... this.
(verrorReportSupplemental): Rename to ...
(vsupplementalDiagnostic): ... this.
* d-lang.cc (d_handle_option): Handle -std=d2024 and -std=d202y.
(d_parse_file): Update for new front-end interface.
* d-target.cc (Target::fieldalign): Likewise.
(Target::isVectorTypeSupported): Likewise.
(Target::isVectorOpSupported): Likewise.
* decl.cc (get_symbol_decl): Likewise.
(DeclVisitor::visit): Likewise.
(DeclVisitor::visit (FuncDeclaration *)): Do NRVO on `__result' decl.
* expr.cc (needs_postblit): Remove.
(needs_dtor): Remove.
(lvalue_p): Remove.
(ExprVisitor::visit): Update for new front-end interface.
(ExprVisitor::visit (AssignExp *)): Update for front-end lowering
expression using templates.
* imports.cc (ImportVisitor::visit): Update for new front-end
interface.
* intrinsics.def (INTRINSIC_VA_ARG): Update signature.
(INTRINSIC_C_VA_ARG): Update signature.
(INTRINSIC_VASTART): Update signature.
* lang.opt: Add -std=d2024 and -std=d202y.
* toir.cc (IRVisitor::visit): Update for new front-end interface.
* typeinfo.cc (TypeInfoVisitor::visit): Likewise.
(TypeInfoVisitor::visit (TypeInfoStructDeclaration *)): Ensure
semantic is ran on all TypeInfo members.
(base_vtable_offset): Update for new front-end interface.
* types.cc (TypeVisitor::visit): Likewise.

libphobos/ChangeLog:

* libdruntime/MERGE: Merge upstream druntime 24a41073c2.
* libdruntime/__importc_builtins.di: Reimplement.
* src/MERGE: Merge upstream phobos 808314eb2.
* testsuite/libphobos.aa/test_aa.d: Adjust test.
* testsuite/libphobos.gc/forkgc2.d: Removed.
* testsuite/libphobos.thread/filterthrownglobal.d: New test.
* testsuite/libphobos.thread/filterthrownmethod.d: New test.

gcc/testsuite/ChangeLog:

* gdc.dg/pr90601.d: Adjust test.
* lib/gdc-utils.exp: Handle new compiler options.

367 files changed:
gcc/d/d-attribs.cc
gcc/d/d-builtins.cc
gcc/d/d-codegen.cc
gcc/d/d-compiler.cc
gcc/d/d-convert.cc
gcc/d/d-diagnostic.cc
gcc/d/d-lang.cc
gcc/d/d-target.cc
gcc/d/decl.cc
gcc/d/dmd/MERGE
gcc/d/dmd/README.md
gcc/d/dmd/VERSION
gcc/d/dmd/aggregate.d
gcc/d/dmd/aggregate.h
gcc/d/dmd/arrayop.d
gcc/d/dmd/astcodegen.d
gcc/d/dmd/astenums.d
gcc/d/dmd/attrib.d
gcc/d/dmd/blockexit.d
gcc/d/dmd/builtin.d
gcc/d/dmd/canthrow.d
gcc/d/dmd/chkformat.d
gcc/d/dmd/common/bitfields.d
gcc/d/dmd/common/file.d
gcc/d/dmd/common/smallbuffer.d
gcc/d/dmd/cond.d
gcc/d/dmd/constfold.d
gcc/d/dmd/cparse.d
gcc/d/dmd/ctfeexpr.d
gcc/d/dmd/cxxfrontend.d
gcc/d/dmd/dcast.d
gcc/d/dmd/dclass.d
gcc/d/dmd/declaration.d
gcc/d/dmd/declaration.h
gcc/d/dmd/delegatize.d
gcc/d/dmd/denum.d
gcc/d/dmd/dimport.d
gcc/d/dmd/dinterpret.d
gcc/d/dmd/dmodule.d
gcc/d/dmd/doc.d
gcc/d/dmd/dscope.d
gcc/d/dmd/dstruct.d
gcc/d/dmd/dsymbol.d
gcc/d/dmd/dsymbol.h
gcc/d/dmd/dsymbolsem.d
gcc/d/dmd/dtemplate.d
gcc/d/dmd/dtoh.d
gcc/d/dmd/dversion.d
gcc/d/dmd/enum.h
gcc/d/dmd/enumsem.d
gcc/d/dmd/errors.d
gcc/d/dmd/escape.d
gcc/d/dmd/expression.d
gcc/d/dmd/expression.h
gcc/d/dmd/expressionsem.d
gcc/d/dmd/file_manager.d
gcc/d/dmd/func.d
gcc/d/dmd/funcsem.d
gcc/d/dmd/globals.d
gcc/d/dmd/globals.h
gcc/d/dmd/hdrgen.d
gcc/d/dmd/hdrgen.h
gcc/d/dmd/iasm/gcc.d
gcc/d/dmd/id.d
gcc/d/dmd/identifier.d
gcc/d/dmd/identifier.h
gcc/d/dmd/imphint.d
gcc/d/dmd/import.h
gcc/d/dmd/importc.d
gcc/d/dmd/initsem.d
gcc/d/dmd/intrange.d
gcc/d/dmd/json.d
gcc/d/dmd/lambdacomp.d
gcc/d/dmd/lexer.d
gcc/d/dmd/mangle/cpp.d
gcc/d/dmd/mangle/package.d
gcc/d/dmd/module.h
gcc/d/dmd/mtype.d
gcc/d/dmd/mtype.h
gcc/d/dmd/nogc.d
gcc/d/dmd/objc.d
gcc/d/dmd/opover.d
gcc/d/dmd/optimize.d
gcc/d/dmd/parse.d
gcc/d/dmd/pragmasem.d
gcc/d/dmd/printast.d
gcc/d/dmd/root/array.d
gcc/d/dmd/root/bitarray.d
gcc/d/dmd/root/file.d
gcc/d/dmd/root/filename.d
gcc/d/dmd/root/hash.d
gcc/d/dmd/root/region.d
gcc/d/dmd/root/rmem.d
gcc/d/dmd/root/string.d
gcc/d/dmd/root/stringtable.d
gcc/d/dmd/rootobject.d
gcc/d/dmd/rootobject.h
gcc/d/dmd/safe.d
gcc/d/dmd/scope.h
gcc/d/dmd/semantic2.d
gcc/d/dmd/semantic3.d
gcc/d/dmd/statementsem.d
gcc/d/dmd/staticassert.d
gcc/d/dmd/targetcompiler.d [new file with mode: 0644]
gcc/d/dmd/template.h
gcc/d/dmd/templateparamsem.d
gcc/d/dmd/templatesem.d
gcc/d/dmd/timetrace.d
gcc/d/dmd/tokens.d
gcc/d/dmd/tokens.h
gcc/d/dmd/traits.d
gcc/d/dmd/typesem.d
gcc/d/dmd/typinf.d
gcc/d/expr.cc
gcc/d/imports.cc
gcc/d/intrinsics.def
gcc/d/lang.opt
gcc/d/toir.cc
gcc/d/typeinfo.cc
gcc/d/types.cc
gcc/testsuite/gdc.dg/pr90601.d
gcc/testsuite/gdc.test/compilable/autoreflambda.d [new file with mode: 0644]
gcc/testsuite/gdc.test/compilable/dbitfield.d
gcc/testsuite/gdc.test/compilable/dotvar_ref_return.d [new file with mode: 0644]
gcc/testsuite/gdc.test/compilable/extra-files/ucn_vars.i [new file with mode: 0644]
gcc/testsuite/gdc.test/compilable/fix21894.d [new file with mode: 0644]
gcc/testsuite/gdc.test/compilable/fix21945.d [new file with mode: 0644]
gcc/testsuite/gdc.test/compilable/fix22291.d
gcc/testsuite/gdc.test/compilable/issue19163.d [new file with mode: 0644]
gcc/testsuite/gdc.test/compilable/issue21997.d [new file with mode: 0644]
gcc/testsuite/gdc.test/compilable/mangle1.d [new file with mode: 0644]
gcc/testsuite/gdc.test/compilable/pragmamangle1.d [new file with mode: 0644]
gcc/testsuite/gdc.test/compilable/pragmamangle2.d [new file with mode: 0644]
gcc/testsuite/gdc.test/compilable/profilegc_typename.d [new file with mode: 0644]
gcc/testsuite/gdc.test/compilable/shortened_methods.d
gcc/testsuite/gdc.test/compilable/test21299d.d
gcc/testsuite/gdc.test/compilable/test21495.d [new file with mode: 0644]
gcc/testsuite/gdc.test/compilable/test21504a.d [new file with mode: 0644]
gcc/testsuite/gdc.test/compilable/test21504b.d [new file with mode: 0644]
gcc/testsuite/gdc.test/compilable/test21835.d [new file with mode: 0644]
gcc/testsuite/gdc.test/compilable/test22916.d
gcc/testsuite/gdc.test/compilable/testexpression.d
gcc/testsuite/gdc.test/compilable/ucn.d [new file with mode: 0644]
gcc/testsuite/gdc.test/compilable/warn3882.d
gcc/testsuite/gdc.test/fail_compilation/b3841.d
gcc/testsuite/gdc.test/fail_compilation/betterc.d
gcc/testsuite/gdc.test/fail_compilation/biterrors.d
gcc/testsuite/gdc.test/fail_compilation/biterrors2.d
gcc/testsuite/gdc.test/fail_compilation/biterrors3.d
gcc/testsuite/gdc.test/fail_compilation/biterrors4.d
gcc/testsuite/gdc.test/fail_compilation/biterrors5.d
gcc/testsuite/gdc.test/fail_compilation/bitintro.d
gcc/testsuite/gdc.test/fail_compilation/bug15613.d
gcc/testsuite/gdc.test/fail_compilation/bug8150b.d
gcc/testsuite/gdc.test/fail_compilation/cast_system.d [new file with mode: 0644]
gcc/testsuite/gdc.test/fail_compilation/covariant_override.d
gcc/testsuite/gdc.test/fail_compilation/dbitfields.d
gcc/testsuite/gdc.test/fail_compilation/diag23295.d
gcc/testsuite/gdc.test/fail_compilation/diag9191.d
gcc/testsuite/gdc.test/fail_compilation/edition_switch.d [new file with mode: 0644]
gcc/testsuite/gdc.test/fail_compilation/fail12901.d [deleted file]
gcc/testsuite/gdc.test/fail_compilation/fail18093.d
gcc/testsuite/gdc.test/fail_compilation/fail19911a.d
gcc/testsuite/gdc.test/fail_compilation/fail222.d
gcc/testsuite/gdc.test/fail_compilation/fail22351.d
gcc/testsuite/gdc.test/fail_compilation/fail24208.d
gcc/testsuite/gdc.test/fail_compilation/fail246.d
gcc/testsuite/gdc.test/fail_compilation/fail262.d
gcc/testsuite/gdc.test/fail_compilation/fail265.d
gcc/testsuite/gdc.test/fail_compilation/fail4375a.d
gcc/testsuite/gdc.test/fail_compilation/fail4375b.d
gcc/testsuite/gdc.test/fail_compilation/fail4375c.d
gcc/testsuite/gdc.test/fail_compilation/fail4375d.d
gcc/testsuite/gdc.test/fail_compilation/fail4375e.d
gcc/testsuite/gdc.test/fail_compilation/fail4375f.d
gcc/testsuite/gdc.test/fail_compilation/fail4375g.d
gcc/testsuite/gdc.test/fail_compilation/fail4375h.d
gcc/testsuite/gdc.test/fail_compilation/fail4375i.d
gcc/testsuite/gdc.test/fail_compilation/fail4375j.d
gcc/testsuite/gdc.test/fail_compilation/fail4375k.d
gcc/testsuite/gdc.test/fail_compilation/fail4375l.d
gcc/testsuite/gdc.test/fail_compilation/fail4375m.d
gcc/testsuite/gdc.test/fail_compilation/fail4375o.d
gcc/testsuite/gdc.test/fail_compilation/fail4375p.d
gcc/testsuite/gdc.test/fail_compilation/fail4375q.d
gcc/testsuite/gdc.test/fail_compilation/fail4375r.d
gcc/testsuite/gdc.test/fail_compilation/fail4375s.d
gcc/testsuite/gdc.test/fail_compilation/fail4375t.d
gcc/testsuite/gdc.test/fail_compilation/fail4375u.d
gcc/testsuite/gdc.test/fail_compilation/fail4375v.d
gcc/testsuite/gdc.test/fail_compilation/fail4375w.d
gcc/testsuite/gdc.test/fail_compilation/fail4375x.d
gcc/testsuite/gdc.test/fail_compilation/fail4375y.d
gcc/testsuite/gdc.test/fail_compilation/fail4544.d
gcc/testsuite/gdc.test/fail_compilation/fail50.d
gcc/testsuite/gdc.test/fail_compilation/fail6497.d
gcc/testsuite/gdc.test/fail_compilation/fail8631.d
gcc/testsuite/gdc.test/fail_compilation/fail_contracts5.d [new file with mode: 0644]
gcc/testsuite/gdc.test/fail_compilation/fail_pretty_errors.d
gcc/testsuite/gdc.test/fail_compilation/failexpression1.d [new file with mode: 0644]
gcc/testsuite/gdc.test/fail_compilation/failexpression2.d [new file with mode: 0644]
gcc/testsuite/gdc.test/fail_compilation/failexpression3.d [new file with mode: 0644]
gcc/testsuite/gdc.test/fail_compilation/failexpression4.d [new file with mode: 0644]
gcc/testsuite/gdc.test/fail_compilation/ice13788.d
gcc/testsuite/gdc.test/fail_compilation/ice21095.d
gcc/testsuite/gdc.test/fail_compilation/ice8795.d
gcc/testsuite/gdc.test/fail_compilation/ice9273a.d
gcc/testsuite/gdc.test/fail_compilation/ice9284.d
gcc/testsuite/gdc.test/fail_compilation/issue21203.d
gcc/testsuite/gdc.test/fail_compilation/issue22682.d
gcc/testsuite/gdc.test/fail_compilation/lexer23465.d
gcc/testsuite/gdc.test/fail_compilation/mangle1.d
gcc/testsuite/gdc.test/fail_compilation/mangle2.d
gcc/testsuite/gdc.test/fail_compilation/opApply_return.d [new file with mode: 0644]
gcc/testsuite/gdc.test/fail_compilation/placenew.d
gcc/testsuite/gdc.test/fail_compilation/pragmamangle1.d [new file with mode: 0644]
gcc/testsuite/gdc.test/fail_compilation/pragmamangle2.d [new file with mode: 0644]
gcc/testsuite/gdc.test/fail_compilation/reserved_version.d
gcc/testsuite/gdc.test/fail_compilation/reserved_version_switch.d
gcc/testsuite/gdc.test/fail_compilation/retref2.d
gcc/testsuite/gdc.test/fail_compilation/retscope.d
gcc/testsuite/gdc.test/fail_compilation/retscope2.d
gcc/testsuite/gdc.test/fail_compilation/retscope6.d
gcc/testsuite/gdc.test/fail_compilation/safeprintf.d [new file with mode: 0644]
gcc/testsuite/gdc.test/fail_compilation/scope_infer_diagnostic.d [new file with mode: 0644]
gcc/testsuite/gdc.test/fail_compilation/systemvariables_void_init.d
gcc/testsuite/gdc.test/fail_compilation/test13867.d
gcc/testsuite/gdc.test/fail_compilation/test16188.d
gcc/testsuite/gdc.test/fail_compilation/test17284.d
gcc/testsuite/gdc.test/fail_compilation/test17422.d
gcc/testsuite/gdc.test/fail_compilation/test17423.d
gcc/testsuite/gdc.test/fail_compilation/test18282.d
gcc/testsuite/gdc.test/fail_compilation/test19097.d
gcc/testsuite/gdc.test/fail_compilation/test20245.d
gcc/testsuite/gdc.test/fail_compilation/test20489.d
gcc/testsuite/gdc.test/fail_compilation/test20863a.d [new file with mode: 0644]
gcc/testsuite/gdc.test/fail_compilation/test20863b.d [new file with mode: 0644]
gcc/testsuite/gdc.test/fail_compilation/test21246.d
gcc/testsuite/gdc.test/fail_compilation/test21634.d [new file with mode: 0644]
gcc/testsuite/gdc.test/fail_compilation/test23073.d
gcc/testsuite/gdc.test/fail_compilation/test23159.d
gcc/testsuite/gdc.test/fail_compilation/test23982.d
gcc/testsuite/gdc.test/fail_compilation/test24084.d
gcc/testsuite/gdc.test/fail_compilation/test24353.d
gcc/testsuite/gdc.test/fail_compilation/testrvaluecpctor.d
gcc/testsuite/gdc.test/runnable/bit.d
gcc/testsuite/gdc.test/runnable/closure.d
gcc/testsuite/gdc.test/runnable/dbitfields.d
gcc/testsuite/gdc.test/runnable/dbitfieldsdm.d [new file with mode: 0644]
gcc/testsuite/gdc.test/runnable/imports/pragmainline_a.d [new file with mode: 0644]
gcc/testsuite/gdc.test/runnable/imports/test10442a.d [new file with mode: 0644]
gcc/testsuite/gdc.test/runnable/imports/你好.d [new file with mode: 0644]
gcc/testsuite/gdc.test/runnable/inline4.d [new file with mode: 0644]
gcc/testsuite/gdc.test/runnable/issue22069.d [new file with mode: 0644]
gcc/testsuite/gdc.test/runnable/mars1.d
gcc/testsuite/gdc.test/runnable/nested.d
gcc/testsuite/gdc.test/runnable/opcolon.d [new file with mode: 0644]
gcc/testsuite/gdc.test/runnable/opover3.d
gcc/testsuite/gdc.test/runnable/overload.d
gcc/testsuite/gdc.test/runnable/pragmainline.d [new file with mode: 0644]
gcc/testsuite/gdc.test/runnable/real_to_float.d [new file with mode: 0644]
gcc/testsuite/gdc.test/runnable/rvalue1.d
gcc/testsuite/gdc.test/runnable/staticaa.d
gcc/testsuite/gdc.test/runnable/structlit_rvalue.d [new file with mode: 0644]
gcc/testsuite/gdc.test/runnable/template9.d
gcc/testsuite/gdc.test/runnable/test10442.d [new file with mode: 0644]
gcc/testsuite/gdc.test/runnable/test20275.d [new file with mode: 0644]
gcc/testsuite/gdc.test/runnable/test20301.d [new file with mode: 0644]
gcc/testsuite/gdc.test/runnable/test21478a.d [new file with mode: 0644]
gcc/testsuite/gdc.test/runnable/test21478b.d [new file with mode: 0644]
gcc/testsuite/gdc.test/runnable/test21757.d [new file with mode: 0644]
gcc/testsuite/gdc.test/runnable/test22079.d [new file with mode: 0644]
gcc/testsuite/gdc.test/runnable/test28.d
gcc/testsuite/gdc.test/runnable/test42.d
gcc/testsuite/gdc.test/runnable/test_delegate_init_in_struct.d [new file with mode: 0644]
gcc/testsuite/gdc.test/runnable/test_real_array_param.d [new file with mode: 0644]
gcc/testsuite/gdc.test/runnable/testaa2.d
gcc/testsuite/gdc.test/runnable/testaa3.d
gcc/testsuite/gdc.test/runnable/testassign.d
gcc/testsuite/gdc.test/runnable/traits_initSymbol.d
gcc/testsuite/gdc.test/runnable/uda.d
gcc/testsuite/lib/gdc-utils.exp
libphobos/libdruntime/MERGE
libphobos/libdruntime/__importc_builtins.di
libphobos/libdruntime/core/checkedint.d
libphobos/libdruntime/core/exception.d
libphobos/libdruntime/core/gc/gcinterface.d
libphobos/libdruntime/core/int128.d
libphobos/libdruntime/core/internal/array/appending.d
libphobos/libdruntime/core/internal/array/capacity.d
libphobos/libdruntime/core/internal/array/concatenation.d
libphobos/libdruntime/core/internal/array/construction.d
libphobos/libdruntime/core/internal/array/equality.d
libphobos/libdruntime/core/internal/array/utils.d
libphobos/libdruntime/core/internal/cast_.d
libphobos/libdruntime/core/internal/container/array.d
libphobos/libdruntime/core/internal/convert.d
libphobos/libdruntime/core/internal/gc/bits.d
libphobos/libdruntime/core/internal/gc/impl/conservative/gc.d
libphobos/libdruntime/core/internal/gc/os.d
libphobos/libdruntime/core/internal/hash.d
libphobos/libdruntime/core/internal/newaa.d
libphobos/libdruntime/core/internal/qsort.d
libphobos/libdruntime/core/internal/vararg/gnu.d [new file with mode: 0644]
libphobos/libdruntime/core/lifetime.d
libphobos/libdruntime/core/stdc/locale.d
libphobos/libdruntime/core/stdc/stdarg.d
libphobos/libdruntime/core/stdc/stdatomic.d
libphobos/libdruntime/core/stdc/stdio.d
libphobos/libdruntime/core/stdc/wchar_.d
libphobos/libdruntime/core/sys/freebsd/config.d
libphobos/libdruntime/core/sys/linux/hdlc/ioctl.d [new file with mode: 0644]
libphobos/libdruntime/core/sys/linux/net/if_.d [new file with mode: 0644]
libphobos/libdruntime/core/sys/linux/string.d
libphobos/libdruntime/core/sys/linux/sys/inotify.d
libphobos/libdruntime/core/sys/openbsd/unistd.d
libphobos/libdruntime/core/sys/posix/config.d
libphobos/libdruntime/core/sys/posix/fcntl.d
libphobos/libdruntime/core/sys/posix/pthread.d
libphobos/libdruntime/core/sys/posix/stdc/time.d
libphobos/libdruntime/core/sys/posix/stdio.d
libphobos/libdruntime/core/sys/posix/sys/shm.d
libphobos/libdruntime/core/sys/posix/sys/stat.d
libphobos/libdruntime/core/sys/posix/sys/types.d
libphobos/libdruntime/core/sys/posix/time.d
libphobos/libdruntime/core/sys/posix/ucontext.d
libphobos/libdruntime/core/sys/posix/unistd.d
libphobos/libdruntime/core/sys/solaris/sys/procfs.d [new file with mode: 0644]
libphobos/libdruntime/core/sys/solaris/sys/regset.d [new file with mode: 0644]
libphobos/libdruntime/core/sys/solaris/thread.d [new file with mode: 0644]
libphobos/libdruntime/core/sys/windows/stdc/time.d
libphobos/libdruntime/core/thread/fiber/package.d
libphobos/libdruntime/core/thread/osthread.d
libphobos/libdruntime/core/thread/threadbase.d
libphobos/libdruntime/core/vararg.d
libphobos/libdruntime/object.d
libphobos/libdruntime/rt/util/typeinfo.d
libphobos/src/MERGE
libphobos/src/std/algorithm/comparison.d
libphobos/src/std/algorithm/iteration.d
libphobos/src/std/algorithm/setops.d
libphobos/src/std/array.d
libphobos/src/std/ascii.d
libphobos/src/std/concurrency.d
libphobos/src/std/container/package.d
libphobos/src/std/container/rbtree.d
libphobos/src/std/container/slist.d
libphobos/src/std/conv.d
libphobos/src/std/experimental/allocator/building_blocks/allocator_list.d
libphobos/src/std/format/internal/write.d
libphobos/src/std/internal/math/gammafunction.d
libphobos/src/std/math/operations.d
libphobos/src/std/mathspecial.d
libphobos/src/std/mmfile.d
libphobos/src/std/process.d
libphobos/src/std/random.d
libphobos/src/std/range/package.d
libphobos/src/std/socket.d
libphobos/src/std/sumtype.d
libphobos/src/std/system.d
libphobos/src/std/traits.d
libphobos/src/std/typecons.d
libphobos/src/std/uuid.d
libphobos/testsuite/libphobos.aa/test_aa.d
libphobos/testsuite/libphobos.gc/forkgc2.d [deleted file]
libphobos/testsuite/libphobos.thread/filterthrownglobal.d [new file with mode: 0644]
libphobos/testsuite/libphobos.thread/filterthrownmethod.d [new file with mode: 0644]

index 9eb17d1926af4d04548e194dec0b293fe5ea15d4..1e7493e4d03730091bf09b9e1369e50db96e2f59 100644 (file)
@@ -376,7 +376,7 @@ build_attributes (Expressions *eattrs)
          continue;
        }
 
-      StringExp *se = e0->toStringExp ();
+      StringExp *se = dmd::toStringExp (e0);
       gcc_assert (se->sz == 1);
 
       /* Empty string attribute, just ignore it.  */
index e35b8a34de384abd32e59a6cadbeed997f1169a5..dce03e9f53b976fca0d7b9bd7e89a86d28a3e2d4 100644 (file)
@@ -139,7 +139,7 @@ build_frontend_type (tree type)
          dtype = Type::basic[i];
 
          /* Search for type matching size and signedness.  */
-         if (unsignedp != dtype->isUnsigned ()
+         if (unsignedp != dmd::isUnsigned (dtype)
              || size != dmd::size (dtype))
            continue;
 
@@ -515,6 +515,8 @@ d_init_versions (void)
 
   if (optimize)
     VersionCondition::addPredefinedGlobalIdent ("D_Optimized");
+  if (profile_flag)
+    VersionCondition::addPredefinedGlobalIdent ("D_Profile");
 
   VersionCondition::addPredefinedGlobalIdent ("all");
 
@@ -715,7 +717,7 @@ matches_builtin_type (Type *t1, Type *t2)
       && dmd::implicitConvTo (tb1, tb2) != MATCH::nomatch)
     return true;
 
-  if (tb1->isIntegral () == tb2->isIntegral ()
+  if (dmd::isIntegral (tb1) == dmd::isIntegral (tb2)
       && dmd::size (tb1) == dmd::size (tb2))
     return true;
 
index 02023496cfa1dd9af5aa9d89132eacce1f215c9a..04fc63bda75883446727f04f5ead192fce489ff8 100644 (file)
@@ -335,7 +335,7 @@ get_array_length (tree exp, Type *type)
   switch (tb->ty)
     {
     case TY::Tsarray:
-      return size_int (tb->isTypeSArray ()->dim->toUInteger ());
+      return size_int (dmd::toUInteger (tb->isTypeSArray ()->dim));
 
     case TY::Tarray:
       return d_array_length (exp);
@@ -1037,15 +1037,15 @@ 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 != TY::Tvector && type->isIntegral ())
+      else if (type->ty != TY::Tvector && dmd::isIntegral (type))
        {
          /* Integer comparison, no special handling required.  */
          tcmp = build_boolop (code, t1ref, t2ref);
        }
-      else if (type->ty != TY::Tvector && type->isFloating ())
+      else if (type->ty != TY::Tvector && dmd::isFloating (type))
        {
          /* Floating-point comparison, don't compare padding in type.  */
-         if (!type->isComplex ())
+         if (!dmd::isComplex (type))
            tcmp = build_float_identity (code, t1ref, t2ref);
          else
            {
@@ -1879,7 +1879,7 @@ build_array_from_val (Type *type, tree val)
   if (TREE_CODE (etype) == ARRAY_TYPE && TREE_TYPE (val) != etype)
     val = build_array_from_val (type->nextOf (), val);
 
-  size_t dims = type->isTypeSArray ()->dim->toInteger ();
+  size_t dims = dmd::toInteger (type->isTypeSArray ()->dim);
   vec <constructor_elt, va_gc> *elms = NULL;
   vec_safe_reserve (elms, dims);
 
@@ -2158,7 +2158,7 @@ checkaction_trap_p (void)
 }
 
 /* Returns the TypeFunction class for Type T.
-   Assumes T is already ->toBasetype().  */
+   Assumes T is already the main variant type (toBasetype).  */
 
 TypeFunction *
 get_function_type (Type *t)
@@ -2986,7 +2986,7 @@ get_frameinfo (FuncDeclaration *fd)
   DECL_LANG_FRAMEINFO (fds) = ffi;
 
   const bool requiresClosure = fd->requiresClosure;
-  if (fd->needsClosure ())
+  if (dmd::needsClosure (fd))
     {
       /* This can shift due to templates being expanded that access alias
          symbols, give it a decent error for now.  */
index dba1771ef50036bd38bc5b7bd7be0ded137dd1c7..29a6c98f72cd9eebf0decd4dc35436e99265c18f 100644 (file)
@@ -47,10 +47,10 @@ Compiler::paintAsType (UnionExp *, Expression *expr, Type *type)
 
   Type *tb = type->toBasetype ();
 
-  if (expr->type->isIntegral ())
-    cst = build_integer_cst (expr->toInteger (), build_ctype (expr->type));
-  else if (expr->type->isFloating ())
-    cst = build_float_cst (expr->toReal (), expr->type);
+  if (dmd::isIntegral (expr->type))
+    cst = build_integer_cst (dmd::toInteger (expr), build_ctype (expr->type));
+  else if (dmd::isFloating (expr->type))
+    cst = build_float_cst (dmd::toReal (expr), expr->type);
   else if (expr->op == EXP::arrayLiteral)
     {
       /* Build array as VECTOR_CST, assumes EXPR is constant.  */
@@ -61,15 +61,15 @@ Compiler::paintAsType (UnionExp *, Expression *expr, Type *type)
       for (size_t i = 0; i < elements->length; i++)
        {
          Expression *e = (*elements)[i];
-         if (e->type->isIntegral ())
+         if (dmd::isIntegral (e->type))
            {
-             tree value = build_integer_cst (e->toInteger (),
+             tree value = build_integer_cst (dmd::toInteger (e),
                                              build_ctype (e->type));
              CONSTRUCTOR_APPEND_ELT (elms, size_int (i), value);
            }
-         else if (e->type->isFloating ())
+         else if (dmd::isFloating (e->type))
            {
-             tree value = build_float_cst (e->toReal (), e->type);
+             tree value = build_float_cst (dmd::toReal (e), e->type);
              CONSTRUCTOR_APPEND_ELT (elms, size_int (i), value);
            }
          else
@@ -77,7 +77,7 @@ Compiler::paintAsType (UnionExp *, Expression *expr, Type *type)
        }
 
       /* Build vector type.  */
-      int nunits = expr->type->isTypeSArray ()->dim->toUInteger ();
+      int nunits = dmd::toUInteger (expr->type->isTypeSArray ()->dim);
       Type *telem = expr->type->nextOf ();
       tree vectype = build_vector_type (build_ctype (telem), nunits);
 
@@ -93,7 +93,7 @@ Compiler::paintAsType (UnionExp *, Expression *expr, Type *type)
     {
       /* Interpret value as a vector of the same size,
         then return the array literal.  */
-      int nunits = type->isTypeSArray ()->dim->toUInteger ();
+      int nunits = dmd::toUInteger (type->isTypeSArray ()->dim);
       Type *elem = type->nextOf ();
       tree vectype = build_vector_type (build_ctype (elem), nunits);
 
index 30874e72c0dcf4606112883c29be71ad15aa2967..6000556b99c19d8c97b293c1e05c66dadf46d734 100644 (file)
@@ -459,7 +459,7 @@ convert_expr (tree exp, Type *etype, Type *totype)
        }
       else if (tbtype->ty == TY::Tarray)
        {
-         dinteger_t dim = ebtype->isTypeSArray ()->dim->toInteger ();
+         dinteger_t dim = dmd::toInteger (ebtype->isTypeSArray ()->dim);
          dinteger_t esize = dmd::size (ebtype->nextOf ());
          dinteger_t tsize = dmd::size (tbtype->nextOf ());
 
@@ -548,7 +548,7 @@ convert_expr (tree exp, Type *etype, Type *totype)
       else if (tbtype->ty == TY::Tsarray)
        {
          /* Strings are treated as dynamic arrays in D2.  */
-         if (ebtype->isString () && tbtype->isString ())
+         if (dmd::isString (ebtype) && dmd::isString (tbtype))
            return indirect_ref (build_ctype (totype), d_array_ptr (exp));
        }
       else
@@ -596,8 +596,8 @@ convert_expr (tree exp, Type *etype, Type *totype)
     default:
       /* All casts between imaginary and non-imaginary result in 0.0,
         except for casts between complex and imaginary types.  */
-      if (!ebtype->isComplex () && !tbtype->isComplex ()
-         && (ebtype->isImaginary () != tbtype->isImaginary ()))
+      if (!dmd::isComplex (ebtype) && !dmd::isComplex (tbtype)
+         && (dmd::isImaginary (ebtype) != dmd::isImaginary (tbtype)))
        {
          warning (OPT_Wcast_result,
                   "cast from %qs to %qs will produce zero result",
@@ -671,7 +671,7 @@ convert_for_rvalue (tree expr, Type *etype, Type *totype)
          tree array = d_save_expr (TREE_OPERAND (ptr, 0));
          array = build1 (VIEW_CONVERT_EXPR, TREE_TYPE (expr), array);
 
-         uinteger_t dim = tbtype->isTypeSArray ()->dim->toUInteger ();
+         uinteger_t dim = dmd::toUInteger (tbtype->isTypeSArray ()->dim);
          vec <constructor_elt, va_gc> *elms = NULL;
          for (uinteger_t i = 0; i < dim; i++)
            {
@@ -779,7 +779,7 @@ convert_for_assignment (Expression *expr, Type *totype, bool literalp)
       if (same_type_p (telem, ebtype))
        {
          TypeSArray *sa_type = tbtype->isTypeSArray ();
-         uinteger_t count = sa_type->dim->toUInteger ();
+         uinteger_t count = dmd::toUInteger (sa_type->dim);
 
          tree ctor = build_padded_constructor (build_ctype (totype), NULL);
          if (count)
@@ -807,7 +807,7 @@ convert_for_assignment (Expression *expr, Type *totype, bool literalp)
 
   /* D Front end uses IntegerExp(0) to mean zero-init an array or structure.  */
   if ((tbtype->ty == TY::Tsarray || tbtype->ty == TY::Tstruct)
-      && ebtype->isIntegral ())
+      && dmd::isIntegral (ebtype))
     {
       tree ret = build_expr (expr, false, literalp);
       gcc_assert (integer_zerop (ret));
@@ -974,7 +974,7 @@ d_array_convert (Type *etype, Expression *exp)
       /* Convert single element to an array.  */
       tree expr = build_expr (exp);
 
-      if (!exp->isLvalue ())
+      if (!dmd::isLvalue (exp))
        {
          tree var = build_local_temp (TREE_TYPE (expr));
          expr = compound_expr (modify_expr (var, expr), var);
index 47fe0f2a1fc1c709ed517109b31f6d332f8da7ff..f4c680a4a48ad5d0d817477f6d908aa10c6a501a 100644 (file)
@@ -221,8 +221,8 @@ d_diagnostic_report_diagnostic (const SourceLoc &loc, int opt,
    error count depending on how KIND is treated.  */
 
 void D_ATTRIBUTE_FORMAT(2,0) ATTRIBUTE_GCC_DIAG(2,0)
-verrorReport (const SourceLoc loc, const char *format, va_list ap,
-             ErrorKind kind, const char *prefix1, const char *prefix2)
+vreportDiagnostic (const SourceLoc loc, const char *format, va_list ap,
+                  ErrorKind kind, const char *prefix1, const char *prefix2)
 {
   enum diagnostics::kind diag_kind = diagnostics::kind::unspecified;
   int opt = 0;
@@ -245,12 +245,7 @@ verrorReport (const SourceLoc loc, const char *format, va_list ap,
   else if (kind == ErrorKind::warning)
     {
       if (global.gag || global.params.useWarnings == DIAGNOSTICoff)
-       {
-         if (global.gag)
-           global.gaggedWarnings++;
-
-         return;
-       }
+       return;
 
       /* Warnings don't count if not treated as errors.  */
       if (global.params.useWarnings == DIAGNOSTICerror)
@@ -261,12 +256,12 @@ verrorReport (const SourceLoc loc, const char *format, va_list ap,
   else if (kind == ErrorKind::deprecation)
     {
       if (global.params.useDeprecated == DIAGNOSTICerror)
-       return verrorReport (loc, format, ap, ErrorKind::error, prefix1,
-                            prefix2);
+       return vreportDiagnostic (loc, format, ap, ErrorKind::error, prefix1,
+                                 prefix2);
       else if (global.gag || global.params.useDeprecated != DIAGNOSTICinform)
        {
          if (global.gag)
-           global.gaggedWarnings++;
+           global.gaggedDeprecations++;
 
          return;
        }
@@ -307,8 +302,8 @@ verrorReport (const SourceLoc loc, const char *format, va_list ap,
    explicit location LOC.  This doesn't increase the global error count.  */
 
 void D_ATTRIBUTE_FORMAT(2,0) ATTRIBUTE_GCC_DIAG(2,0)
-verrorReportSupplemental (const SourceLoc loc, const char* format, va_list ap,
-                         ErrorKind kind)
+vsupplementalDiagnostic (const SourceLoc loc, const char* format, va_list ap,
+                        ErrorKind kind)
 {
   if (kind == ErrorKind::error)
     {
@@ -323,7 +318,7 @@ verrorReportSupplemental (const SourceLoc loc, const char* format, va_list ap,
   else if (kind == ErrorKind::deprecation)
     {
       if (global.params.useDeprecated == DIAGNOSTICerror)
-       return verrorReportSupplemental (loc, format, ap, ErrorKind::error);
+       return vsupplementalDiagnostic (loc, format, ap, ErrorKind::error);
       else if (global.params.useDeprecated != DIAGNOSTICinform || global.gag)
        return;
     }
index eed1ee97fe9714475dbb7839709faad502b3f5f3..a522e51f5a2078c93c3d319808ed0d02579b180c 100644 (file)
@@ -777,6 +777,14 @@ d_handle_option (size_t scode, const char *arg, HOST_WIDE_INT value,
       d_option.stdinc = false;
       break;
 
+    case OPT_std_d2024:
+      global.params.edition = Edition::v2024;
+      break;
+
+    case OPT_std_d202y:
+      global.params.edition = Edition::v2025;
+      break;
+
     case OPT_v:
       global.params.v.verbose = value;
       break;
@@ -1270,7 +1278,7 @@ d_parse_file (void)
     }
 
   /* Do deferred semantic analysis.  */
-  Module::runDeferredSemantic ();
+  dmd::runDeferredSemantic ();
 
   if (Module::deferred.length)
     {
@@ -1300,7 +1308,7 @@ d_parse_file (void)
       dmd::semantic2 (m, NULL);
     }
 
-  Module::runDeferredSemantic2 ();
+  dmd::runDeferredSemantic2 ();
 
   if (global.errors)
     goto had_errors;
@@ -1331,7 +1339,7 @@ d_parse_file (void)
        }
     }
 
-  Module::runDeferredSemantic3 ();
+  dmd::runDeferredSemantic3 ();
 
   /* Check again, incase semantic3 pass loaded any more modules.  */
   while (builtin_modules.length != 0)
@@ -1413,7 +1421,7 @@ d_parse_file (void)
 
   /* Generate C++ header files.  */
   if (global.params.cxxhdr.doOutput)
-    dmd::genCppHdrFiles (modules);
+    dmd::genCppHdrFiles (modules, global.errorSink);
 
   if (global.errors)
     goto had_errors;
index 7fdc0ccbc234a7bc587c665d403d7a330694e69c..10911ac227121db92c8f6f21a8643345d10efebd 100644 (file)
@@ -220,7 +220,7 @@ unsigned
 Target::fieldalign (Type *type)
 {
   /* Work out the correct alignment for the field decl.  */
-  unsigned int align = type->alignsize () * BITS_PER_UNIT;
+  unsigned int align = dmd::alignsize (type) * BITS_PER_UNIT;
 
 #ifdef BIGGEST_FIELD_ALIGNMENT
   align = MIN (align, (unsigned) BIGGEST_FIELD_ALIGNMENT);
@@ -274,7 +274,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 == TY::Tbool)
+  if (!type->isTypeBasic () || dmd::isComplex (type) || type->ty == TY::Tbool)
     return 2;
 
   /* In [simd/vector extensions], which vector types are supported depends on
@@ -300,7 +300,7 @@ Target::isVectorOpSupported (Type *type, EXP op, Type *)
     return true;
 
   /* Don't support if type is non-scalar, such as __vector(void[]).  */
-  if (!type->isScalar ())
+  if (!dmd::isScalar (type))
     return false;
 
   /* Don't support if expression cannot be represented.  */
@@ -314,7 +314,7 @@ Target::isVectorOpSupported (Type *type, EXP op, Type *)
     case EXP::mod:
     case EXP::modAssign:
       /* fmod() is lowered as a function call.  */
-      if (type->isFloating ())
+      if (dmd::isFloating (type))
        return false;
       break;
 
index 8e740aedc997f03267e5c876477f732e44f4dc13..2614277d96a7719b20c283c3c863307cddc10fad 100644 (file)
@@ -783,7 +783,7 @@ public:
       {
        /* Do not store variables we cannot take the address of,
           but keep the values for purposes of debugging.  */
-       if (d->type->isScalar () && !dmd::hasPointers (d->type))
+       if (dmd::isScalar (d->type) && !dmd::hasPointers (d->type))
          {
            tree decl = get_symbol_decl (d);
            d_pushdecl (decl);
@@ -969,7 +969,7 @@ public:
 
        doing_semantic_analysis_p = true;
        dmd::functionSemantic3 (d);
-       Module::runDeferredSemantic3 ();
+       dmd::runDeferredSemantic3 ();
        doing_semantic_analysis_p = false;
       }
 
@@ -1049,10 +1049,15 @@ public:
        else
          d->shidden = resdecl;
 
+       tree var = NULL_TREE;
+
        if (d->isNRVO () && d->nrvo_var)
-         {
-           tree var = get_symbol_decl (d->nrvo_var);
+         var = get_symbol_decl (d->nrvo_var);
+       else if (d->vresult)
+         var = get_symbol_decl (d->vresult);
 
+       if (var != NULL_TREE)
+         {
            /* Copy name from VAR to RESULT.  */
            DECL_NAME (resdecl) = DECL_NAME (var);
            /* Don't forget that we take its address.  */
@@ -1250,7 +1255,7 @@ get_symbol_decl (Declaration *decl)
       /* CONST_DECL was initially intended for enumerals and may be used for
         scalars in general, but not for aggregates.  Here a non-constant
         value is generated anyway so as its value can be used.  */
-      if (!vd->canTakeAddressOf () && !vd->type->isScalar ())
+      if (!vd->canTakeAddressOf () && !dmd::isScalar (vd->type))
        {
          gcc_assert (vd->_init && !vd->_init->isVoidInitializer ());
          Expression *ie = dmd::initializerToExpression (vd->_init);
@@ -1310,7 +1315,7 @@ get_symbol_decl (Declaration *decl)
          /* Cannot make an expression out of a void initializer.  */
          gcc_assert (vd->_init && !vd->_init->isVoidInitializer ());
          /* Non-scalar manifest constants have already been dealt with.  */
-         gcc_assert (vd->type->isScalar ());
+         gcc_assert (dmd::isScalar (vd->type));
 
          Expression *ie = dmd::initializerToExpression (vd->_init);
          DECL_INITIAL (decl->csym) = build_expr (ie, true);
index a9235246e938eb70da44bd52b8e67e746c5e2913..423f245b7534efb0220caac5ad66da8c6ab82687 100644 (file)
@@ -1,4 +1,4 @@
-e1f6680f50d147846316c2fa3363461a2aa7ac1d
+24a41073c2dbf456d4f7a6fe6a7965d6ce6fc5cb
 
 The first line of this file holds the git revision number of the last
 merge done from the dlang/dmd repository.
index f10b162d2745e6a45f1ced0d7fa0abfdd44cf5ca..fb1299b01072ef2ec65401bc3d61bfe3dbafb281 100644 (file)
@@ -47,6 +47,7 @@ Note that these groups have no strict meaning, the category assignments are a bi
 | [compiler.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/compiler.d)   | Describe a back-end compiler and implements compiler-specific actions |
 | [deps.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/deps.d)           | Implement the `-deps` and `-makedeps` switches                        |
 | [timetrace.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/timetrace.d) | Build time profiling utility                                          |
+| [targetcompiler.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/targetcompiler.d) | Differences in building gdc, ldc and dmd                    |
 
 ### Lexing / parsing
 
index 67e32f8d7ef1822a1a7ade3e259843c542fd3088..bb14ba5b599c2d98c69db8ddb1740de9f6abbce7 100644 (file)
@@ -1 +1 @@
-v2.112.0-beta.1
+v2.112.0
index d547bb78626c7e0cca71df46459f34165f9099a1..a29ae0334ac70b27cab988bb47c0b87bd884ddd9 100644 (file)
@@ -20,22 +20,15 @@ import core.checkedint;
 import dmd.aliasthis;
 import dmd.arraytypes;
 import dmd.astenums;
-import dmd.attrib;
 import dmd.declaration;
 import dmd.dscope;
-import dmd.dstruct;
 import dmd.dsymbol;
-import dmd.dsymbolsem : determineSize;
-import dmd.dtemplate;
 import dmd.errors;
 import dmd.expression;
 import dmd.func;
-import dmd.hdrgen;
-import dmd.id;
 import dmd.identifier;
 import dmd.location;
 import dmd.mtype;
-import dmd.tokens;
 import dmd.visitor;
 
 /**
@@ -145,6 +138,7 @@ extern (C++) abstract class AggregateDeclaration : ScopeDsymbol
     DtorDeclaration fieldDtor;  /// function destructing (non-inherited) fields
 
     Expression getRTInfo;   /// pointer to GC info generated by object.RTInfo(this)
+    Scope* rtInfoScope;     /// scope to be used when evaluating getRTInfo
 
     ///
     Visibility visibility;
@@ -158,24 +152,6 @@ extern (C++) abstract class AggregateDeclaration : ScopeDsymbol
         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 = false;
-        sc2.aligndecl = null;
-        sc2.userAttribDecl = null;
-        sc2.namespace = null;
-        return sc2;
-    }
-
     /***************************************
      * Returns:
      *      The total number of fields minus the number of hidden fields.
@@ -185,32 +161,6 @@ extern (C++) abstract class AggregateDeclaration : ScopeDsymbol
         return fields.length - isNested() - (vthis2 !is null);
     }
 
-    override final ulong size(Loc loc)
-    {
-        //printf("+AggregateDeclaration::size() %s, scope = %p, sizeok = %d\n", toChars(), _scope, sizeok);
-        bool ok = determineSize(this, loc);
-        //printf("-AggregateDeclaration::size() %s, scope = %p, sizeok = %d\n", toChars(), _scope, sizeok);
-        return ok ? structsize : SIZE_INVALID;
-    }
-
-
-    override final Type getType()
-    {
-        /* Apply storage classes to forward references. (Issue 22254)
-         * Note: Avoid interfaces for now. Implementing qualifiers on interface
-         * definitions exposed some issues in their TypeInfo generation in DMD.
-         * Related PR: https://github.com/dlang/dmd/pull/13312
-         */
-        if (semanticRun == PASS.initial && !isInterfaceDeclaration())
-        {
-            auto stc = storage_class;
-            if (_scope)
-                stc |= _scope.stc;
-            type = type.addSTC(stc);
-        }
-        return type;
-    }
-
     // is aggregate deprecated?
     override final bool isDeprecated() const
     {
@@ -232,120 +182,6 @@ extern (C++) abstract class AggregateDeclaration : ScopeDsymbol
         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_;
index 3c4c84a09398170c7fee34250ffc0fb5d61e5d58..4a5f664ca8fd065e15225e43b175d0f24319c4c6 100644 (file)
@@ -48,6 +48,7 @@ namespace dmd
     bool fill(StructDeclaration* sd, Loc loc, Expressions &elements, bool ctorinit);
     bool isFuncHidden(ClassDeclaration* cd, FuncDeclaration* fd);
     Dsymbol* vtblSymbol(ClassDeclaration *cd);
+    bool fillVtbl(BaseClass *bc, ClassDeclaration *cd, FuncDeclarations *vtbl, int newinstance);
 }
 
 enum class ClassKind : uint8_t
@@ -113,15 +114,13 @@ public:
     DtorDeclaration *fieldDtor; // function destructing (non-inherited) fields
 
     Expression *getRTInfo;      // pointer to GC info generated by object.RTInfo(this)
+    Scope* rtInfoScope;         // scope to be used when evaluating getRTInfo
 
     Visibility visibility;
     d_bool noDefaultCtor;         // no default construction
     d_bool disableNew;            // disallow allocations using `new`
     Sizeok sizeok;              // set when structsize contains valid data
 
-    virtual Scope *newScope(Scope *sc);
-    uinteger_t size(Loc loc) override final;
-    Type *getType() override final;
     bool isDeprecated() const override final; // is aggregate deprecated?
     bool isNested() const;
     bool isExport() const override final;
@@ -171,6 +170,7 @@ public:
     static StructDeclaration *create(Loc loc, Identifier *id, bool inObject);
     StructDeclaration *syntaxCopy(Dsymbol *s) override;
     const char *kind() const override;
+    bool hasCopyConstruction();
     bool zeroInit() const;          // !=0 if initialize with 0 fill
     bool zeroInit(bool v);
     bool hasIdentityAssign() const; // true if has identity opAssign
@@ -216,8 +216,6 @@ struct BaseClass
 
     DArray<BaseClass> baseInterfaces;   // if BaseClass is an interface, these
                                         // are a copy of the InterfaceDeclaration::interfaces
-
-    bool fillVtbl(ClassDeclaration *cd, FuncDeclarations *vtbl, int newinstance);
 };
 
 struct ClassFlags
@@ -275,7 +273,6 @@ public:
     static ClassDeclaration *create(Loc loc, Identifier *id, BaseClasses *baseclasses, Dsymbols *members, bool inObject);
     const char *toPrettyChars(bool QualifyTypes = false) override;
     ClassDeclaration *syntaxCopy(Dsymbol *s) override;
-    Scope *newScope(Scope *sc) override;
 
     #define OFFSET_RUNTIME 0x76543210
     #define OFFSET_FWDREF 0x76543211
@@ -290,8 +287,6 @@ public:
     virtual int vtblOffset() const;
     const char *kind() const override;
 
-    void addObjcSymbols(ClassDeclarations *classes, ClassDeclarations *categories) override final;
-
     // Back end
     Dsymbol *vtblsym;
 
@@ -302,7 +297,6 @@ class InterfaceDeclaration final : public ClassDeclaration
 {
 public:
     InterfaceDeclaration *syntaxCopy(Dsymbol *s) override;
-    Scope *newScope(Scope *sc) override;
     bool isBaseOf(ClassDeclaration *cd, int *poffset) override;
     const char *kind() const override;
     int vtblOffset() const override;
index ea6f87f60ec6dd36c15dd95f15af497c0d00674f..d052cd588f7e96520d3f4b674983e97321c85e31 100644 (file)
@@ -19,7 +19,6 @@ import dmd.astenums;
 import dmd.dcast : implicitConvTo;
 import dmd.declaration;
 import dmd.dscope;
-import dmd.dsymbol;
 import dmd.errors;
 import dmd.expression;
 import dmd.expressionsem;
@@ -31,7 +30,7 @@ import dmd.location;
 import dmd.mtype;
 import dmd.common.outbuffer;
 import dmd.tokens;
-import dmd.typesem : isAssignable;
+import dmd.typesem : isAssignable, nextOf, toBasetype;
 import dmd.visitor;
 
 /**********************************************
index fd8387adb224b6eb646f18631710aed13ba4b2b5..ae0c3629178badec9a887cbeb163dd0047eea31f 100644 (file)
@@ -32,16 +32,16 @@ struct ASTCodegen
     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;
+    public import dmd.typesem;
 
-
+    alias addSTC                    = dmd.typesem.addSTC;
     alias initializerToExpression   = dmd.initsem.initializerToExpression;
-    alias typeToExpression          = dmd.typesem.typeToExpression;
+    alias typeToExpression          = dmd.mtype.typeToExpression;
     alias UserAttributeDeclaration  = dmd.attrib.UserAttributeDeclaration;
     alias Ensure                    = dmd.func.Ensure; // workaround for bug in older DMD frontends
     alias ErrorExp                  = dmd.expression.ErrorExp;
+    alias ArgumentLabel             = dmd.expression.ArgumentLabel;
 
     alias MODFlags                  = dmd.mtype.MODFlags;
     alias Type                      = dmd.mtype.Type;
index 5cde5b334c5ed23d3cb47a3ed44f8eea39b11281..1a5fcb8a55706b45eed353ad78b61ed75d1f6153 100644 (file)
@@ -150,7 +150,7 @@ enum STC : ulong  // transfer changes to declaration.h
 alias StorageClass = ulong;
 
 /********
- * Determine if it's the ambigous case of where `return` attaches to.
+ * Determine if it's the ambiguous case of where `return` attaches to.
  * Params:
  *   stc = STC flags
  * Returns:
@@ -477,7 +477,12 @@ extern (C++) struct structalign_t
   private:
     ushort value = 0;  // unknown
     enum STRUCTALIGN_DEFAULT = 1234;   // default = match whatever the corresponding C compiler does
-    bool pack;         // use #pragma pack semantics
+    ubyte flags;       // Align semantic flags
+    enum : ubyte
+    {
+        PACK = 0x1,     // use #pragma pack semantics
+        ALIGNAS = 0x2,  // use _Alignas semantics
+    }
 
   public:
   pure @safe @nogc nothrow:
@@ -487,8 +492,10 @@ extern (C++) struct structalign_t
     void setUnknown()      { value = 0; }
     void set(uint value)   { this.value = cast(ushort)value; }
     uint get() const       { return value; }
-    bool isPack() const    { return pack; }
-    void setPack(bool pack) { this.pack = pack; }
+    bool isPack() const    { return !!(flags & PACK); }
+    void setPack()         { flags |= PACK; }
+    bool fromAlignas() const { return !!(flags & ALIGNAS); }
+    void setAlignas()      { flags |= ALIGNAS; }
 }
 
 /// Use to return D arrays from C++ functions
index 97dfa5ef683f78a0ac80db471cd5af8017934fbc..8c2202358c80d09d6f83c59ce903b6b37109427a 100644 (file)
 
 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.expression;
-import dmd.func;
 import dmd.hdrgen : visibilityToBuffer;
 import dmd.id;
 import dmd.identifier;
 import dmd.location;
-import dmd.mtype;
-import dmd.objc; // for objc.addSymbols
 import dmd.common.outbuffer;
-import dmd.root.array; // for each
 import dmd.visitor;
 
 /***********************************************************
@@ -108,13 +102,6 @@ extern (C++) abstract class AttribDeclaration : Dsymbol
         return "attribute";
     }
 
-    /****************************************
-     */
-    override final void addObjcSymbols(ClassDeclarations* classes, ClassDeclarations* categories)
-    {
-        objc.addSymbols(this, classes, categories);
-    }
-
     override void accept(Visitor v)
     {
         v.visit(this);
index ae2f12f323205a700bdb81bfc1c941aabd126d90..3f06a24a31a853ceed8c6b988934b5a8f657e119 100644 (file)
@@ -20,6 +20,8 @@ import dmd.dclass;
 import dmd.declaration;
 import dmd.errorsink;
 import dmd.expression;
+import dmd.expressionsem : toBool;
+import dmd.typesem : toBasetype;
 import dmd.func;
 import dmd.globals;
 import dmd.id;
index 53307fca37700e1ff21269fc1b27806819b9b902..facce7989ed8522bb8d8f0b1898977f647ec5b1d 100644 (file)
@@ -15,6 +15,8 @@ module dmd.builtin;
 
 import dmd.arraytypes;
 import dmd.expression;
+import dmd.expressionsem : toInteger, toReal;
+import dmd.typesem : isFloating, toBasetype;
 import dmd.func;
 import dmd.location;
 
index bd4aa197ab93e34f26eb4a148f15de1fd682128f..39536a4f7729f01243a8e7724e836b53fd454343 100644 (file)
 module dmd.canthrow;
 
 import dmd.aggregate;
-import dmd.arraytypes;
-import dmd.attrib;
 import dmd.astenums;
 import dmd.blockexit : BE, checkThrow;
-import dmd.declaration;
 import dmd.dsymbol;
 import dmd.dsymbolsem : include, toAlias;
 import dmd.errorsink;
 import dmd.expression;
-import dmd.expressionsem : errorSupplementalInferredAttr;
+import dmd.expressionsem;
+import dmd.typesem;
 import dmd.func;
 import dmd.globals;
-import dmd.init;
 import dmd.mtype;
 import dmd.tokens;
 import dmd.visitor;
index 8b2d5b4dd4b32a5fbd5191191a7b66de4590430b..58144db681e1519c1755fc8bd699522e5a594470 100644 (file)
@@ -25,6 +25,39 @@ import dmd.typesem;
 import dmd.target;
 
 
+/**********************************************
+ * While in general printf is not @safe (and should be marked @system), many uses of printf are safe.
+ * This function determines if a particular call of printf is safe.
+ * Params:
+ *      format = printf format string
+ * Returns:
+ *      true if @safe
+ */
+public
+bool isFormatSafe(scope const char[] format)
+{
+    //printf("isFormatSafe('%.*s')\n", cast(int)format.length, format.ptr);
+    /* Only need to check the format string, any other errors are checked
+     * for later with checkPrintfFormat()
+     */
+    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);
+        i = j;
+        if (fmt == Format.s || fmt == Format.ls || fmt == Format.error)
+            return false;
+    }
+    return true;
+}
+
 /******************************************
  * Check that arguments to a printf format string are compatible
  * with that string. Issue errors for incompatibilities.
@@ -65,7 +98,7 @@ import dmd.target;
 public
 bool checkPrintfFormat(Loc loc, scope const char[] format, scope Expression[] args, bool isVa_list, ErrorSink eSink)
 {
-    //printf("checkPrintFormat('%.*s')\n", cast(int)format.length, format.ptr);
+    //printf("checkPrintfFormat('%.*s')\n", cast(int)format.length, format.ptr);
     size_t n;    // index in args
     for (size_t i = 0; i < format.length;)
     {
index 5732f9d385f6bcfbe27c6b82ebc2a56399e8d2c1..dd01e8f4fd79ba3d3921706854940440bbd3bee7 100644 (file)
@@ -10,6 +10,9 @@
  */
 module dmd.common.bitfields;
 
+nothrow:
+@safe:
+
 //version = Has_Bitfields; // does not work (yet) because hashOf doesn't work on bitfields
 version(Has_Bitfields)
     version = Debugger_friendly; // without Has_Bitfields, this uses more space by using S
index 1b01a282291b250013ade1591ca8268d697669b6..41a7f3307c57fefd4d69bab4a00ef66af5f4d8df 100644 (file)
@@ -32,10 +32,10 @@ version (Windows)
 {
     import core.stdc.wchar_;
     import core.sys.windows.winbase;
-    import core.sys.windows.winnls : CP_ACP;
+    import core.sys.windows.winnls : CP_UTF8;
     import core.sys.windows.winnt;
 
-    enum CodePage = CP_ACP; // assume filenames encoded in system default Windows ANSI code page
+    enum CodePage = CP_UTF8; // assume filenames already gone through Windows ANSI code page -> UTF8 conversion
     enum invalidHandle = INVALID_HANDLE_VALUE;
 }
 else version (Posix)
@@ -54,7 +54,6 @@ else
 
 
 
-
 nothrow:
 
 /**
@@ -472,21 +471,19 @@ extern(D) static bool writeFile(const(char)* name, const void[] data) nothrow
     {
         int fd = open(name, O_CREAT | O_WRONLY | O_TRUNC, (6 << 6) | (4 << 3) | 4);
         if (fd == -1)
-            goto err;
+            return false;
         if (.write(fd, data.ptr, data.length) != data.length)
-            goto err2;
+        {
+            close(fd);
+            .remove(name);
+            return false;
+        }
         if (close(fd) == -1)
-            goto err;
+            return false;
         return true;
-    err2:
-        close(fd);
-        .remove(name);
-    err:
-        return false;
     }
     else version (Windows)
     {
-        DWORD numwritten; // here because of the gotos
         const nameStr = name[0 .. strlen(name)];
         // work around Windows file path length limitation
         // (see documentation for extendedPathThen).
@@ -499,20 +496,21 @@ extern(D) static bool writeFile(const(char)* name, const void[] data) nothrow
                                 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
                                 null));
         if (h == INVALID_HANDLE_VALUE)
-            goto err;
-
+            return false;
+        bool errorRet()
+        {
+            CloseHandle(h);
+            nameStr.extendedPathThen!(p => DeleteFileW(p.ptr));
+            return false;
+        }
+        DWORD numwritten;
         if (WriteFile(h, data.ptr, cast(DWORD)data.length, &numwritten, null) != TRUE)
-            goto err2;
+            return errorRet();
         if (numwritten != data.length)
-            goto err2;
+            return errorRet();
         if (!CloseHandle(h))
-            goto err;
+            return false;
         return true;
-    err2:
-        CloseHandle(h);
-        nameStr.extendedPathThen!(p => DeleteFileW(p.ptr));
-    err:
-        return false;
     }
     else
     {
index 65adec68f45ee8005062fa4f89d34027808249c6..085c0e34348e796588575ca1d96f422aa96c0dd9 100644 (file)
@@ -121,16 +121,20 @@ version(Windows) wchar[] toWStringz(scope const(char)[] narrow, ref SmallBuffer!
     size_t charsToWchars(scope const(char)[] narrow, scope wchar[] buffer)
     {
         // https://learn.microsoft.com/en-us/windows/win32/api/stringapiset/nf-stringapiset-multibytetowidechar
-        import core.sys.windows.winnls : MultiByteToWideChar, CP_ACP;
-        return MultiByteToWideChar(CP_ACP, 0, narrow.ptr, cast(int) narrow.length, buffer.ptr, cast(int) buffer.length);
+        import core.sys.windows.winnls : MultiByteToWideChar, CP_UTF8;
+        return MultiByteToWideChar(CP_UTF8, 0, narrow.ptr, cast(int) narrow.length, buffer.ptr, cast(int) buffer.length);
     }
 
-    size_t length = charsToWchars(narrow, buffer[]);
-    if (length >= buffer.length) // not enough room in buffer[]
+    size_t length = charsToWchars(narrow, buffer[0..$-1]);
+    if (length == 0 && narrow.length > 0) // not enough room in buffer[]
     {
+        import core.sys.windows.winbase : GetLastError;
+        import core.sys.windows.winerror : ERROR_INSUFFICIENT_BUFFER;
+        assert(GetLastError() == ERROR_INSUFFICIENT_BUFFER);
+        length = charsToWchars(narrow, []);
         buffer.create(length + 1); // extend buffer length
-        length = charsToWchars(narrow, buffer[]);  // try again
-        assert(length < buffer.length);
+        length = charsToWchars(narrow, buffer[0..length]);  // try again
+        assert(length > 0 && length < buffer.length);
     }
     buffer[length] = 0;
     return buffer[0 .. length];
index 96c7db9086cd8ffb3e49a146eb24b8a472df1ad7..416d6bd69285f7b91d98c73e68605ec5e6440808 100644 (file)
@@ -17,25 +17,20 @@ import core.stdc.string;
 import dmd.arraytypes;
 import dmd.astenums;
 import dmd.ast_node;
-import dmd.dcast;
-import dmd.dinterpret;
 import dmd.dmodule;
 import dmd.dscope;
-import dmd.dsymbol;
 import dmd.errors;
 import dmd.expression;
 import dmd.globals;
 import dmd.identifier;
 import dmd.location;
 import dmd.mtype;
-import dmd.optimize;
 import dmd.common.outbuffer;
 import dmd.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;
@@ -154,6 +149,7 @@ extern (C++) final class StaticForeach : RootObject
         auto tf = new TypeFunction(ParameterList(), null, LINK.default_, STC.none);
         auto fd = new FuncLiteralDeclaration(loc, loc, tf, TOK.reserved, null);
         fd.fbody = s;
+        fd.skipCodegen = true;
         auto fe = new FuncExp(loc, fd);
         auto ce = new CallExp(loc, fe, new Expressions());
         return ce;
@@ -234,15 +230,6 @@ extern (C++) final class StaticForeach : RootObject
     {   // TODO: move to druntime?
         return new CallExp(loc, new TypeExp(loc, type), e);
     }
-
-    /*****************************************
-     * 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;
-    }
 }
 
 /***********************************************************
@@ -364,6 +351,7 @@ extern (C++) final class VersionCondition : DVCondition
             case "Alpha_HardFloat":
             case "Alpha_SoftFloat":
             case "Android":
+            case "Apple":
             case "ARM":
             case "ARM_HardFloat":
             case "ARM_SoftFloat":
index 1e0024e92cef3ee2cbdb4c7cda0ac353b20969dd..d7db3c3bf32c5e5dabc59ee88f2f5a4177353704 100644 (file)
@@ -25,7 +25,7 @@ import dmd.declaration;
 import dmd.dstruct;
 import dmd.errors;
 import dmd.expression;
-import dmd.expressionsem : getField;
+import dmd.expressionsem;
 import dmd.globals;
 import dmd.location;
 import dmd.mtype;
@@ -37,7 +37,7 @@ import dmd.root.utf;
 import dmd.sideeffect;
 import dmd.target;
 import dmd.tokens;
-import dmd.typesem : toDsymbol, equivalent, sarrayOf, size;
+import dmd.typesem;
 
 private enum LOG = false;
 
index 4a6e5195f5fda4f28879291c50911607a74e00bc..05f0edaa0c277d07a4a4c2b1873d0a2c9e507549 100644 (file)
@@ -27,7 +27,7 @@ import dmd.root.array;
 import dmd.common.outbuffer;
 import dmd.root.rmem;
 import dmd.tokens;
-import dmd.typesem : size;
+import dmd.typesem : size, makeImmutable, nextOf;
 
 /***********************************************************
  */
@@ -36,6 +36,8 @@ final class CParser(AST) : Parser!AST
     AST.Dsymbols* symbols;      // symbols declared in current scope
 
     bool addFuncName;           /// add declaration of __func__ to function symbol table
+    bool refFuncName;           // declaration of __FUNCTION__
+    bool pretFuncName;          // declaration for __PRETTY_FUNCTION__
     bool importBuiltins;        /// seen use of C compiler builtins, so import __importc_builtins;
 
     private
@@ -143,6 +145,20 @@ final class CParser(AST) : Parser!AST
                 return wrap;
             }
 
+            if (token.value == TOK._module)
+            {
+                token.value = TOK.module_;
+                auto oldMd = this.md;
+                parseModuleDeclaration();
+                if (oldMd)
+                {
+                    // We only use the first module declaration,
+                    // subsequent __module statements should only come from #included files
+                    this.md = oldMd;
+                }
+                continue;
+            }
+
             /* GNU Extensions
              * external-declaration:
              *    simple-asm-expr ;
@@ -262,8 +278,7 @@ final class CParser(AST) : Parser!AST
                 {
                     /* If tokens look like a declaration, assume it is one
                      */
-                    auto tk = &token;
-                    if (isCDeclaration(tk))
+                    if (startsDeclaration(&token))
                         goto Ldeclaration;
                     goto Lexp;
                 }
@@ -739,6 +754,12 @@ final class CParser(AST) : Parser!AST
             const id = token.ident.toString();
             if (id.length > 2 && id[0] == '_' && id[1] == '_')  // leading double underscore
             {
+                if (token.ident is Id.FUNCTION)
+                    refFuncName = true; // implicitly declare __FUNCTION__
+
+                if (token.ident is Id.PRETTY_FUNCTION)
+                    pretFuncName = true; // implicitly declare __PRETTY_FUNCTION__
+
                 if (token.ident is Id.__func__)
                 {
                     addFuncName = true;     // implicitly declare __func__
@@ -861,7 +882,7 @@ final class CParser(AST) : Parser!AST
         case TOK._assert:  // __check(assign-exp) extension
             nextToken();
             check(TOK.leftParenthesis, "`__check`");
-            e = parseAssignExp();
+            e = cparseAssignExp();
             check(TOK.rightParenthesis);
             e = new AST.AssertExp(loc, e, null);
             break;
@@ -1049,49 +1070,8 @@ final class CParser(AST) : Parser!AST
 
         case TOK._Alignof:
         case TOK.sizeof_:
-        {
-            Identifier id = token.value == TOK.sizeof_? Id.__sizeof : Id.__xalignof;
-            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);
-                    }
-                }
-                else
-                {
-                    // must be an expression
-                    e = cparseUnaryExp();
-                }
-            }
-            else
-            {
-                //C11 6.5.3
-                e = cparseUnaryExp();
-            }
-
-            e = new AST.DotIdExp(loc, e, id);
+            e = cparseSizeofOrAlignofExp();
             break;
-        }
 
         case TOK.andAnd:
             /* https://gcc.gnu.org/onlinedocs/gcc/Labels-as-Values.html
@@ -1109,6 +1089,48 @@ final class CParser(AST) : Parser!AST
         return e;
     }
 
+    /**************
+     * C11 6.5.3.4
+     *    sizeof unary-expression
+     *    sizeof ( type-name )
+     *    _Alignof ( type-name )
+     *    _Alignof unary-expression // gcc extension
+     */
+    private AST.Expression cparseSizeofOrAlignofExp()
+    {
+        Identifier id = token.value == TOK.sizeof_? Id.__sizeof : Id.__xalignof;
+        AST.Expression e;
+        nextToken();
+        if (token.value == TOK.leftParenthesis && startsTypeName(peek(&token)))
+        {
+            /* 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);
+            }
+        }
+        else
+        {
+            // C11 6.5.3
+            e = cparseUnaryExp();
+        }
+        return new AST.DotIdExp(loc, e, id);
+    }
+
     /**************
      * C11 6.5.4
      * cast-expression
@@ -1117,74 +1139,32 @@ final class CParser(AST) : Parser!AST
      */
     private AST.Expression cparseCastExp()
     {
-        if (token.value != TOK.leftParenthesis)
-            return cparseUnaryExp();
-
         //printf("cparseCastExp()\n");
-        auto tk = peek(&token);
-        bool iscast;
-        bool isexp;
-        if (tk.value == TOK.identifier)
-        {
-            iscast = isTypedef(tk.ident);
-            isexp = !iscast;
-        }
-        if (isexp)
-        {
-            // ( identifier ) is an expression
-            return cparseUnaryExp();
-        }
 
         // If ( type-name )
-        auto pt = &token;
-
-        if (!isCastExpression(pt))
-            return cparseUnaryExp();
-
-        // 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);
-        pt = &token;
-
-        if (token.value == TOK.leftCurly)
+        if (token.value == TOK.leftParenthesis && startsTypeName(peek(&token)))
         {
-            // C11 6.5.2.5 ( type-name ) { initializer-list }
-            auto ci = cparseInitializer();
-            auto ce = new AST.CompoundLiteralExp(loc, t, ci);
-            return cparsePostfixOperators(ce);
-        }
+            // 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);
+            }
 
-        if (iscast)
-        {
             // ( type-name ) cast-expression
             auto ce = cparseCastExp();
             return new AST.CastExp(loc, ce, t);
         }
 
-        if (t.isTypeIdentifier() &&
-            isexp &&
-            token.value == TOK.leftParenthesis &&
-            !isCastExpression(pt))
-        {
-            /* (t)(...)... might be a cast expression or a function call,
-             * with different grammars: a cast would be cparseCastExp(),
-             * a function call would be cparsePostfixExp(CallExp(cparseArguments())).
-             * We can't know until t is known. So, parse it as a function call
-             * and let semantic() rewrite the AST as a CastExp if it turns out
-             * to be a type.
-             */
-            auto ie = new AST.IdentifierExp(loc, t.isTypeIdentifier().ident);
-            ie.parens = true;    // let semantic know it might be a CastExp
-            AST.Expression e = new AST.CallExp(loc, ie, cparseArguments());
-            return cparsePostfixOperators(e);
-        }
-
-        // ( type-name ) cast-expression
-        auto ce = cparseCastExp();
-        return new AST.CastExp(loc, ce, t);
+        return cparseUnaryExp();
     }
 
     /**************
@@ -1878,7 +1858,7 @@ final class CParser(AST) : Parser!AST
         bool first = true;
         while (1)
         {
-            Identifier id;
+            AST.ArgumentLabel id;
             AST.StringExp asmName;
             auto dt = cparseDeclarator(DTR.xdirect_fd, tspec, id, specifier);
             if (!dt)
@@ -1927,13 +1907,13 @@ final class CParser(AST) : Parser!AST
             }
 
 
-            // Check alignasExp and not alignExps so that gnu
+            // Check lastAlignas and not alignasExps so that gnu
             // __atribute__((aligned())) is silently allowed, matching the
             // behavior of other compilers.
-            if (specifier.alignasExp && dt.isTypeFunction())
-                error(specifier.alignasExp.loc, "no alignment-specifier for function declaration"); // C11 6.7.5-2
-            if (specifier.alignasExp && specifier.scw == SCW.xregister)
-                error(specifier.alignasExp.loc, "no alignment-specifier for `register` storage class"); // C11 6.7.5-2
+            if (specifier.lastAlignas && dt.isTypeFunction())
+                error(specifier.lastAlignas.loc, "no alignment-specifier for function declaration"); // C11 6.7.5-2
+            if (specifier.lastAlignas && specifier.scw == SCW.xregister)
+                error(specifier.lastAlignas.loc, "no alignment-specifier for `register` storage class"); // C11 6.7.5-2
 
             /* C11 6.9.1 Function Definitions
              * function-definition:
@@ -1945,11 +1925,10 @@ final class CParser(AST) : Parser!AST
              */
             auto t = &token;
             if (first &&                   // first declarator
-                id &&
+                id.name &&
                 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
+                t.value != TOK.endOfFile)
             {
                 auto s = cparseFunctionDefinition(id, dt.isTypeFunction(), specifier);
                 typedefTab.setDim(typedefTabLengthSave);
@@ -1977,8 +1956,8 @@ final class CParser(AST) : Parser!AST
             {
                 if (token.value == TOK.assign)
                     error("no initializer for typedef declaration");
-                if (specifier.alignasExp)
-                    error(specifier.alignasExp.loc, "no alignment-specifier for typedef declaration"); // C11 6.7.5-2
+                if (specifier.lastAlignas)
+                    error(specifier.lastAlignas.loc, "no alignment-specifier for typedef declaration"); // C11 6.7.5-2
 
                 if (specifier.vector_size)
                 {
@@ -1992,12 +1971,12 @@ final class CParser(AST) : Parser!AST
                 Identifier idt;
                 if (auto tt = dt.isTypeTag())
                 {
-                    if (!tt.id && id)
+                    if (!tt.id && id.name)
                         /* This applies for enums declared as
                          * typedef enum {A} E;
                          * Or for similar structs and unions.
                          */
-                        tt.id = id;
+                        tt.id = id.name;
                     if (tt.members)
                     {
                         Specifier spec;
@@ -2019,16 +1998,16 @@ final class CParser(AST) : Parser!AST
                 }
                 if (isalias)
                 {
-                    //printf("AliasDeclaration %s %s\n", id.toChars(), dt.toChars());
-                    auto ad = new AST.AliasDeclaration(token.loc, id, dt);
-                    if (id == idt)
+                    //printf("AliasDeclaration %s %s\n", id.name.toChars(), dt.toChars());
+                    auto ad = new AST.AliasDeclaration(id.loc, id.name, dt);
+                    if (id.name == idt)
                         ad.hidden = true; // do not print when generating .di files
                     s = ad;
                 }
 
-                insertTypedefToTypedefTab(id, dt);       // remember typedefs
+                insertTypedefToTypedefTab(id.name, dt);       // remember typedefs
             }
-            else if (id)
+            else if (id.name)
             {
                 if (auto tt = dt.isTypeTag())
                 {
@@ -2064,7 +2043,7 @@ final class CParser(AST) : Parser!AST
                     initializer = cparseInitializer();
                 }
                 // declare the symbol
-                assert(id);
+                assert(id.name);
 
                 if (isFunctionTypedef(dt))
                 {
@@ -2074,7 +2053,7 @@ final class CParser(AST) : Parser!AST
                         error("functions cannot be `_Thread_local`"); // C11 6.7.1-4
                     STC stc = specifiersToSTC(level, specifier);
                     stc &= ~STC.gshared;        // no gshared functions
-                    auto fd = new AST.FuncDeclaration(token.loc, Loc.initial, id, stc, dt, specifier.noreturn);
+                    auto fd = new AST.FuncDeclaration(id.loc, Loc.initial, id.name, stc, dt, specifier.noreturn);
                     specifiersToFuncDeclaration(fd, specifier);
                     s = fd;
                 }
@@ -2085,12 +2064,12 @@ final class CParser(AST) : Parser!AST
                     if (!hasInitializer &&
                         !(specifier.scw & (SCW.xextern | SCW.xstatic | SCW.x_Thread_local) || level == LVL.global))
                         initializer = new AST.VoidInitializer(token.loc);
-                    auto vd = new AST.VarDeclaration(token.loc, dt, id, initializer, specifiersToSTC(level, specifier));
+                    auto vd = new AST.VarDeclaration(id.loc, dt, id.name, initializer, specifiersToSTC(level, specifier));
                     specifiersToVarDeclaration(vd, specifier);
                     s = vd;
                 }
                 if (level != LVL.global)
-                    insertIdToTypedefTab(id);   // non-typedef declarations can hide typedefs in outer scopes
+                    insertIdToTypedefTab(id.name); // non-typedef declarations can hide typedefs in outer scopes
             }
             if (s !is null)
             {
@@ -2129,7 +2108,7 @@ final class CParser(AST) : Parser!AST
                 }
                 symbols.push(s);
             }
-            if (level == LVL.global && !id)
+            if (level == LVL.global && !id.name)
                 error("expected identifier for declaration");
 
             first = false;
@@ -2180,7 +2159,7 @@ final class CParser(AST) : Parser!AST
      * Returns:
      *  Dsymbol for the function
      */
-    AST.Dsymbol cparseFunctionDefinition(Identifier id, AST.TypeFunction ft, ref Specifier specifier)
+    AST.Dsymbol cparseFunctionDefinition(AST.ArgumentLabel id, AST.TypeFunction ft, ref Specifier specifier)
     {
         /* Start function scope
          */
@@ -2192,7 +2171,7 @@ final class CParser(AST) : Parser!AST
             do
             {
                 cparseDeclaration(LVL.parameter);
-            } while (token.value != TOK.leftCurly);
+            } while (token.value != TOK.leftCurly && token.value != TOK.endOfFile);
 
             /* Since there were declarations, the parameter-list must have been
              * an identifier-list.
@@ -2253,7 +2232,10 @@ final class CParser(AST) : Parser!AST
             }
         }
 
-        addFuncName = false;    // gets set to true if somebody references __func__ in this function
+        /* gets set to true if somebody references __func__ in this function, //ditto for __FUNCTION__ */
+        addFuncName = false;
+        refFuncName = false;
+        pretFuncName = false;
         const locFunc = token.loc;
 
         auto body = cparseStatement(ParseStatementFlags.curly);  // don't start a new scope; continue with parameter scope
@@ -2261,14 +2243,23 @@ final class CParser(AST) : Parser!AST
 
         STC stc = specifiersToSTC(LVL.global, specifier);
         stc &= ~STC.gshared;    // no gshared functions
-        auto fd = new AST.FuncDeclaration(locFunc, prevloc, id, stc, ft, specifier.noreturn);
+        auto fd = new AST.FuncDeclaration(id.loc, prevloc, id.name, stc, ft, specifier.noreturn);
         specifiersToFuncDeclaration(fd, specifier);
 
+        auto stmts = new AST.Statements();
+
         if (addFuncName)
-        {
-            auto s = createFuncName(locFunc, id);
-            body = new AST.CompoundStatement(locFunc, s, body);
-        }
+            stmts.push(createFuncName(locFunc, id.name, Id.__func__));
+
+        if (refFuncName)
+            stmts.push(createFuncName(locFunc, id.name, Id.FUNCTION));
+
+        if (pretFuncName)
+            stmts.push(createPrettyFunc(locFunc, fd));
+
+        stmts.push(body);
+
+        body = new AST.CompoundStatement(locFunc, stmts);
         fd.fbody = body;
 
         // TODO add `symbols` to the function's local symbol table `sc2` in FuncDeclaration::semantic3()
@@ -2416,14 +2407,13 @@ final class CParser(AST) : Parser!AST
         }
 
         AST.Type t;
-        Loc loc;
         //printf("parseDeclarationSpecifiers()\n");
 
         TKW tkw;
         SCW scw = specifier.scw & SCW.xtypedef;
         MOD mod;
-        Identifier id;
-        Identifier previd;
+        AST.ArgumentLabel id;
+        AST.ArgumentLabel previd;
 
     Lwhile:
         while (1)
@@ -2468,7 +2458,7 @@ final class CParser(AST) : Parser!AST
 
                 case TOK.identifier:
                     tkwx = TKW.xident;
-                    id = token.ident;
+                    id = AST.ArgumentLabel(token.ident, token.loc);
                     break;
 
                 case TOK.struct_:
@@ -2492,18 +2482,13 @@ final class CParser(AST) : Parser!AST
                 {
                     // C11 6.7.2.4
                     // type-specifier if followed by `( type-name )`
-                    auto tk = peek(&token);
-                    if (tk.value == TOK.leftParenthesis)
+                    if (peekNext() == TOK.leftParenthesis)
                     {
-                        tk = peek(tk);
-                        if (isTypeName(tk) && tk.value == TOK.rightParenthesis)
-                        {
-                            nextToken();
-                            nextToken();
-                            t = cparseTypeName();
-                            tkwx = TKW.x_Atomic;
-                            break;
-                        }
+                        nextToken();
+                        nextToken();
+                        t = cparseTypeName();
+                        tkwx = TKW.x_Atomic;
+                        break;
                     }
                     // C11 6.7.3 type-qualifier if not
                     modx = MOD.x_Atomic;
@@ -2512,37 +2497,11 @@ final class CParser(AST) : Parser!AST
 
                 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);
-                    specifier.alignasExp = exp;
-
-                    check(TOK.rightParenthesis);
+                    auto exp = cparseAlignasSpecifier(level);
+                    if (!specifier.alignasExps)
+                        specifier.alignasExps = new AST.Expressions();
+                    specifier.alignasExps.push(exp);
+                    specifier.lastAlignas = exp;
                     break;
                 }
 
@@ -2566,46 +2525,9 @@ final class CParser(AST) : Parser!AST
 
                 case TOK.typeof_:
                 {
-                    nextToken();
-                    check(TOK.leftParenthesis);
-
-                    auto tk = &token;
-                    AST.Expression e;
-                    if (isTypeName(tk))
-                        e = new AST.TypeExp(loc, cparseTypeName());
-                    else
-                        e = cparseExpression();
-                    t = new AST.TypeTypeof(loc, e);
-
-                    if(token.value == TOK.rightParenthesis)
-                        nextToken();
-                    else
-                    {
-                        t = AST.Type.terror;
-                        error("`typeof` operator expects an expression or type name in parentheses");
-
-                        // skipParens et. al expect to be on the opening parenthesis
-                        int parens;
-                        loop: while(1)
-                        {
-                            switch(token.value)
-                            {
-                                case TOK.leftParenthesis:
-                                    parens++;
-                                    break;
-                                case TOK.rightParenthesis:
-                                    parens--;
-                                    if(parens < 0)
-                                        goto case;
-                                    break;
-                                case TOK.endOfFile:
-                                    break loop;
-                                default:
-                            }
-                            nextToken();
-                        }
-                    }
-
+                    /* GNU extension (added in C23)
+                     */
+                    t = cparseTypeofSpecifier();
                     tkwx = TKW.xtag;
                     break;
                 }
@@ -2758,7 +2680,7 @@ final class CParser(AST) : Parser!AST
 
             case TKW.xident:
             {
-                const idx = previd.toString();
+                const idx = previd.name.toString();
                 if (idx.length > 2 && idx[0] == '_' && idx[1] == '_')  // leading double underscore
                     importBuiltins = true;  // probably one of those compiler extensions
                 t = null;
@@ -2766,12 +2688,12 @@ final class CParser(AST) : Parser!AST
                 /* Punch through to what the typedef is, to support things like:
                  *  typedef T* T;
                  */
-                auto pt = lookupTypedef(previd);
+                auto pt = lookupTypedef(previd.name);
                 if (pt && *pt)      // if previd is a known typedef
                     t = *pt;
 
                 if (!t)
-                    t = new AST.TypeIdentifier(loc, previd);
+                    t = new AST.TypeIdentifier(previd.loc, previd.name);
                 break;
             }
 
@@ -2788,6 +2710,85 @@ final class CParser(AST) : Parser!AST
         return t;
     }
 
+    /**************
+     * C11 6.7.5
+     * alignment-specifier:
+     *    _Alignas ( type-name )
+     *    _Alignas ( constant-expression )
+     */
+    private AST.Expression cparseAlignasSpecifier(LVL level)
+    {
+        if (level & (LVL.parameter | LVL.prototype))
+            error("no alignment-specifier for parameters"); // C11 6.7.5-2
+
+        AST.Expression exp;
+        nextToken();
+        check(TOK.leftParenthesis);
+        if (startsTypeName(&token))
+        {
+            // _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();
+        }
+        check(TOK.rightParenthesis);
+        return exp;
+    }
+
+    /**************
+     * C23 6.7.2.5
+     * typeof-specifier:
+     *    typeof ( expression )
+     *    typeof ( type-name )
+     *    typeof_unqual ( expression )  // not implemented
+     *    typeof_unqual ( type-name )   // not implemented
+     */
+    private AST.Type cparseTypeofSpecifier()
+    {
+        nextToken();
+        check(TOK.leftParenthesis);
+
+        AST.Expression e;
+        if (startsTypeName(&token))
+            e = new AST.TypeExp(loc, cparseTypeName());
+        else
+            e = cparseExpression();
+
+        if (token.value != TOK.rightParenthesis)
+        {
+            // skipParens et. al expect to be on the opening parenthesis
+            error("`typeof` operator expects an expression or type name in parentheses");
+            int parens;
+            loop: while(1)
+            {
+                switch(token.value)
+                {
+                    case TOK.leftParenthesis:
+                        parens++;
+                        break;
+                    case TOK.rightParenthesis:
+                        parens--;
+                        if (parens < 0)
+                            goto case;
+                        break;
+                    case TOK.endOfFile:
+                        break loop;
+                    default:
+                }
+                nextToken();
+            }
+            return AST.Type.terror;
+        }
+        nextToken();
+        return new AST.TypeTypeof(loc, e);
+    }
+
     /********************************
      * C11 6.7.6
      * Parse a declarator (including function definitions).
@@ -2839,7 +2840,7 @@ final class CParser(AST) : Parser!AST
      *  declared struct, union or enum tags.
      */
     private AST.Type cparseDeclarator(DTR declarator, AST.Type tbase,
-        out Identifier pident, ref Specifier specifier)
+        out AST.ArgumentLabel pident, ref Specifier specifier)
     {
         //printf("cparseDeclarator(%d, %s)\n", declarator, tbase.toChars());
         AST.Types constTypes; // all the Types that will need `const` applied to them
@@ -2882,7 +2883,7 @@ final class CParser(AST) : Parser!AST
                     //printf("identifier %s\n", token.ident.toChars());
                     if (declarator == DTR.xabstract)
                         error("identifier not allowed in abstract-declarator");
-                    pident = token.ident;
+                    pident = AST.ArgumentLabel(token.ident, token.loc);
                     ts = t;
                     nextToken();
                     break;
@@ -3057,7 +3058,7 @@ final class CParser(AST) : Parser!AST
                 }
                 break;
             }
-            if (declarator == DTR.xdirect && !pident)
+            if (declarator == DTR.xdirect && !pident.name)
                 error("expected identifier for declarator");
             return ts;
         }
@@ -3163,7 +3164,7 @@ final class CParser(AST) : Parser!AST
             tspec = toConst(tspec);
             specifier.mod = MOD.xnone;      // 'used' it
         }
-        Identifier id;
+        AST.ArgumentLabel id;
         return cparseDeclarator(DTR.xabstract, tspec, id, specifier);
     }
 
@@ -3241,6 +3242,7 @@ final class CParser(AST) : Parser!AST
 
             Specifier specifier;
             specifier.packalign.setDefault();
+            const typeLoc = token.loc;
             auto tspec = cparseDeclarationSpecifiers(LVL.prototype, specifier);
             if (!tspec)
             {
@@ -3258,15 +3260,15 @@ final class CParser(AST) : Parser!AST
                 specifier.mod = MOD.xnone;      // 'used' it
             }
 
-            Identifier id;
-            const paramLoc = token.loc;
+            AST.ArgumentLabel id;
             auto t = cparseDeclarator(DTR.xparameter, tspec, id, specifier);
             if (token.value == TOK.__attribute__)
                 cparseGnuAttributes(specifier);
             if (specifier.mod & MOD.xconst)
                 t = toConst(t);
-            auto param = new AST.Parameter(paramLoc, specifiersToSTC(LVL.parameter, specifier),
-                                           t, id, null, null);
+            auto param = new AST.Parameter(id.name ? id.loc : typeLoc,
+                                           specifiersToSTC(LVL.parameter, specifier),
+                                           t, id.name, null, null);
             parameters.push(param);
             if (token.value == TOK.rightParenthesis || token.value == TOK.endOfFile)
                 break;
@@ -3410,7 +3412,7 @@ final class CParser(AST) : Parser!AST
                         if (n < 1 || n & (n - 1) || 8192 < n)
                             error("__decspec(align(%lld)) must be an integer positive power of 2 and be <= 8,192", cast(ulong)n);
                         specifier.packalign.set(cast(uint)n);
-                        specifier.packalign.setPack(true);
+                        specifier.packalign.setPack();
                         nextToken();
                     }
                     else
@@ -3664,9 +3666,9 @@ final class CParser(AST) : Parser!AST
                 {
                     nextToken();
                     AST.Expression exp = cparseConstantExp();
-                    if (!specifier.alignExps)
-                        specifier.alignExps = new AST.Expressions(0);
-                    specifier.alignExps.push(exp);
+                    if (!specifier.alignAttrs)
+                        specifier.alignAttrs = new AST.Expressions();
+                    specifier.alignAttrs.push(exp);
                     check(TOK.rightParenthesis);
                 }
                 else
@@ -3679,7 +3681,7 @@ final class CParser(AST) : Parser!AST
             else if (token.ident == Id.packed)
             {
                 specifier.packalign.set(1);
-                specifier.packalign.setPack(true);
+                specifier.packalign.setPack();
                 nextToken();
             }
             else if (token.ident == Id.always_inline) // https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html
@@ -4091,7 +4093,7 @@ final class CParser(AST) : Parser!AST
          * redeclaration, or reference to existing declaration.
          * Defer to the semantic() pass with a TypeTag.
          */
-        return new AST.TypeTag(loc, structOrUnion, tag, tagSpecifier.packalign, tagSpecifier.alignExps, null, members);
+        return new AST.TypeTag(loc, structOrUnion, tag, tagSpecifier.packalign, tagSpecifier.alignAttrs, null, members);
     }
 
     /*************************************
@@ -4187,7 +4189,7 @@ final class CParser(AST) : Parser!AST
 
         while (1)
         {
-            Identifier id;
+            AST.ArgumentLabel id;
             AST.Type dt;
             if (token.value == TOK.colon)
             {
@@ -4198,7 +4200,7 @@ final class CParser(AST) : Parser!AST
                 }
 
                 // C11 6.7.2.1-12 unnamed bit-field
-                id = Identifier.generateAnonymousId("BitField");
+                id.name = Identifier.generateAnonymousId("BitField");
                 dt = tspec;
             }
             else
@@ -4217,6 +4219,8 @@ final class CParser(AST) : Parser!AST
             {
                 // C11 6.7.2.1-10 bit-field
                 nextToken();
+                if (id.name.isAnonymous)
+                    id.loc = token.loc;
                 width = cparseConstantExp();
             }
 
@@ -4232,12 +4236,12 @@ final class CParser(AST) : Parser!AST
                 error("specifier-qualifier-list required");
             else if (width)
             {
-                if (specifier.alignasExp)
-                    error(specifier.alignasExp.loc, "no alignment-specifier for bit field declaration"); // C11 6.7.5-2
-                auto s = new AST.BitFieldDeclaration(width.loc, dt, id, width);
+                if (specifier.lastAlignas)
+                    error(specifier.lastAlignas.loc, "no alignment-specifier for bit field declaration"); // C11 6.7.5-2
+                auto s = new AST.BitFieldDeclaration(id.loc, dt, id.name, width);
                 members.push(s);
             }
-            else if (id)
+            else if (id.name)
             {
                 if (dt.ty == AST.Tvoid)
                     error("`void` has no value");
@@ -4245,7 +4249,7 @@ final class CParser(AST) : Parser!AST
                 // declare the symbol
                 // Give member variables an implicit void initializer
                 auto initializer = new AST.VoidInitializer(token.loc);
-                AST.Dsymbol s = new AST.VarDeclaration(token.loc, dt, id, initializer, specifiersToSTC(LVL.member, specifier));
+                AST.Dsymbol s = new AST.VarDeclaration(id.loc, dt, id.name, initializer, specifiersToSTC(LVL.member, specifier));
                 s = applySpecifier(s, specifier);
                 members.push(s);
             }
@@ -4282,229 +4286,54 @@ final class CParser(AST) : Parser!AST
     /************************************
      * 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)
+     *  t = first token
      * Returns:
-     *      true at start of a declaration
+     *  true if at start of a declaration
      */
-    private bool isCDeclaration(ref Token* pt)
+    private bool startsDeclaration(Token* t)
     {
-        auto t = pt;
-        //printf("isCDeclaration() %s\n", t.toChars());
-        if (!isDeclarationSpecifiers(t))
+        // Labels aren't declarations.
+        if (token.value == TOK.identifier && peek(t).value == TOK.colon)
             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;
+        if (token.value == TOK._Static_assert)
             return true;
-        }
 
-        // skip over assignment-expression, ending before comma or semiColon or EOF
-        if (!isAssignmentExpression(t))
-            return false;
-        pt = t;
-        return true;
-    }
+        if (startsDeclarationSpecifiers(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 (startsTypeName(t))
+            return true;
 
-        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;
+        return false;
     }
 
     /********************************
-     * See if match for assignment-expression.
+     * Is this the start of a declaration-specifiers?
      * Params:
-     *  pt = starting token, updated to one past end of assignment-expression if true
+     *  t = first token
      * Returns:
-     *  true if assignment-expression
+     *  true if declaration-specifiers
      */
-    private bool isAssignmentExpression(ref Token* pt)
+    private bool startsDeclarationSpecifiers(Token* t)
     {
-        auto t = pt;
-        //printf("isAssignmentExpression() %s\n", t.toChars());
-
-        /* 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;
+        bool seenType;
         while (1)
         {
             switch (t.value)
             {
-                case TOK.comma:
-                case TOK.semicolon:
-                case TOK.rightParenthesis:
-                case TOK.rightBracket:
-                case TOK.endOfFile:
-                case TOK.endOfLine:
-                    if (!any)
-                        return false;
-                    break;
-
-                case TOK.leftParenthesis:
-                    if (!skipParens(t, &t))
-                        return false;
-                    /*
-                        https://issues.dlang.org/show_bug.cgi?id=22267
-                        If the parser encounters the following
-                            `identifier variableName = (expression);`
-                        the initializer is not identified as such since the parentheses
-                        cause the parser to keep walking indefinitely
-                        (whereas `(1) + 1` would not be affected.).
-                    */
-                    any = true;
-                    continue;
-
-                case TOK.leftBracket:
-                    if (!skipBrackets(t))
-                        return false;
-                    continue;
-
-                case TOK.leftCurly:
-                    if (!skipBraces(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;
+                // typedef-name
+                case TOK.identifier:
+                    if (!seenType)
+                    {
+                        if (isTypedef(t.ident))
+                            return true;
+                        t = peek(t);
+                        seenType = true;
+                        continue;
+                    }
+                    return true;
 
-        bool seenType;
-        bool any;
-        while (1)
-        {
-            switch (t.value)
-            {
                 // type-specifiers
                 case TOK.void_:
                 case TOK.char_:
@@ -4519,50 +4348,12 @@ final class CParser(AST) : Parser!AST
                 case TOK._Bool:
                 //case TOK._Imaginary:
                 case TOK._Complex:
-                    t = peek(t);
-                    seenType = true;
-                    any = true;
-                    continue;
-
-                case TOK.identifier: // typedef-name
-                    if (!seenType)
-                    {
-                        t = peek(t);
-                        seenType = true;
-                        any = true;
-                        continue;
-                    }
-                    break;
 
+                // struct-or-union-specifier
+                // enum-specifier
                 case TOK.struct_:
                 case TOK.union_:
                 case TOK.enum_:
-                    t = peek(t);
-                    if (t.value == TOK.__attribute__ ||
-                        t.value == TOK.__declspec)
-                    {
-                        t = peek(t);
-                        if (!skipParens(t, &t))
-                            return false;
-                    }
-                    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_:
@@ -4582,114 +4373,18 @@ final class CParser(AST) : Parser!AST
                 case TOK.volatile:
                 case TOK.restrict:
                 case TOK.__stdcall:
-                    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;
+                // atomic-type-specifier
+                case TOK._Atomic:
+                case TOK.typeof_:
+                    return true;
 
                 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;
             }
         }
     }
@@ -4838,296 +4533,58 @@ final class CParser(AST) : Parser!AST
         return true;
     }
 
-    /***************************
+    /*******************************************
      * Is this the start of a type-name?
      * Params:
-     *  pt = first token; updated with past end of type-name if true
+     *  t = first token
      * Returns:
      *  true if start of type-name
      */
-    private bool isTypeName(ref Token* pt)
+    private bool startsTypeName(Token* t)
     {
-        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:
-                case TOK.__stdcall:
-
-                // Type Specifiers
-                case TOK.char_:
-                case TOK.signed:
-                case TOK.unsigned:
-                case TOK.int16:
-                case TOK.int32:
-                case TOK.int64:
-                case TOK.__int128:
-                case TOK.float32:
-                case TOK.float64:
-                case TOK.void_:
-                case TOK._Bool:
-                //case TOK._Imaginary: // ? missing in Spec
-                case TOK._Complex:
-                    t = peek(t);
-                    break;
-
-                case TOK.identifier:
-                    // Use typedef table to disambiguate
-                    if (isTypedef(t.ident))
-                    {
-                        t = peek(t);
-                        break;
-                    }
-                    else
-                    {
-                        return false;
-                    }
-
-                // 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:
-                case TOK.typeof_:
-                case TOK.__attribute__:
-                    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)
-    {
-        enum log = false;
-        if (log) printf("isCastExpression(tk: `%s`, afterParenType: %d)\n", token.toChars(pt.value), afterParenType);
-        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 (tk.value == TOK.leftParenthesis && peek(tk).value == TOK.rightParenthesis)
-                {
-                    return false;    // (type-name)() is not a cast (it might be a function call)
-                }
-
-                if (!isCastExpression(tk, true))
-                {
-                    if (afterParenType) // could be ( type-name ) ( unary-expression )
-                        goto default;   // where unary-expression also matched type-name
-                    return true;
-                }
-                // ( 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;
-        if (log) printf("isCastExpression true\n");
-        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.identifier:
+                // Use typedef table to disambiguate
+                return isTypedef(t.ident);
 
-            case TOK._Alignof:
-            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;
+            // Type Qualifiers
+            case TOK.const_:
+            case TOK.restrict:
+            case TOK.volatile:
+            case TOK.__stdcall:
 
-            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;
-    }
+            // Type Specifiers
+            case TOK.char_:
+            case TOK.signed:
+            case TOK.unsigned:
+            case TOK.int16:
+            case TOK.int32:
+            case TOK.int64:
+            case TOK.__int128:
+            case TOK.float32:
+            case TOK.float64:
+            case TOK.void_:
+            case TOK._Bool:
 
-    /********************************
-     * 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.charLiteral:
-            case TOK.wcharLiteral:
-            case TOK.dcharLiteral:
-            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._Imaginary: // ? missing in Spec
+            case TOK._Complex:
 
-            case TOK.leftParenthesis:
-                // ( expression )
-                if (!skipParens(t, &t))
-                    return false;
-                break;
+            // struct-or-union-specifier
+            // enum-specifier
+            case TOK.struct_:
+            case TOK.union_:
+            case TOK.enum_:
 
-            case TOK._Generic:
-                t = peek(t);
-                if (!skipParens(t, &t))
-                    return false;
-                break;
+            // atomic-type-specifier
+            case TOK._Atomic:
+            case TOK.typeof_:
+            case TOK.__attribute__:
+                return true;
 
             default:
                 return false;
         }
-        pt = t;
-        return true;
     }
 
     //}
@@ -5201,9 +4658,10 @@ final class CParser(AST) : Parser!AST
 
         SCW scw;        /// storage-class specifiers
         MOD mod;        /// type qualifiers
-        AST.Expressions*  alignExps;  /// alignment
-        AST.Expression alignasExp; /// Last _Alignas() for errors
-        structalign_t packalign;  /// #pragma pack alignment value
+        AST.Expressions*  alignAttrs;   /// __attribute__((aligned))
+        AST.Expressions*  alignasExps;  /// _Alignas()
+        AST.Expression lastAlignas;     /// Last _Alignas() for errors
+        structalign_t packalign;        /// #pragma pack alignment value
     }
 
     /***********************
@@ -5411,18 +4869,48 @@ final class CParser(AST) : Parser!AST
      *    loc = location for this declaration
      *    id = identifier of function
      * Returns:
-     *    statement representing the declaration of __func__
+     *    statement representing the declaration of __func__, __FUNCTION__, or __PRETTY-FUNCTION__
      */
-    private AST.Statement createFuncName(Loc loc, Identifier id)
+    private AST.Statement createFuncName(Loc loc, Identifier id, Identifier cident)
     {
         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 lenfn = new AST.IntegerExp(loc, fn.length + 1, AST.Type.tuns32); // +1 for terminating 0, ditto for __pretty_chars__
+        auto tfn = new AST.TypeSArray(AST.Type.tchar, lenfn);
+        efn.type = tfn.makeImmutable();
+        efn.committed = true;
+        auto sfn = new AST.VarDeclaration(loc, tfn, cident, ifn, STC.gshared | STC.immutable_);
+        auto e = new AST.DeclarationExp(loc, sfn);
+        return new AST.ExpStatement(loc, e);
+    }
+
+    /* function for C __PRETTY_FUNCTION__ for non-linux systems */
+    private AST.Statement createPrettyFunc(Loc loc, AST.FuncDeclaration fd)
+    {
+        auto tf = fd.type.isTypeFunction();
+        auto funcSig = tf.next.toString();
+        funcSig ~= " ";
+        funcSig ~= fd.ident.toString();
+        funcSig ~= "(";
+
+        foreach (i, fparam ; tf.parameterList)
+        {
+            if (i > 0)
+                funcSig ~= ", ";
+
+            AST.Type t = tf.parameterList[i].type;
+            funcSig ~= t.toString();
+        }
+        funcSig ~= ")";
+
+        auto efn = new AST.StringExp(loc, funcSig);
+        auto ifn = new AST.ExpInitializer(loc, efn);
+        auto lenfn = new AST.IntegerExp(loc, funcSig.length + 1, AST.Type.tuns32); // +1 for terminating 0
         auto tfn = new AST.TypeSArray(AST.Type.tchar, lenfn);
         efn.type = tfn.makeImmutable();
         efn.committed = true;
-        auto sfn = new AST.VarDeclaration(loc, tfn, Id.__func__, ifn, STC.gshared | STC.immutable_);
+        auto sfn = new AST.VarDeclaration(loc, tfn, Id.PRETTY_FUNCTION, ifn, STC.gshared | STC.immutable_);
         auto e = new AST.DeclarationExp(loc, sfn);
         return new AST.ExpStatement(loc, e);
     }
@@ -5449,7 +4937,7 @@ final class CParser(AST) : Parser!AST
         // `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_);
+            tf.next = AST.addSTC(tf.next, STC.const_);
         else if (auto tt = t.isTypeTag())
             tt.mod |= MODFlags.const_;
         else
@@ -5458,7 +4946,7 @@ final class CParser(AST) : Parser!AST
              */
             auto tn = t.nextOf();
             if (!tn || tn.isConst())
-                t = t.addSTC(STC.const_);
+                t = AST.addSTC(t, STC.const_);
         }
         return t;
     }
@@ -5485,13 +4973,23 @@ final class CParser(AST) : Parser!AST
             }
         }
 
-        if (specifier.alignExps)
+        if (specifier.alignAttrs)
+        {
+            //printf("  applying attribute((aligned)) %s\n", (*specifier.alignAttrs)[0].toChars());
+            // Wrap declaration in an AlignDeclaration
+            auto decls = new AST.Dsymbols(1);
+            (*decls)[0] = s;
+            s = new AST.AlignDeclaration(s.loc, specifier.alignAttrs, decls);
+        }
+        if (specifier.alignasExps)
         {
-            //printf("  applying _Alignas %s, packalign %d\n", (*specifier.alignExps)[0].toChars(), cast(int)specifier.packalign);
+            //printf("  applying _Alignas %s, packalign %d\n", (*specifier.alignasExps)[0].toChars(), cast(int)specifier.packalign);
             // Wrap declaration in an AlignDeclaration
             auto decls = new AST.Dsymbols(1);
             (*decls)[0] = s;
-            s = new AST.AlignDeclaration(s.loc, specifier.alignExps, decls);
+            auto ad = new AST.AlignDeclaration(s.loc, specifier.alignasExps, decls);
+            ad.salign.setAlignas();
+            s = ad;
         }
         else if (!specifier.packalign.isDefault() && !specifier.packalign.isUnknown())
         {
@@ -5791,7 +5289,7 @@ final class CParser(AST) : Parser!AST
             if (n < 1 || n & (n - 1) || ushort.max < n)
                 error(loc, "pack must be an integer positive power of 2, not 0x%llx", cast(ulong)n);
             packalign.set(cast(uint)n);
-            packalign.setPack(true);
+            packalign.setPack();
         }
 
         scan(&n);
index 58a00341d4e0b1b97c4f0df2dddacdadf15c2e42..8087db2d25a6635baf94a3b1200da883b1124f5f 100644 (file)
@@ -25,18 +25,17 @@ import dmd.dstruct;
 import dmd.dtemplate;
 import dmd.errors;
 import dmd.expression;
+import dmd.expressionsem;
 import dmd.func;
 import dmd.globals : dinteger_t, sinteger_t, uinteger_t;
 import dmd.location;
 import dmd.mtype;
 import dmd.root.bitarray;
-import dmd.root.complex;
 import dmd.root.ctfloat;
 import dmd.root.port;
 import dmd.root.rmem;
 import dmd.tokens;
 import dmd.typesem;
-import dmd.visitor;
 
 /****************************************************************/
 /* A type meant as a union of all the Expression types,
@@ -777,13 +776,16 @@ Expression pointerDifference(UnionExp* pue, Loc loc, Type type, Expression e1, E
 // and op is EXP.add or EXP.min
 Expression pointerArithmetic(UnionExp* pue, Loc loc, EXP op, Type type, Expression eptr, Expression e2)
 {
-    if (eptr.type.nextOf().ty == Tvoid)
+    Expression cant()
     {
-        error(loc, "cannot perform arithmetic on `void*` pointers at compile time");
-    Lcant:
         emplaceExp!(CTFEExp)(pue, EXP.cantExpression);
         return pue.exp();
     }
+    if (eptr.type.nextOf().ty == Tvoid)
+    {
+        error(loc, "cannot perform arithmetic on `void*` pointers at compile time");
+        return cant();
+    }
     if (eptr.op == EXP.address)
         eptr = eptr.isAddrExp().e1;
     dinteger_t ofs1;
@@ -793,13 +795,13 @@ Expression pointerArithmetic(UnionExp* pue, Loc loc, EXP op, Type type, Expressi
         if (agg1.isSymOffExp().var.type.ty != Tsarray)
         {
             error(loc, "cannot perform pointer arithmetic on arrays of unknown length at compile time");
-            goto Lcant;
+            return cant();
         }
     }
     else if (agg1.op != EXP.string_ && agg1.op != EXP.arrayLiteral)
     {
         error(loc, "cannot perform pointer arithmetic on non-arrays at compile time");
-        goto Lcant;
+        return cant();
     }
     dinteger_t ofs2 = e2.toInteger();
     Type pointee = agg1.type.toBasetype().nextOf();
@@ -825,12 +827,12 @@ Expression pointerArithmetic(UnionExp* pue, Loc loc, EXP op, Type type, Expressi
     else
     {
         error(loc, "CTFE internal error: bad pointer operation");
-        goto Lcant;
+        return cant();
     }
     if (indx < 0 || len < indx)
     {
         error(loc, "cannot assign pointer to index %lld inside memory block `[0..%lld]`", indx, len);
-        goto Lcant;
+        return cant();
     }
     if (agg1.op == EXP.symbolOffset)
     {
@@ -842,7 +844,7 @@ Expression pointerArithmetic(UnionExp* pue, Loc loc, EXP op, Type type, Expressi
     if (agg1.op != EXP.arrayLiteral && agg1.op != EXP.string_)
     {
         error(loc, "CTFE internal error: pointer arithmetic `%s`", agg1.toChars());
-        goto Lcant;
+        return cant();
     }
     if (auto tsa = eptr.type.toBasetype().isTypeSArray())
     {
index ce474c9bbef16d7938e95b0bdd83ff30548abfe7..eb25405efba57c25105d183e7ee24f3b2c17a49d 100644 (file)
@@ -15,8 +15,8 @@ import dmd.arraytypes;
 import dmd.astenums;
 import dmd.attrib;
 import dmd.common.outbuffer : OutBuffer;
-import dmd.dclass : ClassDeclaration;
-import dmd.declaration : TypeInfoDeclaration;
+import dmd.dclass : ClassDeclaration, BaseClass;
+import dmd.declaration : TypeInfoDeclaration, VarDeclaration, TupleDeclaration;
 import dmd.denum : EnumDeclaration;
 import dmd.dmodule /*: Module*/;
 import dmd.dscope : Scope;
@@ -32,6 +32,9 @@ import dmd.init : Initializer, NeedInterpret;
 import dmd.location : Loc;
 import dmd.mtype /*: Covariant, Type, Parameter, ParameterList*/;
 import dmd.rootobject : RootObject;
+import dmd.root.optional;
+import dmd.root.longdouble : real_t = longdouble;
+import dmd.root.complex;
 import dmd.semantic3;
 import dmd.statement : Statement, AsmStatement, GccAsmStatement;
 
@@ -110,14 +113,6 @@ void mangleToBuffer(TemplateInstance ti, ref OutBuffer buf)
     return dmd.mangle.mangleToBuffer(ti, buf);
 }
 
-/***********************************************************
- * dmodule.d
- */
-FuncDeclaration findGetMembers(ScopeDsymbol dsym)
-{
-    return dmd.dmodule.findGetMembers(dsym);
-}
-
 /***********************************************************
  * doc.d
  */
@@ -224,6 +219,109 @@ bool isPOD(StructDeclaration sd)
     return dmd.dsymbolsem.isPOD(sd);
 }
 
+bool fillVtbl(BaseClass* bc, ClassDeclaration cd, FuncDeclarations* vtbl, int newinstance)
+{
+    import dmd.dsymbolsem;
+    return dmd.dsymbolsem.fillVtbl(bc, cd, vtbl, newinstance);
+}
+
+bool overloadInsert(Dsymbol ds, Dsymbol s)
+{
+    import dmd.dsymbolsem;
+    return dmd.dsymbolsem.overloadInsert(ds, s);
+}
+
+bool equals(const Dsymbol ds, const Dsymbol s)
+{
+    import dmd.dsymbolsem;
+    return dmd.dsymbolsem.equals(ds, s);
+}
+
+Type getType(Dsymbol ds)
+{
+    import dmd.dsymbolsem;
+    return dmd.dsymbolsem.getType(ds);
+}
+
+uinteger_t size(Dsymbol ds, Loc loc)
+{
+    import dmd.dsymbolsem;
+    return dmd.dsymbolsem.size(ds, loc);
+}
+
+void semantic3OnDependencies(Module m)
+{
+    import dmd.dsymbolsem;
+    return dmd.dsymbolsem.semantic3OnDependencies(m);
+}
+
+void addDeferredSemantic(Dsymbol s)
+{
+    import dmd.dsymbolsem;
+    return dmd.dsymbolsem.addDeferredSemantic(s);
+}
+
+void addDeferredSemantic2(Dsymbol s)
+{
+    import dmd.dsymbolsem;
+    return dmd.dsymbolsem.addDeferredSemantic2(s);
+}
+
+void addDeferredSemantic3(Dsymbol s)
+{
+    import dmd.dsymbolsem;
+    return dmd.dsymbolsem.addDeferredSemantic3(s);
+}
+
+void runDeferredSemantic()
+{
+    import dmd.dsymbolsem;
+    return dmd.dsymbolsem.runDeferredSemantic();
+}
+
+void runDeferredSemantic2()
+{
+    import dmd.dsymbolsem;
+    return dmd.dsymbolsem.runDeferredSemantic2();
+}
+
+void runDeferredSemantic3()
+{
+    import dmd.dsymbolsem;
+    return dmd.dsymbolsem.runDeferredSemantic3();
+}
+
+bool isOverlappedWith(VarDeclaration vd, VarDeclaration v)
+{
+    import dmd.dsymbolsem;
+    return dmd.dsymbolsem.isOverlappedWith(vd, v);
+}
+
+Scope* newScope(AggregateDeclaration ad, Scope* sc)
+{
+    import dmd.dsymbolsem;
+    return dmd.dsymbolsem.newScope(ad, sc);
+}
+
+Dsymbol search(Scope* sc, Loc loc, Identifier ident, out Dsymbol pscopesym,
+    SearchOptFlags flags = SearchOpt.all)
+{
+    import dmd.dsymbolsem;
+    return dmd.dsymbolsem.search(sc, loc, ident, pscopesym, flags);
+}
+
+void addObjcSymbols(Dsymbol sym, ClassDeclarations* classes, ClassDeclarations* categories)
+{
+    import dmd.dsymbolsem;
+    return dmd.dsymbolsem.addObjcSymbols(sym, classes, categories);
+}
+
+FuncDeclaration findGetMembers(ScopeDsymbol dsym)
+{
+    import dmd.dsymbolsem;
+    return dmd.dsymbolsem.findGetMembers(dsym);
+}
+
 /***********************************************************
  * dtemplate.d
  */
@@ -275,10 +373,10 @@ void printInstantiationTrace(TemplateInstance ti)
 /***********************************************************
  * dtoh.d
  */
-void genCppHdrFiles(ref Modules ms)
+void genCppHdrFiles(ref Modules ms, ErrorSink eSink)
 {
     import dmd.dtoh;
-    return dmd.dtoh.genCppHdrFiles(ms);
+    return dmd.dtoh.genCppHdrFiles(ms, eSink);
 }
 
 /***********************************************************
@@ -290,17 +388,17 @@ Expression getDefaultValue(EnumDeclaration ed, Loc loc)
     return dmd.enumsem.getDefaultValue(ed, loc);
 }
 
+
 /***********************************************************
- * expression.d
+ * expressionsem.d
  */
+
 void expandTuples(Expressions* exps, ArgumentLabels* names = null)
 {
-    return dmd.expression.expandTuples(exps, names);
+    import dmd.expressionsem;
+    return dmd.expressionsem.expandTuples(exps, names);
 }
 
-/***********************************************************
- * expressionsem.d
- */
 Expression expressionSemantic(Expression e, Scope* sc)
 {
     import dmd.expressionsem;
@@ -314,22 +412,93 @@ bool fill(StructDeclaration sd, Loc loc,
     return dmd.expressionsem.fill(sd, loc, elements, ctorinit);
 }
 
+bool isIdentical(const Expression exp, const Expression e)
+{
+    import dmd.expressionsem;
+    return dmd.expressionsem.isIdentical(exp, e);
+}
+
+bool equals(const Expression exp, const Expression e)
+{
+    import dmd.expressionsem;
+    return dmd.expressionsem.equals(exp, e);
+}
+
+bool isLvalue(Expression exp)
+{
+    import dmd.expressionsem;
+    return dmd.expressionsem.isLvalue(exp);
+}
+
+int getFieldIndex(ClassReferenceExp cre, Type fieldtype, uint fieldoffset)
+{
+    import dmd.expressionsem;
+    return dmd.expressionsem.getFieldIndex(cre, fieldtype, fieldoffset);
+}
+
+void fillTupleExpExps(TupleExp te, TupleDeclaration tup)
+{
+    import dmd.expressionsem;
+    return dmd.expressionsem.fillTupleExpExps(te, tup);
+}
+
+Optional!bool toBool(Expression exp)
+{
+    import dmd.expressionsem;
+    return dmd.expressionsem.toBool(exp);
+}
+
+StringExp toStringExp(Expression exp)
+{
+    import dmd.expressionsem;
+    return dmd.expressionsem.toStringExp(exp);
+}
+
+dinteger_t toInteger(Expression exp)
+{
+    import dmd.expressionsem;
+    return dmd.expressionsem.toInteger(exp);
+}
+
+uinteger_t toUInteger(Expression exp)
+{
+    import dmd.expressionsem;
+    return dmd.expressionsem.toUInteger(exp);
+}
+
+real_t toReal(Expression exp)
+{
+    import dmd.expressionsem;
+    return dmd.expressionsem.toReal(exp);
+}
+
+complex_t toComplex(Expression exp)
+{
+    import dmd.expressionsem;
+    return dmd.expressionsem.toComplex(exp);
+}
+
+real_t toImaginary(Expression exp)
+{
+    import dmd.expressionsem;
+    return dmd.expressionsem.toImaginary(exp);
+}
+
 /***********************************************************
- * func.d
+ * funcsem.d
  */
 FuncDeclaration genCfunc(Parameters* fparams, Type treturn, const(char)* name, StorageClass stc = STC.none)
 {
-    return FuncDeclaration.genCfunc(fparams, treturn, name, cast(STC) stc);
+    import dmd.funcsem;
+    return dmd.funcsem.genCfunc(fparams, treturn, name, cast(STC) stc);
 }
 
 FuncDeclaration genCfunc(Parameters* fparams, Type treturn, Identifier id, StorageClass stc = STC.none)
 {
-    return FuncDeclaration.genCfunc(fparams, treturn, id, cast(STC) stc);
+    import dmd.funcsem;
+    return dmd.funcsem.genCfunc(fparams, treturn, id, cast(STC) stc);
 }
 
-/***********************************************************
- * funcsem.d
- */
 bool functionSemantic(FuncDeclaration fd)
 {
     import dmd.funcsem;
@@ -354,6 +523,12 @@ PURE isPure(FuncDeclaration fd)
     return dmd.funcsem.isPure(fd);
 }
 
+bool needsClosure(FuncDeclaration fd)
+{
+    import dmd.funcsem;
+    return dmd.funcsem.needsClosure(fd);
+}
+
 /***********************************************************
  * hdrgen.d
  */
@@ -461,14 +636,6 @@ JsonFieldFlags tryParseJsonField(const(char)* fieldName)
     return dmd.json.tryParseJsonField(fieldName);
 }
 
-/***********************************************************
- * mtype.d
- */
-AggregateDeclaration isAggregate(Type t)
-{
-    return dmd.mtype.isAggregate(t);
-}
-
 /***********************************************************
  * optimize.d
  */
@@ -528,6 +695,18 @@ bool tpsemantic(TemplateParameter tp, Scope* sc, TemplateParameters* parameters)
 /***********************************************************
  * typesem.d
  */
+bool hasDeprecatedAliasThis(Type type)
+{
+    import dmd.typesem;
+    return dmd.typesem.hasDeprecatedAliasThis(type);
+}
+
+AggregateDeclaration isAggregate(Type t)
+{
+    import dmd.typesem;
+    return dmd.typesem.isAggregate(t);
+}
+
 bool hasPointers(Type t)
 {
     import dmd.typesem;
@@ -558,6 +737,12 @@ Type merge2(Type type)
     return dmd.typesem.merge2(type);
 }
 
+Type toBasetype(Type type)
+{
+    import dmd.typesem;
+    return dmd.typesem.toBasetype(type);
+}
+
 Expression defaultInit(Type mt, Loc loc, const bool isCfile = false)
 {
     import dmd.typesem;
@@ -721,6 +906,12 @@ Type referenceTo(Type type)
     return dmd.typesem.referenceTo(type);
 }
 
+Type memType(TypeEnum type)
+{
+    import dmd.typesem;
+    return dmd.typesem.memType(type);
+}
+
 uinteger_t size(Type type)
 {
     import dmd.typesem;
@@ -733,6 +924,18 @@ uinteger_t size(Type type, Loc loc)
     return dmd.typesem.size(type, loc);
 }
 
+structalign_t alignment(Type type)
+{
+    import dmd.typesem;
+    return dmd.typesem.alignment(type);
+}
+
+uint alignsize(Type type)
+{
+    import dmd.typesem;
+    return dmd.typesem.alignsize(type);
+}
+
 MATCH implicitConvTo(Type from, Type to)
 {
     import dmd.dcast;
@@ -751,6 +954,186 @@ Expression defaultInitLiteral(Type t, Loc loc)
     return dmd.typesem.defaultInitLiteral(t, loc);
 }
 
+bool hasUnsafeBitpatterns(Type type)
+{
+    import dmd.typesem;
+    return dmd.typesem.hasUnsafeBitpatterns(type);
+}
+
+bool hasInvariant(Type type)
+{
+    import dmd.typesem;
+    return dmd.typesem.hasInvariant(type);
+}
+
+bool hasVoidInitPointers(Type type)
+{
+    import dmd.typesem;
+    return dmd.typesem.hasVoidInitPointers(type);
+}
+
+void Type_init()
+{
+    import dmd.typesem;
+    return dmd.typesem.Type_init();
+}
+
+void transitive(TypeNext type)
+{
+    import dmd.typesem;
+    return dmd.typesem.transitive(type);
+}
+
+Type makeConst(Type type)
+{
+    import dmd.typesem;
+    return dmd.typesem.makeConst(type);
+}
+
+Type makeImmutable(Type type)
+{
+    import dmd.typesem;
+    return dmd.typesem.makeImmutable(type);
+}
+
+Type makeMutable(Type type)
+{
+    import dmd.typesem;
+    return dmd.typesem.makeMutable(type);
+}
+
+Type makeShared(Type type)
+{
+    import dmd.typesem;
+    return dmd.typesem.makeShared(type);
+}
+
+Type makeSharedConst(Type type)
+{
+    import dmd.typesem;
+    return dmd.typesem.makeSharedConst(type);
+}
+
+Type makeWild(Type type)
+{
+    import dmd.typesem;
+    return dmd.typesem.makeWild(type);
+}
+
+Type makeWildConst(Type type)
+{
+    import dmd.typesem;
+    return dmd.typesem.makeWildConst(type);
+}
+
+Type makeSharedWild(Type type)
+{
+    import dmd.typesem;
+    return dmd.typesem.makeSharedWild(type);
+}
+
+Type makeSharedWildConst(Type type)
+{
+    import dmd.typesem;
+    return dmd.typesem.makeSharedWildConst(type);
+}
+
+Type nextOf(Type type)
+{
+    import dmd.typesem;
+    return dmd.typesem.nextOf(type);
+}
+
+Type baseElemOf(Type type)
+{
+    import dmd.typesem;
+    return dmd.typesem.baseElemOf(type);
+}
+
+Type isLazyArray(Parameter param)
+{
+    import dmd.typesem;
+    return dmd.typesem.isLazyArray(param);
+}
+
+MOD deduceWild(Type type, Type t, bool isRef)
+{
+    import dmd.typesem;
+    return dmd.typesem.deduceWild(type, t, isRef);
+}
+
+bool isIntegral(Type type)
+{
+    import dmd.typesem;
+    return dmd.typesem.isIntegral(type);
+}
+
+bool isFloating(Type type)
+{
+    import dmd.typesem;
+    return dmd.typesem.isFloating(type);
+}
+
+bool isScalar(Type type)
+{
+    import dmd.typesem;
+    return dmd.typesem.isScalar(type);
+}
+
+bool isReal(Type type)
+{
+    import dmd.typesem;
+    return dmd.typesem.isReal(type);
+}
+
+bool isComplex(Type type)
+{
+    import dmd.typesem;
+    return dmd.typesem.isComplex(type);
+}
+
+bool isImaginary(Type type)
+{
+    import dmd.typesem;
+    return dmd.typesem.isImaginary(type);
+}
+
+bool isString(Type type)
+{
+    import dmd.typesem;
+    return dmd.typesem.isString(type);
+}
+
+bool isBoolean(Type type)
+{
+    import dmd.typesem;
+    return dmd.typesem.isBoolean(type);
+}
+
+bool isUnsigned(Type type)
+{
+    import dmd.typesem;
+    return dmd.typesem.isUnsigned(type);
+}
+
+bool needsNested(Type type)
+{
+    import dmd.typesem;
+    return dmd.typesem.needsNested(type);
+}
+
+bool needsDestruction(Type type)
+{
+    import dmd.typesem;
+    return dmd.typesem.needsDestruction(type);
+}
+
+bool needsCopyOrPostblit(Type type)
+{
+    import dmd.typesem;
+    return dmd.typesem.needsCopyOrPostblit(type);
+}
+
 /***********************************************************
  * typinf.d
  */
@@ -787,6 +1170,12 @@ TypeInfoDeclaration getTypeInfoAssocArrayDeclaration(TypeAArray t, Scope* sc)
 /**
  * templatesem.d
  */
+bool declareParameter(TemplateParameter tp, Scope* sc)
+{
+    import dmd.templatesem;
+    return dmd.templatesem.declareParameter(tp, sc);
+}
+
 bool needsCodegen(TemplateInstance ti)
 {
     import dmd.templatesem;
index 4ac8b1f44711a6b9bfef2f8202e19b56c4898dce..8ab49b3c470fce1a9b1f4c213fff72a9a5a0ea29 100644 (file)
@@ -14,7 +14,6 @@ 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;
@@ -90,44 +89,42 @@ Expression implicitCastTo(Expression e, Scope* sc, Type t)
             }
 
             auto ad = isAggregate(e.type);
-            if (ad && ad.aliasthis)
+            if (!ad || !ad.aliasthis)
+                return e.castTo(sc, t);
+
+            if (!ad.type || ad.type.isTypeError())
+                return e;
+            auto ts = ad.type.isTypeStruct();
+            const adMatch = ts
+                ? ts.implicitConvToWithoutAliasThis(t)
+                : ad.type.isTypeClass().implicitConvToWithoutAliasThis(t);
+
+            if (adMatch)
+                return e.castTo(sc, t);
+
+            Type tob = t.toBasetype();
+            Type t1b = e.type.toBasetype();
+            if (ad == isAggregate(tob))
+                return e.castTo(sc, t);
+
+            if (t1b.ty == Tclass && tob.ty == Tclass)
             {
-                if (!ad.type || ad.type.isTypeError())
-                    return e;
-                auto ts = ad.type.isTypeStruct();
-                const adMatch = ts
-                    ? ts.implicitConvToWithoutAliasThis(t)
-                    : ad.type.isTypeClass().implicitConvToWithoutAliasThis(t);
-
-                if (!adMatch)
+                ClassDeclaration t1cd = t1b.isClassHandle();
+                ClassDeclaration tocd = tob.isClassHandle();
+                int offset;
+                if (tocd.isBaseOf(t1cd, &offset))
                 {
-                    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))
-                            {
-                                auto result = new CastExp(e.loc, e, t);
-                                result.type = t;
-                                return result;
-                            }
-                        }
-
-                        /* Forward the cast to our alias this member, rewrite to:
-                         *   cast(to)e1.aliasthis
-                         */
-                        auto result = resolveAliasThis(sc, e);
-                        return result.castTo(sc, t);
-                   }
+                    auto result = new CastExp(e.loc, e, t);
+                    result.type = t;
+                    return result;
                 }
             }
 
-            return e.castTo(sc, t);
+            /* Forward the cast to our alias this member, rewrite to:
+             *   cast(to)e1.aliasthis
+             */
+            auto result = resolveAliasThis(sc, e);
+            return result.castTo(sc, t);
         }
 
         auto result = e.optimize(WANTvalue);
@@ -136,54 +133,54 @@ Expression implicitCastTo(Expression e, Scope* sc, Type t)
             return implicitCastTo(result, sc, t);
         }
 
-        if (t.ty != Terror && e.type.ty != Terror)
+        if (t.ty == Terror || e.type.ty == Terror)
+            return ErrorExp.get();
+
+        if (!t.deco)
+        {
+            error(e.loc, "forward reference to type `%s`", t.toChars());
+            return ErrorExp.get();
+        }
+
+        //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);
+
+        // Special case for improved diagnostic when const to mutable conversion
+        // fails due to struct/union having pointers
+        if (e.type.ty == Tstruct && t.ty == Tstruct &&
+            e.type.isTypeStruct().sym == t.isTypeStruct().sym &&
+            e.type.mod == MODFlags.const_ && t.mod == 0 && e.type.hasPointers)
         {
-            if (!t.deco)
+            auto sym = e.type.isTypeStruct().sym;
+            error(e.loc, "cannot implicitly convert expression `%s` of type `%s` to `%s` because %s `%s` contains pointers or references",
+                e.toErrMsg(), ts[0], ts[1], sym.kind(), sym.toErrMsg());
+            return ErrorExp.get();
+        }
+
+        // Special case for pointer conversions
+        if (e.type.toBasetype().ty == Tpointer && t.toBasetype().ty == Tpointer)
+        {
+            Type fromPointee = e.type.nextOf();
+            Type toPointee = t.nextOf();
+            // Const -> mutable conversion (disallowed)
+            if (fromPointee.isConst() && !toPointee.isConst())
             {
-                error(e.loc, "forward reference to type `%s`", t.toChars());
+                error(e.loc, "cannot implicitly convert `%s` to `%s`", e.type.toChars(), t.toChars());
+                errorSupplemental(e.loc, "Note: Converting const to mutable requires an explicit cast (`cast(int*)`).");
+                return ErrorExp.get();
             }
-            else
+            // Incompatible pointee types (e.g., int* -> float* )
+            else if (fromPointee.toBasetype().ty != toPointee.toBasetype().ty)
             {
-                //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);
-
-                // Special case for improved diagnostic when const to mutable conversion
-                // fails due to struct/union having pointers
-                if (e.type.ty == Tstruct && t.ty == Tstruct &&
-                    e.type.isTypeStruct().sym == t.isTypeStruct().sym &&
-                    e.type.mod == MODFlags.const_ && t.mod == 0 && e.type.hasPointers)
-                {
-                    auto sym = e.type.isTypeStruct().sym;
-                    error(e.loc, "cannot implicitly convert expression `%s` of type `%s` to `%s` because %s `%s` contains pointers or references",
-                        e.toErrMsg(), ts[0], ts[1], sym.kind(), sym.toErrMsg());
-                    return ErrorExp.get();
-                }
-
-                // Special case for pointer conversions
-                if (e.type.toBasetype().ty == Tpointer && t.toBasetype().ty == Tpointer)
-                {
-                    Type fromPointee = e.type.nextOf();
-                    Type toPointee = t.nextOf();
-                    // Const -> mutable conversion (disallowed)
-                    if (fromPointee.isConst() && !toPointee.isConst())
-                    {
-                        error(e.loc, "cannot implicitly convert `%s` to `%s`", e.type.toChars(), t.toChars());
-                        errorSupplemental(e.loc, "Note: Converting const to mutable requires an explicit cast (`cast(int*)`).");
-                        return ErrorExp.get();
-                    }
-                    // Incompatible pointee types (e.g., int* -> float* )
-                    else if (fromPointee.toBasetype().ty != toPointee.toBasetype().ty)
-                    {
-                        error(e.loc, "cannot implicitly convert `%s` to `%s`", e.type.toChars(), t.toChars());
-                        errorSupplemental(e.loc, "Note: Pointer types point to different base types (`%s` vs `%s`)", fromPointee.toChars(), toPointee.toChars());
-                        return ErrorExp.get();
-                    }
-                }
-                error(e.loc, "cannot implicitly convert expression `%s` of type `%s` to `%s`", e.toErrMsg(), ts[0], ts[1]);
+                error(e.loc, "cannot implicitly convert `%s` to `%s`", e.type.toChars(), t.toChars());
+                errorSupplemental(e.loc, "Note: Pointer types point to different base types (`%s` vs `%s`)", fromPointee.toChars(), toPointee.toChars());
+                return ErrorExp.get();
             }
         }
+        error(e.loc, "cannot implicitly convert expression `%s` of type `%s` to `%s`", e.toErrMsg(), ts[0], ts[1]);
+
         return ErrorExp.get();
     }
 
@@ -683,7 +680,10 @@ MATCH implicitConvTo(Expression e, Type t)
                 {
                     if (e.committed && tynto != tyn)
                         return MATCH.nomatch;
-                    size_t fromlen = e.numberOfCodeUnits(tynto);
+                    string s;
+                    size_t fromlen = e.numberOfCodeUnits(tynto, s);
+                    if (s)
+                        error(e.loc, "%.*s", cast(int)s.length, s.ptr);
                     size_t tolen = cast(size_t)t.isTypeSArray().dim.toInteger();
                     if (tolen < fromlen)
                         return MATCH.nomatch;
@@ -705,7 +705,10 @@ MATCH implicitConvTo(Expression e, Type t)
                 {
                     if (e.committed && tynto != tyn)
                         return MATCH.nomatch;
-                    size_t fromlen = e.numberOfCodeUnits(tynto);
+                    string s;
+                    size_t fromlen = e.numberOfCodeUnits(tynto, s);
+                    if (s)
+                        error(e.loc, "%.*s", cast(int)s.length, s.ptr);
                     size_t tolen = cast(size_t)t.isTypeSArray().dim.toInteger();
                     if (tolen < fromlen)
                         return MATCH.nomatch;
@@ -1316,73 +1319,69 @@ MATCH implicitConvTo(Expression e, Type t)
             if (sd.isNested())
                 return MATCH.nomatch;
         }
-        if (ntb.isZeroInit(e.loc))
+        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;
+            Expression earg = e.newtype.defaultInitLiteral(e.loc);
+            Type targ = e.newtype.toBasetype();
 
-                cd.size(e.loc); // resolve any forward references
+            if (implicitMod(earg, targ, mod) == MATCH.nomatch)
+                return MATCH.nomatch;
+            return MATCH.constant;
+        }
+        /* Zeros are implicitly convertible, except for special cases.
+         */
+        auto tc = ntb.isTypeClass();
+        if (!tc)
+            return MATCH.constant;
 
-                if (cd.isNested())
-                    return MATCH.nomatch; // uplevel reference may not be convertible
+        /* With new() must look at the class instance initializer.
+         */
+        ClassDeclaration cd = tc.sym;
+
+        cd.size(e.loc); // resolve any forward references
 
-                assert(!cd.isInterfaceDeclaration());
+        if (cd.isNested())
+            return MATCH.nomatch; // uplevel reference may not be convertible
 
-                struct ClassCheck
+        assert(!cd.isInterfaceDeclaration());
+
+        static bool convertible(Expression e, 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)
                 {
-                    extern (C++) static bool convertible(Expression e, ClassDeclaration cd, MOD mod)
+                    if (_init.isVoidInitializer())
+                    {
+                    }
+                    else if (ExpInitializer ei = _init.isExpInitializer())
                     {
-                        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())
-                                {
-                                    // 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;
+                        // 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;
                     }
                 }
-
-                if (!ClassCheck.convertible(e, cd, mod))
-                    return MATCH.nomatch;
+                else if (!v.type.isZeroInit(e.loc))
+                    return false;
             }
+            return cd.baseClass ? convertible(e, cd.baseClass, mod) : true;
         }
-        else
-        {
-            Expression earg = e.newtype.defaultInitLiteral(e.loc);
-            Type targ = e.newtype.toBasetype();
 
-            if (implicitMod(earg, targ, mod) == MATCH.nomatch)
-                return MATCH.nomatch;
-        }
+        if (!convertible(e, cd, mod))
+            return MATCH.nomatch;
 
         /* Success
          */
@@ -1662,40 +1661,42 @@ MATCH implicitConvTo(Type from, Type to)
             }
             return MATCH.nomatch;
         }
-        if (auto tsa = to.isTypeSArray())
-        {
-            if (from == to)
-                return MATCH.exact;
+        auto tsa = to.isTypeSArray();
+        if (!tsa)
+            return MATCH.nomatch;
 
-            if (from.dim.equals(tsa.dim))
-            {
-                MATCH m = from.next.implicitConvTo(tsa.next);
+        if (from == to)
+            return MATCH.exact;
 
-                /* Allow conversion to non-interface base class.
-                 */
-                if (m == MATCH.convert &&
-                    from.next.ty == Tclass)
-                {
-                    if (auto toc = tsa.next.isTypeClass)
-                    {
-                        if (!toc.sym.isInterfaceDeclaration)
-                            return MATCH.convert;
-                    }
-                }
+        if (!from.dim.equals(tsa.dim))
+            return MATCH.nomatch;
 
-                /* 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 (from.mod != to.mod)
-                        m = MATCH.constant;
-                    return m;
-                }
+        MATCH m = from.next.implicitConvTo(tsa.next);
+
+        /* Allow conversion to non-interface base class.
+         */
+        if (m == MATCH.convert &&
+            from.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 (from.mod != to.mod)
+                m = MATCH.constant;
+            return m;
+        }
+
         return MATCH.nomatch;
     }
 
@@ -1705,25 +1706,26 @@ MATCH implicitConvTo(Type from, Type to)
         if (from.equals(to))
             return MATCH.exact;
 
-        if (auto ta = to.isTypeDArray())
-        {
-            if (!MODimplicitConv(from.next.mod, ta.next.mod))
-                return MATCH.nomatch; // not const-compatible
+        auto ta = to.isTypeDArray();
+        if (!ta)
+            return visitType(from);
 
-            /* Allow conversion to void[]
-             */
-            if (from.next.ty != Tvoid && ta.next.ty == Tvoid)
-            {
-                return MATCH.convert;
-            }
+        if (!MODimplicitConv(from.next.mod, ta.next.mod))
+            return MATCH.nomatch; // not const-compatible
 
-            MATCH m = from.next.constConv(ta.next);
-            if (m > MATCH.nomatch)
-            {
-                if (m == MATCH.exact && from.mod != to.mod)
-                    m = MATCH.constant;
-                return m;
-            }
+        /* Allow conversion to void[]
+         */
+        if (from.next.ty != Tvoid && ta.next.ty == Tvoid)
+        {
+            return MATCH.convert;
+        }
+
+        MATCH m = from.next.constConv(ta.next);
+        if (m > MATCH.nomatch)
+        {
+            if (m == MATCH.exact && from.mod != to.mod)
+                m = MATCH.constant;
+            return m;
         }
 
         return visitType(from);
@@ -1735,21 +1737,23 @@ MATCH implicitConvTo(Type from, Type to)
         if (from.equals(to))
             return MATCH.exact;
 
-        if (auto ta = to.isTypeAArray())
-        {
-            if (!MODimplicitConv(from.next.mod, ta.next.mod))
-                return MATCH.nomatch; // not const-compatible
+        auto ta = to.isTypeAArray();
+        if (!ta)
+            return visitType(from);
 
-            if (!MODimplicitConv(from.index.mod, ta.index.mod))
-                return MATCH.nomatch; // not const-compatible
+        if (!MODimplicitConv(from.next.mod, ta.next.mod))
+            return MATCH.nomatch; // not const-compatible
 
-            MATCH m = from.next.constConv(ta.next);
-            MATCH mi = from.index.constConv(ta.index);
-            if (m > MATCH.nomatch && mi > MATCH.nomatch)
-            {
-                return MODimplicitConv(from.mod, to.mod) ? MATCH.constant : MATCH.nomatch;
-            }
+        if (!MODimplicitConv(from.index.mod, ta.index.mod))
+            return MATCH.nomatch; // not const-compatible
+
+        MATCH m = from.next.constConv(ta.next);
+        MATCH mi = from.index.constConv(ta.index);
+        if (m > MATCH.nomatch && mi > MATCH.nomatch)
+        {
+            return MODimplicitConv(from.mod, to.mod) ? MATCH.constant : MATCH.nomatch;
         }
+
         return visitType(from);
     }
 
@@ -1772,26 +1776,24 @@ MATCH implicitConvTo(Type from, Type to)
         if (tf.equals(to))
             return MATCH.constant;
 
-        if (tf.covariant(to) == Covariant.yes)
+        if (tf.covariant(to) != Covariant.yes)
+            return MATCH.nomatch;
+
+        Type tret = tf.nextOf();
+        Type toret = to.nextOf();
+        if (tret.ty == Tclass && toret.ty == Tclass)
         {
-            Type tret = tf.nextOf();
-            Type toret = to.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;
+            /* 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.nomatch;
+        return MATCH.convert;
     }
 
     MATCH visitPointer(TypePointer from)
@@ -1841,19 +1843,17 @@ MATCH implicitConvTo(Type from, Type to)
         if (from.equals(to))
             return MATCH.exact;
 
-        if (auto toDg = to.isTypeDelegate())
-        {
-            MATCH m = implicitPointerConv(from.next.isTypeFunction(), toDg.next);
-
-            // Retain the old behaviour for this refactoring
-            // Should probably be changed to constant to match function pointers
-            if (m > MATCH.convert)
-                m = MATCH.convert;
+        auto toDg = to.isTypeDelegate();
+        if (!toDg)
+            return MATCH.nomatch;
 
-            return m;
-        }
+        MATCH m = implicitPointerConv(from.next.isTypeFunction(), toDg.next);
 
-        return MATCH.nomatch;
+        // Retain the old behaviour for this refactoring
+        // Should probably be changed to constant to match function pointers
+        if (m > MATCH.convert)
+            return MATCH.convert;
+        return m;
     }
 
     MATCH visitStruct(TypeStruct from)
@@ -1889,23 +1889,23 @@ MATCH implicitConvTo(Type from, Type to)
     {
         if (from == to)
             return MATCH.exact;
-        if (auto tt = to.isTypeTuple())
+        auto tt = to.isTypeTuple();
+        if (!tt)
+            return MATCH.nomatch;
+
+        if (from.arguments.length != tt.arguments.length)
+            return MATCH.nomatch;
+
+        MATCH m = MATCH.exact;
+        for (size_t i = 0; i < tt.arguments.length; i++)
         {
-            if (from.arguments.length == tt.arguments.length)
-            {
-                MATCH m = MATCH.exact;
-                for (size_t i = 0; i < tt.arguments.length; i++)
-                {
-                    Parameter arg1 = (*from.arguments)[i];
-                    Parameter arg2 = (*tt.arguments)[i];
-                    MATCH mi = arg1.type.implicitConvTo(arg2.type);
-                    if (mi < m)
-                        m = mi;
-                }
-                return m;
-            }
+            Parameter arg1 = (*from.arguments)[i];
+            Parameter arg2 = (*tt.arguments)[i];
+            MATCH mi = arg1.type.implicitConvTo(arg2.type);
+            if (mi < m)
+                m = mi;
         }
-        return MATCH.nomatch;
+        return m;
     }
 
     MATCH visitNull(TypeNull from)
@@ -2092,6 +2092,13 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null)
         const(bool) tob_isA = ((tob.isIntegral() || tob.isFloating()) && tob.ty != Tvector);
         const(bool) t1b_isA = ((t1b.isIntegral() || t1b.isFloating()) && t1b.ty != Tvector);
 
+        Expression ok()
+        {
+            auto result = new CastExp(e.loc, e, t);
+            result.type = t; // Don't call semantic()
+            //printf("Returning: %s\n", result.toChars());
+            return result;
+        }
         // Try casting the alias this member.
         // Return the expression if it succeeds, null otherwise.
         Expression tryAliasThisCast()
@@ -2109,6 +2116,21 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null)
         }
 
         bool hasAliasThis;
+
+        Expression fail()
+        {
+            /* if the cast cannot be performed, maybe there is an alias
+             * this that can be used for casting.
+             */
+            if (hasAliasThis)
+            {
+                if (auto result = tryAliasThisCast())
+                    return result;
+            }
+            error(e.loc, "cannot cast expression `%s` of type `%s` to `%s`", e.toChars(), e.type.toChars(), t.toChars());
+            return ErrorExp.get();
+        }
+
         if (AggregateDeclaration t1ad = isAggregate(t1b))
         {
             AggregateDeclaration toad = isAggregate(tob);
@@ -2120,7 +2142,7 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null)
                     ClassDeclaration tocd = tob.isClassHandle();
                     int offset;
                     if (tocd.isBaseOf(t1cd, &offset))
-                        goto Lok;
+                        return ok();
                 }
                 hasAliasThis = true;
             }
@@ -2131,7 +2153,7 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null)
             {
                 // Casting static array to vector with same size, e.g. `cast(int4) int[4]`
                 if (t1b.size(e.loc) != tob.size(e.loc))
-                    goto Lfail;
+                    return fail();
                 return new VectorExp(e.loc, e, tob).expressionSemantic(sc);
             }
             //printf("test1 e = %s, e.type = %s, tob = %s\n", e.toChars(), e.type.toChars(), tob.toChars());
@@ -2147,9 +2169,9 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null)
             if (tob.ty == Tsarray)
             {
                 if (t1b.size(e.loc) == tob.size(e.loc))
-                    goto Lok;
+                    return ok();
             }
-            goto Lfail;
+            return fail();
         }
         else if (t1b.implicitConvTo(tob) == MATCH.constant && t.equals(e.type.constOf()))
         {
@@ -2162,13 +2184,13 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null)
         // arithmetic values vs. T*
         if (tob_isA && (t1b_isA || t1b.ty == Tpointer) || t1b_isA && (tob_isA || tob.ty == Tpointer))
         {
-            goto Lok;
+            return ok();
         }
 
         // arithmetic values vs. references or fat values
         if (tob_isA && (t1b_isR || t1b_isFV) || t1b_isA && (tob_isR || tob_isFV))
         {
-            goto Lfail;
+            return fail();
         }
 
         // Bugzlla 3133: A cast between fat values is possible only when the sizes match.
@@ -2181,7 +2203,7 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null)
             }
 
             if (t1b.size(e.loc) == tob.size(e.loc))
-                goto Lok;
+                return ok();
 
             auto ts = toAutoQualChars(e.type, t);
             error(e.loc, "cannot cast expression `%s` of type `%s` to `%s` because of different sizes",
@@ -2218,9 +2240,9 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null)
                         return ErrorExp.get();
                     }
                 }
-                goto Lok;
+                return ok();
             }
-            goto Lfail;
+            return fail();
         }
 
         /* For references, any reinterpret casts are allowed to same 'ty' type.
@@ -2232,14 +2254,14 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null)
          *      class/interface A to B  (will be a dynamic cast if possible)
          */
         if (tob.ty == t1b.ty && tob_isR && t1b_isR)
-            goto Lok;
+            return ok();
 
         // 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
+            return fail(); // 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;
+            return ok();
 
         // Check size mismatch of references.
         // Tarray and Tdelegate are (void*).sizeof*2, but others have (void*).sizeof.
@@ -2249,7 +2271,7 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null)
             {
                 // T[] da;
                 // cast(U*)da; // ==> cast(U*)da.ptr;
-                goto Lok;
+                return ok();
             }
             if (tob.ty == Tpointer && t1b.ty == Tdelegate)
             {
@@ -2257,31 +2279,17 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null)
                 // cast(U*)dg; // ==> cast(U*)dg.ptr;
                 // Note that it happens even when U is a Tfunction!
                 deprecation(e.loc, "casting from %s to %s is deprecated", e.type.toChars(), t.toChars());
-                goto Lok;
+                return ok();
             }
-            goto Lfail;
+            return fail();
         }
 
         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)
-            {
-                if (auto result = tryAliasThisCast())
-                    return result;
-            }
-            error(e.loc, "cannot cast expression `%s` of type `%s` to `%s`", e.toChars(), e.type.toChars(), t.toChars());
-            return ErrorExp.get();
+            return fail();
         }
+        return ok();
 
-    Lok:
-        auto result = new CastExp(e.loc, e, t);
-        result.type = t; // Don't call semantic()
-        //printf("Returning: %s\n", result.toChars());
-        return result;
     }
 
     Expression visitError(ErrorExp e)
@@ -2291,34 +2299,32 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null)
 
     Expression visitReal(RealExp e)
     {
-        if (!e.type.equals(t))
+        if (e.type.equals(t))
+            return e;
+
+        if ((e.type.isReal() && t.isReal()) || (e.type.isImaginary() && t.isImaginary()))
         {
-            if ((e.type.isReal() && t.isReal()) || (e.type.isImaginary() && t.isImaginary()))
-            {
-                auto result = e.copy();
-                result.type = t;
-                return result;
-            }
-            else
-                return visit(e);
+            auto result = e.copy();
+            result.type = t;
+            return result;
         }
-        return e;
+        else
+            return visit(e);
     }
 
     Expression visitComplex(ComplexExp e)
     {
-        if (!e.type.equals(t))
+        if (e.type.equals(t))
+            return e;
+
+        if (e.type.isComplex() && t.isComplex())
         {
-            if (e.type.isComplex() && t.isComplex())
-            {
-                auto result = e.copy();
-                result.type = t;
-                return result;
-            }
-            else
-                return visit(e);
+            auto result = e.copy();
+            result.type = t;
+            return result;
         }
-        return e;
+        else
+            return visit(e);
     }
 
     Expression visitStructLiteral(StructLiteralExp e)
@@ -2469,7 +2475,7 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null)
         }
 
         if (e.committed)
-            goto Lcast;
+            return lcast();
 
         static auto X(T, U)(T tf, U tt)
         {
@@ -2582,7 +2588,7 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null)
 
             default:
                 assert(typeb.nextOf().size() != tb.nextOf().size());
-                goto Lcast;
+                return lcast();
             }
         }
     L2:
@@ -2609,11 +2615,6 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null)
         }
         se.type = t;
         return se;
-
-    Lcast:
-        auto result = new CastExp(e.loc, se, t);
-        result.type = t; // so semantic() won't be run on e
-        return result;
     }
 
     Expression visitAddr(AddrExp e)
@@ -3170,47 +3171,50 @@ Expression inferType(Expression e, Type t, int flag = 0)
     Expression visitAle(ArrayLiteralExp ale)
     {
         Type tb = t.toBasetype();
-        if (tb.isStaticOrDynamicArray())
+        if (!tb.isStaticOrDynamicArray())
+            return ale;
+
+        Type tn = tb.nextOf();
+        if (ale.basis)
+            ale.basis = inferType(ale.basis, tn, flag);
+        for (size_t i = 0; i < ale.elements.length; i++)
         {
-            Type tn = tb.nextOf();
-            if (ale.basis)
-                ale.basis = inferType(ale.basis, tn, flag);
-            for (size_t i = 0; i < ale.elements.length; i++)
+            if (Expression e = (*ale.elements)[i])
             {
-                if (Expression e = (*ale.elements)[i])
-                {
-                    e = inferType(e, tn, flag);
-                    (*ale.elements)[i] = e;
-                }
+                e = inferType(e, tn, flag);
+                (*ale.elements)[i] = e;
             }
         }
+
         return ale;
     }
 
     Expression visitAar(AssocArrayLiteralExp aale)
     {
         Type tb = t.toBasetype();
-        if (auto taa = tb.isTypeAArray())
+        auto taa = tb.isTypeAArray();
+        if (!taa)
+            return aale;
+
+        Type ti = taa.index;
+        Type tv = taa.nextOf();
+        for (size_t i = 0; i < aale.keys.length; i++)
         {
-            Type ti = taa.index;
-            Type tv = taa.nextOf();
-            for (size_t i = 0; i < aale.keys.length; i++)
+            if (Expression e = (*aale.keys)[i])
             {
-                if (Expression e = (*aale.keys)[i])
-                {
-                    e = inferType(e, ti, flag);
-                    (*aale.keys)[i] = e;
-                }
+                e = inferType(e, ti, flag);
+                (*aale.keys)[i] = e;
             }
-            for (size_t i = 0; i < aale.values.length; i++)
+        }
+        for (size_t i = 0; i < aale.values.length; i++)
+        {
+            if (Expression e = (*aale.values)[i])
             {
-                if (Expression e = (*aale.values)[i])
-                {
-                    e = inferType(e, tv, flag);
-                    (*aale.values)[i] = e;
-                }
+                e = inferType(e, tv, flag);
+                (*aale.values)[i] = e;
             }
         }
+
         return aale;
     }
 
@@ -3447,6 +3451,60 @@ Lagain:
     t1b = t1.toBasetype();
     t2b = t2.toBasetype();
 
+    static bool isComplexStruct(Type t)
+    {
+        if (t.ty != Tstruct)
+            return false;
+
+        TypeStruct ts = t.isTypeStruct();
+
+        return ts.sym.toString() == "_Complex";
+    }
+
+    static bool isComplexStructOfType(Type t, Type t2)
+    {
+        if (!isComplexStruct(t))
+            return false;
+
+        TypeStruct ts = t.toBasetype().isTypeStruct();
+
+        Type memberType = ts.sym.fields[0].type.toBasetype();
+
+        /* encure the complex member types fall under one of the complex types */
+        switch (memberType.toBasetype().ty)
+        {
+            case Tfloat32:
+            case Tfloat64:
+            case Tfloat80:
+                break;
+            default:
+                return false;
+        }
+
+        switch (t2.toBasetype().ty)
+        {
+            case Tfloat32:
+            case Tfloat64:
+            case Tfloat80:
+                return true;
+            default:
+                return false;
+        }
+    }
+
+    /* check for complex structure and an associate complex type in a single condexp */
+    if (sc && sc.inCfile)
+    {
+        if (isComplexStructOfType(t1b, t2b))
+        {
+            return Lret(e1.type.toBasetype());
+        }
+        else if (isComplexStructOfType(t2b, t1b))
+        {
+            return Lret(e2.type.toBasetype());
+        }
+    }
+
     const ty = implicitConvCommonTy(t1b.ty, t2b.ty);
     if (ty != Terror)
     {
@@ -4239,37 +4297,39 @@ Expression integralPromotions(Expression e, Scope* sc)
 void fix16997(Scope* sc, UnaExp ue)
 {
     if (global.params.fix16997 || sc.inCfile)
+    {
         ue.e1 = integralPromotions(ue.e1, sc);          // desired C-like behavor
-    else
+        return;
+    }
+
+    switch (ue.e1.type.toBasetype.ty)
     {
-        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:
-                deprecation(ue.loc, "integral promotion not done for `%s`, remove '-revert=intpromote' switch or `%scast(int)(%s)`",
-                    ue.toChars(), EXPtoString(ue.op).ptr, ue.e1.toChars());
-                break;
+        case Tint8:
+        case Tuns8:
+        case Tint16:
+        case Tuns16:
+        //case Tbool:       // these operations aren't allowed on bool anyway
+        case Tchar:
+        case Twchar:
+        case Tdchar:
+            deprecation(ue.loc, "integral promotion not done for `%s`, remove '-revert=intpromote' switch or `%scast(int)(%s)`",
+                ue.toChars(), EXPtoString(ue.op).ptr, ue.e1.toChars());
+            return;
 
-            default:
-                break;
-        }
+        default:
+            return;
     }
 }
 
 /***********************************
- * See if both types are arrays that can be compared
+ * See if an AA key 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 (D) bool arrayTypeCompatibleWithoutCasting(Type t1, Type t2)
+extern (D) bool keyCompatibleWithoutCasting(Expression ekey, Type t2)
 {
+    Type t1 = ekey.type;
     t1 = t1.toBasetype();
     t2 = t2.toBasetype();
 
@@ -4277,8 +4337,15 @@ extern (D) bool arrayTypeCompatibleWithoutCasting(Type t1, Type t2)
     {
         if (t1.nextOf().implicitConvTo(t2.nextOf()) >= MATCH.constant || t2.nextOf().implicitConvTo(t1.nextOf()) >= MATCH.constant)
             return true;
+        return false;
     }
-    return false;
+    if (implicitConvTo(ekey, t2) < MATCH.constant)
+        return false;
+    if (auto ts = t1.isTypeStruct())
+        return implicitConvToThroughAliasThis(ts, t2) == MATCH.nomatch;
+    if (auto tc = t1.isTypeClass())
+        return implicitConvToThroughAliasThis(tc, t2) == MATCH.nomatch;
+    return true;
 }
 
 /******************************************************************/
index 2129aa00cf600cf612f7127e9c928c819cd795ba..dcbad395d9389d6c9432e53a8942283e3ddb0a28 100644 (file)
@@ -30,7 +30,6 @@ import dmd.location;
 import dmd.mtype;
 import dmd.objc;
 import dmd.root.rmem;
-import dmd.target;
 import dmd.visitor;
 
 /***********************************************************
@@ -55,48 +54,6 @@ extern (C++) struct BaseClass
         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.length);
-
-        // first entry is ClassInfo reference
-        for (size_t j = sym.vtblOffset(); j < sym.vtbl.length; 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());
@@ -224,144 +181,42 @@ extern (C++) class ClassDeclaration : AggregateDeclaration
         if (id == Id.__sizeof || id == Id.__xalignof || id == Id._mangleof)
             classError("%s `%s` illegal class name", null);
 
-        // BUG: What if this is the wrong TypeInfo, i.e. it is nested?
-        if (id.toChars()[0] == 'T')
+        final /* final to work around dscanner bug*/
+        void check(Identifier _id, ref ClassDeclaration cd)
         {
-            if (id == Id.TypeInfo)
+            if (id == _id)
             {
                 if (!inObject)
                     classError("%s `%s` %s", msg.ptr);
-                Type.dtypeinfo = this;
+                cd = this;
             }
-            if (id == Id.TypeInfo_Class)
-            {
-                if (!inObject)
-                    classError("%s `%s` %s", msg.ptr);
-                Type.typeinfoclass = this;
-            }
-            if (id == Id.TypeInfo_Interface)
-            {
-                if (!inObject)
-                    classError("%s `%s` %s", msg.ptr);
-                Type.typeinfointerface = this;
-            }
-            if (id == Id.TypeInfo_Struct)
-            {
-                if (!inObject)
-                    classError("%s `%s` %s", msg.ptr);
-                Type.typeinfostruct = this;
-            }
-            if (id == Id.TypeInfo_Pointer)
-            {
-                if (!inObject)
-                    classError("%s `%s` %s", msg.ptr);
-                Type.typeinfopointer = this;
-            }
-            if (id == Id.TypeInfo_Array)
-            {
-                if (!inObject)
-                    classError("%s `%s` %s", msg.ptr);
-                Type.typeinfoarray = this;
-            }
-            if (id == Id.TypeInfo_StaticArray)
-            {
-                //if (!inObject)
-                //    Type.typeinfostaticarray.classError("%s `%s` %s", msg);
-                Type.typeinfostaticarray = this;
-            }
-            if (id == Id.TypeInfo_AssociativeArray)
-            {
-                if (!inObject)
-                    classError("%s `%s` %s", msg.ptr);
-                Type.typeinfoassociativearray = this;
-            }
-            if (id == Id.TypeInfo_Enum)
-            {
-                if (!inObject)
-                    classError("%s `%s` %s", msg.ptr);
-                Type.typeinfoenum = this;
-            }
-            if (id == Id.TypeInfo_Function)
-            {
-                if (!inObject)
-                    classError("%s `%s` %s", msg.ptr);
-                Type.typeinfofunction = this;
-            }
-            if (id == Id.TypeInfo_Delegate)
-            {
-                if (!inObject)
-                    classError("%s `%s` %s", msg.ptr);
-                Type.typeinfodelegate = this;
-            }
-            if (id == Id.TypeInfo_Tuple)
-            {
-                if (!inObject)
-                    classError("%s `%s` %s", msg.ptr);
-                Type.typeinfotypelist = this;
-            }
-            if (id == Id.TypeInfo_Const)
-            {
-                if (!inObject)
-                    classError("%s `%s` %s", msg.ptr);
-                Type.typeinfoconst = this;
-            }
-            if (id == Id.TypeInfo_Invariant)
-            {
-                if (!inObject)
-                    classError("%s `%s` %s", msg.ptr);
-                Type.typeinfoinvariant = this;
-            }
-            if (id == Id.TypeInfo_Shared)
-            {
-                if (!inObject)
-                    classError("%s `%s` %s", msg.ptr);
-                Type.typeinfoshared = this;
-            }
-            if (id == Id.TypeInfo_Wild)
-            {
-                if (!inObject)
-                    classError("%s `%s` %s", msg.ptr);
-                Type.typeinfowild = this;
-            }
-            if (id == Id.TypeInfo_Vector)
-            {
-                if (!inObject)
-                    classError("%s `%s` %s", msg.ptr);
-                Type.typeinfovector = this;
-            }
-        }
-
-        if (id == Id.Object)
-        {
-            if (!inObject)
-                classError("%s `%s` %s", msg.ptr);
-            object = this;
-        }
-
-        if (id == Id.Throwable)
-        {
-            if (!inObject)
-                classError("%s `%s` %s", msg.ptr);
-            throwable = this;
         }
-        if (id == Id.Exception)
-        {
-            if (!inObject)
-                classError("%s `%s` %s", msg.ptr);
-            exception = this;
-        }
-        if (id == Id.Error)
-        {
-            if (!inObject)
-                classError("%s `%s` %s", msg.ptr);
-            errorException = this;
-        }
-        if (id == Id.cpp_type_info_ptr)
+        // BUG: What if this is the wrong TypeInfo, i.e. it is nested?
+        if (id.toChars()[0] == 'T')
         {
-            if (!inObject)
-                classError("%s `%s` %s", msg.ptr);
-            cpp_type_info_ptr = this;
+            check(Id.TypeInfo,           Type.dtypeinfo);
+            check(Id.TypeInfo_Class,     Type.typeinfoclass);
+            check(Id.TypeInfo_Interface, Type.typeinfointerface);
+            check(Id.TypeInfo_Struct,    Type.typeinfostruct);
+            check(Id.TypeInfo_Pointer,   Type.typeinfopointer);
+            check(Id.TypeInfo_Array,     Type.typeinfoarray);
+            check(Id.TypeInfo_StaticArray, Type.typeinfostaticarray);
+            check(Id.TypeInfo_AssociativeArray, Type.typeinfoassociativearray);
+            check(Id.TypeInfo_Enum,      Type.typeinfoenum);
+            check(Id.TypeInfo_Function,  Type.typeinfofunction);
+            check(Id.TypeInfo_Delegate,  Type.typeinfodelegate);
+            check(Id.TypeInfo_Tuple,     Type.typeinfotypelist);
+            check(Id.TypeInfo_Const,     Type.typeinfoconst);
+            check(Id.TypeInfo_Invariant, Type.typeinfoinvariant);
+            check(Id.TypeInfo_Shared,    Type.typeinfoshared);
+            check(Id.TypeInfo_Wild,      Type.typeinfowild);
+            check(Id.TypeInfo_Vector,    Type.typeinfovector);
         }
+        check(Id.Object, object);
+        check(Id.Throwable, throwable);
+        check(Id.Exception, exception);
+        check(Id.Error, errorException);
+        check(Id.cpp_type_info_ptr, cpp_type_info_ptr);
 
         baseok = Baseok.none;
     }
@@ -405,19 +260,6 @@ extern (C++) class ClassDeclaration : AggregateDeclaration
         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.
@@ -499,127 +341,6 @@ extern (C++) class ClassDeclaration : AggregateDeclaration
         return classKind == ClassKind.d;
     }
 
-    /****************
-     * 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
-     */
-    extern (D) 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)
-        {
-            import dmd.typesem : covariant;
-            bool seenInterfaceVirtual;
-            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;
-
-                /* Functions overriding interface functions for extern(C++) with VC++
-                 * are not in the normal vtbl, but in vtblFinal. If the implementation
-                 * is again overridden in a child class, both would be found here.
-                 * The function in the child class should override the function
-                 * in the base class, which is done here, because searchVtbl is first
-                 * called for the child class. Checking seenInterfaceVirtual makes
-                 * sure, that the compared functions are not in the same vtbl.
-                 */
-                if (fd.interfaceVirtual &&
-                    fd.interfaceVirtual is fdmatch.interfaceVirtual &&
-                    !seenInterfaceVirtual &&
-                    fdmatch.type.covariant(fd.type) == Covariant.yes)
-                {
-                    seenInterfaceVirtual = true;
-                    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)
-            classError("%s `%s` ambiguous virtual function `%s`", fdambig.toChars());
-
-        return fdmatch;
-    }
-
     /****************************************
      */
     final bool isCOMclass() const
@@ -663,13 +384,6 @@ extern (C++) class ClassDeclaration : AggregateDeclaration
         return "class";
     }
 
-    /****************************************
-     */
-    override final void addObjcSymbols(ClassDeclarations* classes, ClassDeclarations* categories)
-    {
-        .objc.addSymbols(this, classes, categories);
-    }
-
     // Back end
     Dsymbol vtblsym;
 
@@ -708,19 +422,6 @@ extern (C++) final class InterfaceDeclaration : ClassDeclaration
         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)
index 1bda9948cbf4d0f32996ee5eba8d031166f52284..faeab44ed1d343fdd22539a59ef0f86acd793024 100644 (file)
@@ -16,19 +16,13 @@ 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 : toAlias;
 import dmd.dtemplate;
 import dmd.errors;
+import dmd.errorsink;
 import dmd.expression;
 import dmd.func;
 import dmd.globals;
-import dmd.hdrgen;
 import dmd.id;
 import dmd.identifier;
 import dmd.init;
@@ -36,35 +30,21 @@ import dmd.intrange;
 import dmd.location;
 import dmd.mtype;
 import dmd.common.outbuffer;
-import dmd.rootobject;
 import dmd.root.filename;
 import dmd.target;
-import dmd.tokens;
-import dmd.typesem : typeSemantic, size;
+import dmd.targetcompiler;
 import dmd.visitor;
 
-version (IN_GCC) {}
-else version (IN_LLVM) {}
-else version = MARS;
 
 /******************************************
  */
 void ObjectNotFound(Loc loc, Identifier id)
 {
     global.gag = 0; // never gag the fatal error
-    error(loc, "`%s` not found. object.d may be incorrectly installed or corrupt.", id.toChars());
-    version (IN_LLVM)
-    {
-        errorSupplemental(loc, "ldc2 might not be correctly installed.");
-        errorSupplemental(loc, "Please check your ldc2.conf configuration file.");
-        errorSupplemental(loc, "Installation instructions can be found at http://wiki.dlang.org/LDC.");
-    }
-    else version (MARS)
-    {
-        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);
-    }
+    const dmdConfFile = global.inifilename.length ? FileName.canonicalName(global.inifilename) : "not found";
+
+    mixin HostObjectNotFound;
+    hostObjectNotFound(loc, id.toChars(), dmdConfFile, global.errorSink); // print host-specific diagnostic
     fatal();
 }
 
@@ -120,15 +100,6 @@ extern (C++) abstract class Declaration : Dsymbol
         return "declaration";
     }
 
-    override final ulong size(Loc loc)
-    {
-        assert(type);
-        const sz = type.size();
-        if (sz == SIZE_INVALID)
-            errors = true;
-        return sz;
-    }
-
     final bool isStatic() const pure nothrow @nogc @safe
     {
         return (storage_class & STC.static_) != 0;
@@ -302,60 +273,6 @@ extern (C++) final class TupleDeclaration : Declaration
         return "sequence";
     }
 
-    override Type getType()
-    {
-        /* If this tuple represents a type, return that type
-         */
-
-        //printf("TupleDeclaration::getType() %s\n", toChars());
-        if (isexp || building)
-            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.isType())
-                {
-                    //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.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());
-                version (none)
-                {
-                    buf.printf("_%s_%d", ident.toChars(), i);
-                    auto id = Identifier.idPool(buf.extractSlice());
-                    auto arg = new Parameter(Loc.initial, STC.in_, t, id, null);
-                }
-                else
-                {
-                    auto arg = new Parameter(Loc.initial, STC.none, 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 bool needThis()
     {
         //printf("TupleDeclaration::needThis(%s)\n", toChars());
@@ -449,144 +366,11 @@ extern (C++) final class AliasDeclaration : Declaration
         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)
-            {
-                /*
-                    If type has been resolved already we could
-                    still be inserting an alias from an import.
-
-                    If we are handling an alias then pretend
-                    it was inserting and return true, if not then
-                    false since we didn't even pretend to insert something.
-                */
-                return this._import && this.equals(s);
-            }
-
-            // https://issues.dlang.org/show_bug.cgi?id=23865
-            // only insert if the symbol can be part of a set
-            const s1 = s.toAlias();
-            const isInsertCandidate = s1.isFuncDeclaration() || s1.isOverDeclaration() || s1.isTemplateDeclaration();
-
-            /* 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;
-                if (isInsertCandidate)
-                    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;
-                if (isInsertCandidate)
-                    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;
-                }
-                if (isInsertCandidate)
-                    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;
-                }
-                if (isInsertCandidate)
-                {
-                    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(this).getType();
-    }
-
     override bool isOverloadable() const
     {
         // assume overloadable until alias is resolved
@@ -625,31 +409,6 @@ extern (C++) final class OverDeclaration : Declaration
         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;
@@ -706,11 +465,7 @@ extern (C++) class VarDeclaration : Declaration
         bool isCmacro;          /// it is a C macro turned into a C declaration
         bool dllImport;         /// __declspec(dllimport)
         bool dllExport;         /// __declspec(dllexport)
-        version (MARS)
-        {
-            bool inClosure;         /// is inserted into a GC allocated closure
-            bool inAlignSection;    /// is inserted into an aligned section on stack
-        }
+        mixin VarDeclarationExtra;
         bool systemInferred;    /// @system was inferred from initializer
     }
 
@@ -766,18 +521,19 @@ extern (C++) class VarDeclaration : Declaration
 
     override final inout(AggregateDeclaration) isThis() inout
     {
-        if (!(storage_class & (STC.static_ | STC.extern_ | STC.manifest | STC.templateparameter | STC.gshared | STC.ctfe)))
+        if (storage_class & (STC.static_ | STC.extern_ | STC.manifest | STC.templateparameter | STC.gshared | STC.ctfe))
+            return null;
+
+        /* The casting is necessary because `s = s.parent` is otherwise rejected
+         */
+        for (auto s = cast(Dsymbol)this; s; s = s.parent)
         {
-            /* The casting is necessary because `s = s.parent` is otherwise rejected
-             */
-            for (auto s = cast(Dsymbol)this; s; s = s.parent)
-            {
-                if (auto ad = (cast(inout)s).isMember())
-                    return ad;
-                if (!s.parent || !s.parent.isTemplateMixin())
-                    break;
-            }
+            if (auto ad = (cast(inout)s).isMember())
+                return ad;
+            if (!s.parent || !s.parent.isTemplateMixin())
+                break;
         }
+
         return null;
     }
 
@@ -874,39 +630,6 @@ extern (C++) class VarDeclaration : Declaration
         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 bitfield widths.
-        ulong tbitsize;
-        if (auto bf = isBitFieldDeclaration())
-        {
-            bitoffset += bf.bitOffset;
-            tbitsize = bf.fieldWidth;
-        }
-        else
-            tbitsize = tsz * 8;
-
-        ulong vbitsize;
-        if (auto vbf = v.isBitFieldDeclaration())
-        {
-            vbitoffset += vbf.bitOffset;
-            vbitsize = vbf.fieldWidth;
-        }
-        else
-            vbitsize = vsz * 8;
-
-        return   bitoffset < vbitoffset + vbitsize &&
-                vbitoffset <  bitoffset + tbitsize;
-    }
-
     /*************************************
      * Return true if we can take the address of this variable.
      */
@@ -961,32 +684,6 @@ extern (C++) class BitFieldDeclaration : VarDeclaration
     {
         v.visit(this);
     }
-
-    /***********************************
-     * Retrieve the .min or .max values.
-     * Only valid after semantic analysis.
-     * Params:
-     *  id = Id.min or Id.max
-     * Returns:
-     *  the min or max value
-     */
-    final ulong getMinMax(Identifier id)
-    {
-        const width = fieldWidth;
-        const uns = type.isUnsigned();
-        const min = id == Id.min;
-        ulong v;
-        assert(width != 0);  // should have been rejected in semantic pass
-        if (width == ulong.sizeof * 8)
-            v = uns ? (min ? ulong.min : ulong.max)
-                    : (min ?  long.min :  long.max);
-        else
-            v = uns ? (min ? 0
-                           : (1L << width) - 1)
-                    : (min ? -(1L << (width - 1))
-                           :  (1L << (width - 1)) - 1);
-        return v;
-    }
 }
 
 /***********************************************************
index 7a160770e144e348eb22ef7c7aad453372a25d93..fbca3074fe4745c30dd15a2b452427cfb895a780 100644 (file)
@@ -37,9 +37,12 @@ namespace dmd
     bool checkClosure(FuncDeclaration* fd);
     MATCH leastAsSpecialized(FuncDeclaration *f, FuncDeclaration *g, ArgumentLabels *names);
     PURE isPure(FuncDeclaration *f);
+    bool needsClosure(FuncDeclaration *fd);
     FuncDeclaration *genCfunc(Parameters *args, Type *treturn, const char *name, StorageClass stc=0);
     FuncDeclaration *genCfunc(Parameters *args, Type *treturn, Identifier *id, StorageClass stc=0);
     bool isAbstract(ClassDeclaration *cd);
+    bool overloadInsert(Dsymbol *ds, Dsymbol *s);
+    bool equals(const Dsymbol * const ds, const Dsymbol * const s);
 }
 
 //enum STC : ulong from astenums.d:
@@ -135,8 +138,6 @@ public:
     bool noUnderscore() const;
 
     const char *kind() const override;
-    uinteger_t size(Loc loc) override final;
-
 
     bool isStatic() const { return (storage_class & STCstatic) != 0; }
     LINK resolvedLinkage() const; // returns the linkage, resolving the target-specific `System` one
@@ -183,7 +184,6 @@ public:
 
     TupleDeclaration *syntaxCopy(Dsymbol *) override;
     const char *kind() const override;
-    Type *getType() override;
     bool needThis() override;
 
     void accept(Visitor *v) override { v->visit(this); }
@@ -200,9 +200,7 @@ public:
 
     static AliasDeclaration *create(Loc loc, Identifier *id, Type *type);
     AliasDeclaration *syntaxCopy(Dsymbol *) override;
-    bool overloadInsert(Dsymbol *s) override;
     const char *kind() const override;
-    Type *getType() override;
     bool isOverloadable() const override;
 
     void accept(Visitor *v) override { v->visit(this); }
@@ -217,8 +215,6 @@ public:
     Dsymbol *aliassym;
 
     const char *kind() const override;
-    bool equals(const RootObject * const o) const override;
-    bool overloadInsert(Dsymbol *s) override;
 
     Dsymbol *isUnique();
     bool isOverloadable() const override;
@@ -294,7 +290,6 @@ public:
     bool isDataseg() override final;
     bool isThreadlocal() override final;
     bool isCTFE();
-    bool isOverlappedWith(VarDeclaration *v);
     bool canTakeAddressOf();
     bool needsScopeDtor();
     // Eliminate need for dynamic_cast
@@ -616,6 +611,8 @@ public:
     AttributeViolation* pureViolation;
     AttributeViolation* nothrowViolation;
 
+    void* parametersDFAInfo;
+
     // Formerly FUNCFLAGS
     uint32_t flags;
     bool purityInprocess() const;
@@ -697,9 +694,7 @@ public:
     FuncDeclaration *fdensure(FuncDeclaration *fde);
     Expressions *fdrequireParams(Expressions *fdrp);
     Expressions *fdensureParams(Expressions *fdep);
-    bool equals(const RootObject * const o) const override final;
 
-    bool overloadInsert(Dsymbol *s) override;
     bool inUnittest();
     LabelDsymbol *searchLabel(Identifier *ident, Loc loc);
     const char *toPrettyChars(bool QualifyTypes = false) override;
@@ -725,7 +720,6 @@ public:
     virtual bool addPreInvariant();
     virtual bool addPostInvariant();
     const char *kind() const override;
-    bool needsClosure();
     bool hasNestedFrameRefs();
     ParameterList getParameterList();
 
@@ -787,7 +781,6 @@ public:
     bool isVirtual() const override;
     bool addPreInvariant() override;
     bool addPostInvariant() override;
-    bool overloadInsert(Dsymbol *s) override;
 
     void accept(Visitor *v) override { v->visit(this); }
 };
@@ -800,7 +793,6 @@ public:
     bool isVirtual() const override;
     bool addPreInvariant() override;
     bool addPostInvariant() override;
-    bool overloadInsert(Dsymbol *s) override;
 
     void accept(Visitor *v) override { v->visit(this); }
 };
index f37c83cbca7c15224ee7e063dd58ec8b2c9e8044..c28af9ba07ef64ed428104a549c6c5e8c058d7b1 100644 (file)
@@ -18,6 +18,7 @@ import dmd.astenums;
 import dmd.declaration;
 import dmd.dscope;
 import dmd.dsymbol;
+import dmd.dsymbolsem : toParentP;
 import dmd.expression;
 import dmd.expressionsem;
 import dmd.func;
index 8b7d5e501235fa3ff8101ef7454d4bb15bb9a385..a48308c5ff246d951be06e689f3fdd6d646ea236 100644 (file)
@@ -81,11 +81,6 @@ extern (C++) final class EnumDeclaration : ScopeDsymbol
         return ed;
     }
 
-    override Type getType()
-    {
-        return type;
-    }
-
     override const(char)* kind() const
     {
         return "enum";
index 66810cb35fdb5a83077b1b0965824489749d1836..e634081e86fe1b6318802ff594aebcdb30dfafd2 100644 (file)
@@ -141,19 +141,6 @@ extern (C++) final class Import : Dsymbol
         scopesym.addAccessiblePackage(mod, visibility); // d
      }
 
-    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);
-        if (aliasId)
-            return false;
-        const imp = s.isImport();
-        return imp && !imp.aliasId;
-    }
-
     override void accept(Visitor v)
     {
         v.visit(this);
index eaf3d993b019242098e293b688c6793161f52e79..61088e02bbfd7a91109144390f406d240f69e471 100644 (file)
@@ -49,8 +49,9 @@ import dmd.root.region;
 import dmd.rootobject;
 import dmd.root.utf;
 import dmd.statement;
+import dmd.semantic2 : findFunc;
 import dmd.tokens;
-import dmd.typesem : mutableOf, equivalent, pointerTo, sarrayOf, arrayOf, size, merge, defaultInitLiteral;
+import dmd.typesem;
 import dmd.utils : arrayCastBigEndian;
 import dmd.visitor;
 
@@ -4701,34 +4702,6 @@ public:
                     result = CTFEExp.voidexp;
                 return;
             }
-            else if (isArrayConstruction(fd.ident))
-            {
-                // In expressionsem.d, `T[x] ea = eb;` was lowered to:
-                // `_d_array{,set}ctor(ea[], eb[]);`.
-                // The following code will rewrite it back to `ea = eb` and
-                // then interpret that expression.
-
-                if (fd.ident == Id._d_arrayctor)
-                    assert(e.arguments.length == 3);
-                else
-                    assert(e.arguments.length == 2);
-
-                Expression ea = (*e.arguments)[0];
-                if (ea.isCastExp)
-                    ea = ea.isCastExp.e1;
-
-                Expression eb = (*e.arguments)[1];
-                if (eb.isCastExp() && fd.ident == Id._d_arrayctor)
-                    eb = eb.isCastExp.e1;
-
-                ConstructExp ce = new ConstructExp(e.loc, ea, eb);
-                ce.type = ea.type;
-
-                ce.type = ea.type;
-                result = interpret(ce, istate);
-
-                return;
-            }
         }
         else if (auto soe = ecall.isSymOffExp())
         {
index 08400433d099333475f28ace2786d5295bfed72e..9b99718700592154240c5b7349bc69a2617e3b66 100644 (file)
@@ -25,14 +25,10 @@ import dmd.common.outbuffer;
 import dmd.compiler;
 import dmd.cparse;
 import dmd.declaration;
-import dmd.dimport;
 import dmd.dmacro;
 import dmd.doc;
-import dmd.dscope;
 import dmd.dsymbol;
-import dmd.dsymbolsem : dsymbolSemantic;
 import dmd.errors;
-import dmd.errorsink;
 import dmd.expression;
 import dmd.file_manager;
 import dmd.func;
@@ -41,18 +37,12 @@ import dmd.id;
 import dmd.identifier;
 import dmd.location;
 import dmd.parse;
-import dmd.root.aav;
 import dmd.root.array;
 import dmd.root.file;
 import dmd.root.filename;
 import dmd.root.port;
-import dmd.root.rmem;
 import dmd.root.string;
-import dmd.rootobject;
-import dmd.semantic2;
-import dmd.semantic3;
 import dmd.target;
-import dmd.utils;
 import dmd.visitor;
 
 version (Windows)
@@ -66,25 +56,6 @@ else version (Posix)
 else
     static assert(0);
 
-version (IN_GCC) {}
-else version (IN_LLVM) {}
-else version = MARS;
-
-// 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.length)
-        semantic3OnDependencies(m.aimports[i]);
-}
-
 /**
  * Remove generated .di files on error and exit
  */
@@ -185,15 +156,6 @@ extern (C++) class Package : ScopeDsymbol
         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
@@ -475,7 +437,7 @@ extern (C++) final class Module : Package
         if (doHdrGen)
             hdrfile = setOutfilename(global.params.dihdr.name, global.params.dihdr.dir, arg, hdr_ext);
 
-        this.edition = Edition.min;
+        this.edition = global.params.edition;
     }
 
     extern (D) this(const(char)[] filename, Identifier ident, int doDocComment, int doHdrGen)
@@ -795,7 +757,13 @@ extern (C++) final class Module : Package
             p.nextToken();
             checkCompiledImport();
             members = p.parseModule();
-            assert(!p.md); // C doesn't have module declarations
+            md = p.md;
+            if (md)
+            {
+                this.ident = md.id;
+                dst = Package.resolve(md.packages, &this.parent, &ppack);
+            }
+
             numlines = p.linnum;
         }
         else
@@ -978,125 +946,6 @@ extern (C++) final class Module : Package
             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());
-        if (!s.deferred)
-        {
-            s.deferred = true;
-            deferred.push(s);
-        }
-    }
-
-    extern (D) static void addDeferredSemantic2(Dsymbol s)
-    {
-        //printf("Module::addDeferredSemantic2('%s')\n", s.toChars());
-        if (!s.deferred2)
-        {
-            s.deferred2 = true;
-            deferred2.push(s);
-        }
-    }
-
-    extern (D) static void addDeferredSemantic3(Dsymbol s)
-    {
-        //printf("Module::addDeferredSemantic3('%s')\n", s.toChars());
-        if (!s.deferred3)
-        {
-            s.deferred3 = true;
-            deferred3.push(s);
-        }
-    }
-
-    /******************************************
-     * Run semantic() on deferred symbols.
-     */
-    static void runDeferredSemantic()
-    {
-        __gshared int nested;
-        if (nested)
-            return;
-        //if (deferred.length) printf("+Module::runDeferredSemantic(), len = %ld\n", deferred.length);
-        nested++;
-
-        size_t len;
-        do
-        {
-            len = deferred.length;
-            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);
-            foreach (Dsymbol s; Module.deferred[])
-                s.deferred = false;
-            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.length = %ld, len = %ld\n", deferred.length, len);
-            if (todoalloc)
-                free(todoalloc);
-        }
-        while (deferred.length != len); // while making progress
-        nested--;
-        //printf("-Module::runDeferredSemantic(), len = %ld\n", deferred.length);
-    }
-
-    static void runDeferredSemantic2()
-    {
-        Module.runDeferredSemantic();
-
-        Dsymbols* a = &Module.deferred2;
-        for (size_t i = 0; i < a.length; i++)
-        {
-            Dsymbol s = (*a)[i];
-            s.deferred2 = false;
-            //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.length; i++)
-        {
-            Dsymbol s = (*a)[i];
-            s.deferred3 = false;
-            //printf("[%d] %s semantic3a\n", i, s.toPrettyChars());
-            s.semantic3(null);
-
-            if (global.errors)
-                break;
-        }
-        a.setDim(0);
-    }
-
     extern (D) static void clearCache() nothrow
     {
         foreach (Module m; amodules)
@@ -1412,35 +1261,3 @@ private const(char)[] processSource (const(ubyte)[] src, Module mod)
 
     return buf;
 }
-
-/*******************************************
- * Look for member of the form:
- *      const(MemberInfo)[] getMembers(string);
- * Returns NULL if not found
- */
-FuncDeclaration findGetMembers(ScopeDsymbol dsym)
-{
-    import dmd.opover : search_function;
-    Dsymbol s = search_function(dsym, Id.getmembers);
-    FuncDeclaration fdx = s ? s.isFuncDeclaration() : null;
-    version (none)
-    {
-        // Finish
-        __gshared TypeFunction tfgetmembers;
-        if (!tfgetmembers)
-        {
-            Scope sc;
-            sc.eSink = global.errorSink;
-            Parameters* p = new Parameter(STC.in_, Type.tchar.constOf().arrayOf(), null, null);
-            auto parameters = new Parameters(p);
-            Type tret = null;
-            TypeFunction tf = new TypeFunction(parameters, tret, VarArg.none, LINK.d);
-            tfgetmembers = tf.dsymbolSemantic(Loc.initial, &sc).isTypeFunction();
-        }
-        if (fdx)
-            fdx = fdx.overloadExactMatch(tfgetmembers);
-    }
-    if (fdx && fdx.isVirtual())
-        fdx = null;
-    return fdx;
-}
index dc4a0b9acebcdd88497dd65a63ad384b3b3212e5..4328391d6900c4c76390365806fbcb3ec364c260 100644 (file)
@@ -33,6 +33,7 @@ import dmd.dscope;
 import dmd.dstruct;
 import dmd.dsymbol;
 import dmd.dsymbolsem;
+import dmd.templatesem : computeOneMember;
 import dmd.dtemplate;
 import dmd.errorsink;
 import dmd.func;
@@ -44,8 +45,6 @@ import dmd.lexer;
 import dmd.location;
 import dmd.mtype;
 import dmd.root.array;
-import dmd.root.file;
-import dmd.root.filename;
 import dmd.common.outbuffer;
 import dmd.root.port;
 import dmd.root.rmem;
@@ -54,7 +53,164 @@ import dmd.root.utf;
 import dmd.tokens;
 import dmd.visitor;
 
-private:
+/****************************************************
+ * Generate Ddoc text for Module `m` and append it to `outbuf`.
+ * Params:
+ *      m = Module
+ *      ddoctext = combined text of .ddoc files for macro definitions
+ *      datetime = charz returned by ctime()
+ *      eSink = send error messages to eSink
+ *      outbuf = append the Ddoc text to this
+ */
+public
+void gendocfile(Module m, const char[] ddoctext, const char* datetime, ErrorSink eSink, ref OutBuffer outbuf)
+{
+    // Load internal default macros first
+    DocComment.parseMacros(m.escapetable, m.macrotable, ddoc_default[]);
+
+    // Ddoc files override default macros
+    DocComment.parseMacros(m.escapetable, m.macrotable, ddoctext);
+
+    Scope* sc = scopeCreateGlobal(m, eSink); // 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
+    m.macrotable.define("DATETIME", datetime[0 .. 26]);
+    m.macrotable.define("YEAR", datetime[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_);
+    }
+
+    OutBuffer buf;
+    if (m.filetype == FileType.ddoc)
+    {
+        Loc loc = m.md ? m.md.loc : m.loc;
+
+        size_t commentlen = m.comment ? strlen(cast(char*)m.comment) : 0;
+        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;
+
+    // Expand buf in place with macro expansions
+    const success = m.macrotable.expand(buf2, 0, end, null, global.recursionLimit, &isIdStart, &isIdTail);
+    if (!success)
+        eSink.error(Loc.initial, "DDoc macro expansion limit exceeded; more than %d expansions.", global.recursionLimit);
+
+    /* Remove all the escape sequences from buf,
+     * and make CR-LF the newline.
+     */
+    const slice = buf2[];
+    outbuf.reserve(slice.length);
+    auto p = slice.ptr;
+    for (size_t j = 0; j < slice.length; j++)
+    {
+        const c = p[j];
+        if (c == 0xFF && j + 1 < slice.length)
+        {
+            j++;
+            continue;
+        }
+        if (c == '\n')
+            outbuf.writeByte('\r');
+        else if (c == '\r')
+        {
+            outbuf.writestring("\r\n");
+            if (j + 1 < slice.length && p[j + 1] == '\n')
+            {
+                j++;
+            }
+            continue;
+        }
+        outbuf.writeByte(c);
+    }
+}
+
+/****************************************************
+ * 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.
+ */
+public
+void escapeDdocString(ref OutBuffer buf, size_t start)
+{
+    for (size_t u = start; u < buf.length; u++)
+    {
+        const 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;
+        }
+    }
+}
+/****************************************************
+ * Generate Ddoc file for Module m.
+ * Params:
+ *      m = Module
+ *      ddoctext_ptr = combined text of .ddoc files for macro definitions
+ *      ddoctext_length = extant of ddoctext_ptr
+ *      datetime = charz returned by ctime()
+ *      eSink = send error messages to eSink
+ *      outbuf = append the Ddoc text to this
+ */
+public
+void gendocfile(Module m, const char* ddoctext_ptr, size_t ddoctext_length, const char* datetime, ErrorSink eSink, ref OutBuffer outbuf)
+{
+    gendocfile(m, ddoctext_ptr[0 .. ddoctext_length], datetime, eSink, outbuf);
+}
 
 public
 struct Escape
@@ -96,78 +252,52 @@ struct Escape
 
 /***********************************************************
  */
-class Section
+public
+struct DocComment
 {
-    const(char)[] name;
-    const(char)[] body_;
-    int nooutput;
-
-    override string toString() const
-    {
-        assert(0);
-    }
+    Sections sections;      // Section*[]
+    Section summary;
+    Section copyright;
+    Section macros;
+    MacroTable* pmacrotable;
+    Escape* escapetable;
+    Dsymbols a;
 
-    void write(Loc loc, DocComment* dc, Scope* sc, Dsymbols* a, ref OutBuffer buf)
+    static DocComment* parse(Dsymbol s, const(char)* comment)
     {
-        assert(a.length);
-        if (name.length)
+        //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.length; i++)
         {
-            static immutable table =
-            [
-                "AUTHORS",
-                "BUGS",
-                "COPYRIGHT",
-                "DATE",
-                "DEPRECATED",
-                "EXAMPLES",
-                "HISTORY",
-                "LICENSE",
-                "RETURNS",
-                "SEE_ALSO",
-                "STANDARDS",
-                "THROWS",
-                "VERSION",
-            ];
-            foreach (entry; table)
+            Section sec = dc.sections[i];
+            if (iequals("copyright", sec.name))
             {
-                if (iequals(entry, name))
-                {
-                    buf.printf("$(DDOC_%s ", entry.ptr);
-                    goto L1;
-                }
+                dc.copyright = sec;
+            }
+            if (iequals("macros", sec.name))
+            {
+                dc.macros = sec;
             }
-            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, sc.eSink);
-            buf.writestring(")");
-        }
-        else
-        {
-            buf.writestring("$(DDOC_DESCRIPTION ");
         }
-    L1:
-        size_t o = buf.length;
-        buf.write(body_);
-        escapeStrayParenthesis(loc, buf, o, true, sc.eSink);
-        highlightText(sc, a, loc, buf, o);
-        buf.writestring(")");
+        return dc;
     }
-}
 
-/***********************************************************
- */
-final class ParamSection : Section
-{
-    override void write(Loc loc, DocComment* dc, Scope* sc, Dsymbols* a, ref OutBuffer buf)
+    /************************************************
+     * 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)
     {
-        assert(a.length);
-        Dsymbol s = (*a)[0]; // test
-        const(char)* p = body_.ptr;
-        size_t len = body_.length;
+        const(char)* p = m.ptr;
+        size_t len = m.length;
         const(char)* pend = p + len;
         const(char)* tempstart = null;
         size_t templen = 0;
@@ -175,362 +305,639 @@ final class ParamSection : Section
         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)
             {
+                if (p >= pend)
+                    goto Ldone;
                 switch (*p)
                 {
                 case ' ':
                 case '\t':
                     p++;
                     continue;
+                case '\r':
                 case '\n':
                     p++;
                     goto Lcont;
                 default:
-                    if (isIdStart(p) || isCVariadicArg(p[0 .. cast(size_t)(pend - p)]))
+                    if (isIdStart(p))
                         break;
                     if (namelen)
-                        goto Ltext;
-                    // continuation of prev macro
+                        goto Ltext; // continuation of prev macro
                     goto Lskipline;
                 }
                 break;
             }
             tempstart = p;
-            while (isIdTail(p))
+            while (1)
+            {
+                if (p >= pend)
+                    goto Ldone;
+                if (!isIdTail(p))
+                    break;
                 p += utfStride(p);
-            if (isCVariadicArg(p[0 .. cast(size_t)(pend - p)]))
-                p += 3;
+            }
             templen = p - tempstart;
-            while (*p == ' ' || *p == '\t')
+            while (1)
+            {
+                if (p >= pend)
+                    goto Ldone;
+                if (!(*p == ' ' || *p == '\t'))
+                    break;
                 p++;
+            }
             if (*p != '=')
             {
                 if (namelen)
-                    goto Ltext;
-                // continuation of prev macro
+                    goto Ltext; // continuation of prev macro
                 goto Lskipline;
             }
             p++;
+            if (p >= pend)
+                goto Ldone;
             if (namelen)
             {
-                // Output existing param
+                // Output existing macro
             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[0 .. 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[0 ..  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)
-                            {
-                                sc.eSink.warning(s.loc, "Ddoc: function declaration has no parameter '%.*s'", cast(int)namelen, namestart);
-                            }
-                            buf.write(namestart[0 .. namelen]);
-                        }
-                        escapeStrayParenthesis(loc, buf, o, true, sc.eSink);
-                        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, sc.eSink);
-                        highlightText(sc, a, loc, buf, o);
-                    }
-                    buf.writestring(")");
-                }
-                buf.writestring(")");
+                //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 == ' ' || *p == '\t')
+            while (p < pend && (*p == ' ' || *p == '\t'))
                 p++;
             textstart = p;
         Ltext:
-            while (*p != '\n')
+            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++ != '\n')
-            {
-            }
+            while (p < pend && *p != '\r' && *p != '\n')
+                p++;
         }
+    Ldone:
         if (namelen)
-            goto L1;
-        // write out last one
-        buf.writestring(")");
-        TypeFunction tf = a.length == 1 ? isTypeFunction(s) : null;
-        if (tf)
+            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)
         {
-            size_t pcount = (tf.parameterList.parameters ? tf.parameterList.parameters.length : 0) +
-                            cast(int)(tf.parameterList.varargs == VarArg.variadic);
-            if (pcount != paramcount)
+            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)
             {
-                sc.eSink.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
-                    sc.eSink.warningSupplemental(s.loc, "Note that the format is `param = description`");
-                }
+                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++;
         }
     }
-}
 
-/***********************************************************
- */
-final class MacroSection : Section
-{
-    override void write(Loc loc, DocComment* dc, Scope* sc, Dsymbols* a, ref OutBuffer buf)
+    /*****************************************
+     * 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)
     {
-        //printf("MacroSection::write()\n");
-        DocComment.parseMacros(dc.escapetable, *dc.pmacrotable, body_);
-    }
-}
+        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;
 
-alias Sections = Array!(Section);
+            // 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;
+            }
 
-// Workaround for missing Parameter instance for variadic params. (it's unnecessary to instantiate one).
-bool isCVariadicParameter(Dsymbols* a, const(char)[] p) @safe
-{
-    foreach (member; *a)
-    {
-        TypeFunction tf = isTypeFunction(member);
-        if (tf && tf.parameterList.varargs == VarArg.variadic && p == "...")
-            return true;
-    }
-    return false;
-}
+            /* 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);
 
-Dsymbol getEponymousMember(TemplateDeclaration td) @safe
-{
-    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;
-}
+                    // 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;
+            }
+        }
+    }
 
-TemplateDeclaration getEponymousParent(Dsymbol s) @safe
-{
-    if (!s.parent)
-        return null;
-    TemplateDeclaration td = s.parent.isTemplateDeclaration();
-    return (td && getEponymousMember(td)) ? td : null;
+    void writeSections(Scope* sc, Dsymbols* a, ref 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", 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, sc.eSink);
+                highlightText(sc, a, loc, buf, o);
+                buf.writestring(")");
+            }
+            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.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(")");
+    }
 }
 
-immutable ddoc_default = import("default_ddoc_theme." ~ ddoc_ext);
-immutable ddoc_decl_s = "$(DDOC_DECL ";
-immutable ddoc_decl_e = ")\n";
-immutable ddoc_decl_dd_s = "$(DDOC_DECL_DD ";
-immutable ddoc_decl_dd_e = ")\n";
-
-/****************************************************
- * Generate Ddoc file for Module m.
- * Params:
- *      m = Module
- *      ddoctext_ptr = combined text of .ddoc files for macro definitions
- *      ddoctext_length = extant of ddoctext_ptr
- *      datetime = charz returned by ctime()
- *      eSink = send error messages to eSink
- *      outbuf = append the Ddoc text to this
- */
-public
-void gendocfile(Module m, const char* ddoctext_ptr, size_t ddoctext_length, const char* datetime, ErrorSink eSink, ref OutBuffer outbuf)
-{
-    gendocfile(m, ddoctext_ptr[0 .. ddoctext_length], datetime, eSink, outbuf);
-}
+private:
 
-/****************************************************
- * Generate Ddoc text for Module `m` and append it to `outbuf`.
- * Params:
- *      m = Module
- *      ddoctext = combined text of .ddoc files for macro definitions
- *      datetime = charz returned by ctime()
- *      eSink = send error messages to eSink
- *      outbuf = append the Ddoc text to this
+/***********************************************************
  */
-public
-void gendocfile(Module m, const char[] ddoctext, const char* datetime, ErrorSink eSink, ref OutBuffer outbuf)
+class Section
 {
-    // Load internal default macros first
-    DocComment.parseMacros(m.escapetable, m.macrotable, ddoc_default[]);
-
-    // Ddoc files override default macros
-    DocComment.parseMacros(m.escapetable, m.macrotable, ddoctext);
-
-    Scope* sc = Scope.createGlobal(m, eSink); // 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
-    m.macrotable.define("DATETIME", datetime[0 .. 26]);
-    m.macrotable.define("YEAR", datetime[20 .. 20 + 4]);
+    const(char)[] name;
+    const(char)[] body_;
+    int nooutput;
 
-    const srcfilename = m.srcfile.toString();
-    m.macrotable.define("SRCFILENAME", srcfilename);
-    const docfilename = m.docfile.toString();
-    m.macrotable.define("DOCFILENAME", docfilename);
-    if (dc.copyright)
+    override string toString() const
     {
-        dc.copyright.nooutput = 1;
-        m.macrotable.define("COPYRIGHT", dc.copyright.body_);
+        assert(0);
     }
 
-    OutBuffer buf;
-    if (m.filetype == FileType.ddoc)
+    void write(Loc loc, DocComment* dc, Scope* sc, Dsymbols* a, ref OutBuffer buf)
     {
-        Loc loc = m.md ? m.md.loc : m.loc;
-
-        size_t commentlen = m.comment ? strlen(cast(char*)m.comment) : 0;
-        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)
+        assert(a.length);
+        if (name.length)
         {
-            commentlen = dc.macros.name.ptr - m.comment;
-            dc.macros.write(loc, dc, sc, &a, buf);
+            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, sc.eSink);
+            buf.writestring(")");
+        }
+        else
+        {
+            buf.writestring("$(DDOC_DESCRIPTION ");
         }
-        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);
+    L1:
+        size_t o = buf.length;
+        buf.write(body_);
+        escapeStrayParenthesis(loc, buf, o, true, sc.eSink);
+        highlightText(sc, a, loc, buf, o);
+        buf.writestring(")");
     }
-    //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;
-
-    // Expand buf in place with macro expansions
-    const success = m.macrotable.expand(buf2, 0, end, null, global.recursionLimit, &isIdStart, &isIdTail);
-    if (!success)
-        eSink.error(Loc.initial, "DDoc macro expansion limit exceeded; more than %d expansions.", global.recursionLimit);
+}
 
-    /* Remove all the escape sequences from buf,
-     * and make CR-LF the newline.
-     */
-    const slice = buf2[];
-    outbuf.reserve(slice.length);
-    auto p = slice.ptr;
-    for (size_t j = 0; j < slice.length; j++)
+/***********************************************************
+ */
+final class ParamSection : Section
+{
+    override void write(Loc loc, DocComment* dc, Scope* sc, Dsymbols* a, ref OutBuffer buf)
     {
-        const c = p[j];
-        if (c == 0xFF && j + 1 < slice.length)
+        assert(a.length);
+        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)
         {
-            j++;
+            // 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[0 .. 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[0 ..  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)
+                            {
+                                sc.eSink.warning(s.loc, "Ddoc: function declaration has no parameter '%.*s'", cast(int)namelen, namestart);
+                            }
+                            buf.write(namestart[0 .. namelen]);
+                        }
+                        escapeStrayParenthesis(loc, buf, o, true, sc.eSink);
+                        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, sc.eSink);
+                        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 (c == '\n')
-            outbuf.writeByte('\r');
-        else if (c == '\r')
+        if (namelen)
+            goto L1;
+        // write out last one
+        buf.writestring(")");
+        TypeFunction tf = a.length == 1 ? isTypeFunction(s) : null;
+        if (tf)
         {
-            outbuf.writestring("\r\n");
-            if (j + 1 < slice.length && p[j + 1] == '\n')
+            size_t pcount = (tf.parameterList.parameters ? tf.parameterList.parameters.length : 0) +
+                            cast(int)(tf.parameterList.varargs == VarArg.variadic);
+            if (pcount != paramcount)
             {
-                j++;
+                sc.eSink.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
+                    sc.eSink.warningSupplemental(s.loc, "Note that the format is `param = description`");
+                }
             }
-            continue;
         }
-        outbuf.writeByte(c);
     }
 }
 
-/****************************************************
- * 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.
+/***********************************************************
  */
-public
-void escapeDdocString(ref OutBuffer buf, size_t start)
+final class MacroSection : Section
 {
-    for (size_t u = start; u < buf.length; u++)
+    override void write(Loc loc, DocComment* dc, Scope* sc, Dsymbols* a, ref OutBuffer buf)
     {
-        const 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;
-        }
+        //printf("MacroSection::write()\n");
+        DocComment.parseMacros(dc.escapetable, *dc.pmacrotable, body_);
+    }
+}
+
+alias Sections = Array!(Section);
+
+// Workaround for missing Parameter instance for variadic params. (it's unnecessary to instantiate one).
+bool isCVariadicParameter(Dsymbols* a, const(char)[] p) @safe
+{
+    foreach (member; *a)
+    {
+        TypeFunction tf = isTypeFunction(member);
+        if (tf && tf.parameterList.varargs == VarArg.variadic && p == "...")
+            return true;
     }
+    return false;
+}
+
+Dsymbol getEponymousMember(TemplateDeclaration td)
+{
+    td.computeOneMember();
+    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;
+}
+
+TemplateDeclaration getEponymousParent(Dsymbol s)
+{
+    if (!s.parent)
+        return null;
+    TemplateDeclaration td = s.parent.isTemplateDeclaration();
+    return (td && getEponymousMember(td)) ? td : null;
 }
 
+immutable ddoc_default = import("default_ddoc_theme." ~ ddoc_ext);
+immutable ddoc_decl_s = "$(DDOC_DECL ";
+immutable ddoc_decl_e = ")\n";
+immutable ddoc_decl_dd_s = "$(DDOC_DECL_DD ";
+immutable ddoc_decl_dd_e = ")\n";
+
 /****************************************************
  * Having unmatched parentheses can hose the output of Ddoc,
  * as the macros depend on properly nested parentheses.
@@ -652,1279 +1059,872 @@ private void escapeStrayParenthesis(Loc loc, ref OutBuffer buf, size_t start, bo
                     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.
-Scope* skipNonQualScopes(Scope* sc) @safe
-{
-    while (sc && !sc.scopesym)
-        sc = sc.enclosing;
-    return sc;
-}
-
-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
-    {
-        buf.writestring(s.ident ? s.ident.toString : "__anonymous");
-    }
-    return true;
-}
-
-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.length > 0)
-        {
-            for(int i = 0; i < imp.aliases.length; 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;
-                Dsymbol pscopesym;
-                if (auto symFromId = sc.search(loc, id, pscopesym))
-                {
-                    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
-    {
-        // buf.writestring("<<<");
-        // buf.writestring(typeof(ident).stringof);
-        // buf.writestring(">>>");
-        // auto symbolName = ident.toString();
-        auto symbolName = ident.toChars().toDString();
-        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. */
-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. */
-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.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, 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.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(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);
+                break;
+            }
+        }
     }
-    else
-        buf.writestring(")");
 }
 
-void emitVisibility(ref OutBuffer buf, Import i)
+// 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.
+Scope* skipNonQualScopes(Scope* sc) @safe
 {
-    // imports are private by default, which is different from other declarations
-    // so they should explicitly show their visibility
-    emitVisibility(buf, i.visibility);
+    while (sc && !sc.scopesym)
+        sc = sc.enclosing;
+    return sc;
 }
 
-void emitVisibility(ref OutBuffer buf, Declaration d)
+bool emitAnchorName(ref OutBuffer buf, Dsymbol s, Scope* sc, bool includeParent)
 {
-    auto vis = d.visibility;
-    if (vis.kind != Visibility.Kind.undefined && vis.kind != Visibility.Kind.public_)
+    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 = s.isTemplateDeclaration();
+    td.computeOneMember();
+    if (s.isCtorDeclaration() || (td !is null && td.onemember && td.onemember.isCtorDeclaration()))
     {
-        emitVisibility(buf, vis);
+        buf.writestring("this");
     }
+    else
+    {
+        buf.writestring(s.ident ? s.ident.toString : "__anonymous");
+    }
+    return true;
 }
 
-void emitVisibility(ref OutBuffer buf, Visibility vis)
-{
-    visibilityToBuffer(buf, vis);
-    buf.writeByte(' ');
-}
-
-void emitComment(Dsymbol s, ref OutBuffer buf, Scope* sc)
+void emitAnchor(ref OutBuffer buf, Dsymbol s, Scope* sc, bool forHeader = false)
 {
-    extern (C++) final class EmitComment : Visitor
+    Identifier ident;
     {
-        alias visit = Visitor.visit;
-    public:
-        OutBuffer* buf;
-        Scope* sc;
-
-        extern (D) this(ref OutBuffer buf, Scope* sc) scope
-        {
-            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.length > 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.length; 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;
-            }
-        }
+        OutBuffer anc;
+        emitAnchorName(anc, s, skipNonQualScopes(sc), true);
+        ident = Identifier.idPool(anc[]);
+    }
 
-        override void visit(Import imp)
+    auto pcount = cast(void*)ident in sc.anchorCounts;
+    typeof(*pcount) count;
+    if (!forHeader)
+    {
+        if (pcount)
         {
-            if (imp.visible().kind != Visibility.Kind.public_ && sc.visibility.kind != Visibility.Kind.export_)
+            // 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;
 
-            if (imp.comment)
-                emit(sc, imp, imp.comment);
+            count = ++*pcount;
         }
-
-        override void visit(Declaration d)
+        else
         {
-            //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);
+            sc.anchorCounts[cast(void*)ident] = 1;
+            count = 1;
         }
+    }
 
-        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);
-        }
+    // cache anchor name
+    sc.prevAnchor = ident;
+    auto macroName = forHeader ? "DDOC_HEADER_ANCHOR" : "DDOC_ANCHOR";
 
-        override void visit(TemplateDeclaration td)
+    if (auto imp = s.isImport())
+    {
+        // For example: `public import core.stdc.string : memcpy, memcmp;`
+        if (imp.aliases.length > 0)
         {
-            //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))
+            for(int i = 0; i < imp.aliases.length; i++)
             {
-                ss.accept(this);
-                return;
+                // 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;
+                Dsymbol pscopesym;
+                if (auto symFromId = sc.search(loc, id, pscopesym))
+                {
+                    emitAnchor(buf, symFromId, sc, forHeader);
+                }
             }
-            emit(sc, td, td.comment);
         }
-
-        override void visit(EnumDeclaration ed)
+        else
         {
-            if (ed.visible().kind == Visibility.Kind.private_ || sc.visibility.kind == Visibility.Kind.private_)
-                return;
-            if (ed.isAnonymous() && ed.members)
+            // For example: `public import str = core.stdc.string;`
+            if (imp.aliasId)
             {
-                for (size_t i = 0; i < ed.members.length; i++)
+                auto symbolName = imp.aliasId.toString();
+
+                buf.printf("$(%.*s %.*s", cast(int) macroName.length, macroName.ptr,
+                    cast(int) symbolName.length, symbolName.ptr);
+
+                if (forHeader)
                 {
-                    Dsymbol s = (*ed.members)[i];
-                    emitComment(s, *buf, sc);
+                    buf.printf(", %.*s", cast(int) symbolName.length, symbolName.ptr);
                 }
-                return;
             }
-            if (!ed.comment)
-                return;
-            if (ed.isAnonymous())
-                return;
-            emit(sc, ed, ed.comment);
-        }
+            else
+            {
+                // The general case:  `public import core.stdc.string;`
 
-        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);
-        }
+                // 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());
+                }
 
-        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.length; i++)
+                buf.printf("$(%.*s ", cast(int) macroName.length, macroName.ptr);
+                printFullyQualifiedImport();
+
+                if (forHeader)
                 {
-                    Dsymbol s = (*d)[i];
-                    //printf("AttribDeclaration::emitComment %s\n", s.toChars());
-                    emitComment(s, *buf, sc);
+                    buf.printf(", ");
+                    printFullyQualifiedImport();
                 }
             }
-        }
 
-        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();
-            }
+            buf.writeByte(')');
         }
+    }
+    else
+    {
+        // buf.writestring("<<<");
+        // buf.writestring(typeof(ident).stringof);
+        // buf.writestring(">>>");
+        // auto symbolName = ident.toString();
+        auto symbolName = ident.toChars().toDString();
+        buf.printf("$(%.*s %.*s", cast(int) macroName.length, macroName.ptr,
+            cast(int) symbolName.length, symbolName.ptr);
 
-        override void visit(ConditionalDeclaration cd)
+        // only append count once there's a duplicate
+        if (count > 1)
+            buf.printf(".%u", count);
+
+        if (forHeader)
         {
-            //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.length; i++)
+            Identifier shortIdent;
             {
-                Dsymbol s = (*d)[i];
-                emitComment(s, *buf, sc);
+                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);
         }
-    }
 
-    scope EmitComment v = new EmitComment(buf, sc);
-    if (!s)
-        v.emit(sc, null, null);
-    else
-        s.accept(v);
+        buf.writeByte(')');
+    }
 }
 
-void toDocBuffer(Dsymbol s, ref OutBuffer buf, Scope* sc)
+/******************************* emitComment **********************************/
+
+/** Get leading indentation from 'src' which represents lines of code. */
+size_t getCodeIndent(const(char)* src)
 {
-    extern (C++) final class ToDocBuffer : Visitor
+    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'))
     {
-        alias visit = Visitor.visit;
-    public:
-        OutBuffer* buf;
-        Scope* sc;
-
-        extern (D) this(ref OutBuffer buf, Scope* sc) scope
-        {
-            this.buf = &buf;
-            this.sc = sc;
-        }
+        codeIndent++;
+        src++;
+    }
+    return codeIndent;
+}
 
-        override void visit(Dsymbol s)
+/** Recursively expand template mixin member docs into the scope. */
+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.length; i++)
         {
-            //printf("Dsymbol::toDocbuffer() %s\n", s.toChars());
-            HdrGenState hgs;
-            hgs.ddoc = true;
-            toCBuffer(s, *buf, hgs);
+            Dsymbol sm = (*td.members)[i];
+            TemplateMixin tmc = sm.isTemplateMixin();
+            if (tmc && tmc.comment)
+                expandTemplateMixinComments(tmc, buf, sc);
+            else
+                emitComment(sm, buf, sc);
         }
+    }
+}
 
-        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 ");
+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.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(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(")");
+}
 
-                if (d.isFuncDeclaration())      // functionToBufferFull handles this
-                    return;
+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);
+}
 
-                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 ");
+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);
+    }
+}
 
-                if (d.isSynchronized())
-                    buf.writestring("synchronized ");
+void emitVisibility(ref OutBuffer buf, Visibility vis)
+{
+    visibilityToBuffer(buf, vis);
+    buf.writeByte(' ');
+}
 
-                if (d.storage_class & STC.manifest)
-                    buf.writestring("enum ");
+void emitComment(Dsymbol s, ref OutBuffer buf, Scope* sc)
+{
+    extern (C++) final class EmitComment : Visitor
+    {
+        alias visit = Visitor.visit;
+    public:
+        OutBuffer* buf;
+        Scope* sc;
 
-                // 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 ");
-                }
-            }
+        extern (D) this(ref OutBuffer buf, Scope* sc) scope
+        {
+            this.buf = &buf;
+            this.sc = sc;
         }
 
-        override void visit(Import i)
+        override void visit(Dsymbol)
         {
-            HdrGenState hgs;
-            hgs.ddoc = true;
-            emitVisibility(*buf, i);
-            toCBuffer(i, *buf, hgs);
         }
 
-        override void visit(Declaration d)
+        override void visit(InvariantDeclaration)
         {
-            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.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)
-            {
-                bool noFuncDecl = td.isFuncDeclaration() is null;
-                if (noFuncDecl)
-                {
-                    buf.writestring("$(DDOC_CONSTRAINT ");
-                }
+        }
 
-                toCBuffer(td.constraint, *buf, hgs);
+        override void visit(UnitTestDeclaration)
+        {
+        }
 
-                if (noFuncDecl)
-                {
-                    buf.writestring(")");
-                }
-            }
-            if (d.isDeprecated())
-                buf.writestring(")");
-            buf.writestring(";\n");
+        override void visit(PostBlitDeclaration)
+        {
         }
 
-        override void visit(AliasDeclaration ad)
+        override void visit(DtorDeclaration)
         {
-            //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)
-                {
-                    import dmd.typesem : toDsymbol;
-                    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)
+        override void visit(StaticCtorDeclaration)
         {
-            if (s && !s.isPackage() && !s.isModule())
-            {
-                parentToBuffer(s.parent);
-                buf.writestring(s.toChars());
-                buf.writestring(".");
-            }
         }
 
-        static bool inSameModule(Dsymbol s, Dsymbol p) @safe
+        override void visit(StaticDtorDeclaration)
         {
-            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)
+        override void visit(TypeInfoDeclaration)
         {
-            if (s.parent && (s.parent == parent)) // in current scope -> naked name
+        }
+
+        void emit(Scope* sc, Dsymbol s, const(char)* com)
+        {
+            if (s && sc.lastdc && isDitto(com))
             {
-                buf.writestring(s.toChars());
+                sc.lastdc.a.push(s);
+                return;
             }
-            else if (!inSameModule(s, parent)) // in another module -> full name
+            // Put previous doc comment if exists
+            if (DocComment* dc = sc.lastdc)
             {
-                buf.writestring(s.toPrettyChars());
+                assert(dc.a.length > 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.length; 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);
             }
-            else // nested in a type in this module -> full name w/o module name
+            if (s)
             {
-                // 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());
+                DocComment* dc = DocComment.parse(s, com);
+                dc.pmacrotable = &sc._module.macrotable;
+                sc.lastdc = dc;
             }
         }
 
-        override void visit(AggregateDeclaration ad)
+        override void visit(Import imp)
         {
-            if (!ad.ident)
+            if (imp.visible().kind != Visibility.Kind.public_ && sc.visibility.kind != Visibility.Kind.export_)
                 return;
-            version (none)
-            {
-                emitVisibility(buf, ad);
-            }
-            buf.printf("%s %s", ad.kind(), ad.toChars());
-            buf.writestring(";\n");
+
+            if (imp.comment)
+                emit(sc, imp, imp.comment);
         }
 
-        override void visit(StructDeclaration sd)
+        override void visit(Declaration d)
         {
-            //printf("StructDeclaration::toDocbuffer() %s\n", sd.toChars());
-            if (!sd.ident)
-                return;
-            version (none)
-            {
-                emitVisibility(buf, sd);
-            }
-            if (TemplateDeclaration td = getEponymousParent(sd))
+            //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))
             {
-                toDocBuffer(td, *buf, sc);
+                if (isDitto(td.comment))
+                    com = td.comment;
+                else
+                    com = Lexer.combineComments(td.comment.toDString(), com.toDString(), true);
             }
             else
             {
-                buf.printf("%s %s", sd.kind(), sd.toChars());
+                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;
             }
-            buf.writestring(";\n");
+            if (!com)
+                return;
+            emit(sc, d, com);
         }
 
-        override void visit(ClassDeclaration cd)
+        override void visit(AggregateDeclaration ad)
         {
-            //printf("ClassDeclaration::toDocbuffer() %s\n", cd.toChars());
-            if (!cd.ident)
-                return;
-            version (none)
-            {
-                emitVisibility(*buf, cd);
-            }
-            if (TemplateDeclaration td = getEponymousParent(cd))
+            //printf("AggregateDeclaration::emitComment() '%s'\n", ad.toChars());
+            const(char)* com = ad.comment;
+            if (TemplateDeclaration td = getEponymousParent(ad))
             {
-                toDocBuffer(td, *buf, sc);
+                if (isDitto(td.comment))
+                    com = td.comment;
+                else
+                    com = Lexer.combineComments(td.comment.toDString(), com.toDString(), true);
             }
             else
             {
-                if (!cd.isInterfaceDeclaration() && cd.isAbstract())
-                    buf.writestring("abstract ");
-                buf.printf("%s %s", cd.kind(), cd.toChars());
+                if (ad.visible().kind == Visibility.Kind.private_ || sc.visibility.kind == Visibility.Kind.private_)
+                    return;
+                if (!ad.comment)
+                    return;
             }
-            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;
-                }
+            if (!com)
+                return;
+            emit(sc, ad, com);
+        }
 
-                if (bc.sym)
-                {
-                    buf.printf("$(DDOC_PSUPER_SYMBOL %s)", bc.sym.toPrettyChars());
-                }
-                else
-                {
-                    HdrGenState hgs;
-                    toCBuffer(bc.type, *buf, null, hgs);
-                }
+        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;
             }
-            buf.writestring(";\n");
+            emit(sc, td, td.comment);
         }
 
         override void visit(EnumDeclaration ed)
         {
-            if (!ed.ident)
+            if (ed.visible().kind == Visibility.Kind.private_ || sc.visibility.kind == Visibility.Kind.private_)
                 return;
-            buf.printf("%s %s", ed.kind(), ed.toChars());
-            if (ed.memtype)
+            if (ed.isAnonymous() && ed.members)
             {
-                buf.writestring(": $(DDOC_ENUM_BASETYPE ");
-                HdrGenState hgs;
-                toCBuffer(ed.memtype, *buf, null, hgs);
-                buf.writestring(")");
+                for (size_t i = 0; i < ed.members.length; i++)
+                {
+                    Dsymbol s = (*ed.members)[i];
+                    emitComment(s, *buf, sc);
+                }
+                return;
             }
-            buf.writestring(";\n");
+            if (!ed.comment)
+                return;
+            if (ed.isAnonymous())
+                return;
+            emit(sc, ed, ed.comment);
         }
 
         override void visit(EnumMember em)
         {
-            if (!em.ident)
+            //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;
-            buf.writestring(em.toChars());
-        }
-    }
-
-    scope ToDocBuffer v = new ToDocBuffer(buf, sc);
-    s.accept(v);
-}
-
-/***********************************************************
- */
-public
-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.length; i++)
-        {
-            Section sec = dc.sections[i];
-            if (iequals("copyright", sec.name))
-            {
-                dc.copyright = sec;
-            }
-            if (iequals("macros", sec.name))
-            {
-                dc.macros = sec;
-            }
+            if (!em.comment)
+                return;
+            emit(sc, em, em.comment);
         }
-        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)
+        override void visit(AttribDeclaration ad)
         {
-            // Skip to start of macro
-            while (1)
+            //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)
             {
-                if (p >= pend)
-                    goto Ldone;
-                switch (*p)
+                for (size_t i = 0; i < d.length; i++)
                 {
-                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++;
+                    Dsymbol s = (*d)[i];
+                    //printf("AttribDeclaration::emitComment %s\n", s.toChars());
+                    emitComment(s, *buf, sc);
+                }
+            }
         }
-    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)
+        override void visit(VisibilityDeclaration pd)
         {
-            escapetable = new Escape();
-            memset(escapetable, 0, Escape.sizeof);
+            if (pd.decl)
+            {
+                Scope* scx = sc;
+                sc = sc.copy();
+                sc.visibility = pd.visibility;
+                visit(cast(AttribDeclaration)pd);
+                scx.lastdc = sc.lastdc;
+                sc = sc.pop();
+            }
         }
-        //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)
+
+        override void visit(ConditionalDeclaration cd)
         {
-            while (1)
+            //printf("ConditionalDeclaration::emitComment(sc = %p)\n", sc);
+            if (cd.condition.inc != Include.notComputed)
             {
-                if (p + 4 >= pend)
-                    return;
-                if (!(*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n' || *p == ','))
-                    break;
-                p++;
-            }
-            if (p[0] != '/' || p[2] != '/')
+                visit(cast(AttribDeclaration)cd);
                 return;
-            char c = p[1];
-            p += 3;
-            const(char)* start = p;
-            while (1)
+            }
+            /* 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++)
             {
-                if (p >= pend)
-                    return;
-                if (*p == '/')
-                    break;
-                p++;
+                Dsymbol s = (*d)[i];
+                emitComment(s, *buf, sc);
             }
-            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)
+    scope EmitComment v = new EmitComment(buf, sc);
+    if (!s)
+        v.emit(sc, null, null);
+    else
+        s.accept(v);
+}
+
+void toDocBuffer(Dsymbol s, ref OutBuffer buf, Scope* sc)
+{
+    extern (C++) final class ToDocBuffer : Visitor
     {
-        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)
+        alias visit = Visitor.visit;
+    public:
+        OutBuffer* buf;
+        Scope* sc;
+
+        extern (D) this(ref OutBuffer buf, Scope* sc) scope
         {
-            const(char)* pstart0 = p;
-            p = skipwhitespace(p);
-            pstart = p;
-            pend = p;
+            this.buf = &buf;
+            this.sc = sc;
+        }
 
-            // Undo indent if starting with a list item
-            if ((*p == '-' || *p == '+' || *p == '*') && (*(p+1) == ' ' || *(p+1) == '\t'))
-                pstart = pstart0;
-            else
+        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())
             {
-                const(char)* pitem = p;
-                while (*pitem >= '0' && *pitem <= '9')
-                    ++pitem;
-                if (pitem > p && *pitem == '.' && (*(pitem+1) == ' ' || *(pitem+1) == '\t'))
-                    pstart = pstart0;
+                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 ");
+                }
             }
+        }
 
-            /* 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)
+        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)
             {
-                // Check for start/end of a code section
-                if (*p == '-' || *p == '`' || *p == '~')
+                Type origType = d.originalType ? d.originalType : d.type;
+                if (origType.ty == Tfunction)
                 {
-                    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;
+                    functionToBufferFull(cast(TypeFunction)origType, *buf, d.ident, hgs, td);
                 }
-                if (!inCode && isIdStart(p))
+                else
+                    toCBuffer(origType, *buf, d.ident, hgs);
+            }
+            else
+                buf.writestring(d.ident.toString());
+            if (d.isVarDeclaration() && td)
+            {
+                buf.writeByte('(');
+                if (td.origParameters && td.origParameters.length)
                 {
-                    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))
+                    for (size_t i = 0; i < td.origParameters.length; i++)
                     {
-                        idlen = q - p;
-                        idstart = p;
-                        for (pend = p; pend > pstart; pend--)
-                        {
-                            if (pend[-1] == '\n')
-                                break;
-                        }
-                        p = q + 1;
-                        break;
+                        if (i)
+                            buf.writestring(", ");
+                        toCBuffer((*td.origParameters)[i], *buf, hgs);
                     }
                 }
-                while (1)
+                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)
                 {
-                    if (!*p)
-                        goto L1;
-                    if (*p == '\n')
-                    {
-                        p++;
-                        if (*p == '\n' && !summary && !namelen && !inCode)
-                        {
-                            pend = p;
-                            p++;
-                            goto L1;
-                        }
-                        break;
-                    }
-                    p++;
-                    pend = p;
+                    buf.writestring(")");
                 }
-                p = skipwhitespace(p);
             }
-        L1:
-            if (namelen || pstart < pend)
+            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
             {
-                Section s;
-                if (iequals("Params", name[0 .. namelen]))
-                    s = new ParamSection();
-                else if (iequals("Macros", name[0 .. namelen]))
-                    s = new MacroSection();
+                prettyPrintDsymbol(s, ad.parent);
+            }
+            else if (Type type = dmd.dsymbolsem.getType(ad)) // type alias
+            {
+                if (type.ty == Tclass || type.ty == Tstruct || type.ty == Tenum)
+                {
+                    import dmd.typesem : toDsymbol;
+                    if (Dsymbol s = type.toDsymbol(null)) // elaborate type
+                        prettyPrintDsymbol(s, ad.parent);
+                    else
+                        buf.writestring(type.toChars());
+                }
                 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;
+                {
+                    // simple type
+                    buf.writestring(type.toChars());
+                }
             }
-            if (idlen)
+            buf.writestring(";\n");
+        }
+
+        void parentToBuffer(Dsymbol s)
+        {
+            if (s && !s.isPackage() && !s.isModule())
             {
-                name = idstart;
-                namelen = idlen;
+                parentToBuffer(s.parent);
+                buf.writestring(s.toChars());
+                buf.writestring(".");
             }
-            else
+        }
+
+        static bool inSameModule(Dsymbol s, Dsymbol p) @safe
+        {
+            for (; s; s = s.parent)
             {
-                name = null;
-                namelen = 0;
-                if (!*p)
+                if (s.isModule())
+                    break;
+            }
+            for (; p; p = p.parent)
+            {
+                if (p.isModule())
                     break;
             }
+            return s == p;
         }
-    }
 
-    void writeSections(Scope* sc, Dsymbols* a, ref OutBuffer buf)
-    {
-        assert(a.length);
-        //printf("DocComment::writeSections()\n");
-        Loc loc = (*a)[0].loc;
-        if (Module m = (*a)[0].isModule())
+        void prettyPrintDsymbol(Dsymbol s, Dsymbol parent)
         {
-            if (m.md)
-                loc = m.md.loc;
+            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());
+            }
         }
-        size_t offset1 = buf.length;
-        buf.writestring("$(DDOC_SECTIONS ");
-        size_t offset2 = buf.length;
-        for (size_t i = 0; i < sections.length; i++)
+
+        override void visit(AggregateDeclaration ad)
         {
-            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)
+            if (!ad.ident)
+                return;
+            version (none)
             {
-                buf.writestring("$(DDOC_SUMMARY ");
-                size_t o = buf.length;
-                buf.write(sec.body_);
-                escapeStrayParenthesis(loc, buf, o, true, sc.eSink);
-                highlightText(sc, a, loc, buf, o);
-                buf.writestring(")");
+                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
-                sec.write(loc, &this, sc, a, buf);
+            {
+                buf.printf("%s %s", sd.kind(), sd.toChars());
+            }
+            buf.writestring(";\n");
         }
-        for (size_t i = 0; i < a.length; i++)
+
+        override void visit(ClassDeclaration cd)
         {
-            Dsymbol s = (*a)[i];
-            if (Dsymbol td = getEponymousParent(s))
-                s = td;
-            for (UnitTestDeclaration utd = s.ddocUnittest; utd; utd = utd.ddocUnittest)
+            //printf("ClassDeclaration::toDocbuffer() %s\n", cd.toChars());
+            if (!cd.ident)
+                return;
+            version (none)
             {
-                if (utd.visibility.kind == Visibility.Kind.private_ || !utd.comment || !utd.fbody)
+                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.length; i++)
+            {
+                BaseClass* bc = (*cd.baseclasses)[i];
+                if (bc.sym && bc.sym.ident == Id.Object)
                     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)
+                if (any)
+                    buf.writestring(", ");
+                else
                 {
-                    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(": ");
+                    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");
         }
-        if (buf.length == offset2)
+
+        override void visit(EnumMember em)
         {
-            /* Didn't write out any sections, so back out last write
-             */
-            buf.setsize(offset1);
-            buf.writestring("\n");
+            if (!em.ident)
+                return;
+            buf.writestring(em.toChars());
         }
-        else
-            buf.writestring(")");
     }
+
+    scope ToDocBuffer v = new ToDocBuffer(buf, sc);
+    s.accept(v);
 }
 
 /*****************************************
@@ -2623,11 +2623,12 @@ Parameter isFunctionParameter(Dsymbols* a, const(char)[] p) @safe
 
 /****************************************************
  */
-Parameter isEponymousFunctionParameter(Dsymbols* a, const(char)[] p) @safe
+Parameter isEponymousFunctionParameter(Dsymbols* a, const(char)[] p)
 {
     foreach (Dsymbol dsym; *a)
     {
         TemplateDeclaration td = dsym.isTemplateDeclaration();
+        td.computeOneMember();
         if (td && td.onemember)
         {
             /* Case 1: we refer to a template declaration inside the template
@@ -3110,7 +3111,9 @@ struct MarkdownLink
     {
         size_t iStart = i + 1;
         size_t iEnd = iStart;
-        if (iEnd >= buf.length || buf[iEnd] != '[' || (iEnd+1 < buf.length && buf[iEnd+1] == ']'))
+        if (iEnd >= buf.length)
+            return i;
+        if (buf[iEnd] != '[' || (iEnd+1 < buf.length && buf[iEnd+1] == ']'))
         {
             // collapsed reference [foo][] or shortcut reference [foo]
             iStart = delimiter.iStart + delimiter.count - 1;
index 879ffc11268e97109aa98f09545e3e122c2d946b..cdec3218b08cc35a085b3fb3c4e2ff75a28e9a62 100644 (file)
@@ -16,7 +16,6 @@ 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;
@@ -26,23 +25,13 @@ import dmd.dmodule;
 import dmd.doc;
 import dmd.dstruct;
 import dmd.dsymbol;
-import dmd.dsymbolsem;
 import dmd.dtemplate;
-import dmd.expression;
-import dmd.errors;
 import dmd.errorsink;
 import dmd.func;
 import dmd.globals;
-import dmd.id;
 import dmd.identifier;
-import dmd.importc;
-import dmd.location;
-import dmd.common.outbuffer;
 import dmd.root.rmem;
-import dmd.root.speller;
 import dmd.statement;
-import dmd.target;
-import dmd.tokens;
 
 //version=LOGSEARCH;
 
@@ -73,6 +62,14 @@ private extern (D) struct FlagBitFields
     bool canFree;            /// is on free list
     bool fullinst;          /// fully instantiate templates
     bool ctfeBlock;         /// inside a `if (__ctfe)` block
+
+    /**
+    Is any symbol this scope is applied to known to have a compile time only accessible context.
+    This is not the same as `skipCodegen` nor can it be used to contribute to this.
+    It is meant for analysis engines to be conservative in what functions they analyse,
+      to prevent perceived false positives for meta-programming heavy code.
+    */
+    bool knownACompileTimeOnlyContext;
 }
 
 private extern (D) struct NonFlagBitFields
@@ -217,32 +214,6 @@ extern (C++) struct Scope
         return new Scope();
     }
 
-    extern (D) static Scope* createGlobal(Module _module, ErrorSink eSink)
-    {
-        Scope* sc = Scope.alloc();
-        *sc = Scope.init;
-        sc._module = _module;
-        sc.minst = _module;
-        sc.scopesym = new ScopeDsymbol();
-        sc.scopesym.symtab = new DsymbolTable();
-        sc.eSink = eSink;
-        assert(eSink);
-        // 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()
-        sc.previews.setFromParams(global.params);
-
-        if (_module.filetype == FileType.c)
-            sc.inCfile = true;
-        // Create the module scope underneath the global scope
-        sc = sc.push(_module);
-        sc.parent = _module;
-        return sc;
-    }
-
     extern (D) Scope* copy()
     {
         Scope* sc = Scope.alloc();
@@ -288,6 +259,7 @@ extern (C++) struct Scope
         s.ctfeBlock = this.ctfeBlock;
         s.previews = this.previews;
         s.lastdc = null;
+        s.knownACompileTimeOnlyContext = this.knownACompileTimeOnlyContext;
         assert(&this != s);
         return s;
     }
@@ -380,312 +352,6 @@ extern (C++) struct Scope
         return pop();
     }
 
-
-    /*******************************
-     * Merge results of `ctorflow` into `this`.
-     * Params:
-     *   loc = for error messages
-     *   ctorflow = flow results to merge in
-     */
-    extern (D) void merge(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)
-            return;
-        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, otherwise set to null
-     *  flags = modify search based on flags
-     *
-     * Returns:
-     *  symbol if found, null if not
-     */
-    extern (C++) Dsymbol search(Loc loc, Identifier ident, out Dsymbol pscopesym, SearchOptFlags flags = SearchOpt.all)
-    {
-        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 & (SearchOpt.localsOnly | SearchOpt.importsOnly)));
-
-        /* 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);
-                    pscopesym = sc.scopesym;
-                    return s;
-                }
-            }
-            return null;
-        }
-
-        Dsymbol checkAliasThis(AggregateDeclaration ad, Identifier ident, SearchOptFlags 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(SearchOptFlags 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 |= SearchOpt.unqualifiedModule;    // tell Module.search() that SearchOpt.localsOnly is to be obeyed
-                else if (sc.inCfile && sc.scopesym.isStructDeclaration())
-                    continue;                                // C doesn't have struct scope
-
-                if (Dsymbol s = sc.scopesym.search(loc, ident, flags))
-                {
-                    if (flags & SearchOpt.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;
-                        }
-                    }
-                    //printMsg("\tfound local", s);
-                    pscopesym = sc.scopesym;
-                    return s;
-                }
-
-            NotFound:
-                if (sc.previews.fixAliasThis)
-                {
-                    Expression exp = new ThisExp(loc);
-                    if (Dsymbol aliasSym = checkAliasThis(sc.scopesym.isAggregateDeclaration(), ident, flags, &exp))
-                    {
-                        //printf("found aliassym: %s\n", aliasSym.toChars());
-                        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.ignoresymbolvisibility)
-            flags |= SearchOpt.ignoreVisibility;
-
-        // First look in local scopes
-        Dsymbol s = searchScopes(flags | SearchOpt.localsOnly);
-        version (LOGSEARCH) if (s) printMsg("-Scope.search() found local", s);
-        if (!s)
-        {
-            // Second look in imported modules
-            s = searchScopes(flags | SearchOpt.importsOnly);
-            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;
-            Dsymbol s = sc.search(Loc.initial, id, scopesym, SearchOpt.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;
-        // search for exact name first
-        if (auto s = search(Loc.initial, ident, scopesym, SearchOpt.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:
@@ -701,49 +367,6 @@ extern (C++) struct Scope
         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 (!this.inCfile)
-            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.
      */
@@ -834,22 +457,7 @@ extern (C++) struct Scope
             //    assert(0);
         }
     }
-    /******************************
-     */
-    extern (D) structalign_t alignment()
-    {
-        if (aligndecl)
-        {
-            auto ad = aligndecl.getAlignment(&this);
-            return ad.salign;
-        }
-        else
-        {
-            structalign_t sa;
-            sa.setDefault();
-            return sa;
-        }
-    }
+
     @safe @nogc pure nothrow const:
     /**********************************
     * Checks whether the current scope (or any of its parents) is deprecated.
@@ -918,4 +526,10 @@ extern (C++) struct Scope
     {
         return _module && _module.edition >= edition;
     }
+
+    extern (D) bool isKnownToHaveACompileTimeContext()
+    {
+        return this.knownACompileTimeOnlyContext || this.intypeof != 0 || this.traitsCompiles || this.ctfe ||
+            this.condition || this.inTemplateConstraint || this.ctfeBlock;
+    }
 }
index b565f159aec82183740413773eab78262e0c4bfb..ac5ee046cc1dc423db371be55b12742b6451a111 100644 (file)
@@ -18,22 +18,13 @@ import core.stdc.stdio;
 import dmd.aggregate;
 import dmd.arraytypes;
 import dmd.astenums;
-import dmd.attrib;
-import dmd.declaration;
 import dmd.dmodule;
-import dmd.dscope;
 import dmd.dsymbol;
-import dmd.dtemplate;
-import dmd.expression;
 import dmd.func;
 import dmd.id;
 import dmd.identifier;
 import dmd.location;
 import dmd.mtype;
-import dmd.opover;
-import dmd.target;
-import dmd.tokens;
-import dmd.typinf;
 import dmd.visitor;
 
 enum StructFlags : int
index 2059978ef8a5ce8bd834f666506cb177d334d499..640eda90e5a119e88f2472cc8c0ef31fc30e1c48 100644 (file)
@@ -17,7 +17,6 @@ import core.stdc.string;
 import core.stdc.stdlib;
 
 import dmd.aggregate;
-import dmd.aliasthis;
 import dmd.arraytypes;
 import dmd.attrib;
 import dmd.astenums;
@@ -30,16 +29,11 @@ import dmd.dmodule;
 import dmd.dversion;
 import dmd.dscope;
 import dmd.dstruct;
-import dmd.dsymbolsem : toAlias;
 import dmd.dtemplate;
 import dmd.errors;
 import dmd.expression;
-import dmd.expressionsem : getDsymbol;
 import dmd.func;
-import dmd.id;
 import dmd.identifier;
-import dmd.init;
-import dmd.lexer;
 import dmd.location;
 import dmd.mtype;
 import dmd.nspace;
@@ -181,8 +175,8 @@ enum PASS : ubyte
     semantic2done,  // semantic2() done
     semantic3,      // semantic3() started
     semantic3done,  // semantic3() done
-    inline,         // inline started
-    inlinedone,     // inline done
+    inlinePragma,   // inline pragma(inline, true) functions started
+    inlineAll,      // inline all functions started
     obj,            // toObjFile() run
 }
 
@@ -413,21 +407,6 @@ extern (C++) class Dsymbol : ASTNode
         return toChars();
     }
 
-    override bool equals(const RootObject o) const
-    {
-        if (this == o)
-            return true;
-        const s = o.isDsymbol();
-        if (!s)
-            return false;
-        // 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;
@@ -592,16 +571,6 @@ extern (C++) class Dsymbol : ASTNode
         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)
@@ -612,50 +581,6 @@ extern (C++) class Dsymbol : ASTNode
         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.hasDualContext;
-            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;
-                    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,
@@ -733,22 +658,6 @@ extern (C++) class Dsymbol : ASTNode
         return "symbol";
     }
 
-    bool overloadInsert(Dsymbol s)
-    {
-        //printf("Dsymbol::overloadInsert('%s')\n", s.toChars());
-        return false;
-    }
-
-    /*********************************
-     * Returns:
-     *  SIZE_INVALID when the size cannot be determined
-     */
-    ulong size(Loc loc)
-    {
-        .error(loc, "%s `%s` symbol `%s` has no size", kind, toPrettyChars, toChars());
-        return SIZE_INVALID;
-    }
-
     bool isforwardRef()
     {
         return false;
@@ -832,12 +741,6 @@ extern (C++) class Dsymbol : ASTNode
         return ad ? ad.isClassDeclaration() : null;
     }
 
-    // is this a type?
-    Type getType()
-    {
-        return null;
-    }
-
     // need a 'this' pointer?
     bool needThis()
     {
@@ -862,10 +765,6 @@ extern (C++) class Dsymbol : ASTNode
         assert(0);
     }
 
-    void addObjcSymbols(ClassDeclarations* classes, ClassDeclarations* categories)
-    {
-    }
-
     /****************************************
      * Add documentation comment to Dsymbol.
      * Ignore NULL comments.
@@ -1179,52 +1078,6 @@ public:
         return sds;
     }
 
-    extern (D) final 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.length == 0)
-            {
-                os.a.setDim(os2.a.length);
-                memcpy(os.a.tdata(), os2.a.tdata(), (os.a[0]).sizeof * 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.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) nothrow
     {
         //printf("%s.ScopeDsymbol::importScope(%s, %d)\n", toChars(), s.toChars(), visibility);
index 259817e6038d5a0387dbbd1191c7d634808b4d94..a22a93c53ca9228ebc2d4996c7e0f2bfec20bccc 100644 (file)
@@ -138,8 +138,8 @@ enum class PASS : uint8_t
     semantic2done,  // semantic2() done
     semantic3,      // semantic3() started
     semantic3done,  // semantic3() done
-    inline_,         // inline started
-    inlinedone,     // inline done
+    inlinePragma,   // inline pragma(inline, true) functions started
+    inlineAll,      // inline all functions started
     obj             // toObjFile() run
 };
 
@@ -205,7 +205,6 @@ public:
     CPPNamespaceDeclaration* cppnamespace(CPPNamespaceDeclaration* ns);
     UserAttributeDeclaration* userAttribDecl(UserAttributeDeclaration* uad);
     virtual const char *toPrettyCharsHelper(); // helper to print fully qualified (template) arguments
-    bool equals(const RootObject * const o) const override;
     bool isAnonymous() const;
     Module *getModule();
     bool isCsymbol();
@@ -227,8 +226,6 @@ public:
     virtual Identifier *getIdent();
     virtual const char *toPrettyChars(bool QualifyTypes = false);
     virtual const char *kind() const;
-    virtual bool overloadInsert(Dsymbol *s);
-    virtual uinteger_t size(Loc loc);
     virtual bool isforwardRef();
     virtual AggregateDeclaration *isThis();     // is a 'this' required to access the member
     virtual bool isExport() const;              // is Dsymbol exported?
@@ -241,11 +238,9 @@ public:
     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 Visibility visible();
     virtual Dsymbol *syntaxCopy(Dsymbol *s);    // copy only syntax trees
-    virtual void addObjcSymbols(ClassDeclarations *, ClassDeclarations *) { }
 
     virtual void addComment(const utf8_t *comment);
     const utf8_t *comment();                      // current value of comment
@@ -430,4 +425,17 @@ namespace dmd
     void setScope(Dsymbol *d, Scope *sc);
     void importAll(Dsymbol *d, Scope *sc);
     bool hasPointers(Dsymbol *d);
+    Type *getType(Dsymbol *d);
+    uinteger_t size(Dsymbol *ds, Loc loc);
+    void semantic3OnDependencies(Module *m);
+    void addDeferredSemantic(Dsymbol *s);
+    void addDeferredSemantic2(Dsymbol *s);
+    void addDeferredSemantic3(Dsymbol *s);
+    void runDeferredSemantic();
+    void runDeferredSemantic2();
+    void runDeferredSemantic3();
+    bool isOverlappedWith(VarDeclaration *vd, VarDeclaration *v);
+    Dsymbol* search(Scope *sc, Loc loc, Identifier* ident, Dsymbol*& pscopesym, uint32_t flags = 0u);
+    Scope *newScope(AggregateDeclaration * ad, Scope *sc);
+    void addObjcSymbols(Dsymbol *s, ClassDeclarations *, ClassDeclarations *);
 }
index 2c4996573d92693820932e4f0c7d8cee135da913..0e2632f64bc403fcf225a654f5bf02f16a280e2f 100644 (file)
@@ -14,6 +14,7 @@ module dmd.dsymbolsem;
 
 import core.stdc.stdio;
 import core.stdc.string;
+import core.stdc.stdlib;
 
 import dmd.aggregate;
 import dmd.aliasthis;
@@ -40,7 +41,7 @@ import dmd.dtemplate;
 import dmd.dversion;
 import dmd.enumsem;
 import dmd.errors;
-import dmd.escape;
+import dmd.errorsink;
 import dmd.expression;
 import dmd.expressionsem;
 import dmd.func;
@@ -51,7 +52,6 @@ import dmd.identifier;
 import dmd.importc;
 import dmd.init;
 import dmd.initsem;
-import dmd.intrange;
 import dmd.hdrgen;
 import dmd.lexer;
 import dmd.location;
@@ -59,15 +59,13 @@ import dmd.mtype;
 import dmd.mustuse;
 import dmd.nspace;
 import dmd.objc;
-import dmd.opover;
 import dmd.optimize;
 import dmd.parse;
-debug import dmd.printast;
 import dmd.root.array;
-import dmd.root.filename;
 import dmd.root.string;
-import dmd.common.outbuffer;
 import dmd.root.rmem;
+import dmd.root.speller;
+import dmd.common.outbuffer;
 import dmd.rootobject;
 import dmd.safe;
 import dmd.semantic2;
@@ -75,4355 +73,4978 @@ import dmd.semantic3;
 import dmd.sideeffect;
 import dmd.staticassert;
 import dmd.tokens;
-import dmd.utils;
 import dmd.statement;
+import dmd.statementsem : ready;
 import dmd.target;
-import dmd.templateparamsem;
+import dmd.targetcompiler;
 import dmd.templatesem;
 import dmd.typesem;
 import dmd.visitor;
 
-version (IN_GCC) {}
-else version (IN_LLVM) {}
-else version = MARS;
-
 enum LOG = false;
 
-/*************************************
- * Does semantic analysis on the public face of declarations.
- */
-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:
- *      ad with alignment value determined
+/***************************************
+ * Create a new scope from sc.
+ * semantic, semantic2 and semantic3 will use this for aggregate members.
  */
-AlignDeclaration getAlignment(AlignDeclaration ad, Scope* sc)
+Scope* newScope(AggregateDeclaration _this, Scope* sc)
 {
-    if (!ad.salign.isUnknown())   // UNKNOWN is 0
-        return ad;
-
-    if (!ad.exps)
+    static Scope* defaultNewScope(AggregateDeclaration _this, Scope* sc)
     {
-        ad.salign.setDefault();
-        return ad;
+        auto sc2 = sc.push(_this);
+        sc2.stc &= STC.flowThruAggregate;
+        sc2.parent = _this;
+        sc2.inunion = _this.isUnionDeclaration();
+        sc2.visibility = Visibility(Visibility.Kind.public_);
+        sc2.explicitVisibility = false;
+        sc2.aligndecl = null;
+        sc2.userAttribDecl = null;
+        sc2.namespace = null;
+        return sc2;
     }
 
-    dinteger_t strictest = 0;   // strictest alignment
-    bool errors;
-    foreach (ref exp; (*ad.exps)[])
+    static Scope* classNewScope(ClassDeclaration _this, Scope* sc)
     {
-        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 == EXP.error)
-            errors = true;
-        else
+        auto sc2 = defaultNewScope(_this, sc);
+        if (_this.isCOMclass())
         {
-            auto n = e.toInteger();
-            if (sc.inCfile && n == 0)       // C11 6.7.5-6 allows 0 for alignment
-                continue;
-
-            if (n < 1 || n & (n - 1) || ushort.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;
+            /* This enables us to use COM objects under Linux and
+             * work with things like XPCOM
+             */
+            sc2.linkage = target.systemLinkage();
         }
+        return sc2;
     }
 
-    if (errors || strictest == 0)  // C11 6.7.5-6 says alignment of 0 means no effect
-        ad.salign.setDefault();
-    else
-        ad.salign.set(cast(uint) strictest);
+    static Scope* interfaceNewScope(InterfaceDeclaration _this, Scope* sc)
+    {
+        auto sc2 = classNewScope(_this, sc);
+        if (_this.com)
+            sc2.linkage = LINK.windows;
+        else if (_this.classKind == ClassKind.cpp)
+            sc2.linkage = LINK.cpp;
+        else if (_this.classKind == ClassKind.objc)
+            sc2.linkage = LINK.objc;
+        return sc2;
+    }
 
-    return ad;
+    if (auto _id = _this.isInterfaceDeclaration())
+        return interfaceNewScope(_id, sc);
+    else if (auto cd = _this.isClassDeclaration())
+        return classNewScope(cd, sc);
+    return defaultNewScope(_this, sc);
 }
 
+void addObjcSymbols(Dsymbol _this, ClassDeclarations* classes, ClassDeclarations* categories)
+{
+    if (auto ad = _this.isAttribDeclaration())
+        objc.addSymbols(ad, classes, categories);
+    else if (auto cd = _this.isClassDeclaration())
+        objc.addSymbols(cd, classes, categories);
+}
 
-/*********************************
- * Resolve recursive tuple expansion in eponymous template.
+/************************************
+ * 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
  */
-Dsymbol toAlias2(Dsymbol s)
+const(char)* search_correct_C(Identifier ident)
 {
-    if (auto ad = s.isAliasDeclaration())
+    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);
+}
+
+/******************************
+ * Add symbol s to innermost symbol table.
+ * Params:
+ *  _this = scope object
+ *  s = symbol to insert
+ * Returns:
+ *  null if already in table, `s` if not
+ */
+Dsymbol insert(Scope* _this, Dsymbol s)
+{
+    //printf("insert() %s\n", s.toChars());
+    if (VarDeclaration vd = s.isVarDeclaration())
     {
-        if (ad.inuse)
-        {
-            .error(ad.loc, "%s `%s` recursive alias declaration", ad.kind, ad.toPrettyChars);
-            return ad;
-        }
-        ad.inuse = 1;
-        Dsymbol ds = ad.aliassym ? ad.aliassym.toAlias2() : ad;
-        ad.inuse = 0;
-        return ds;
+        if (_this.lastVar)
+            vd.lastVar = _this.lastVar;
+        _this.lastVar = vd;
     }
-    if (auto td = s.isTupleDeclaration())
+    else if (WithScopeSymbol ss = s.isWithScopeSymbol())
     {
-        //printf("TupleDeclaration::toAlias2() '%s' objects = %s\n", toChars(), objects.toChars());
-        for (size_t i = 0; i < td.objects.length; i++)
+        if (VarDeclaration vd = ss.withstate.wthis)
         {
-            RootObject o = (*td.objects)[i];
-            if (Dsymbol ds = isDsymbol(o))
-            {
-                ds = ds.toAlias2();
-                (*td.objects)[i] = ds;
-            }
+            if (_this.lastVar)
+                vd.lastVar = _this.lastVar;
+            _this.lastVar = vd;
         }
-        return td;
+        return null;
     }
-    return toAlias(s);
-}
 
-private Dsymbol toAliasImpl(AliasDeclaration ad)
-{
-    static if (0)
-    printf("[%s] AliasDeclaration::toAlias('%s', this = %p, aliassym: %s, kind: '%s', inuse = %d)\n",
-        ad.loc.toChars(), ad.toChars(), ad, ad.aliassym ? ad.aliassym.toChars() : "", ad.aliassym ? ad.aliassym.kind() : "", ad.inuse);
-    assert(ad != ad.aliassym);
-    //static int count; if (++count == 10) *(char*)0=0;
+    auto scopesym = _this.inner().scopesym;
+    //printf("\t\tscopesym = %p\n", scopesym);
+    if (!scopesym.symtab)
+        scopesym.symtab = new DsymbolTable();
+    if (!_this.inCfile)
+        return scopesym.symtabInsert(s);
 
-    Dsymbol err()
+    // ImportC insert
+    if (!scopesym.symtabInsert(s)) // if already in table
     {
-        // Avoid breaking "recursive alias" state during errors gagged
-        if (global.gag)
-            return ad;
-        ad.aliassym = new AliasDeclaration(ad.loc, ad.ident, Type.terror);
-        ad.type = Type.terror;
-        return ad.aliassym;
-    }
-    // Reading the AliasDeclaration
-    if (!ad.ignoreRead)
-        ad.wasRead = true;                 // can never assign to this AliasDeclaration again
+        Dsymbol s2 = scopesym.symtabLookup(s, s.ident); // s2 is existing entry
 
-    if (ad.inuse == 1 && ad.type && ad._scope)
-    {
-        ad.inuse = 2;
-        const olderrors = global.errors;
-        Dsymbol s = ad.type.toDsymbol(ad._scope);
-        //printf("[%s] type = %s, s = %p, this = %p\n", loc.toChars(), type.toChars(), s, this);
-        if (global.errors != olderrors)
-            return err();
-        if (s)
+        auto svar = s.isVarDeclaration();
+        auto s2var = s2.isVarDeclaration();
+        if (((svar && svar.storage_class & STC.extern_) &&
+                (s2var && s2var.storage_class & STC.extern_) && _this.func) ||
+                s.isFuncDeclaration())
         {
-            s = s.toAlias();
-            if (global.errors != olderrors)
-                return err();
-            ad.aliassym = s;
-            ad.inuse = 0;
+            return handleSymbolRedeclarations(*_this, s, s2, scopesym);
         }
-        else
+        else // aside externs and func decls, we should be free to handle tags
         {
-            Type t = ad.type.typeSemantic(ad.loc, ad._scope);
-            if (t.ty == Terror)
-                return err();
-            if (global.errors != olderrors)
-                return err();
-            //printf("t = %s\n", t.toChars());
-            ad.inuse = 0;
+            return handleTagSymbols(*_this, s, s2, scopesym);
         }
     }
-    if (ad.inuse)
-    {
-        .error(ad.loc, "%s `%s` recursive alias declaration", ad.kind, ad.toPrettyChars);
-        return err();
-    }
-
-    if (ad.semanticRun >= PASS.semanticdone)
-    {
-        // semantic is already done.
-
-        // Do not see aliassym !is null, because of lambda aliases.
+    return s; // inserted
+}
 
-        // 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
+/*******************************************
+ * Look for member of the form:
+ *      const(MemberInfo)[] getMembers(string);
+ * Returns NULL if not found
+ */
+FuncDeclaration findGetMembers(ScopeDsymbol dsym)
+{
+    import dmd.opover : search_function;
+    Dsymbol s = search_function(dsym, Id.getmembers);
+    FuncDeclaration fdx = s ? s.isFuncDeclaration() : null;
+    version (none)
     {
-        // stop AliasAssign tuple building
-        if (ad.aliassym)
-        {
-            if (auto td = ad.aliassym.isTupleDeclaration())
-            {
-                if (td.building)
-                {
-                    td.building = false;
-                    ad.semanticRun = PASS.semanticdone;
-                    return td;
-                }
-            }
-        }
-        if (ad._import && ad._import._scope)
-        {
-            /* If this is an internal alias for selective/renamed import,
-             * load the module first.
-             */
-            ad._import.dsymbolSemantic(null);
-        }
-        if (ad._scope)
+        // Finish
+        __gshared TypeFunction tfgetmembers;
+        if (!tfgetmembers)
         {
-            aliasSemantic(ad, ad._scope);
+            Scope sc;
+            sc.eSink = global.errorSink;
+            Parameters* p = new Parameter(STC.in_, Type.tchar.constOf().arrayOf(), null, null);
+            auto parameters = new Parameters(p);
+            Type tret = null;
+            TypeFunction tf = new TypeFunction(parameters, tret, VarArg.none, LINK.d);
+            tfgetmembers = tf.dsymbolSemantic(Loc.initial, &sc).isTypeFunction();
         }
+        if (fdx)
+            fdx = fdx.overloadExactMatch(tfgetmembers);
     }
+    if (fdx && fdx.isVirtual())
+        fdx = null;
+    return fdx;
+}
 
-    ad.inuse = 1;
-    Dsymbol s = ad.aliassym ? ad.aliassym.toAlias() : ad;
-    ad.inuse = 0;
-    return s;
+/***********************************
+ * Retrieve the .min or .max values.
+ * Only valid after semantic analysis.
+ * Params:
+ *  _this = bit field instance
+ *  id = Id.min or Id.max
+ * Returns:
+ *  the min or max value
+ */
+ulong getMinMax(BitFieldDeclaration _this, Identifier id)
+{
+    const width = _this.fieldWidth;
+    const uns = _this.type.isUnsigned();
+    const min = id == Id.min;
+    ulong v;
+    assert(width != 0);  // should have been rejected in semantic pass
+    if (width == ulong.sizeof * 8)
+        v = uns ? (min ? ulong.min : ulong.max)
+                : (min ?  long.min :  long.max);
+    else
+        v = uns ? (min ? 0
+                       : (1L << width) - 1)
+                : (min ? -(1L << (width - 1))
+                       :  (1L << (width - 1)) - 1);
+    return v;
 }
 
-/*********************************
- * If this symbol is really an alias for another,
- * return that other.
- * If needed, semantic() is invoked due to resolve forward reference.
+/* Append vthis field (this.tupleof[$-1]) to make this aggregate type nested.
  */
-Dsymbol toAlias(Dsymbol s)
+void makeNested(AggregateDeclaration _this)
 {
-    if (auto ad = s.isAliasDeclaration())
-    {
-        return ad.toAliasImpl();
-    }
-    if (auto imp = s.isImport())
-    {
-        if (imp.aliasId)
-            return imp.mod;
-        return imp;
-    }
-    if (auto vd = s.isVarDeclaration())
+    if (_this.enclosing) // if already nested
+        return;
+    if (_this.sizeok == Sizeok.done)
+        return;
+    if (_this.isUnionDeclaration() || _this.isInterfaceDeclaration())
+        return;
+    if (_this.storage_class & STC.static_)
+        return;
+
+    // If nested struct, add in hidden 'this' pointer to outer scope
+    auto s = _this.toParentLocal();
+    if (!s)
+        s = _this.toParent2();
+    if (!s)
+        return;
+    Type t = null;
+    if (auto fd = s.isFuncDeclaration())
     {
-        //printf("VarDeclaration::toAlias('%s', this = %p, aliassym = %p)\n", toChars(), this, aliassym);
-        if ((!vd.type || !vd.type.deco) && vd._scope)
-            dsymbolSemantic(vd, vd._scope);
+        _this.enclosing = fd;
 
-        assert(vd != vd.aliasTuple);
-        return vd.aliasTuple ? vd.aliasTuple.toAlias() : vd;
+        /* 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;
     }
-    // resolve real symbol
-    if (auto ti = s.isTemplateInstance())
+    else if (auto ad = s.isAggregateDeclaration())
     {
-        static if (LOG)
+        if (_this.isClassDeclaration() && ad.isClassDeclaration())
         {
-            printf("TemplateInstance.toAlias()\n");
+            _this.enclosing = ad;
         }
-        if (!ti.inst)
+        else if (_this.isStructDeclaration())
         {
-            // Maybe we can resolve it
-            if (ti._scope)
-            {
-                dsymbolSemantic(ti, ti._scope);
-            }
-            if (!ti.inst)
+            if (auto ti = ad.parent.isTemplateInstance())
             {
-                .error(ti.loc, "%s `%s` cannot resolve forward reference", ti.kind, ti.toPrettyChars);
-                ti.errors = true;
-                return ti;
+                _this.enclosing = ti.enclosing;
             }
         }
+        t = ad.handleType();
+    }
+    if (_this.enclosing)
+    {
+        import dmd.typesem : alignment;
+        //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
 
-        if (ti.inst != ti)
-            return ti.inst.toAlias();
+        assert(!_this.vthis);
+        _this.vthis = new ThisDeclaration(_this.loc, t);
+        //vthis.storage_class |= STC.ref_;
 
-        if (ti.aliasdecl)
-        {
-            return ti.aliasdecl.toAlias();
-        }
+        // Emulate vthis.addMember()
+        _this.members.push(_this.vthis);
 
-        return ti.inst;
+        // Emulate vthis.dsymbolSemantic()
+        _this.vthis.storage_class |= STC.field;
+        _this.vthis.parent = _this;
+        _this.vthis.visibility = Visibility(Visibility.Kind.public_);
+        _this.vthis.alignment = t.alignment();
+        _this.vthis.semanticRun = PASS.semanticdone;
+
+        if (_this.sizeok == Sizeok.fwd)
+            _this.fields.push(_this.vthis);
+
+        _this.makeNested2();
     }
-    return s;
 }
 
-const(char)* getMessage(DeprecatedDeclaration dd)
+/* Append vthis2 field (this.tupleof[$-1]) to add a second context pointer.
+ */
+void makeNested2(AggregateDeclaration _this)
 {
-    if (auto sc = dd._scope)
-    {
-        dd._scope = null;
+    import dmd.typesem : alignment;
 
-        sc = sc.startCTFE();
-        dd.msg = dd.msg.expressionSemantic(sc);
-        dd.msg = resolveProperties(sc, dd.msg);
-        sc = sc.endCTFE();
-        dd.msg = dd.msg.ctfeInterpret();
+    if (_this.vthis2)
+        return;
+    if (!_this.vthis)
+        _this.makeNested();   // can't add second before first
+    if (!_this.vthis)
+        return;
+    if (_this.sizeok == Sizeok.done)
+        return;
+    if (_this.isUnionDeclaration() || _this.isInterfaceDeclaration())
+        return;
+    if (_this.storage_class & STC.static_)
+        return;
 
-        if (auto se = dd.msg.toStringExp())
-            dd.msgstr = se.toStringz().ptr;
-        else
-            error(dd.msg.loc, "compile time constant expected, not `%s`", dd.msg.toChars());
-    }
-    return dd.msgstr;
-}
+    auto s0 = _this.toParentLocal();
+    auto s = _this.toParent2();
+    if (!s || !s0 || s == s0)
+        return;
+    auto cd = s.isClassDeclaration();
+    Type t = cd ? cd.type : Type.tvoidptr;
 
-bool checkDeprecated(Dsymbol d, Loc loc, Scope* sc)
-{
-    if (global.params.useDeprecated == DiagnosticReporting.off)
-        return false;
-    if (!d.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.inTemplateConstraint)
-        return false;
+    _this.vthis2 = new ThisDeclaration(_this.loc, t);
+    //vthis2.storage_class |= STC.ref_;
 
-    const(char)* message = null;
-    for (Dsymbol p = d; p; p = p.parent)
-    {
-        message = p.depdecl ? p.depdecl.getMessage() : null;
-        if (message)
-            break;
-    }
-    if (message)
-        deprecation(loc, "%s `%s` is deprecated - %s", d.kind, d.toPrettyChars, message);
-    else
-        deprecation(loc, "%s `%s` is deprecated", d.kind, d.toPrettyChars);
+    // Emulate vthis2.addMember()
+    _this.members.push(_this.vthis2);
 
-    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);
+    // Emulate vthis2.dsymbolSemantic()
+    _this.vthis2.storage_class |= STC.field;
+    _this.vthis2.parent = _this;
+    _this.vthis2.visibility = Visibility(Visibility.Kind.public_);
+    _this.vthis2.alignment = t.alignment();
+    _this.vthis2.semanticRun = PASS.semanticdone;
 
-    return true;
+    if (_this.sizeok == Sizeok.fwd)
+        _this.fields.push(_this.vthis2);
 }
 
-/*********************************
- * Check type to see if it is based on a deprecated symbol.
+/************************************
+ * Perform unqualified name lookup by following the chain of scopes up
+ * until found.
+ *
+ * Params:
+ *  _this = Scope object
+ *  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, otherwise set to null
+ *  flags = modify search based on flags
+ *
+ * Returns:
+ *  symbol if found, null if not
  */
-private void checkDeprecated(Type type, Loc loc, Scope* sc)
+Dsymbol search(Scope* _this, Loc loc, Identifier ident, out Dsymbol pscopesym, SearchOptFlags flags = SearchOpt.all)
 {
-    if (Dsymbol s = type.toDsymbol(sc))
+    version (LOGSEARCH)
     {
-        s.checkDeprecated(loc, sc);
-    }
+        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());
+        }
 
-    if (auto tn = type.nextOf())
-        tn.checkDeprecated(loc, sc);
-}
+        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());
+        }
+    }
 
-// Returns true if a contract can appear without a function body.
-package bool allowsContractWithoutBody(FuncDeclaration funcdecl)
-{
-    assert(!funcdecl.fbody);
+    // This function is called only for unqualified lookup
+    assert(!(flags & (SearchOpt.localsOnly | SearchOpt.importsOnly)));
 
-    /* Contracts can only appear without a body when they are virtual
-     * interface functions or abstract.
+    /* If ident is "start at module scope", only look at module scope
      */
-    Dsymbol parent = funcdecl.toParent();
-    InterfaceDeclaration id = parent.isInterfaceDeclaration();
-
-    if (!funcdecl.isAbstract() &&
-        (funcdecl.fensures || funcdecl.frequires) &&
-        !(id && funcdecl.isVirtual()))
+    if (ident == Id.empty)
     {
-        auto cd = parent.isClassDeclaration();
-        if (!(cd && cd.isAbstract()))
-            return false;
+        // 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);
+                pscopesym = sc.scopesym;
+                return s;
+            }
+        }
+        return null;
     }
-    return true;
-}
 
-/***************************************
- * 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
- */
-bool isPOD(StructDeclaration sd)
-{
-    // If we've already determined whether this struct is POD.
-    if (sd.ispod != ThreeState.none)
-        return (sd.ispod == ThreeState.yes);
+    Dsymbol checkAliasThis(AggregateDeclaration ad, Identifier ident, SearchOptFlags flags, Expression* exp)
+    {
+        if (!ad || !ad.aliasthis)
+            return null;
 
-    import dmd.clone;
+        Declaration decl = ad.aliasthis.sym.isDeclaration();
+        if (!decl)
+            return null;
 
-    bool hasCpCtorLocal;
-    bool hasMoveCtorLocal;
-    bool needCopyCtor;
-    bool needMoveCtor;
-    needCopyOrMoveCtor(sd, hasCpCtorLocal, hasMoveCtorLocal, needCopyCtor, needMoveCtor);
+        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 (sd.enclosing                    || // is nested
-        search(sd, sd.loc, Id.postblit) || // has postblit
-        search(sd, sd.loc, Id.dtor)     || // has destructor
-        /* This is commented out because otherwise buildkite vibe.d:
-           `canCAS!Task` fails to compile
-         */
-        //hasMoveCtorLocal               || // has move constructor
-        hasCpCtorLocal)                   // has copy constructor
-    {
-        sd.ispod = ThreeState.no;
-        return false;
-    }
+        if (!sds)
+            return null;
 
-    // Recursively check all fields are POD.
-    for (size_t i = 0; i < sd.fields.length; i++)
-    {
-        VarDeclaration v = sd.fields[i];
-        if (v.storage_class & STC.ref_)
+        Dsymbol ret = sds.search(loc, ident, flags);
+        if (ret)
         {
-            sd.ispod = ThreeState.no;
-            return false;
+            *exp = new DotIdExp(loc, *exp, ad.aliasthis.ident);
+            *exp = new DotIdExp(loc, *exp, ident);
+            return ret;
         }
 
-        if (auto ts = v.type.baseElemOf().isTypeStruct())
+        if (!ts && !tc)
+            return null;
+
+        Dsymbol s;
+        *exp = new DotIdExp(loc, *exp, ad.aliasthis.ident);
+        if (ts && !(ts.att & AliasThisRec.tracing))
         {
-            if (!ts.sym.isPOD())
+            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(SearchOptFlags 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 |= SearchOpt.unqualifiedModule;    // tell Module.search() that SearchOpt.localsOnly is to be obeyed
+            else if (sc.inCfile && sc.scopesym.isStructDeclaration())
+                continue;                                // C doesn't have struct scope
+
+            if (Dsymbol s = sc.scopesym.search(loc, ident, flags))
             {
-                sd.ispod = ThreeState.no;
-                return false;
+                if (flags & SearchOpt.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;
+                    }
+                }
+                //printMsg("\tfound local", s);
+                pscopesym = sc.scopesym;
+                return s;
+            }
+
+        NotFound:
+            if (sc.previews.fixAliasThis)
+            {
+                Expression exp = new ThisExp(loc);
+                if (Dsymbol aliasSym = checkAliasThis(sc.scopesym.isAggregateDeclaration(), ident, flags, &exp))
+                {
+                    //printf("found aliassym: %s\n", aliasSym.toChars());
+                    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;
     }
 
-    sd.ispod = ThreeState.yes;
-    return true;
-}
-
-/*
-If sd has a copy constructor and ctor is an rvalue constructor,
-issue an error.
+    if (_this.ignoresymbolvisibility)
+        flags |= SearchOpt.ignoreVisibility;
 
-Params:
-    sd = struct declaration that may contain both an rvalue and copy constructor
-    ctor = constructor that will be checked if it is an rvalue constructor
-    ti = template instance the ctor is part of
+    // First look in local scopes
+    Dsymbol s = searchScopes(flags | SearchOpt.localsOnly);
+    version (LOGSEARCH) if (s) printMsg("-Scope.search() found local", s);
+    if (!s)
+    {
+        // Second look in imported modules
+        s = searchScopes(flags | SearchOpt.importsOnly);
+        version (LOGSEARCH) if (s) printMsg("-Scope.search() found import", s);
+    }
+    return s;
+}
 
-Return:
-    `true` if sd has a copy constructor and ctor is an rvalue constructor
-*/
-bool checkHasBothRvalueAndCpCtor(StructDeclaration sd, CtorDeclaration ctor, TemplateInstance ti)
+Dsymbol search_correct(Scope* _this, Identifier ident)
 {
-    //printf("checkHasBothRvalueAndCpCtor() sd: %s ctor: %s ti: %s\n", sd.toChars(), ctor.toChars(), ti.toChars());
-    /* cannot use ctor.isMoveCtor because semantic pass may not have been run yet,
-     * so use isRvalueConstructor()
+    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
      */
-    if (sd && sd.hasCopyCtor && isRvalueConstructor(sd, ctor))
+    Dsymbol scope_search_fp(const(char)[] seed, out int cost)
     {
-        .error(ctor.loc, "cannot define both an rvalue constructor and a copy constructor for `struct %s`", sd.toChars());
-        .errorSupplemental(ti.loc, "Template instance `%s` creates an rvalue constructor for `struct %s`",
-                ti.toPrettyChars(), sd.toChars());
+        //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;
+        Dsymbol s = sc.search(Loc.initial, id, scopesym, SearchOpt.ignoreErrors);
+        if (!s)
+            return null;
 
-        return true;
+        // 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;
     }
 
-    return false;
+    Dsymbol scopesym;
+    // search for exact name first
+    if (auto s = _this.search(Loc.initial, ident, scopesym, SearchOpt.ignoreErrors))
+        return s;
+    return speller!scope_search_fp(ident.toString());
 }
 
-/************************************************
- * Check if ctor is an rvalue constructor.
- * A constructor that receives a single parameter of the same type as
- * `Unqual!typeof(this)` is an rvalue constructor.
- * Params:
- *      sd = struct that ctor is a member of
- *      ctor = constructor to test
- * Returns:
- *      true if it is an rvalue constructor
- */
-bool isRvalueConstructor(StructDeclaration sd, CtorDeclaration ctor)
+structalign_t alignment(Scope* _this)
 {
-    // note commonality with setting isMoveCtor in the semantic code for CtorDeclaration
-    auto tf = ctor.type.isTypeFunction();
-    const dim = tf.parameterList.length;
-    if (dim == 1 || (dim > 1 && tf.parameterList[1].defaultArg))
+    if (_this.aligndecl)
     {
-        auto param = tf.parameterList[0];
-        if (!(param.storageClass & STC.ref_) && param.type.mutableOf().unSharedOf() == sd.type.mutableOf().unSharedOf())
-        {
-            return true;
-        }
+        auto ad = _this.aligndecl.getAlignment(_this);
+        return ad.salign;
     }
-    return false;
+    else
+    {
+        structalign_t sa;
+        sa.setDefault();
+        return sa;
+    }
+}
+
+Scope* scopeCreateGlobal(Module _module, ErrorSink eSink)
+{
+    Scope* sc = Scope.alloc();
+    *sc = Scope.init;
+    sc._module = _module;
+    sc.minst = _module;
+    sc.scopesym = new ScopeDsymbol();
+    sc.scopesym.symtab = new DsymbolTable();
+    sc.eSink = eSink;
+    assert(eSink);
+    // 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()
+    sc.previews.setFromParams(global.params);
+
+    if (_module.filetype == FileType.c)
+        sc.inCfile = true;
+    // Create the module scope underneath the global scope
+    sc = sc.push(_module);
+    sc.parent = _module;
+    return sc;
 }
 
 /*************************************
- * Find the `alias this` symbol of e's type.
- * Params:
- *      sc = context
- *      e = expression forming the `this`
- *      gag = do not print errors, return `null` instead
- *      findOnly = don't do further processing like resolving properties,
- *                 i.e. just return plain dotExp() result.
- * Returns:
- *      Expression that is `e.aliasthis`
+ * Does semantic analysis on the public face of declarations.
  */
-Expression resolveAliasThis(Scope* sc, Expression e, bool gag = false, bool findOnly = false)
+void dsymbolSemantic(Dsymbol dsym, Scope* sc)
 {
-    //printf("resolveAliasThis() %s\n", toChars(e));
-    import dmd.typesem : dotExp;
-    for (AggregateDeclaration ad = isAggregate(e.type); ad;)
-    {
-        if (ad.aliasthis)
-        {
-            Loc loc = e.loc;
-            Type tthis = (e.op == EXP.type ? e.type : null);
-            const flags = cast(DotExpFlag) (DotExpFlag.noAliasThis | (gag * DotExpFlag.gag));
-            const olderrors = gag ? global.startGagging() : 0;
-            e = dotExp(ad.type, sc, e, ad.aliasthis.ident, flags);
-            if (!e || findOnly)
-                return gag && global.endGagging(olderrors) ? null : e;
-
-            if (tthis && ad.aliasthis.sym.needThis())
-            {
-                if (auto ve = e.isVarExp())
-                {
-                    if (auto fd = ve.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.
-                 */
-                {
-                    ubyte 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;
+    scope v = new DsymbolSemanticVisitor(sc);
+    dsym.accept(v);
 }
 
-/**
- * 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.
- */
-private bool checkDeprecatedAliasThis(AliasThis at, Loc loc, Scope* sc)
+// function used to call semantic3 on a module's dependencies
+void semantic3OnDependencies(Module m)
 {
-    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 (!m)
+        return;
 
-        if (auto ti = sc.parent ? sc.parent.isInstantiated() : null)
-            ti.printInstantiationTrace(Classification.deprecation);
+    if (m.semanticRun > PASS.semantic3)
+        return;
 
-        return true;
-    }
-    return false;
-}
+    m.semantic3(null);
 
-// Save the scope and defer semantic analysis on the Dsymbol.
-void deferDsymbolSemantic(Scope* sc, Dsymbol s, Scope* scx)
-{
-    s._scope = scx ? scx : sc.copy();
-    s._scope.setNoFree();
-    Module.addDeferredSemantic(s);
+    foreach (i; 1 .. m.aimports.length)
+        semantic3OnDependencies(m.aimports[i]);
 }
 
-struct Ungag
+/*******************************************
+ * Can't run semantic on s now, try again later.
+ */
+void addDeferredSemantic(Dsymbol s)
 {
-    uint oldgag;
-
-    extern (D) this(uint old) nothrow @safe
+    //printf("Module::addDeferredSemantic('%s')\n", s.toChars());
+    if (!s.deferred)
     {
-        this.oldgag = old;
+        s.deferred = true;
+        Module.deferred.push(s);
     }
+}
 
-    extern (C++) ~this() nothrow
+void addDeferredSemantic2(Dsymbol s)
+{
+    //printf("Module::addDeferredSemantic2('%s')\n", s.toChars());
+    if (!s.deferred2)
     {
-        global.gag = oldgag;
+        s.deferred2 = true;
+        Module.deferred2.push(s);
     }
 }
 
-Ungag ungagSpeculative(const Dsymbol s)
+void addDeferredSemantic3(Dsymbol s)
 {
-    const oldgag = global.gag;
-    if (global.gag && !s.isSpeculative() && !s.toParent2().isFuncDeclaration())
-        global.gag = 0;
-    return Ungag(oldgag);
+    //printf("Module::addDeferredSemantic3('%s')\n", s.toChars());
+    if (!s.deferred3)
+    {
+        s.deferred3 = true;
+        Module.deferred3.push(s);
+    }
 }
 
-/*******************************************
- * Print deprecation warning if we're deprecated, when
- * this module is imported from scope sc.
- *
- * Params:
- *  m = the module
- *  sc = the scope into which we are imported
- *  loc = the location of the import statement
+/******************************************
+ * Run semantic() on deferred symbols.
  */
-private void checkImportDeprecation(Module m, Loc loc, Scope* sc)
+void runDeferredSemantic()
 {
-    if (!m.md || !m.md.isdeprecated || sc.isDeprecated)
+    __gshared int nested;
+    if (nested)
         return;
+    //if (Module.deferred.length) printf("+Module::runDeferredSemantic(), len = %ld\n", deferred.length);
+    nested++;
 
-    Expression msg = m.md.msg;
-    if (StringExp se = msg ? msg.toStringExp() : null)
+    size_t len;
+    do
     {
-        const slice = se.peekString();
-        if (slice.length)
+        len = Module.deferred.length;
+        if (!len)
+            break;
+
+        Dsymbol* todo;
+        Dsymbol* todoalloc = null;
+        Dsymbol tmp;
+        if (len == 1)
         {
-            deprecation(m.loc, "%s `%s` is deprecated - %.*s", m.kind, m.toPrettyChars, cast(int)slice.length, slice.ptr);
-            return;
+            todo = &tmp;
+        }
+        else
+        {
+            todo = cast(Dsymbol*)Mem.check(malloc(len * Dsymbol.sizeof));
+            todoalloc = todo;
+        }
+        memcpy(todo, Module.deferred.tdata(), len * Dsymbol.sizeof);
+        foreach (Dsymbol s; Module.deferred[])
+            s.deferred = false;
+        Module.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.length = %ld, len = %ld\n", deferred.length, len);
+        if (todoalloc)
+            free(todoalloc);
     }
-    deprecation(m.loc, "%s `%s` is deprecated", m.kind, m.toPrettyChars);
+    while (Module.deferred.length != len); // while making progress
+    nested--;
+    //printf("-Module::runDeferredSemantic(), len = %ld\n", deferred.length);
 }
 
-private extern(C++) final class DsymbolSemanticVisitor : Visitor
+void runDeferredSemantic2()
 {
-    alias visit = Visitor.visit;
+    runDeferredSemantic();
 
-    Scope* sc;
-    this(Scope* sc) scope @safe
+    Dsymbols* a = &Module.deferred2;
+    for (size_t i = 0; i < a.length; i++)
     {
-        this.sc = sc;
+        Dsymbol s = (*a)[i];
+        s.deferred2 = false;
+        //printf("[%d] %s semantic2a\n", i, s.toPrettyChars());
+        s.semantic2(null);
+
+        if (global.errors)
+            break;
     }
+    a.setDim(0);
+}
 
-    override void visit(Dsymbol dsym)
+void runDeferredSemantic3()
+{
+    runDeferredSemantic2();
+
+    Dsymbols* a = &Module.deferred3;
+    for (size_t i = 0; i < a.length; i++)
     {
-        .error(dsym.loc, "%s `%s` %p has no semantic routine", dsym.kind, dsym.toPrettyChars, dsym);
+        Dsymbol s = (*a)[i];
+        s.deferred3 = false;
+        //printf("[%d] %s semantic3a\n", i, s.toPrettyChars());
+        s.semantic3(null);
+
+        if (global.errors)
+            break;
     }
+    a.setDim(0);
+}
 
-    override void visit(ScopeDsymbol) { }
-    override void visit(Declaration) { }
+bool isOverlappedWith(VarDeclaration _this, VarDeclaration v)
+{
+    import dmd.typesem : size;
+    const vsz = v.type.size();
+    const tsz = _this.type.size();
+    assert(vsz != SIZE_INVALID && tsz != SIZE_INVALID);
 
-    override void visit(AliasThis dsym)
+    // Overlap is checked by comparing bit offsets
+    auto bitoffset  = _this.offset * 8;
+    auto vbitoffset =     v.offset * 8;
+
+    // Bitsize of types are overridden by any bitfield widths.
+    ulong tbitsize;
+    if (auto bf = _this.isBitFieldDeclaration())
     {
-        if (dsym.semanticRun != PASS.initial)
-            return;
+        bitoffset += bf.bitOffset;
+        tbitsize = bf.fieldWidth;
+    }
+    else
+        tbitsize = tsz * 8;
 
-        if (dsym._scope)
-        {
-            sc = dsym._scope;
-            dsym._scope = null;
-        }
+    ulong vbitsize;
+    if (auto vbf = v.isBitFieldDeclaration())
+    {
+        vbitoffset += vbf.bitOffset;
+        vbitsize = vbf.fieldWidth;
+    }
+    else
+        vbitsize = vsz * 8;
 
-        if (!sc)
-            return;
+    return   bitoffset < vbitoffset + vbitsize &&
+            vbitoffset <  bitoffset + tbitsize;
+}
 
-        dsym.semanticRun = PASS.semantic;
-        dsym.isDeprecated_ = !!(sc.stc & STC.deprecated_);
+private Type tupleDeclGetType(TupleDeclaration _this)
+{
+    /* If this tuple represents a type, return that type
+     */
 
-        Dsymbol p = sc.parent.pastMixin();
-        AggregateDeclaration ad = p.isAggregateDeclaration();
-        if (!ad)
+    //printf("TupleDeclaration::getType() %s\n", toChars());
+    if (_this.isexp || _this.building)
+        return null;
+    if (_this.tupletype)
+        return _this.tupletype;
+
+    /* It's only a type tuple if all the Object's are types
+     */
+    for (size_t i = 0; i < _this.objects.length; i++)
+    {
+        RootObject o = (*_this.objects)[i];
+        if (!o.isType())
         {
-            error(dsym.loc, "alias this can only be a member of aggregate, not %s `%s`", p.kind(), p.toChars());
-            return;
+            //printf("\tnot[%d], %p, %d\n", i, o, o.dyncast());
+            return null;
         }
+    }
 
-        assert(ad.members);
-        Dsymbol s = ad.search(dsym.loc, dsym.ident);
-        if (!s)
+    /* We know it's a type tuple, so build the TypeTuple
+     */
+    Types* types = cast(Types*)_this.objects;
+    auto args = new Parameters(_this.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());
+        version (none)
         {
-            Dsymbol pscopesym;
-            s = sc.search(dsym.loc, dsym.ident, pscopesym);
-            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;
+            buf.printf("_%s_%d", _this.ident.toChars(), i);
+            auto id = Identifier.idPool(buf.extractSlice());
+            auto arg = new Parameter(Loc.initial, STC.in_, t, id, null);
         }
-        if (ad.aliasthis && s != ad.aliasthis)
+        else
         {
-            error(dsym.loc, "there can be only one alias this");
-            return;
+            auto arg = new Parameter(Loc.initial, STC.none, t, null, null, null);
         }
+        (*args)[i] = arg;
+        if (!t.deco)
+            hasdeco = 0;
+    }
 
-        /* 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);
+    _this.tupletype = new TypeTuple(args);
+    if (hasdeco)
+        return _this.tupletype.typeSemantic(Loc.initial, null);
 
-            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());
-            }
-        }
+    return _this.tupletype;
+}
 
-        dsym.sym = s;
-        // Restore alias this
-        ad.aliasthis = dsym;
-        dsym.semanticRun = PASS.semanticdone;
-    }
+private Type aliasDeclGetType(AliasDeclaration _this)
+{
+    if (_this.type)
+        return _this.type;
+    return toAlias(_this).getType();
+}
 
-    override void visit(AliasDeclaration dsym)
+private Type aggregateDeclGetType(AggregateDeclaration _this)
+{
+    /* Apply storage classes to forward references. (Issue 22254)
+     * Note: Avoid interfaces for now. Implementing qualifiers on interface
+     * definitions exposed some issues in their TypeInfo generation in DMD.
+     * Related PR: https://github.com/dlang/dmd/pull/13312
+     */
+    if (_this.semanticRun == PASS.initial && !_this.isInterfaceDeclaration())
     {
-        if (dsym.semanticRun >= PASS.semanticdone)
-            return;
-        assert(dsym.semanticRun <= PASS.semantic);
-
-        if (!sc)
-            return;
-
-        dsym.semanticRun = PASS.semantic;
+        auto stc = _this.storage_class;
+        if (_this._scope)
+            stc |= _this._scope.stc;
+        _this.type = _this.type.addSTC(stc);
+    }
+    return _this.type;
+}
 
-        dsym.storage_class |= sc.stc & STC.deprecated_;
-        dsym.visibility = sc.visibility;
-        dsym.userAttribDecl = sc.userAttribDecl;
+Type getType(Dsymbol _this)
+{
+    if (auto td = _this.isTupleDeclaration())
+        return tupleDeclGetType(td);
+    else if (auto ad = _this.isAliasDeclaration())
+        return aliasDeclGetType(ad);
+    else if (auto agd = _this.isAggregateDeclaration())
+        return aggregateDeclGetType(agd);
+    else if (auto ed = _this.isEnumDeclaration())
+        return ed.type;
+
+    // is this a type?
+    return null;
+}
 
-        if (!sc.func && dsym.inNonRoot())
-            return;
+private uinteger_t aggregateDeclSize(AggregateDeclaration _this, Loc loc)
+{
+    //printf("+AggregateDeclaration::size() %s, scope = %p, sizeok = %d\n", toChars(), _scope, sizeok);
+    bool ok = determineSize(_this, loc);
+    //printf("-AggregateDeclaration::size() %s, scope = %p, sizeok = %d\n", toChars(), _scope, sizeok);
+    return ok ? _this.structsize : SIZE_INVALID;
+}
 
-        aliasSemantic(dsym, sc);
-    }
+private uinteger_t declSize(Declaration _this, Loc loc)
+{
+    import dmd.typesem: size;
+    assert(_this.type);
+    const sz = _this.type.size();
+    if (sz == SIZE_INVALID)
+        _this.errors = true;
+    return sz;
+}
 
-    override void visit(AliasAssign dsym)
-    {
-        //printf("visit(AliasAssign)\n");
-        if (dsym.semanticRun >= PASS.semanticdone)
-            return;
-        assert(dsym.semanticRun <= PASS.semantic);
+/*********************************
+ * Returns:
+ *  SIZE_INVALID when the size cannot be determined
+ */
+uinteger_t size(Dsymbol _this, Loc loc)
+{
+    if (auto ad = _this.isAggregateDeclaration())
+        return aggregateDeclSize(ad, loc);
+    else if (auto d = _this.isDeclaration())
+        return declSize(d, loc);
+    .error(loc, "%s `%s` symbol `%s` has no size", _this.kind, _this.toPrettyChars, _this.toChars());
+    return SIZE_INVALID;
+}
 
-        if (!sc.func && dsym.inNonRoot())
-            return;
+private bool funcDeclEquals(const FuncDeclaration _this, const Dsymbol s)
+{
+    auto fd1 = _this;
+    auto fd2 = s.isFuncDeclaration();
+    if (!fd2)
+        return false;
 
-        aliasAssignSemantic(dsym, sc);
-    }
+    auto fa1 = fd1.isFuncAliasDeclaration();
+    auto faf1 = fa1 ? fa1.toAliasFunc() : fd1;
 
-    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.initial)
-        //    return;
-        //semanticRun = PSSsemantic;
+    auto fa2 = fd2.isFuncAliasDeclaration();
+    auto faf2 = fa2 ? fa2.toAliasFunc() : fd2;
 
-        if (dsym.semanticRun >= PASS.semanticdone)
-            return;
+    if (fa1 && fa2)
+        return faf1.equals(faf2) && fa1.hasOverloads == fa2.hasOverloads;
 
-        if (sc && sc.inunion && sc.inunion.isAnonDeclaration())
-            dsym.overlapped = true;
+    bool b1 = fa1 !is null;
+    if (b1 && faf1.isUnique() && !fa1.hasOverloads)
+        b1 = false;
 
-        dsym.sequenceNumber = global.varSequenceNumber++;
-        if (!dsym.isScope())
-            dsym.maybeScope = true;
+    bool b2 = fa2 !is null;
+    if (b2 && faf2.isUnique() && !fa2.hasOverloads)
+        b2 = false;
 
-        Scope* scx = null;
-        if (dsym._scope)
-        {
-            sc = dsym._scope;
-            scx = sc;
-            dsym._scope = null;
-        }
+    if (b1 != b2)
+        return false;
 
-        if (!sc)
-            return;
+    return faf1.toParent().equals(faf2.toParent()) &&
+           faf1.ident.equals(faf2.ident) &&
+           faf1.type.equals(faf2.type);
+}
 
-        dsym.semanticRun = PASS.semantic;
+private bool overDeclEquals(const OverDeclaration _this, const Dsymbol s)
+{
+    if (auto od2 = s.isOverDeclaration())
+        return _this.aliassym.equals(od2.aliassym);
+    return _this.aliassym == s;
+}
 
-        // 'static foreach' variables should not inherit scope properties
-        // https://issues.dlang.org/show_bug.cgi?id=19482
-        if ((dsym.storage_class & (STC.foreach_ | STC.local)) == (STC.foreach_ | STC.local))
-        {
-            dsym._linkage = LINK.d;
-            dsym.visibility = Visibility(Visibility.Kind.public_);
-            dsym.overlapped = false; // unset because it is modified early on this function
-            dsym.userAttribDecl = null; // unset because it is set by Dsymbol.setScope()
-        }
-        else
-        {
-            /* 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_));
-            dsym.userAttribDecl = sc.userAttribDecl;
-            dsym.cppnamespace = sc.namespace;
-            dsym._linkage = sc.linkage;
-            dsym.visibility = sc.visibility;
-            dsym.alignment = sc.alignment();
-        }
+private bool packageEquals(const Package _this, const Dsymbol s)
+{
+    // custom 'equals' for bug 17441. "package a" and "module a" are not equal
+    auto p = cast(Package)s;
+    return p && _this.isModule() == p.isModule() && _this.ident.equals(p.ident);
+}
 
-        if (dsym.storage_class & STC.extern_ && dsym._init)
-        {
-            if (sc.inCfile)
-            {
-                // https://issues.dlang.org/show_bug.cgi?id=24447
-                // extern int x = 3; is allowed in C
-                dsym.storage_class &= ~STC.extern_;
-            }
-            else
-                .error(dsym.loc, "%s `%s` extern symbols cannot have initializers", dsym.kind, dsym.toPrettyChars);
+bool equals(const Dsymbol _this, const Dsymbol s)
+{
+    if (_this == s)
+        return true;
 
-        }
+    if(auto fd = _this.isFuncDeclaration())
+        return funcDeclEquals(fd, s);
+    else if (auto od = _this.isOverDeclaration())
+        return overDeclEquals(od, s);
+    else if (auto pkg = _this.isPackage())
+        return packageEquals(pkg, s);
+
+    // Overload sets don't have an ident
+    // Function-local declarations may have identical names
+    // if they are declared in different scopes
+    if (s && _this.ident && s.ident && _this.ident.equals(s.ident) && _this.localNum == s.localNum)
+        return true;
 
-        AggregateDeclaration ad = dsym.isThis();
-        if (ad)
-            dsym.storage_class |= ad.storage_class & STC.TYPECTOR;
+    return false;
+}
 
-        if ((dsym.storage_class & STC.auto_) && (dsym.storage_class & STC.ref_))
-        {
-            if (!(dsym.storage_class & STC.autoref))
-            {
-                .error(dsym.loc, "%s `%s` - `auto ref` variable must have `auto` and `ref` adjacent", dsym.kind, dsym.toChars());
-                dsym.storage_class |= STC.autoref;
-            }
-        }
+private bool aliasOverloadInsert(AliasDeclaration ad, Dsymbol s)
+{
+    //printf("[%s] AliasDeclaration::overloadInsert('%s') s = %s %s @ [%s]\n",
+    //       loc.toChars(), toChars(), s.kind(), s.toChars(), s.loc.toChars());
 
-        /* If auto type inference, do the inference
+    /** 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 (ad.semanticRun < PASS.semanticdone)
+    {
+        /* Don't know yet what the aliased symbol is, so assume it can
+         * be overloaded and check later for correctness.
          */
-        int inferred = 0;
-        if (!dsym.type)
-        {
-            dsym.inuse++;
+        if (ad.overnext)
+            return ad.overnext.overloadInsert(s);
+        if (s is ad)
+            return true;
+        ad.overnext = s;
+        return true;
+    }
+    /* Semantic analysis is already finished, and the aliased entity
+     * is not overloadable.
+     */
+    if (ad.type)
+    {
+        /*
+            If type has been resolved already we could
+            still be inserting an alias from an import.
 
-            // Infering the type requires running semantic,
-            // so mark the scope as ctfe if required
-            bool needctfe = (dsym.storage_class & (STC.manifest | STC.static_)) != 0 || !sc.func;
-            if (needctfe)
-            {
-                sc.condition = true;
-                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(null, sc.inCfile).type;
-            if (needctfe)
-                sc = sc.endCTFE();
+            If we are handling an alias then pretend
+            it was inserting and return true, if not then
+            false since we didn't even pretend to insert something.
+        */
+        return ad._import && ad.equals(s);
+    }
 
-            dsym.inuse--;
-            inferred = 1;
+    // https://issues.dlang.org/show_bug.cgi?id=23865
+    // only insert if the symbol can be part of a set
+    const s1 = s.toAlias();
+    const isInsertCandidate = s1.isFuncDeclaration() || s1.isOverDeclaration() || s1.isTemplateDeclaration();
 
-            /* This is a kludge to support the existing syntax for RAII
-             * declarations.
-             */
-            dsym.storage_class &= ~STC.auto_;
-            dsym.originalType = dsym.type.syntaxCopy();
+    /* 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 = ad.aliassym.toAlias();
+
+    if (auto fd = sa.isFuncDeclaration())
+    {
+        auto fa = new FuncAliasDeclaration(ad.ident, fd);
+        fa.visibility = ad.visibility;
+        fa.parent = ad.parent;
+        ad.aliassym = fa;
+        if (isInsertCandidate)
+            return ad.aliassym.overloadInsert(s);
+    }
+    if (auto td = sa.isTemplateDeclaration())
+    {
+        auto od = new OverDeclaration(ad.ident, td.funcroot ? td.funcroot : td);
+        od.visibility = ad.visibility;
+        od.parent = ad.parent;
+        ad.aliassym = od;
+        if (isInsertCandidate)
+            return ad.aliassym.overloadInsert(s);
+    }
+    if (auto od = sa.isOverDeclaration())
+    {
+        if (sa.ident != ad.ident || sa.parent != ad.parent)
+        {
+            od = new OverDeclaration(ad.ident, od);
+            od.visibility = ad.visibility;
+            od.parent = ad.parent;
+            ad.aliassym = od;
+        }
+        if (isInsertCandidate)
+            return od.overloadInsert(s);
+    }
+    if (auto os = sa.isOverloadSet())
+    {
+        if (sa.ident != ad.ident || sa.parent != ad.parent)
+        {
+            os = new OverloadSet(ad.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 = ad.parent;
+            ad.aliassym = os;
         }
-        else
+        if (isInsertCandidate)
         {
-            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();
+            os.push(s);
+            return true;
         }
-        //printf(" semantic type = %s\n", dsym.type ? dsym.type.toChars() : "null");
-        if (dsym.type.ty == Terror)
-            dsym.errors = true;
+    }
+    return false;
+}
 
-        dsym.type.checkDeprecated(dsym.loc, sc);
-        dsym.parent = sc.parent;
-        //printf("this = %p, parent = %p, '%s'\n", dsym, dsym.parent, dsym.parent.toChars());
+/**********************************
+ * Overload existing TemplateDeclaration '_this' with the new one 's'.
+ * Params:
+ *    s = symbol to be inserted
+ * Return: true if successful; i.e. no conflict.
+ */
+private bool templateOverloadInsert(TemplateDeclaration _this, Dsymbol s){
+    static if (LOG)
+    {
+        printf("TemplateDeclaration.overloadInsert('%s')\n", s.toChars());
+    }
 
-        /* If scope's alignment is the default, use the type's alignment,
-         * otherwise the scope overrrides.
-         */
-        if (dsym.alignment.isDefault())
-            dsym.alignment = dsym.type.alignment(); // use type's alignment
+    if (FuncDeclaration fd = s.isFuncDeclaration())
+    {
+        if (_this.funcroot)
+            return _this.funcroot.overloadInsert(fd);
+        _this.funcroot = fd;
+        return _this.funcroot.overloadInsert(_this);
+    }
 
-        //printf("sc.stc = %x\n", sc.stc);
-        //printf("storage_class = x%x\n", storage_class);
+    // 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;
+    }
 
-        dsym.type.checkComplexTransition(dsym.loc, sc);
+    TemplateDeclaration td = s.toAlias().isTemplateDeclaration();
+    if (!td)
+        return false;
 
-        // Calculate type size + safety checks
-        if (dsym.storage_class & STC.gshared && !dsym.isMember())
-        {
-            sc.setUnsafe(false, dsym.loc, "using `__gshared` instead of `shared`");
-        }
+    TemplateDeclaration pthis = _this;
+    TemplateDeclaration* ptd;
+    for (ptd = &pthis; *ptd; ptd = &(*ptd).overnext)
+    {
+    }
 
-        Dsymbol parent = dsym.toParent();
+    td.overroot = _this;
+    *ptd = td;
+    static if (LOG)
+    {
+        printf("\ttrue: no conflict\n");
+    }
+    return true;
+}
 
-        Type tb = dsym.type.toBasetype();
-        Type tbn = tb.baseElemOf();
-        if (tb.ty == Tvoid && !(dsym.storage_class & STC.lazy_))
+private bool importOverloadInsert(Import _this, Dsymbol s){
+    /* Allow multiple imports with the same package base, but disallow
+     * alias collisions
+     * https://issues.dlang.org/show_bug.cgi?id=5412
+     */
+    assert(_this.ident && _this.ident == s.ident);
+    if (_this.aliasId)
+        return false;
+    const imp = s.isImport();
+    return imp && !imp.aliasId;
+}
+
+/**
+ * Overload _this FuncDeclaration with the new one f.
+ * Return true if successful; i.e. no conflict.
+*/
+private bool funcOverloadInsert(FuncDeclaration _this, Dsymbol s){
+    //printf("FuncDeclaration::overloadInsert(s = %s) _this = %s\n", s.toChars(), toChars());
+    assert(s != _this);
+    if (AliasDeclaration ad = s.isAliasDeclaration())
+    {
+        if (_this.overnext)
+            return _this.overnext.overloadInsert(ad);
+        if (!ad.aliassym && ad.type.ty != Tident && ad.type.ty != Tinstance && ad.type.ty != Ttypeof)
         {
-            if (inferred)
-            {
-                .error(dsym.loc, "%s `%s` - type `%s` is inferred from initializer `%s`, and variables cannot be of type `void`",
-                    dsym.kind, dsym.toPrettyChars, dsym.type.toChars(), toChars(dsym._init));
-            }
-            else
-                .error(dsym.loc, "%s `%s` - variables cannot be of type `void`", dsym.kind, dsym.toPrettyChars);
-            dsym.type = Type.terror;
-            tb = dsym.type;
-        }
-        if (tb.ty == Tfunction)
-        {
-            .error(dsym.loc, "%s `%s` cannot be declared to be a function", dsym.kind, dsym.toPrettyChars);
-            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_)))
-            {
-                .error(dsym.loc, "%s `%s` - no definition of struct `%s`", dsym.kind, dsym.toPrettyChars, 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());
-                }
-                errorSupplemental(dsym.loc, "see https://dlang.org/spec/struct.html#opaque_struct_unions");
-                errorSupplemental(dsym.loc, "perhaps declare a variable with pointer type `%s*` instead", dsym.type.toChars());
-
-                // Flag variable as error to avoid invalid error messages due to unknown size
-                dsym.type = Type.terror;
-            }
+            //printf("\tad = '%s'\n", ad.type.toChars());
+            return false;
         }
-        if ((dsym.storage_class & STC.auto_) && !inferred && !(dsym.storage_class & STC.autoref))
-            .error(dsym.loc, "%s `%s` - storage class `auto` has no effect if type is not inferred, did you mean `scope`?", dsym.kind, dsym.toPrettyChars);
+        _this.overnext = ad;
+        //printf("\ttrue: no conflict\n");
+        return true;
+    }
+    TemplateDeclaration td = s.isTemplateDeclaration();
+    if (td)
+    {
+        if (!td.funcroot)
+            td.funcroot = _this;
+        if (_this.overnext)
+            return _this.overnext.overloadInsert(td);
+        _this.overnext = td;
+        return true;
+    }
+    FuncDeclaration fd = s.isFuncDeclaration();
+    if (!fd)
+        return false;
 
-        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, sc.inCfile) : null;
-            if (ie)
-                ie = ie.expressionSemantic(sc);
-            if (nelems > 0 && ie)
-            {
-                auto iexps = new Expressions(ie);
-                auto 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 = arg.type.typeSemantic(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 (_this.overnext)
+    {
+        td = _this.overnext.isTemplateDeclaration();
+        if (td)
+            fd.overloadInsert(td);
+        else
+            return _this.overnext.overloadInsert(fd);
+    }
+    _this.overnext = fd;
+    //printf("\ttrue: no conflict\n");
+    return true;
+}
 
-                    if (e != ie)
-                    {
-                        if (iexps.length > nelems)
-                            goto Lnomatch;
-                        if (e.type.implicitConvTo(arg.type))
-                            continue;
-                    }
+private bool overdeclOverloadInsert(OverDeclaration _this, Dsymbol s){
+    //printf("OverDeclaration::overloadInsert('%s') _this.aliassym = %p, _this.overnext = %p\n", s.toChars(), _this.aliassym, _this.overnext);
+    if (_this.overnext)
+        return _this.overnext.overloadInsert(s);
+    if (s == _this)
+        return true;
+    _this.overnext = s;
+    return true;
+}
 
-                    if (auto te = e.isTupleExp())
-                    {
-                        if (iexps.length - 1 + te.exps.length > nelems)
-                            goto Lnomatch;
+bool overloadInsert(Dsymbol _this, Dsymbol s)
+{
+    // Cannot overload postblits or destructors
+    if(_this.isPostBlitDeclaration() || _this.isDtorDeclaration())
+        return false;
+    else if(OverDeclaration od = _this.isOverDeclaration())
+        return overdeclOverloadInsert(od, s);
+    else if(FuncDeclaration fd = _this.isFuncDeclaration())
+        return funcOverloadInsert(fd, s);
+    if(Import imp = _this.isImport())
+        return importOverloadInsert(imp, s);
+    else if(TemplateDeclaration td = _this.isTemplateDeclaration())
+        return templateOverloadInsert(td, s);
+    else if (AliasDeclaration ad = _this.isAliasDeclaration())
+        return aliasOverloadInsert(ad, s);
 
-                        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(STC.none, "__tup", e);
-                        v.dsymbolSemantic(sc);
-                        auto ve = new VarExp(dsym.loc, v);
-                        ve.type = e.type;
+    return false;
+}
 
-                        exps.setDim(1);
-                        (*exps)[0] = ve;
-                        expandAliasThisTuples(exps, 0);
+/***************************************************
+ * Determine the numerical value of the AlignmentDeclaration
+ * Params:
+ *      ad = AlignmentDeclaration
+ *      sc = context
+ * Returns:
+ *      ad with alignment value determined
+ */
+AlignDeclaration getAlignment(AlignDeclaration ad, Scope* sc)
+{
+    if (!ad.salign.isUnknown())   // UNKNOWN is 0
+        return ad;
 
-                        for (size_t u = 0; u < exps.length; 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.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());
+    if (!ad.exps)
+    {
+        ad.salign.setDefault();
+        return ad;
+    }
 
-                            size_t iexps_dim = iexps.length - 1 + exps.length;
-                            if (iexps_dim > nelems)
-                                goto Lnomatch;
-                            if (ee.type.implicitConvTo(arg.type))
-                                continue;
+    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 == EXP.error)
+            errors = true;
+        else
+        {
+            auto n = e.toInteger();
+            if (sc.inCfile && n == 0)       // C11 6.7.5-6 allows 0 for alignment
+                continue;
 
-                            if (expandAliasThisTuples(exps, u) != -1)
-                                goto Lexpand2;
-                        }
+            if (n < 1 || n & (n - 1) || ushort.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;
+        }
+    }
 
-                        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;
+    if (errors || strictest == 0)  // C11 6.7.5-6 says alignment of 0 means no effect
+        ad.salign.setDefault();
+    else
+        ad.salign.set(cast(uint) strictest);
 
-                            iexps.remove(pos);
-                            iexps.insert(pos, exps);
-                            goto Lexpand1;
-                        }
-                    }
-                }
-                if (iexps.length < nelems)
-                    goto Lnomatch;
+    return ad;
+}
 
-                ie = new TupleExp(dsym._init.loc, iexps);
-            }
-        Lnomatch:
 
-            if (ie && ie.op == EXP.tuple)
+/*********************************
+ * Resolve recursive tuple expansion in eponymous template.
+ */
+Dsymbol toAlias2(Dsymbol s)
+{
+    if (auto ad = s.isAliasDeclaration())
+    {
+        if (ad.inuse)
+        {
+            .error(ad.loc, "%s `%s` recursive alias declaration", ad.kind, ad.toPrettyChars);
+            return ad;
+        }
+        ad.inuse = 1;
+        Dsymbol ds = ad.aliassym ? ad.aliassym.toAlias2() : ad;
+        ad.inuse = 0;
+        return ds;
+    }
+    if (auto td = s.isTupleDeclaration())
+    {
+        //printf("TupleDeclaration::toAlias2() '%s' objects = %s\n", toChars(), objects.toChars());
+        for (size_t i = 0; i < td.objects.length; i++)
+        {
+            RootObject o = (*td.objects)[i];
+            if (Dsymbol ds = isDsymbol(o))
             {
-                auto te = ie.isTupleExp();
-                size_t tedim = te.exps.length;
-                if (tedim != nelems)
-                {
-                    error(dsym.loc, "sequence of %d elements cannot be assigned to sequence 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());
-                }
+                ds = ds.toAlias2();
+                (*td.objects)[i] = ds;
             }
+        }
+        return td;
+    }
+    return toAlias(s);
+}
 
-            auto exps = new Objects(nelems);
-            for (size_t i = 0; i < nelems; i++)
-            {
-                Parameter arg = Parameter.getNth(tt.arguments, i);
+private Dsymbol toAliasImpl(AliasDeclaration ad)
+{
+    static if (0)
+    printf("[%s] AliasDeclaration::toAlias('%s', this = %p, aliassym: %s, kind: '%s', inuse = %d)\n",
+        ad.loc.toChars(), ad.toChars(), ad, ad.aliassym ? ad.aliassym.toChars() : "", ad.aliassym ? ad.aliassym.kind() : "", ad.inuse);
+    assert(ad != ad.aliassym);
+    //static int count; if (++count == 10) *(char*)0=0;
 
-                OutBuffer buf;
-                buf.printf("__%s_field_%llu", dsym.ident.toChars(), cast(ulong)i);
-                auto id = Identifier.idPool(buf[]);
+    Dsymbol err()
+    {
+        // Avoid breaking "recursive alias" state during errors gagged
+        if (global.gag)
+            return ad;
+        ad.aliassym = new AliasDeclaration(ad.loc, ad.ident, Type.terror);
+        ad.type = Type.terror;
+        return ad.aliassym;
+    }
+    // Reading the AliasDeclaration
+    if (!ad.ignoreRead)
+        ad.wasRead = true;                 // can never assign to this AliasDeclaration again
 
-                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;
-
-                STC storage_class = STC.temp | 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.overlapped = dsym.overlapped;
-
-                v.dsymbolSemantic(sc);
-
-                Expression e = new VarExp(dsym.loc, v);
-                (*exps)[i] = e;
-            }
-            auto v2 = new TupleDeclaration(dsym.loc, dsym.ident, exps);
-            v2.parent = dsym.parent;
-            v2.isexp = true;
-            dsym.aliasTuple = 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())
+    if (ad.inuse == 1 && ad.type && ad._scope)
+    {
+        ad.inuse = 2;
+        const olderrors = global.errors;
+        Dsymbol s = ad.type.toDsymbol(ad._scope);
+        //printf("[%s] type = %s, s = %p, this = %p\n", loc.toChars(), type.toChars(), s, this);
+        if (global.errors != olderrors)
+            return err();
+        if (s)
         {
-            dsym.storage_class |= STC.const_;
-            if (dsym.type.isShared())
-                dsym.storage_class |= STC.shared_;
+            s = s.toAlias();
+            if (global.errors != olderrors)
+                return err();
+            ad.aliassym = s;
+            ad.inuse = 0;
         }
-        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 (STC stc = dsym.storage_class & (STC.synchronized_ | STC.override_ | STC.abstract_ | STC.final_))
+        else
         {
-            if (stc == STC.final_)
-                .error(dsym.loc, "%s `%s` cannot be `final`, perhaps you meant `const`?", dsym.kind, dsym.toPrettyChars);
-            else
-            {
-                OutBuffer buf;
-                stcToBuffer(buf, stc);
-                .error(dsym.loc, "%s `%s` cannot be `%s`", dsym.kind, dsym.toPrettyChars, buf.peekChars());
-            }
-            dsym.storage_class &= ~stc; // strip off
+            Type t = ad.type.typeSemantic(ad.loc, ad._scope);
+            if (t.ty == Terror)
+                return err();
+            if (global.errors != olderrors)
+                return err();
+            //printf("t = %s\n", t.toChars());
+            ad.inuse = 0;
         }
+    }
+    if (ad.inuse)
+    {
+        .error(ad.loc, "%s `%s` recursive alias declaration", ad.kind, ad.toPrettyChars);
+        return err();
+    }
 
-        // 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.constscoperef)
-            dsym.storage_class |= STC.scope_;
+    if (ad.semanticRun >= PASS.semanticdone)
+    {
+        // semantic is already done.
 
-        import dmd.typesem : hasPointers;
+        // Do not see aliassym !is null, because of lambda aliases.
 
-        if (dsym.storage_class & STC.scope_)
+        // 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
+    {
+        // stop AliasAssign tuple building
+        if (ad.aliassym)
         {
-            STC stc = dsym.storage_class & (STC.static_ | STC.extern_ | STC.manifest | STC.gshared);
-            if (stc)
-            {
-                OutBuffer buf;
-                stcToBuffer(buf, stc);
-                .error(dsym.loc, "%s `%s` cannot be `scope` and `%s`", dsym.kind, dsym.toPrettyChars, buf.peekChars());
-            }
-            else if (dsym.isMember())
-            {
-                error(dsym.loc, "field `%s` cannot be `scope`", dsym.toChars());
-            }
-            else if (!dsym.type.hasPointers())
+            if (auto td = ad.aliassym.isTupleDeclaration())
             {
-                dsym.storage_class &= ~STC.scope_;     // silently ignore; may occur in generic code
-                // https://issues.dlang.org/show_bug.cgi?id=23168
-                if (dsym.storage_class & STC.returnScope)
+                if (td.building)
                 {
-                    dsym.storage_class &= ~(STC.return_ | STC.returnScope);
+                    td.building = false;
+                    ad.semanticRun = PASS.semanticdone;
+                    return td;
                 }
             }
         }
-
-        if (dsym.storage_class & (STC.static_ | STC.extern_ | STC.manifest | STC.templateparameter | STC.gshared | STC.ctfe))
-        {
-        }
-        else
+        if (ad._import && ad._import._scope)
         {
-            AggregateDeclaration aad = parent.isAggregateDeclaration();
-            if (aad)
-            {
-                if (global.params.v.field && 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)
-            {
-                error(dsym.loc, "field `%s` not allowed in interface", dsym.toChars());
-            }
-            else if (aad && aad.sizeok == Sizeok.done)
-            {
-                error(dsym.loc, "cannot declare field `%s` because it will change the determined size of `%s`", dsym.toChars(), aad.toChars());
-            }
-
-            /* Templates cannot add fields to aggregates
+            /* If this is an internal alias for selective/renamed import,
+             * load the module first.
              */
-            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.none)
-                {
-                    .error(dsym.loc, "%s `%s` - cannot use template to add field to aggregate `%s`", dsym.kind, dsym.toPrettyChars, ad2.toChars());
-                }
-            }
+            ad._import.dsymbolSemantic(null);
         }
-
-        /* If the alignment of a stack local is greater than the stack alignment,
-         * note it in the enclosing function's alignSectionVars
-         */
-        version (MARS)
+        if (ad._scope)
         {
-            if (!dsym.alignment.isDefault() && sc.func &&
-                dsym.alignment.get() > target.stackAlign() &&
-                sc.func && !dsym.isDataseg() && !dsym.isParameter() && !dsym.isField())
-            {
-                auto fd = sc.func;
-                if (!fd.alignSectionVars)
-                    fd.alignSectionVars = new VarDeclarations();
-                fd.alignSectionVars.push(dsym);
-            }
+            aliasSemantic(ad, ad._scope);
         }
+    }
 
-        if ((dsym.storage_class & (STC.ref_ | STC.field)) == (STC.ref_ | STC.field) && dsym.ident != Id.This)
+    ad.inuse = 1;
+    Dsymbol s = ad.aliassym ? ad.aliassym.toAlias() : ad;
+    ad.inuse = 0;
+    return s;
+}
+
+/*********************************
+ * If this symbol is really an alias for another,
+ * return that other.
+ * If needed, semantic() is invoked due to resolve forward reference.
+ */
+Dsymbol toAlias(Dsymbol s)
+{
+    if (auto ad = s.isAliasDeclaration())
+    {
+        return ad.toAliasImpl();
+    }
+    if (auto imp = s.isImport())
+    {
+        if (imp.aliasId)
+            return imp.mod;
+        return imp;
+    }
+    if (auto vd = s.isVarDeclaration())
+    {
+        //printf("VarDeclaration::toAlias('%s', this = %p, aliassym = %p)\n", toChars(), this, aliassym);
+        if ((!vd.type || !vd.type.deco) && vd._scope)
+            dsymbolSemantic(vd, vd._scope);
+
+        assert(vd != vd.aliasTuple);
+        return vd.aliasTuple ? vd.aliasTuple.toAlias() : vd;
+    }
+    // resolve real symbol
+    if (auto ti = s.isTemplateInstance())
+    {
+        static if (LOG)
         {
-            .error(dsym.loc, "%s `%s` - field declarations cannot be `ref`", dsym.kind, dsym.toPrettyChars);
+            printf("TemplateInstance.toAlias()\n");
         }
-
-        if (dsym.type.hasWild())
+        if (!ti.inst)
         {
-            if (dsym.storage_class & (STC.static_ | STC.extern_ | STC.gshared | STC.manifest | STC.field) || dsym.isDataseg())
+            // Maybe we can resolve it
+            if (ti._scope)
             {
-                .error(dsym.loc, "%s `%s` - only parameters or stack-based variables can be `inout`", dsym.kind, dsym.toPrettyChars);
+                dsymbolSemantic(ti, ti._scope);
             }
-            FuncDeclaration func = sc.func;
-            if (func)
+            if (!ti.inst)
             {
-                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)
-                {
-                    .error(dsym.loc, "%s `%s` - `inout` variables can only be declared inside `inout` functions", dsym.kind, dsym.toPrettyChars);
-                }
+                .error(ti.loc, "%s `%s` cannot resolve forward reference", ti.kind, ti.toPrettyChars);
+                ti.errors = true;
+                return ti;
             }
         }
 
-        if (!(dsym.storage_class & (STC.ctfe | STC.extern_ | STC.ref_ | STC.result)) &&
-            tbn.ty == Tstruct && tbn.isTypeStruct().sym.noDefaultCtor)
+        if (ti.inst != ti)
+            return ti.inst.toAlias();
+
+        if (ti.aliasdecl)
         {
-            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
-                    .error(dsym.loc, "%s `%s` - default construction is disabled for type `%s`", dsym.kind, dsym.toPrettyChars, dsym.type.toChars());
-            }
+            return ti.aliasdecl.toAlias();
         }
 
-        bool dsymIsRef = (dsym.storage_class & (STC.ref_ | STC.field | STC.parameter | STC.temp | STC.foreach_)) == STC.ref_;
-        if (dsymIsRef)
+        return ti.inst;
+    }
+    return s;
+}
+
+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
+            error(dd.msg.loc, "compile time constant expected, not `%s`", dd.msg.toChars());
+    }
+    return dd.msgstr;
+}
+
+bool checkDeprecated(Dsymbol d, Loc loc, Scope* sc)
+{
+    if (global.params.useDeprecated == DiagnosticReporting.off)
+        return false;
+    if (!d.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.inTemplateConstraint)
+        return false;
+
+    const(char)* message = null;
+    for (Dsymbol p = d; p; p = p.parent)
+    {
+        message = p.depdecl ? p.depdecl.getMessage() : null;
+        if (message)
+            break;
+    }
+    if (message)
+        deprecation(loc, "%s `%s` is deprecated - %s", d.kind, d.toPrettyChars, message);
+    else
+        deprecation(loc, "%s `%s` is deprecated", d.kind, d.toPrettyChars);
+
+    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;
+}
+
+/*********************************
+ * Check type to see if it is based on a deprecated symbol.
+ */
+private void checkDeprecated(Type type, Loc loc, Scope* sc)
+{
+    if (Dsymbol s = type.toDsymbol(sc))
+    {
+        s.checkDeprecated(loc, sc);
+    }
+
+    if (auto tn = type.nextOf())
+        tn.checkDeprecated(loc, sc);
+}
+
+// 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;
+}
+
+/***************************************
+ * 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
+ */
+bool isPOD(StructDeclaration sd)
+{
+    // If we've already determined whether this struct is POD.
+    if (sd.ispod != ThreeState.none)
+        return (sd.ispod == ThreeState.yes);
+
+
+    bool hasCpCtorLocal;
+    bool hasMoveCtorLocal;
+    bool needCopyCtor;
+    bool needMoveCtor;
+    needCopyOrMoveCtor(sd, hasCpCtorLocal, hasMoveCtorLocal, needCopyCtor, needMoveCtor);
+
+    if (sd.enclosing                    || // is nested
+        search(sd, sd.loc, Id.postblit) || // has postblit
+        search(sd, sd.loc, Id.dtor)     || // has destructor
+        /* This is commented out because otherwise buildkite vibe.d:
+           `canCAS!Task` fails to compile
+         */
+        //hasMoveCtorLocal               || // has move constructor
+        hasCpCtorLocal)                   // has copy constructor
+    {
+        sd.ispod = ThreeState.no;
+        return false;
+    }
+
+    // Recursively check all fields are POD.
+    for (size_t i = 0; i < sd.fields.length; i++)
+    {
+        VarDeclaration v = sd.fields[i];
+        if (v.storage_class & STC.ref_)
         {
-            if (!dsym._init && dsym.ident != Id.This)
-            {
-                if (dsym.storage_class & STC.autoref)
-                {
-                    dsymIsRef = false;
-                    dsym.storage_class &= ~STC.ref_;
-                }
-                else
-                    .error(dsym.loc, "%s `%s` - initializer is required for `ref` variable", dsym.kind, dsym.toPrettyChars);
-            }
-            else if (dsym._init.isVoidInitializer())
-            {
-                .error(dsym.loc, "%s `%s` - void initializer not allowed for `ref` variable", dsym.kind, dsym.toPrettyChars);
-            }
+            sd.ispod = ThreeState.no;
+            return false;
         }
 
-        FuncDeclaration fd = parent.isFuncDeclaration();
-        if (dsym.type.isScopeClass() && !(dsym.storage_class & STC.nodtor))
+        if (auto ts = v.type.baseElemOf().isTypeStruct())
         {
-            if (dsym.storage_class & (STC.field | STC.out_ | STC.ref_ | STC.static_ | STC.manifest | STC.gshared) || !fd)
-            {
-                .error(dsym.loc, "%s `%s` globals, statics, fields, manifest constants, ref and out parameters cannot be `scope`", dsym.kind, dsym.toPrettyChars);
-            }
-
-            // @@@DEPRECATED_2.097@@@  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 (!(dsym.storage_class & STC.scope_))
+            if (!ts.sym.isPOD())
             {
-                if (!(dsym.storage_class & STC.parameter) && dsym.ident != Id.withSym)
-                    .error(dsym.loc, "%s `%s` reference to `scope class` must be `scope`", dsym.kind, dsym.toPrettyChars);
+                sd.ispod = ThreeState.no;
+                return false;
             }
         }
+    }
 
-        // Calculate type size + safety checks
-        if (sc && sc.func)
-        {
-            if (dsym._init && dsym._init.isVoidInitializer() && !(dsym.storage_class & STC.temp))
-            {
-                // Don't do these checks for STC.temp vars because the generated `opAssign`
-                // for a struct with postblit and destructor void initializes a temporary
-                // __swap variable, which can be trusted
+    sd.ispod = ThreeState.yes;
+    return true;
+}
 
-                if (dsym.type.hasPointers()) // also computes type size
-                    sc.setUnsafe(false, dsym.loc,
-                        "`void` initializing a pointer");
-                else if (dsym.type.hasInvariant())
-                    sc.setUnsafe(false, dsym.loc,
-                        "`void` initializing a struct with an invariant");
-                else if (dsym.type.toBasetype().ty == Tbool)
-                    sc.setUnsafePreview(global.params.systemVariables, false, dsym.loc,
-                        "void intializing a bool (which must always be 0 or 1)");
-                else if (dsym.type.hasUnsafeBitpatterns())
-                    sc.setUnsafePreview(global.params.systemVariables, false, dsym.loc,
-                        "`void` initializing a type with unsafe bit patterns");
-            }
-            else if (!dsym._init &&
-                     !(dsym.storage_class & (STC.static_ | STC.extern_ | STC.gshared | STC.manifest | STC.field | STC.parameter)) &&
-                     dsym.type.hasVoidInitPointers())
-            {
-                sc.setUnsafe(false, dsym.loc, "`void` initializers for pointers");
-            }
-        }
+/****************************************
+ * Fill in vtbl[] for base class based on member functions of class cd.
+ * Input:
+ *      bc              BaseClass
+ *      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 fillVtbl(BaseClass* bc, ClassDeclaration cd, FuncDeclarations* vtbl, int newinstance)
+{
+    bool result = false;
 
-        if ((!dsym._init || dsym._init.isVoidInitializer) && !fd)
-        {
-            // If not mutable, initializable by constructor only
-            dsym.setInCtorOnly = true;
-        }
+    //printf("BaseClass.fillVtbl(this='%s', cd='%s')\n", sym.toChars(), cd.toChars());
+    if (vtbl)
+        vtbl.setDim(bc.sym.vtbl.length);
 
-        if (dsym._init)
-        { } // remember we had an explicit initializer
-        else if (dsym.storage_class & STC.manifest)
-            .error(dsym.loc, "%s `%s` - manifest constants must have initializers", dsym.kind, dsym.toPrettyChars);
+    // first entry is ClassInfo reference
+    for (size_t j = bc.sym.vtblOffset(); j < bc.sym.vtbl.length; j++)
+    {
+        FuncDeclaration ifd = bc.sym.vtbl[j].isFuncDeclaration();
 
-        // Don't allow non-extern, non-__gshared variables to be interfaced with C++
-        if (dsym._linkage == LINK.cpp && !(dsym.storage_class & (STC.ctfe | STC.extern_ | STC.gshared)) && dsym.isDataseg())
-        {
-            const char* p = (dsym.storage_class & STC.shared_) ? "shared" : "static";
-            .error(dsym.loc, "%s `%s` cannot have `extern(C++)` linkage because it is `%s`", dsym.kind, dsym.toPrettyChars, p);
-            errorSupplemental(dsym.loc, "perhaps declare it as `__gshared` instead");
-            dsym.errors = true;
-        }
+        //printf("        vtbl[%d] is '%s'\n", j, ifd ? ifd.toChars() : "null");
+        assert(ifd);
 
-        bool isBlit = false;
-        uinteger_t sz;
-        if (sc.inCfile && !dsym._init)
+        // Find corresponding function in this class
+        auto tf = ifd.type.toTypeFunction();
+        auto fd = cd.findFunc(ifd.ident, tf);
+        if (fd && !fd.isAbstract())
         {
-            addDefaultCInitializer(dsym);
+            if (fd.toParent() == cd)
+                result = true;
         }
-        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", dsym.toChars());
-            if (sz == SIZE_INVALID && dsym.type.ty != Terror)
-                .error(dsym.loc, "%s `%s` - size of type `%s` is invalid", dsym.kind, dsym.toPrettyChars, dsym.type.toChars());
+        else
+            fd = null;
+        if (vtbl)
+            (*vtbl)[j] = fd;
+    }
+    return result;
+}
 
-            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);
+/*
+If sd has a copy constructor and ctor is an rvalue constructor,
+issue an error.
 
-                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)
-            {
-                .error(dsym.loc, "%s `%s` of type `%s` does not have a default initializer", dsym.kind, dsym.toPrettyChars, dsym.type.toChars());
-            }
-            else if (auto e = dsym.type.defaultInit(dsym.loc))
-            {
-                dsym._init = new ExpInitializer(dsym.loc, e);
-            }
+Params:
+    sd = struct declaration that may contain both an rvalue and copy constructor
+    ctor = constructor that will be checked if it is an rvalue constructor
+    ti = template instance the ctor is part of
 
-            // 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);
+Return:
+    `true` if sd has a copy constructor and ctor is an rvalue constructor
+*/
+bool checkHasBothRvalueAndCpCtor(StructDeclaration sd, CtorDeclaration ctor, TemplateInstance ti)
+{
+    //printf("checkHasBothRvalueAndCpCtor() sd: %s ctor: %s ti: %s\n", sd.toChars(), ctor.toChars(), ti.toChars());
+    /* cannot use ctor.isMoveCtor because semantic pass may not have been run yet,
+     * so use isRvalueConstructor()
+     */
+    if (sd && sd.hasCopyCtor && isRvalueConstructor(sd, ctor))
+    {
+        .error(ctor.loc, "cannot define both an rvalue constructor and a copy constructor for `struct %s`", sd.toChars());
+        .errorSupplemental(ti.loc, "Template instance `%s` creates an rvalue constructor for `struct %s`",
+                ti.toPrettyChars(), sd.toChars());
 
-            if (sc.inCfile &&
-                dsym.type.isTypeSArray() &&
-                dsym.type.isTypeSArray().isIncomplete() &&
-                dsym._init.isVoidInitializer() &&
-                !(dsym.storage_class & STC.field))
-            {
-                .error(dsym.loc, "%s `%s` - incomplete array type must have initializer", dsym.kind, dsym.toPrettyChars);
-            }
+        return true;
+    }
 
-            ExpInitializer ei = dsym._init.isExpInitializer();
+    return false;
+}
 
-            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);
+/************************************************
+ * Check if ctor is an rvalue constructor.
+ * A constructor that receives a single parameter of the same type as
+ * `Unqual!typeof(this)` is an rvalue constructor.
+ * Params:
+ *      sd = struct that ctor is a member of
+ *      ctor = constructor to test
+ * Returns:
+ *      true if it is an rvalue constructor
+ */
+bool isRvalueConstructor(StructDeclaration sd, CtorDeclaration ctor)
+{
+    // note commonality with setting isMoveCtor in the semantic code for CtorDeclaration
+    auto tf = ctor.type.isTypeFunction();
+    const dim = tf.parameterList.length;
+    if (dim == 1 || (dim > 1 && tf.parameterList[1].defaultArg))
+    {
+        auto param = tf.parameterList[0];
+        if (!(param.storageClass & STC.ref_) && param.type.mutableOf().unSharedOf() == sd.type.mutableOf().unSharedOf())
+        {
+            return true;
+        }
+    }
+    return false;
+}
 
-            // If inside function, there is no semantic3() call
-            if (sc.func || sc.intypeof == 1)
+/*************************************
+ * Find the `alias this` symbol of e's type.
+ * Params:
+ *      sc = context
+ *      e = expression forming the `this`
+ *      gag = do not print errors, return `null` instead
+ *      findOnly = don't do further processing like resolving properties,
+ *                 i.e. just return plain dotExp() result.
+ * Returns:
+ *      Expression that is `e.aliasthis`
+ */
+Expression resolveAliasThis(Scope* sc, Expression e, bool gag = false, bool findOnly = false)
+{
+    //printf("resolveAliasThis() %s\n", toChars(e));
+    import dmd.typesem : dotExp;
+    for (AggregateDeclaration ad = isAggregate(e.type); ad;)
+    {
+        if (ad.aliasthis)
+        {
+            Loc loc = e.loc;
+            Type tthis = (e.op == EXP.type ? e.type : null);
+            const flags = cast(DotExpFlag) (DotExpFlag.noAliasThis | (gag * DotExpFlag.gag));
+            const olderrors = gag ? global.startGagging() : 0;
+            e = dotExp(ad.type, sc, e, ad.aliasthis.ident, flags);
+            if (!e || findOnly)
+                return gag && global.endGagging(olderrors) ? null : e;
+
+            if (tthis && ad.aliasthis.sym.needThis())
             {
-                // If local variable, use AssignExp to handle all the various
-                // possibilities.
-                if (fd && !(dsym.storage_class & (STC.manifest | STC.static_ | STC.gshared | STC.extern_)) && !dsym._init.isVoidInitializer())
+                if (auto ve = e.isVarExp())
                 {
-                    //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 = dsym._init.initializerToExpression(null, sc.inCfile);
-                        if (!e)
-                        {
-                            // Run semantic, but don't need to interpret
-                            dsym._init = dsym._init.initializerSemantic(sc, dsym.type, INITnointerpret);
-                            e = dsym._init.initializerToExpression(null, sc.inCfile);
-                            if (!e)
-                            {
-                                .error(dsym.loc, "%s `%s` is not a static and cannot have static initializer", dsym.kind, dsym.toPrettyChars);
-                                e = ErrorExp.get();
-                            }
-                        }
-                        ei = new ExpInitializer(dsym._init.loc, e);
-                        dsym._init = ei;
-                    }
-                    else if (sc.inCfile && dsym.type.isTypeSArray() &&
-                             dsym.type.isTypeSArray().isIncomplete())
-                    {
-                        // C11 6.7.9-22 determine the size of the incomplete array,
-                        // or issue an error that the initializer is invalid.
-                        dsym._init = dsym._init.initializerSemantic(sc, dsym.type, INITinterpret);
-                    }
-
-                    if (ei && dsym.isScope())
+                    if (auto fd = ve.var.isFuncDeclaration())
                     {
-                        Expression ex = ei.exp.lastComma();
-                        if (ex.op == EXP.blit || ex.op == EXP.construct)
-                            ex = (cast(AssignExp)ex).e2;
-                        if (auto ne = ex.isNewExp())
-                        {
-                            if (ne.placement)
-                            {
-                            }
-                            /* See if initializer is a NewExp that can be allocated on the stack.
-                             */
-                            else if (dsym.type.toBasetype().ty == Tclass)
-                            {
-                                /* Unsafe to allocate on stack if constructor is not `scope` because the `this` can leak.
-                                 * https://issues.dlang.org/show_bug.cgi?id=23145
-                                 */
-                                if (ne.member && !(ne.member.storage_class & STC.scope_))
-                                {
-                                    import dmd.escape : setUnsafeDIP1000;
-                                    const inSafeFunc = sc.func && sc.func.isSafeBypassingInference();   // isSafeBypassingInference may call setUnsafe().
-                                    if (setUnsafeDIP1000(*sc, false, dsym.loc, "`scope` allocation of `%s` with a non-`scope` constructor", dsym))
-                                        errorSupplemental(ne.member.loc, "is the location of the constructor");
-                                }
-                                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 if (auto ale = ex.isArrayLiteralExp())
+                        // 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))
                         {
-                            // or an array literal assigned to a `scope` variable
-                            if (sc.useDIP1000 == FeatureState.enabled
-                                && !dsym.type.nextOf().needsDestruction())
-                                ale.onstack = true;
+                            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.
+                 */
+                {
+                    ubyte 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;
+        }
 
-                    Expression exp = ei.exp;
-                    Expression e1 = new VarExp(dsym.loc, dsym);
-
-                    void constructInit(bool isBlit)
-                    {
-                        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--;
-                    }
+        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;
+}
 
-                    if (dsymIsRef) // follow logic similar to typesem.argumentMatchParameter() and statementsem.visitForeach()
-                    {
-                        dsym.storage_class |= STC.nodtor;
-                        exp = exp.expressionSemantic(sc);
-                        Type tp = dsym.type;
-                        Type ta = exp.type;
-                        if (!exp.isLvalue())
-                        {
-                            if (dsym.storage_class & STC.autoref)
-                            {
-                                dsym.storage_class &= ~STC.ref_;
-                                constructInit(isBlit);
-                            }
-                            else
-                            {
-                                .error(dsym.loc, "rvalue `%s` cannot be assigned to `ref %s`", exp.toChars(), dsym.toChars());
-                                exp = ErrorExp.get();
-                            }
-                        }
-                        else if (!ta.constConv(tp))
-                        {
-                            if (dsym.storage_class & STC.autoref)
-                            {
-                                dsym.storage_class &= ~STC.ref_;
-                                constructInit(false);
-                            }
-                            else
-                            {
-                                .error(dsym.loc, "type `%s` cannot be assigned to `ref %s %s`", ta.toChars(), tp.toChars(), dsym.toChars());
-                                exp = ErrorExp.get();
-                            }
-                        }
-                        else if (exp.isBitField())
-                        {
-                            if (dsym.storage_class & STC.autoref)
-                            {
-                                dsym.storage_class &= ~STC.ref_;
-                                constructInit(false);
-                            }
-                            else
-                            {
-                                .error(dsym.loc, "bitfield `%s` cannot be assigned to `ref %s`", exp.toChars(), dsym.toChars());
-                                exp = ErrorExp.get();
-                            }
-                        }
-                        else
-                        {
-                            constructInit(false);
-                        }
-                    }
-                    else
-                    {
-                        constructInit(isBlit);
-                    }
+/**
+ * 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.
+ */
+private bool checkDeprecatedAliasThis(AliasThis at, Loc loc, Scope* sc)
+{
+    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 (exp.op == EXP.error)
-                    {
-                        dsym._init = new ErrorInitializer();
-                        ei = null;
-                    }
-                    else
-                        ei.exp = exp.optimize(WANTvalue);
-                }
-                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);
-                    import dmd.semantic2 : lowerStaticAAs;
-                    lowerStaticAAs(dsym, sc);
-                    auto init_err = dsym._init.isExpInitializer();
-                    if (init_err && init_err.exp.op == EXP.showCtfeContext)
-                    {
-                        init_err.exp = ErrorExp.get();
-                        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.inCfile)
-            {
-                /* 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)
-                {
-                    const 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 != EXP.scope_ ? true : !ei.exp.isScopeExp().sds.isPackage()))
-                    {
-                        if (ei.exp.type)
-                        {
-                            // If exp is already resolved we are done, our original init exp
-                            // could have a type painting that we need to respect
-                            // e.g.  ['a'] typed as string, or [['z'], ""] as string[]
-                            // See https://issues.dlang.org/show_bug.cgi?id=15711
-                        }
-                        else
-                        {
-                            Expression exp = ei.exp.syntaxCopy();
+        if (auto ti = sc.parent ? sc.parent.isInstantiated() : null)
+            ti.printInstantiationTrace(Classification.deprecation);
 
-                            bool needctfe = dsym.isDataseg() || (dsym.storage_class & STC.manifest);
-                            if (needctfe)
-                                sc = sc.startCTFE();
-                            sc = sc.push();
-                            sc.varDecl = dsym; // https://issues.dlang.org/show_bug.cgi?id=24051
-                            exp = exp.expressionSemantic(sc);
-                            exp = resolveProperties(sc, exp);
-                            sc = sc.pop();
-                            if (needctfe)
-                                sc = sc.endCTFE();
-                            ei.exp = exp;
-                        }
+        return true;
+    }
+    return false;
+}
 
-                        Type tb2 = dsym.type.toBasetype();
-                        Type ti = ei.exp.type.toBasetype();
+// Save the scope and defer semantic analysis on the Dsymbol.
+void deferDsymbolSemantic(Scope* sc, Dsymbol s, Scope* scx)
+{
+    s._scope = scx ? scx : sc.copy();
+    s._scope.setNoFree();
+    addDeferredSemantic(s);
+}
 
-                        /* 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 (ei.exp.isLvalue())
-                                    .error(dsym.loc, "%s `%s` of type struct `%s` uses `this(this)`, which is not allowed in static initialization", dsym.kind, dsym.toPrettyChars, tb2.toChars());
-                            }
-                        }
-                    }
+struct Ungag
+{
+    uint oldgag;
 
-                    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();
+    extern (D) this(uint old) nothrow @safe
+    {
+        this.oldgag = old;
+    }
+
+    extern (C++) ~this() nothrow
+    {
+        global.gag = oldgag;
+    }
+}
+
+Ungag ungagSpeculative(const Dsymbol s)
+{
+    const oldgag = global.gag;
+    if (global.gag && !s.isSpeculative() && !s.toParent2().isFuncDeclaration())
+        global.gag = 0;
+    return Ungag(oldgag);
+}
+
+/*******************************************
+ * Print deprecation warning if we're deprecated, when
+ * this module is imported from scope sc.
+ *
+ * Params:
+ *  m = the module
+ *  sc = the scope into which we are imported
+ *  loc = the location of the import statement
+ */
+private void checkImportDeprecation(Module m, Loc loc, Scope* sc)
+{
+    if (!m.md || !m.md.isdeprecated || sc.isDeprecated)
+        return;
+
+    Expression msg = m.md.msg;
+    if (StringExp se = msg ? msg.toStringExp() : null)
+    {
+        const slice = se.peekString();
+        if (slice.length)
+        {
+            deprecation(m.loc, "%s `%s` is deprecated - %.*s", m.kind, m.toPrettyChars, cast(int)slice.length, slice.ptr);
+            return;
         }
+    }
+    deprecation(m.loc, "%s `%s` is deprecated", m.kind, m.toPrettyChars);
+}
 
-    Ldtor:
-        /* Build code to execute destruction, if necessary
-         */
-        dsym.edtor = dsym.callScopeDtor(sc);
-        if (dsym.edtor)
+private extern(C++) final class DsymbolSemanticVisitor : Visitor
+{
+    import dmd.typesem: size;
+
+    alias visit = Visitor.visit;
+
+    Scope* sc;
+    this(Scope* sc) scope @safe
+    {
+        this.sc = sc;
+    }
+
+    override void visit(Dsymbol dsym)
+    {
+        .error(dsym.loc, "%s `%s` %p has no semantic routine", dsym.kind, dsym.toPrettyChars, dsym);
+    }
+
+    override void visit(ScopeDsymbol) { }
+    override void visit(Declaration) { }
+
+    override void visit(AliasThis dsym)
+    {
+        if (dsym.semanticRun != PASS.initial)
+            return;
+
+        if (dsym._scope)
         {
-            if (sc.func && dsym.storage_class & (STC.static_ | STC.gshared))
-                dsym.edtor = dsym.edtor.expressionSemantic(sc._module._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)
+        {
+            Dsymbol pscopesym;
+            s = sc.search(dsym.loc, dsym.ident, pscopesym);
+            if (s)
+                error(dsym.loc, "`%s` is not a member of `%s`", s.toChars(), ad.toChars());
             else
-                dsym.edtor = dsym.edtor.expressionSemantic(sc);
+                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);
 
-            version (none)
+            Type t = d.type;
+            assert(t);
+            if (ad.type.implicitConvTo(t) > MATCH.nomatch)
             {
-                // currently disabled because of std.stdio.stdin, stdout and stderr
-                if (dsym.isDataseg() && !(dsym.storage_class & STC.extern_))
-                    .error(dsym.loc, "%s `%s` static storage variables cannot have destructors", dsym.kind, dsym.toPrettyChars);
+                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;
-
-        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(CAsmDeclaration dsym)
+    override void visit(AliasDeclaration dsym)
     {
         if (dsym.semanticRun >= PASS.semanticdone)
             return;
-        import dmd.iasm : asmSemantic;
-        asmSemantic(dsym, sc);
-        dsym.semanticRun = PASS.semanticdone;
+        assert(dsym.semanticRun <= PASS.semantic);
+
+        if (!sc)
+            return;
+
+        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(BitFieldDeclaration dsym)
+    override void visit(AliasAssign dsym)
     {
-        //printf("BitField::semantic('%s')\n", dsym.toChars());
+        //printf("visit(AliasAssign)\n");
         if (dsym.semanticRun >= PASS.semanticdone)
             return;
+        assert(dsym.semanticRun <= PASS.semantic);
 
-        visit(cast(VarDeclaration)dsym);
-        if (dsym.errors)
+        if (!sc.func && dsym.inNonRoot())
             return;
 
-        if (!(sc.previews.bitfields || sc.inCfile))
-        {
-            version (IN_GCC)
-                .error(dsym.loc, "%s `%s` use `-fpreview=bitfields` for bitfield support", dsym.kind, dsym.toPrettyChars);
-            else
-                .error(dsym.loc, "%s `%s` use -preview=bitfields for bitfield support", dsym.kind, dsym.toPrettyChars);
-        }
-
-        if (!dsym.parent.isStructDeclaration() && !dsym.parent.isClassDeclaration())
-        {
-            .error(dsym.loc, "%s `%s` - bitfield must be member of struct, union, or class", dsym.kind, dsym.toPrettyChars);
-        }
-
-        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
-            error(width.loc, "bitfield type `%s` is not an integer type", dsym.type.toChars());
-            dsym.errors = true;
-        }
-        if (!width.isIntegerExp())
-        {
-            error(width.loc, "bitfield 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())
-        {
-            error(width.loc, "bitfield `%s` has zero width", dsym.toChars());
-            dsym.errors = true;
-        }
-        const sz = dsym.type.size();
-        if (sz == SIZE_INVALID)
-            dsym.errors = true;
-        const max_width = sz * 8;
-        if (uwidth > max_width)
-        {
-            error(width.loc, "width `%lld` of bitfield `%s` does not fit in type `%s`", cast(long)uwidth, dsym.toChars(), dsym.type.toChars());
-            dsym.errors = true;
-        }
-        dsym.fieldWidth = cast(uint)uwidth;
+        aliasAssignSemantic(dsym, sc);
     }
 
-    override void visit(Import imp)
+    override void visit(VarDeclaration dsym)
     {
-        timeTraceBeginEvent(TimeTraceEventType.sema1Import);
-        scope (exit) timeTraceEndEvent(TimeTraceEventType.sema1Import, imp);
-        static if (LOG)
+        version (none)
         {
-            printf("Import::semantic('%s') %s\n", imp.toPrettyChars(), imp.id.toChars());
-            scope(exit)
-                printf("-Import::semantic('%s'), pkg = %p\n", imp.toChars(), imp.pkg);
+            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 (imp.semanticRun > PASS.initial)
+        //if (semanticRun > PASS.initial)
+        //    return;
+        //semanticRun = PSSsemantic;
+
+        if (dsym.semanticRun >= PASS.semanticdone)
             return;
 
-        if (imp._scope)
+        if (sc && sc.inunion && sc.inunion.isAnonDeclaration())
+            dsym.overlapped = true;
+
+        dsym.sequenceNumber = global.varSequenceNumber++;
+        if (!dsym.isScope())
+            dsym.maybeScope = true;
+
+        Scope* scx = null;
+        if (dsym._scope)
         {
-            sc = imp._scope;
-            imp._scope = null;
+            sc = dsym._scope;
+            scx = sc;
+            dsym._scope = null;
         }
+
         if (!sc)
             return;
 
-        imp.parent = sc.parent;
+        dsym.semanticRun = PASS.semantic;
 
-        imp.semanticRun = PASS.semantic;
+        // 'static foreach' variables should not inherit scope properties
+        // https://issues.dlang.org/show_bug.cgi?id=19482
+        if ((dsym.storage_class & (STC.foreach_ | STC.local)) == (STC.foreach_ | STC.local))
+        {
+            dsym._linkage = LINK.d;
+            dsym.visibility = Visibility(Visibility.Kind.public_);
+            dsym.overlapped = false; // unset because it is modified early on this function
+            dsym.userAttribDecl = null; // unset because it is set by Dsymbol.setScope()
+        }
+        else
+        {
+            /* 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_));
+            dsym.userAttribDecl = sc.userAttribDecl;
+            dsym.cppnamespace = sc.namespace;
+            dsym._linkage = sc.linkage;
+            dsym.visibility = sc.visibility;
+            dsym.alignment = sc.alignment();
+        }
 
-        // Load if not already done so
-        if (!imp.mod)
+        if (dsym.storage_class & STC.extern_ && dsym._init)
         {
-            // https://issues.dlang.org/show_bug.cgi?id=22857
-            // if parser errors occur when loading a module
-            // we should just stop compilation
-            if (imp.load(sc))
+            if (sc.inCfile)
             {
-                for (size_t i = 0; i < imp.aliasdecls.length; i++)
-                    imp.aliasdecls[i].type = Type.terror;
-                return;
+                // https://issues.dlang.org/show_bug.cgi?id=24447
+                // extern int x = 3; is allowed in C
+                dsym.storage_class &= ~STC.extern_;
             }
+            else
+                .error(dsym.loc, "%s `%s` extern symbols cannot have initializers", dsym.kind, dsym.toPrettyChars);
 
-            if (imp.mod)
+        }
+
+        AggregateDeclaration ad = dsym.isThis();
+        if (ad)
+            dsym.storage_class |= ad.storage_class & STC.TYPECTOR;
+
+        if ((dsym.storage_class & STC.auto_) && (dsym.storage_class & STC.ref_))
+        {
+            if (!(dsym.storage_class & STC.autoref))
             {
-                imp.mod.importAll(null);
-                imp.mod.checkImportDeprecation(imp.loc, sc);
+                .error(dsym.loc, "%s `%s` - `auto ref` variable must have `auto` and `ref` adjacent", dsym.kind, dsym.toChars());
+                dsym.storage_class |= STC.autoref;
             }
         }
-        if (!imp.mod)
+
+        /* If auto type inference, do the inference
+         */
+        int inferred = 0;
+        if (!dsym.type)
         {
-            imp.semanticRun = PASS.semanticdone;
-            addImportDep(global.params.moduleDeps, imp, sc._module);
-        }
+            dsym.inuse++;
 
-        // Modules need a list of each imported module
+            // Infering the type requires running semantic,
+            // so mark the scope as ctfe if required
+            bool needctfe = (dsym.storage_class & (STC.manifest | STC.static_)) != 0 || !sc.func;
+            if (needctfe)
+            {
+                sc.condition = true;
+                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(null, sc.inCfile).type;
+            if (needctfe)
+                sc = sc.endCTFE();
 
-        // 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)
+            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
         {
-            importer = sc.minst;
-            if (!sc.tinst.importedModules.contains(imp.mod))
-                sc.tinst.importedModules.push(imp.mod);
+            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("%s imports %s\n", importer.toChars(), imp.mod.toChars());
-        if (!importer.aimports.contains(imp.mod))
-            importer.aimports.push(imp.mod);
+        //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.parent = sc.parent;
+        //printf("this = %p, parent = %p, '%s'\n", dsym, dsym.parent, dsym.parent.toChars());
 
-        if (sc.explicitVisibility)
-            imp.visibility = sc.visibility;
+        /* If scope's alignment is the default, use the type's alignment,
+         * otherwise the scope overrrides.
+         */
+        import dmd.typesem : alignment;
+        if (dsym.alignment.isDefault())
+            dsym.alignment = dsym.type.alignment(); // use type's alignment
 
-        if (!imp.aliasId && !imp.names.length) // neither a selective nor a renamed import
+        if (sc.inCfile && !dsym.alignment.isDefault())
         {
-            ScopeDsymbol scopesym = sc.getScopesym();
-
-            if (!imp.isstatic)
+            /* C11 6.7.5-4 alignment declaration cannot be less strict than the
+             * type alignment of the object or member being declared.
+             */
+            if (dsym.alignment.get() < dsym.type.alignsize())
             {
-                scopesym.importScope(imp.mod, imp.visibility);
+                if (dsym.alignment.fromAlignas())
+                {
+                    error(dsym.loc, "`_Alignas` specifier cannot be less strict than alignment of `%s`",
+                          dsym.toChars());
+                }
+                if (!dsym.alignment.isPack())
+                    dsym.alignment.setDefault();
             }
-
-
-            imp.addPackageAccess(scopesym);
         }
 
-        // if a module has errors it means that parsing has failed.
-        if (!imp.mod.errors)
-            imp.mod.dsymbolSemantic(null);
+        //printf("sc.stc = %x\n", sc.stc);
+        //printf("storage_class = x%x\n", storage_class);
 
-        if (imp.mod.needmoduleinfo)
+        dsym.type.checkComplexTransition(dsym.loc, sc);
+
+        // Calculate type size + safety checks
+        if (dsym.storage_class & STC.gshared && !dsym.isMember())
         {
-            //printf("module4 %s because of %s\n", importer.toChars(), imp.mod.toChars());
-            importer.needmoduleinfo = 1;
+            sc.setUnsafe(false, dsym.loc, "using `__gshared` instead of `shared`");
         }
 
-        sc = sc.push(imp.mod);
-        sc.visibility = imp.visibility;
-        for (size_t i = 0; i < imp.aliasdecls.length; i++)
+        Dsymbol parent = dsym.toParent();
+
+        Type tb = dsym.type.toBasetype();
+        Type tbn = tb.baseElemOf();
+        if (tb.ty == Tvoid && !(dsym.storage_class & STC.lazy_))
         {
-            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], SearchOpt.ignorePrivateImports);
-            if (sym)
+            if (inferred)
             {
-                import dmd.access : symbolIsVisible;
-                if (!symbolIsVisible(sc, sym) && !sym.errors)
-                {
-                    .error(imp.loc, "%s `%s` member `%s` is not visible from module `%s`", imp.mod.kind, imp.mod.toPrettyChars,
-                        imp.names[i].toChars(), sc._module.toChars());
-                    sym.errors = true;
-                }
-                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.
+                .error(dsym.loc, "%s `%s` - type `%s` is inferred from initializer `%s`, and variables cannot be of type `void`",
+                    dsym.kind, dsym.toPrettyChars, dsym.type.toChars(), toChars(dsym._init));
             }
             else
-            {
-                Dsymbol s = imp.mod.search_correct(imp.names[i]);
-                // https://issues.dlang.org/show_bug.cgi?id=23908
-                // Don't suggest symbols from the importer's module
-                if (s && s.parent != importer)
-                    .error(imp.loc, "%s `%s` import `%s` not found, did you mean %s `%s`?", imp.mod.kind, imp.mod.toPrettyChars, imp.names[i].toChars(), s.kind(), s.toPrettyChars());
-                else
-                    .error(imp.loc, "%s `%s` import `%s` not found", imp.mod.kind, imp.mod.toPrettyChars, imp.names[i].toChars());
-                ad.type = Type.terror;
-            }
+                .error(dsym.loc, "%s `%s` - variables cannot be of type `void`", dsym.kind, dsym.toPrettyChars);
+            dsym.type = Type.terror;
+            tb = dsym.type;
         }
-        sc = sc.pop();
-
-        imp.semanticRun = PASS.semanticdone;
-        addImportDep(global.params.moduleDeps, imp, sc._module);
-    }
-
-    void attribSemantic(AttribDeclaration ad)
-    {
-        if (ad.semanticRun != PASS.initial)
-            return;
-        ad.semanticRun = PASS.semantic;
-        Dsymbols* d = ad.include(sc);
-        //printf("\tAttribDeclaration::semantic '%s', d = %p\n",toChars(), d);
-        if (!d)
+        if (tb.ty == Tfunction)
         {
-            ad.semanticRun = PASS.semanticdone;
-            return;
+            .error(dsym.loc, "%s `%s` cannot be declared to be a function", dsym.kind, dsym.toPrettyChars);
+            dsym.type = Type.terror;
+            tb = dsym.type;
         }
-
-        Scope* sc2 = ad.newScope(sc);
-        bool errors;
-        for (size_t i = 0; i < d.length; i++)
+        if (auto ts = tb.isTypeStruct())
         {
-            Dsymbol s = (*d)[i];
-            s.dsymbolSemantic(sc2);
-            errors |= s.errors;
-        }
-        if (errors)
-            ad.errors = true;
-        if (sc2 != sc)
-            sc2.pop();
-
-        ad.semanticRun = PASS.semanticdone;
-    }
+            // 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_)))
+            {
+                .error(dsym.loc, "%s `%s` - no definition of struct `%s`", dsym.kind, dsym.toPrettyChars, ts.toChars());
 
-    override void visit(AttribDeclaration atd)
-    {
-        attribSemantic(atd);
-    }
+                // 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());
+                }
+                errorSupplemental(dsym.loc, "see https://dlang.org/spec/struct.html#opaque_struct_unions");
+                errorSupplemental(dsym.loc, "perhaps declare a variable with pointer type `%s*` instead", dsym.type.toChars());
 
-    override void visit(AnonDeclaration scd)
-    {
-        //printf("\tAnonDeclaration::semantic isunion:%d ptr:%p\n", scd.isunion, scd);
-        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;
+                // 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.storage_class & STC.autoref))
+            .error(dsym.loc, "%s `%s` - storage class `auto` has no effect if type is not inferred, did you mean `scope`?", dsym.kind, dsym.toPrettyChars);
 
-        if (!scd.decl)
-            return;
-
-        sc = sc.push();
-        sc.stc &= ~(STC.auto_ | STC.scope_ | STC.static_ | STC.gshared);
-        sc.inunion = scd.isunion ? scd : null;
-        sc.resetAllFlags();
-        for (size_t i = 0; i < scd.decl.length; i++)
+        if (auto tt = tb.isTypeTuple())
         {
-            Dsymbol s = (*scd.decl)[i];
-            if (auto var = s.isVarDeclaration)
+            /* 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, sc.inCfile) : null;
+            if (ie)
+                ie = ie.expressionSemantic(sc);
+            if (nelems > 0 && ie)
             {
-                if (scd.isunion)
-                    var.overlapped = true;
-            }
-            s.dsymbolSemantic(sc);
-        }
-        sc = sc.pop();
-    }
+                auto iexps = new Expressions(ie);
+                auto 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 = arg.type.typeSemantic(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());
 
-    override void visit(PragmaDeclaration pd)
-    {
-        import dmd.pragmasem : pragmaDeclSemantic;
-        pragmaDeclSemantic(pd, sc);
-    }
+                    if (e != ie)
+                    {
+                        if (iexps.length > nelems)
+                            goto Lnomatch;
+                        if (e.type.implicitConvTo(arg.type))
+                            continue;
+                    }
 
-    override void visit(StaticIfDeclaration sid)
-    {
-        attribSemantic(sid);
-    }
+                    if (auto te = e.isTupleExp())
+                    {
+                        if (iexps.length - 1 + te.exps.length > nelems)
+                            goto Lnomatch;
 
-    override void visit(StaticForeachDeclaration sfd)
-    {
-        attribSemantic(sfd);
-    }
+                        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(STC.none, "__tup", e);
+                        v.dsymbolSemantic(sc);
+                        auto ve = new VarExp(dsym.loc, v);
+                        ve.type = e.type;
 
-    private Dsymbols* compileIt(MixinDeclaration cd)
-    {
-        //printf("MixinDeclaration::compileIt(loc = %d) %s\n", cd.loc.linnum, cd.exp.toChars());
-        OutBuffer buf;
-        if (expressionsToString(buf, sc, cd.exps, cd.loc, null, true))
-            return null;
+                        exps.setDim(1);
+                        (*exps)[0] = ve;
+                        expandAliasThisTuples(exps, 0);
 
-        const errors = global.errors;
-        const len = buf.length;
-        buf.writeByte(0);
-        const str = buf.extractSlice()[0 .. len];
-        const bool doUnittests = global.params.parsingUnittestsRequired();
-        scope p = new Parser!ASTCodegen(sc._module, str, false, global.errorSink, &global.compileEnv, doUnittests);
-        adjustLocForMixin(str, cd.loc, *p.baseLoc, global.params.mixinOut);
-        p.linnum = p.baseLoc.startLine;
-        p.nextToken();
+                        for (size_t u = 0; u < exps.length; 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.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());
 
-        auto d = p.parseDeclDefs(0);
-        if (global.errors != errors)
-            return null;
+                            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;
 
-        if (p.token.value != TOK.endOfFile)
-        {
-            .error(cd.loc, "%s `%s` incomplete mixin declaration `%s`", cd.kind, cd.toPrettyChars, str.ptr);
-            return null;
-        }
-        return d;
-    }
+                            iexps.remove(pos);
+                            iexps.insert(pos, exps);
+                            goto Lexpand1;
+                        }
+                    }
+                }
+                if (iexps.length < nelems)
+                    goto Lnomatch;
 
-    /***********************************************************
-     * https://dlang.org/spec/module.html#mixin-declaration
-     */
-    override void visit(MixinDeclaration cd)
-    {
-        //printf("MixinDeclaration::semantic()\n");
-        if (!cd.compiled)
-        {
-            cd.decl = compileIt(cd);
-            attribAddMember(cd, sc, cd.scopesym);
-            cd.compiled = true;
+                ie = new TupleExp(dsym._init.loc, iexps);
+            }
+        Lnomatch:
 
-            if (cd._scope && cd.decl)
+            if (ie && ie.op == EXP.tuple)
             {
-                for (size_t i = 0; i < cd.decl.length; i++)
+                auto te = ie.isTupleExp();
+                size_t tedim = te.exps.length;
+                if (tedim != nelems)
                 {
-                    Dsymbol s = (*cd.decl)[i];
-                    s.setScope(cd._scope);
+                    error(dsym.loc, "sequence of %d elements cannot be assigned to sequence 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());
                 }
             }
-        }
-        attribSemantic(cd);
-    }
 
-    override void visit(CPPNamespaceDeclaration ns)
-    {
-        Identifier identFromSE (StringExp se)
-        {
-            const sident = se.toStringz();
-            if (!sident.length || !Identifier.isValidIdentifier(sident))
+            auto exps = new Objects(nelems);
+            for (size_t i = 0; i < nelems; i++)
             {
-                error(ns.exp.loc, "expected valid identifier for C++ namespace but got `%s`", se.toErrMsg());
-                return null;
-            }
-            else
-                return Identifier.idPool(sident);
-        }
+                Parameter arg = Parameter.getNth(tt.arguments, i);
 
-        if (ns.ident !is null)
-            return attribSemantic(ns);
+                OutBuffer buf;
+                buf.printf("__%s_field_%llu", dsym.ident.toChars(), cast(ulong)i);
+                auto id = Identifier.idPool(buf[]);
 
-        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.length; ++d)
-            {
-                auto exp = (*te.exps)[d];
-                auto prev = d ? current : ns.cppnamespace;
-                current = (d + 1) != te.exps.length
-                    ? new CPPNamespaceDeclaration(ns.loc, exp, null)
-                    : ns;
-                current.exp = exp;
-                current.cppnamespace = prev;
-                if (auto se = exp.toStringExp())
+                Initializer ti;
+                if (ie)
                 {
-                    current.ident = identFromSE(se);
-                    if (current.ident is null)
-                        return; // An error happened in `identFromSE`
+                    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
-                    error(ns.exp.loc, "`%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 if (!ns.exp.type.isTypeError())
-            error(ns.exp.loc, "compile time string constant (or sequence) 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.peekSlice(), sc, true);
-        return attribSemantic(uad);
-    }
-
-    override void visit(StaticAssert sa)
-    {
-        if (sa.semanticRun < PASS.semanticdone)
-            sa.semanticRun = PASS.semanticdone;
-        else
-            return;
-
-        // https://issues.dlang.org/show_bug.cgi?id=24645
-        // This is a short-circuit. Usually, static assert conditions are evaluated
-        // in semantic2, but it's not uncommon to use this pattern:
-        // ---
-        // version(X)
-        // {}
-        // else
-        //   static assert(false, "unsupported platform");
-        // ---
-        // However, without this short-circuit, the static assert error may get drowned
-        // out by subsequent semantic1 (import) errors. Only short-circuit at module scope though,
-        // inside mixin templates you want an instantiation trace (which you don't get here).
-        if (sc.parent && sc.parent.isModule())
-            if (auto i = sa.exp.isIntegerExp())
-                if (i.toInteger() == 0)
-                    staticAssertFail(sa, sc);
-    }
-
-    override void visit(DebugSymbol ds)
-    {
-        //printf("DebugSymbol::semantic() %s\n", toChars());
-        if (ds.semanticRun < PASS.semanticdone)
-            ds.semanticRun = PASS.semanticdone;
-    }
+                    ti = dsym._init ? dsym._init.syntaxCopy() : null;
 
-    override void visit(VersionSymbol vs)
-    {
-        if (vs.semanticRun < PASS.semanticdone)
-            vs.semanticRun = PASS.semanticdone;
-    }
+                STC storage_class = STC.temp | 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.overlapped = dsym.overlapped;
 
-    override void visit(Package pkg)
-    {
-        if (pkg.semanticRun < PASS.semanticdone)
-            pkg.semanticRun = PASS.semanticdone;
-    }
+                v.dsymbolSemantic(sc);
 
-    override void visit(Module m)
-    {
-        if (m.semanticRun != PASS.initial)
+                Expression e = new VarExp(dsym.loc, v);
+                (*exps)[i] = e;
+            }
+            auto v2 = new TupleDeclaration(dsym.loc, dsym.ident, exps);
+            v2.parent = dsym.parent;
+            v2.isexp = true;
+            dsym.aliasTuple = v2;
+            dsym.semanticRun = PASS.semanticdone;
             return;
-
-        timeTraceBeginEvent(TimeTraceEventType.sema1Module);
-        scope (exit) timeTraceEndEvent(TimeTraceEventType.sema1Module, m);
-
-        //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, global.errorSink); // 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();
-        });
+        /* Storage class can modify the type
+         */
+        dsym.type = dsym.type.addStorageClass(dsym.storage_class);
 
-        if (m.userAttribDecl)
+        /* Adjust storage class to reflect type
+         */
+        if (dsym.type.isConst())
         {
-            m.userAttribDecl.dsymbolSemantic(sc);
+            dsym.storage_class |= STC.const_;
+            if (dsym.type.isShared())
+                dsym.storage_class |= STC.shared_;
         }
-        if (!m._scope)
+        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 (STC stc = dsym.storage_class & (STC.synchronized_ | STC.override_ | STC.abstract_ | STC.final_))
         {
-            sc = sc.pop();
-            sc.pop(); // 2 pops because Scope.createGlobal() created 2
+            if (stc == STC.final_)
+                .error(dsym.loc, "%s `%s` cannot be `final`, perhaps you meant `const`?", dsym.kind, dsym.toPrettyChars);
+            else
+            {
+                OutBuffer buf;
+                stcToBuffer(buf, stc);
+                .error(dsym.loc, "%s `%s` cannot be `%s`", dsym.kind, dsym.toPrettyChars, buf.peekChars());
+            }
+            dsym.storage_class &= ~stc; // strip off
         }
-        m.semanticRun = PASS.semanticdone;
-        //printf("-Module::semantic(this = %p, '%s'): parent = %p\n", this, toChars(), parent);
-    }
-
-    override void visit(EnumDeclaration ed)
-    {
-        enumSemantic(sc, ed);
-    }
-
-    override void visit(EnumMember em)
-    {
-        enumMemberSemantic(sc, em);
-    }
 
-    override void visit(TemplateDeclaration tempdecl)
-    {
-        templateDeclarationSemantic(sc, tempdecl);
-    }
+        // 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.constscoperef)
+            dsym.storage_class |= STC.scope_;
 
-    override void visit(TemplateInstance ti)
-    {
-        templateInstanceSemantic(ti, sc, ArgumentList());
-    }
+        import dmd.typesem : hasPointers;
 
-    override void visit(TemplateMixin tm)
-    {
-        static if (LOG)
-        {
-            printf("+TemplateMixin.dsymbolSemantic('%s', this=%p)\n", tm.toChars(), tm);
-            fflush(stdout);
-        }
-        if (tm.semanticRun != PASS.initial)
+        if (dsym.storage_class & STC.scope_)
         {
-            // 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.initial.
-            static if (LOG)
+            STC stc = dsym.storage_class & (STC.static_ | STC.extern_ | STC.manifest | STC.gshared);
+            if (stc)
             {
-                printf("\tsemantic done\n");
+                OutBuffer buf;
+                stcToBuffer(buf, stc);
+                .error(dsym.loc, "%s `%s` cannot be `scope` and `%s`", dsym.kind, dsym.toPrettyChars, buf.peekChars());
+            }
+            else if (dsym.isMember())
+            {
+                error(dsym.loc, "field `%s` cannot be `scope`", dsym.toChars());
+            }
+            else if (!dsym.type.hasPointers())
+            {
+                dsym.storage_class &= ~STC.scope_;     // silently ignore; may occur in generic code
+                // https://issues.dlang.org/show_bug.cgi?id=23168
+                if (dsym.storage_class & STC.returnScope)
+                {
+                    dsym.storage_class &= ~(STC.return_ | STC.returnScope);
+                }
             }
-            return;
-        }
-        tm.semanticRun = PASS.semantic;
-        static if (LOG)
-        {
-            printf("\tdo semantic\n");
         }
 
-        Scope* scx = null;
-        if (tm._scope)
+        if (dsym.storage_class & (STC.static_ | STC.extern_ | STC.manifest | STC.templateparameter | STC.gshared | STC.ctfe))
         {
-            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.findMixinTempDecl(sc) || !tm.semanticTiargs(sc) || !tm.findBestMatch(sc, ArgumentList()))
+        else
         {
-            if (tm.semanticRun == PASS.initial) // forward reference had occurred
+            AggregateDeclaration aad = parent.isAggregateDeclaration();
+            if (aad)
             {
-                //printf("forward reference - deferring\n");
-                return deferDsymbolSemantic(sc, tm, scx);
+                if (global.params.v.field && 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;
+                    }
             }
 
-            tm.inst = tm;
-            tm.errors = true;
-            return; // error recovery
-        }
-
-        auto tempdecl = tm.tempdecl.isTemplateDeclaration();
-        assert(tempdecl);
+            InterfaceDeclaration id = parent.isInterfaceDeclaration();
+            if (id)
+            {
+                error(dsym.loc, "field `%s` not allowed in interface", dsym.toChars());
+            }
+            else if (aad && aad.sizeok == Sizeok.done)
+            {
+                error(dsym.loc, "cannot declare field `%s` because it will change the determined size of `%s`", dsym.toChars(), aad.toChars());
+            }
 
-        if (!tm.ident)
-        {
-            /* Assign scope local unique identifier, as same as lambdas.
+            /* Templates cannot add fields to aggregates
              */
-            const(char)[] s = "__mixin";
-
-            if (FuncDeclaration func = sc.parent.isFuncDeclaration())
+            TemplateInstance ti = parent.isTemplateInstance();
+            if (ti)
             {
-                tm.symtab = func.localsymtab;
-                if (tm.symtab)
+                // Take care of nested templates
+                while (1)
                 {
-                    // Inside template constraint, symtab is not set yet.
-                    goto L1;
+                    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.none)
+                {
+                    .error(dsym.loc, "%s `%s` - cannot use template to add field to aggregate `%s`", dsym.kind, dsym.toPrettyChars, ad2.toChars());
                 }
-            }
-            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;
+        mixin alignSectionVarsExtra; doAlign(); // align section variables
 
-        /* Detect recursive mixin instantiations.
-         */
-        for (Dsymbol s = tm.parent; s; s = s.parent)
+        if ((dsym.storage_class & (STC.ref_ | STC.field)) == (STC.ref_ | STC.field) && dsym.ident != Id.This)
         {
-            //printf("\ts = '%s'\n", s.toChars());
-            TemplateMixin tmix = s.isTemplateMixin();
-            if (!tmix || tempdecl != tmix.tempdecl)
-                continue;
+            .error(dsym.loc, "%s `%s` - field declarations cannot be `ref`", dsym.kind, dsym.toPrettyChars);
+        }
 
-            /* Different argument list lengths happen with variadic args
-             */
-            if (tm.tiargs.length != tmix.tiargs.length)
-                continue;
+        if (dsym.type.hasWild())
+        {
+            if (dsym.storage_class & (STC.static_ | STC.extern_ | STC.gshared | STC.manifest | STC.field) || dsym.isDataseg())
+            {
+                .error(dsym.loc, "%s `%s` - only parameters or stack-based variables can be `inout`", dsym.kind, dsym.toPrettyChars);
+            }
+            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)
+                {
+                    .error(dsym.loc, "%s `%s` - `inout` variables can only be declared inside `inout` functions", dsym.kind, dsym.toPrettyChars);
+                }
+            }
+        }
 
-            for (size_t i = 0; i < tm.tiargs.length; i++)
+        if (!(dsym.storage_class & (STC.ctfe | STC.extern_ | STC.ref_ | STC.result)) &&
+            tbn.ty == Tstruct && tbn.isTypeStruct().sym.noDefaultCtor)
+        {
+            if (!dsym._init)
             {
-                RootObject o = (*tm.tiargs)[i];
-                Type ta = isType(o);
-                Expression ea = isExpression(o);
-                Dsymbol sa = isDsymbol(o);
-                RootObject tmo = (*tmix.tiargs)[i];
-                if (ta)
+                if (dsym.isField())
                 {
-                    Type tmta = isType(tmo);
-                    if (!tmta)
-                        goto Lcontinue;
-                    if (!ta.equals(tmta))
-                        goto Lcontinue;
+                    /* For fields, we'll check the constructor later to make sure it is initialized
+                     */
+                    dsym.storage_class |= STC.nodefaultctor;
                 }
-                else if (ea)
+                else if (dsym.storage_class & STC.parameter)
                 {
-                    Expression tme = isExpression(tmo);
-                    if (!tme || !ea.equals(tme))
-                        goto Lcontinue;
                 }
-                else if (sa)
+                else
+                    .error(dsym.loc, "%s `%s` - default construction is disabled for type `%s`", dsym.kind, dsym.toPrettyChars, dsym.type.toChars());
+            }
+        }
+
+        bool dsymIsRef = (dsym.storage_class & (STC.ref_ | STC.field | STC.parameter | STC.temp | STC.foreach_)) == STC.ref_;
+        if (dsymIsRef)
+        {
+            if (!dsym._init && dsym.ident != Id.This)
+            {
+                if (dsym.storage_class & STC.autoref)
                 {
-                    Dsymbol tmsa = isDsymbol(tmo);
-                    if (sa != tmsa)
-                        goto Lcontinue;
+                    dsymIsRef = false;
+                    dsym.storage_class &= ~STC.ref_;
                 }
                 else
-                    assert(0);
+                    .error(dsym.loc, "%s `%s` - initializer is required for `ref` variable", dsym.kind, dsym.toPrettyChars);
             }
-            .error(tm.loc, "%s `%s` recursive mixin instantiation", tm.kind, tm.toPrettyChars);
-            return;
+            else if (dsym._init.isVoidInitializer())
+            {
+                .error(dsym.loc, "%s `%s` - void initializer not allowed for `ref` variable", dsym.kind, dsym.toPrettyChars);
+            }
+        }
 
-        Lcontinue:
-            continue;
+        FuncDeclaration fd = parent.isFuncDeclaration();
+        if (dsym.type.isScopeClass() && !(dsym.storage_class & STC.nodtor))
+        {
+            if (dsym.storage_class & (STC.field | STC.out_ | STC.ref_ | STC.static_ | STC.manifest | STC.gshared) || !fd)
+            {
+                .error(dsym.loc, "%s `%s` globals, statics, fields, manifest constants, ref and out parameters cannot be `scope`", dsym.kind, dsym.toPrettyChars);
+            }
+
+            // @@@DEPRECATED_2.097@@@  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 (!(dsym.storage_class & STC.scope_))
+            {
+                if (!(dsym.storage_class & STC.parameter) && dsym.ident != Id.withSym)
+                    .error(dsym.loc, "%s `%s` reference to `scope class` must be `scope`", dsym.kind, dsym.toPrettyChars);
+            }
+        }
+
+        // Calculate type size + safety checks
+        if (sc && sc.func)
+        {
+            if (dsym._init && dsym._init.isVoidInitializer() && !(dsym.storage_class & STC.temp))
+            {
+                // Don't do these checks for STC.temp vars because the generated `opAssign`
+                // for a struct with postblit and destructor void initializes a temporary
+                // __swap variable, which can be trusted
+
+                if (dsym.type.hasPointers()) // also computes type size
+                    sc.setUnsafe(false, dsym.loc,
+                        "`void` initializing a pointer");
+                else if (dsym.type.hasInvariant())
+                    sc.setUnsafe(false, dsym.loc,
+                        "`void` initializing a struct with an invariant");
+                else if (dsym.type.toBasetype().ty == Tbool)
+                    sc.setUnsafePreview(global.params.systemVariables, false, dsym.loc,
+                        "`void` initializing a `bool` (which must always be 0 or 1)");
+                else if (dsym.type.hasUnsafeBitpatterns())
+                    sc.setUnsafePreview(global.params.systemVariables, false, dsym.loc,
+                        "`void` initializing a type with unsafe bit patterns");
+            }
+            else if (!dsym._init &&
+                     !(dsym.storage_class & (STC.static_ | STC.extern_ | STC.gshared | STC.manifest | STC.field | STC.parameter)) &&
+                     dsym.type.hasVoidInitPointers())
+            {
+                sc.setUnsafe(false, dsym.loc, "`void` initializers for pointers");
+            }
         }
 
-        // 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)
+        if ((!dsym._init || dsym._init.isVoidInitializer) && !fd)
         {
-            printf("\tcreate scope for template parameters '%s'\n", tm.toChars());
+            // If not mutable, initializable by constructor only
+            dsym.setInCtorOnly = true;
         }
-        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);
-
-        const 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));
+        if (dsym._init)
+        { } // remember we had an explicit initializer
+        else if (dsym.storage_class & STC.manifest)
+            .error(dsym.loc, "%s `%s` - manifest constants must have initializers", dsym.kind, dsym.toPrettyChars);
 
-        // Do semantic() analysis on template instance members
-        static if (LOG)
+        // Don't allow non-extern, non-__gshared variables to be interfaced with C++
+        if (dsym._linkage == LINK.cpp && !(dsym.storage_class & (STC.ctfe | STC.extern_ | STC.gshared)) && dsym.isDataseg())
         {
-            printf("\tdo semantic() on template instance members '%s'\n", tm.toChars());
+            const char* p = (dsym.storage_class & STC.shared_) ? "shared" : "static";
+            .error(dsym.loc, "%s `%s` cannot have `extern(C++)` linkage because it is `%s`", dsym.kind, dsym.toPrettyChars, p);
+            errorSupplemental(dsym.loc, "perhaps declare it as `__gshared` instead");
+            dsym.errors = true;
         }
-        Scope* sc2 = argscope.push(tm);
-        //size_t deferred_dim = Module.deferred.length;
 
-        __gshared int nest;
-        //printf("%d\n", nest);
-        if (++nest > global.recursionLimit)
+        bool isBlit = false;
+        uinteger_t sz;
+        if (sc.inCfile && !dsym._init)
         {
-            global.gag = 0; // ensure error message gets printed
-            .error(tm.loc, "%s `%s` recursive expansion", tm.kind, tm.toPrettyChars);
-            fatal();
+            addDefaultCInitializer(dsym);
         }
+        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
 
-        tm.members.foreachDsymbol( s => s.setScope(sc2) );
+            //printf("Providing default initializer for '%s'\n", dsym.toChars());
+            if (sz == SIZE_INVALID && dsym.type.ty != Terror)
+                .error(dsym.loc, "%s `%s` - size of type `%s` is invalid", dsym.kind, dsym.toPrettyChars, dsym.type.toChars());
 
-        tm.members.foreachDsymbol( s => s.importAll(sc2) );
+            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);
 
-        tm.members.foreachDsymbol( s => s.dsymbolSemantic(sc2) );
+                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)
+            {
+                .error(dsym.loc, "%s `%s` of type `%s` does not have a default initializer", dsym.kind, dsym.toPrettyChars, dsym.type.toChars());
+            }
+            else if (auto e = dsym.type.defaultInit(dsym.loc))
+            {
+                dsym._init = new ExpInitializer(dsym.loc, e);
+            }
 
-        nest--;
+            // 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);
 
-        /* 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.length > deferred_dim) {}
+            if (sc.inCfile &&
+                dsym.type.isTypeSArray() &&
+                dsym.type.isTypeSArray().isIncomplete() &&
+                dsym._init.isVoidInitializer() &&
+                !(dsym.storage_class & STC.field))
+            {
+                .error(dsym.loc, "%s `%s` - incomplete array type must have initializer", dsym.kind, dsym.toPrettyChars);
+            }
 
-        AggregateDeclaration ad = tm.isMember();
-        if (sc.func && !ad)
-        {
-            tm.semantic2(sc2);
-            tm.semantic3(sc2);
-        }
+            ExpInitializer ei = dsym._init.isExpInitializer();
 
-        // Give additional context info if error occurred during instantiation
-        if (global.errors != errorsave)
-        {
-            .error(tm.loc, "%s `%s` error instantiating", tm.kind, tm.toPrettyChars);
-            tm.errors = true;
-        }
+            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);
 
-        sc2.pop();
-        argscope.pop();
-        scy.pop();
+            /*
+             * https://issues.dlang.org/show_bug.cgi?id=24474
+             * at function scope, the compiler thinks the type of the variable is not known yet
+             * (semantically complete) so looks like typeof gets locked in a cyclic situation
+             * semantics is actually done. just set it for importc
+             */
+            if (sc.func && dsym.type && dsym.type.deco && sc.inCfile)
+                dsym.semanticRun = PASS.semanticdone;
 
-        static if (LOG)
-        {
-            printf("-TemplateMixin.dsymbolSemantic('%s', this=%p)\n", tm.toChars(), tm);
-        }
-    }
+            // 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.gshared | STC.extern_)) && !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 = dsym._init.initializerToExpression(dsym.type, sc.inCfile);
+                        if (!e)
+                        {
+                            // Run semantic, but don't need to interpret
+                            dsym._init = dsym._init.initializerSemantic(sc, dsym.type, INITnointerpret);
+                            e = dsym._init.initializerToExpression(null, sc.inCfile);
+                            if (!e)
+                            {
+                                .error(dsym.loc, "%s `%s` is not a static and cannot have static initializer", dsym.kind, dsym.toPrettyChars);
+                                e = ErrorExp.get();
+                            }
+                        }
+                        ei = new ExpInitializer(dsym._init.loc, e);
+                        dsym._init = ei;
+                    }
+                    else if (sc.inCfile && dsym.type.isTypeSArray() &&
+                             dsym.type.isTypeSArray().isIncomplete())
+                    {
+                        // C11 6.7.9-22 determine the size of the incomplete array,
+                        // or issue an error that the initializer is invalid.
+                        dsym._init = dsym._init.initializerSemantic(sc, dsym.type, INITinterpret);
+                    }
 
-    override void visit(Nspace ns)
-    {
-        if (ns.semanticRun != PASS.initial)
-            return;
-        static if (LOG)
-        {
-            printf("+Nspace::semantic('%s')\n", ns.toChars());
-            scope(exit) printf("-Nspace::semantic('%s')\n", ns.toChars());
-        }
-        if (ns._scope)
-        {
-            sc = ns._scope;
-            ns._scope = null;
-        }
-        if (!sc)
-            return;
+                    if (ei && dsym.isScope())
+                    {
+                        Expression ex = ei.exp.lastComma();
+                        if (ex.op == EXP.blit || ex.op == EXP.construct)
+                            ex = (cast(AssignExp)ex).e2;
+                        if (auto ne = ex.isNewExp())
+                        {
+                            if (ne.placement)
+                            {
+                            }
+                            /* See if initializer is a NewExp that can be allocated on the stack.
+                             */
+                            else if (dsym.type.toBasetype().ty == Tclass)
+                            {
+                                /* Unsafe to allocate on stack if constructor is not `scope` because the `this` can leak.
+                                 * https://issues.dlang.org/show_bug.cgi?id=23145
+                                 */
+                                if (ne.member && !(ne.member.storage_class & STC.scope_))
+                                {
+                                    import dmd.escape : setUnsafeDIP1000;
+                                    const inSafeFunc = sc.func && sc.func.isSafeBypassingInference();   // isSafeBypassingInference may call setUnsafe().
+                                    if (setUnsafeDIP1000(*sc, false, dsym.loc, "`scope` allocation of `%s` with a non-`scope` constructor", dsym))
+                                        errorSupplemental(ne.member.loc, "is the location of the constructor");
+                                }
+                                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 if (auto ale = ex.isArrayLiteralExp())
+                        {
+                            // or an array literal assigned to a `scope` variable
+                            if (sc.useDIP1000 == FeatureState.enabled
+                                && !dsym.type.nextOf().needsDestruction())
+                                ale.onstack = true;
+                        }
+                    }
 
-        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.isTupleExp();
-            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)
+                    Expression exp = ei.exp;
+                    Expression e1 = new VarExp(dsym.loc, dsym);
+
+                    void constructInit(bool isBlit)
                     {
-                        error(ns.loc, "expected string expression for namespace name, got `%s`", exp.toChars());
-                        return;
+                        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--;
                     }
-                    const(char)[] ident = name.toStringz();
-                    if (ident.length == 0 || !Identifier.isValidIdentifier(ident))
+
+                    if (dsymIsRef) // follow logic similar to typesem.argumentMatchParameter() and statementsem.visitForeach()
                     {
-                        error(ns.loc, "expected valid identifier for C++ namespace but got `%.*s`", cast(int)ident.length, ident.ptr);
-                        return;
+                        dsym.storage_class |= STC.nodtor;
+                        exp = exp.expressionSemantic(sc);
+                        Type tp = dsym.type;
+                        Type ta = exp.type;
+                        if (!exp.isLvalue())
+                        {
+                            if (dsym.storage_class & STC.autoref)
+                            {
+                                dsym.storage_class &= ~STC.ref_;
+                                constructInit(isBlit);
+                            }
+                            else
+                            {
+                                .error(dsym.loc, "rvalue `%s` cannot be assigned to `ref %s`", exp.toChars(), dsym.toChars());
+                                exp = ErrorExp.get();
+                            }
+                        }
+                        else if (!ta.constConv(tp))
+                        {
+                            if (dsym.storage_class & STC.autoref)
+                            {
+                                dsym.storage_class &= ~STC.ref_;
+                                constructInit(false);
+                            }
+                            else
+                            {
+                                .error(dsym.loc, "type `%s` cannot be assigned to `ref %s %s`", ta.toChars(), tp.toChars(), dsym.toChars());
+                                exp = ErrorExp.get();
+                            }
+                        }
+                        else if (exp.isBitField())
+                        {
+                            if (dsym.storage_class & STC.autoref)
+                            {
+                                dsym.storage_class &= ~STC.ref_;
+                                constructInit(false);
+                            }
+                            else
+                            {
+                                .error(dsym.loc, "bitfield `%s` cannot be assigned to `ref %s`", exp.toChars(), dsym.toChars());
+                                exp = ErrorExp.get();
+                            }
+                        }
+                        else
+                        {
+                            constructInit(false);
+                        }
                     }
-                    if (i == 0)
+                    else
                     {
-                        ns.ident = Identifier.idPool(ident);
+                        constructInit(isBlit);
+                    }
+
+                    if (exp.op == EXP.error)
+                    {
+                        dsym._init = new ErrorInitializer();
+                        ei = null;
                     }
                     else
+                        ei.exp = exp.optimize(WANTvalue);
+                }
+                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);
+                    import dmd.semantic2 : lowerStaticAAs;
+                    lowerStaticAAs(dsym, sc);
+                    auto init_err = dsym._init.isExpInitializer();
+                    if (init_err && init_err.exp.op == EXP.showCtfeContext)
                     {
-                        // 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;
+                        init_err.exp = ErrorExp.get();
+                        errorSupplemental(dsym.loc, "compile time context created here");
                     }
                 }
             }
-        }
-
-        ns.semanticRun = PASS.semantic;
-        ns.parent = sc.parent;
-        // Link does not matter here, if the UDA is present it will error
-        checkGNUABITag(ns, LINK.cpp);
-
-        if (!ns.members)
-        {
-            ns.semanticRun = PASS.semanticdone;
-            return;
-        }
-        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)
+            else if (parent.isAggregateDeclaration())
             {
-                s.addMember(sc, sc.scopesym);
-                s.setScope(sc);
+                dsym._scope = scx ? scx : sc.copy();
+                dsym._scope.setNoFree();
             }
-            s.importAll(sc);
-        }
-        foreach (s; *ns.members)
-        {
-            static if (LOG)
+            else if (dsym.storage_class & (STC.const_ | STC.immutable_ | STC.manifest) ||
+                     dsym.type.isConst() || dsym.type.isImmutable() ||
+                     sc.inCfile)
             {
-                printf("\tmember '%s', kind = '%s'\n", s.toChars(), s.kind());
-            }
-            s.dsymbolSemantic(sc);
-        }
-        sc.pop();
-        ns.semanticRun = PASS.semanticdone;
-    }
-
-     /// Do the semantic analysis on the external interface to the function.
-    override void visit(FuncDeclaration funcdecl)
-    {
-        funcDeclarationSemantic(sc, funcdecl);
-    }
-
-    override void visit(CtorDeclaration ctd)
-    {
-        //printf("CtorDeclaration::semantic() %p %s\n", ctd, ctd.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;
-        }
+                /* 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)
+                {
+                    const 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 != EXP.scope_ ? true : !ei.exp.isScopeExp().sds.isPackage()))
+                    {
+                        if (ei.exp.type)
+                        {
+                            // If exp is already resolved we are done, our original init exp
+                            // could have a type painting that we need to respect
+                            // e.g.  ['a'] typed as string, or [['z'], ""] as string[]
+                            // See https://issues.dlang.org/show_bug.cgi?id=15711
+                        }
+                        else
+                        {
+                            Expression exp = ei.exp.syntaxCopy();
 
-        sc = sc.push();
+                            bool needctfe = dsym.isDataseg() || (dsym.storage_class & STC.manifest);
+                            if (needctfe)
+                                sc = sc.startCTFE();
+                            sc = sc.push();
+                            sc.varDecl = dsym; // https://issues.dlang.org/show_bug.cgi?id=24051
+                            exp = exp.expressionSemantic(sc);
+                            exp = resolveProperties(sc, exp);
+                            sc = sc.pop();
+                            if (needctfe)
+                                sc = sc.endCTFE();
+                            ei.exp = exp;
+                        }
 
-        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()`");
-        }
+                        Type tb2 = dsym.type.toBasetype();
+                        Type ti = ei.exp.type.toBasetype();
 
-        sc.stc &= ~STC.static_; // not a static constructor
+                        /* 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 (ei.exp.isLvalue())
+                                    .error(dsym.loc, "%s `%s` of type struct `%s` uses `this(this)`, which is not allowed in static initialization", dsym.kind, dsym.toPrettyChars, tb2.toChars());
+                            }
+                        }
+                    }
 
-        funcDeclarationSemantic(sc, ctd);
-        // Check short constructor: this() => expr;
-        if (ctd.fbody)
-        {
-            if (auto s = ctd.fbody.isExpStatement())
-            {
-                if (s.exp)
-                {
-                    auto ce = s.exp.isCallExp();
-                    // check this/super before semantic
-                    if (!ce || (!ce.e1.isThisExp() && !ce.e1.isSuperExp()))
+                    dsym._init = dsym._init.initializerSemantic(sc, dsym.type, INITinterpret);
+                    dsym.inuse--;
+                    if (global.errors > errors)
                     {
-                        s.exp = s.exp.expressionSemantic(sc);
-                        if (s.exp.type.ty != Tvoid)
-                            error(s.loc, "can only return void expression, `this` call or `super` call from constructor");
+                        dsym._init = new ErrorInitializer();
+                        dsym.type = Type.terror;
                     }
                 }
+                else
+                {
+                    dsym._scope = scx ? scx : sc.copy();
+                    dsym._scope.setNoFree();
+                }
             }
+            sc = sc.pop();
         }
 
-        sc.pop();
-
-        if (ctd.errors)
-            return;
-
-        TypeFunction tf = ctd.type.toTypeFunction();
-        immutable dim = tf.parameterList.length;
-        auto sd = ad.isStructDeclaration();
-
-        /* See if it's the default constructor
-         * But, template constructor should not become a default constructor.
+    Ldtor:
+        /* Build code to execute destruction, if necessary
          */
-        if (ad && (!ctd.parent.isTemplateInstance() || ctd.parent.isTemplateMixin()))
+        dsym.edtor = dsym.callScopeDtor(sc);
+        if (dsym.edtor)
         {
-            if (!sd)
-            {
-                if (dim == 0 && tf.parameterList.varargs == VarArg.none)
-                    ad.defaultCtor = ctd;
-                return;
-            }
+            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);
 
-            if (dim == 0 && tf.parameterList.varargs == VarArg.none) // empty default ctor w/o any varargs
-            {
-                if (ctd.fbody || !(ctd.storage_class & STC.disable))
-                {
-                    .error(ctd.loc, "%s `%s` default constructor for structs only allowed " ~
-                        "with `@disable`, no body, and no parameters", ctd.kind, ctd.toPrettyChars);
-                    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.hasArgsWithoutDefault)
-            {
-                if (ctd.storage_class & STC.disable)
-                {
-                    .error(ctd.loc, "%s `%s` is marked `@disable`, so it cannot have default "~
-                              "arguments for all parameters.", ctd.kind, ctd.toPrettyChars);
-                    errorSupplemental(ctd.loc, "Use `@disable this();` if you want to disable default initialization.");
-                }
-                else
-                    .error(ctd.loc, "%s `%s` all parameters have default arguments, "~
-                              "but structs cannot have default constructors.", ctd.kind, ctd.toPrettyChars);
-            }
-            else if ((dim == 1 || (dim > 1 && tf.parameterList[1].defaultArg)))
+            version (none)
             {
-                //printf("tf: %s\n", toChars(tf));
-                auto param = tf.parameterList[0];
-                if (param.type.mutableOf().unSharedOf() == sd.type.mutableOf().unSharedOf())
-                {
-                    //printf("copy constructor %p\n", ctd);
-                    assert(!ctd.isCpCtor && !ctd.isMoveCtor);
-                    if (param.storageClass & STC.ref_)
-                        ctd.isCpCtor = true;            // copy constructor
-                    else
-                        ctd.isMoveCtor = true;          // move constructor
-                    assert(!(ctd.isCpCtor && ctd.isMoveCtor));
-                }
+                // currently disabled because of std.stdio.stdin, stdout and stderr
+                if (dsym.isDataseg() && !(dsym.storage_class & STC.extern_))
+                    .error(dsym.loc, "%s `%s` static storage variables cannot have destructors", dsym.kind, dsym.toPrettyChars);
             }
         }
-        // https://issues.dlang.org/show_bug.cgi?id=22593
-        else if (auto ti = ctd.parent.isTemplateInstance())
+
+        dsym.semanticRun = PASS.semanticdone;
+
+        if (dsym.type.toBasetype().ty == Terror)
+            dsym.errors = true;
+
+        if(sc.scopesym && !sc.scopesym.isAggregateDeclaration())
         {
-            checkHasBothRvalueAndCpCtor(sd, ctd, ti);
+            for (ScopeDsymbol sym = sc.scopesym; sym && dsym.endlinnum == 0;
+                 sym = sym.parent ? sym.parent.isScopeDsymbol() : null)
+                dsym.endlinnum = sym.endlinnum;
         }
     }
 
-    override void visit(PostBlitDeclaration pbd)
+    override void visit(TypeInfoDeclaration dsym)
     {
-        //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;
-        }
+        assert(dsym._linkage == LINK.c);
+    }
 
-        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;
+    override void visit(CAsmDeclaration dsym)
+    {
+        if (dsym.semanticRun >= PASS.semanticdone)
             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(sc, pbd);
-
-        sc.pop();
+        import dmd.iasm : asmSemantic;
+        asmSemantic(dsym, sc);
+        dsym.semanticRun = PASS.semanticdone;
     }
 
-    override void visit(DtorDeclaration dd)
+    override void visit(BitFieldDeclaration dsym)
     {
-        //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 >= PASS.semanticdone)
+        //printf("BitField::semantic('%s')\n", dsym.toChars());
+        if (dsym.semanticRun >= PASS.semanticdone)
             return;
-        if (dd._scope)
+
+        visit(cast(VarDeclaration)dsym);
+        if (dsym.errors)
+            return;
+
+        if (!(sc.previews.bitfields || sc.inCfile))
         {
-            sc = dd._scope;
-            dd._scope = null;
+            .error(dsym.loc, "%s `%s` use `-%spreview=bitfields` for bitfield support", dsym.kind, dsym.toPrettyChars, SwitchPrefix.ptr);
         }
 
-        dd.parent = sc.parent;
-        Dsymbol p = dd.toParent2();
-        AggregateDeclaration ad = p.isAggregateDeclaration();
-        if (!ad)
+        if (!dsym.parent.isStructDeclaration() && !dsym.parent.isClassDeclaration())
         {
-            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;
+            .error(dsym.loc, "%s `%s` - bitfield must be member of struct, union, or class", dsym.kind, dsym.toPrettyChars);
         }
 
-        if (ad.isClassDeclaration() && ad.classKind == ClassKind.d)
+        sc = sc.startCTFE();
+        auto width = dsym.width.expressionSemantic(sc);
+        sc = sc.endCTFE();
+        width = width.ctfeInterpret();
+        if (!dsym.type.isIntegral())
         {
-            // Class destructors are implicitly `scope`
-            dd.storage_class |= STC.scope_;
+            // C11 6.7.2.1-5
+            error(width.loc, "bitfield type `%s` is not an integer type", dsym.type.toChars());
+            dsym.errors = true;
         }
+        if (!width.isIntegerExp())
+        {
+            error(width.loc, "bitfield 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())
+        {
+            error(width.loc, "bitfield `%s` has zero width", dsym.toChars());
+            dsym.errors = true;
+        }
+        const sz = dsym.type.size();
+        if (sz == SIZE_INVALID)
+            dsym.errors = true;
+        const max_width = sz * 8;
+        if (uwidth > max_width)
+        {
+            error(width.loc, "width `%lld` of bitfield `%s` does not fit in type `%s`", cast(long)uwidth, dsym.toChars(), dsym.type.toChars());
+            dsym.errors = true;
+        }
+        dsym.fieldWidth = cast(uint)uwidth;
+    }
 
-        if (dd.ident == Id.dtor && dd.semanticRun < PASS.semantic)
-            ad.userDtors.push(dd);
-        if (!dd.type)
+    override void visit(Import imp)
+    {
+        timeTraceBeginEvent(TimeTraceEventType.sema1Import);
+        scope (exit) timeTraceEndEvent(TimeTraceEventType.sema1Import, imp);
+        static if (LOG)
         {
-            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.length;
-                        cldec.vtbl.push(dd);
-                        if (target.cpp.twoDtorInVtable)
-                            cldec.vtbl.push(dd); // deleting destructor uses a second slot
-                    }
-                }
-            }
+            printf("Import::semantic('%s') %s\n", imp.toPrettyChars(), imp.id.toChars());
+            scope(exit)
+                printf("-Import::semantic('%s'), pkg = %p\n", imp.toChars(), imp.pkg);
         }
+        if (imp.semanticRun > PASS.initial)
+            return;
 
-        sc = sc.push();
-        sc.stc &= ~STC.static_; // not a static destructor
-        if (sc.linkage != LINK.cpp)
-            sc.linkage = LINK.d;
+        if (imp._scope)
+        {
+            sc = imp._scope;
+            imp._scope = null;
+        }
+        if (!sc)
+            return;
 
-        funcDeclarationSemantic(sc, dd);
+        imp.parent = sc.parent;
 
-        sc.pop();
-    }
+        imp.semanticRun = PASS.semantic;
 
-    void visitStaticCDtorDeclaration(FuncDeclaration sd, bool isDestructor)
-    {
-        if (sd.semanticRun >= PASS.semanticdone)
-            return;
-        if (sd._scope)
+        // Load if not already done so
+        if (!imp.mod)
         {
-            sc = sd._scope;
-            sd._scope = null;
+            // https://issues.dlang.org/show_bug.cgi?id=22857
+            // if parser errors occur when loading a module
+            // we should just stop compilation
+            if (imp.load(sc))
+            {
+                for (size_t i = 0; i < imp.aliasdecls.length; i++)
+                    imp.aliasdecls[i].type = Type.terror;
+                return;
+            }
+
+            if (imp.mod)
+            {
+                imp.mod.importAll(null);
+                imp.mod.checkImportDeprecation(imp.loc, sc);
+            }
         }
-        sd.parent = sc.parent;
-        Dsymbol p = sd.parent.pastMixin();
-        const bool isShared = !!(sd.isSharedStaticDtorDeclaration() || sd.isSharedStaticCtorDeclaration());
-        const(char)* what = isDestructor ? "destructor" : "constructor";
-        if (!p.isScopeDsymbol())
+        if (!imp.mod)
         {
-            const(char)* s = isShared ? "shared " : "";
-            error(sd.loc, "`%sstatic` %s can only be member of module/aggregate/template, not %s `%s`", s, what, p.kind(), p.toChars());
-            sd.type = Type.terror;
-            sd.errors = true;
-            return;
+            imp.semanticRun = PASS.semanticdone;
+            addImportDep(global.params.moduleDeps, imp, sc._module);
         }
 
-        if (!sd.type)
-            sd.type = new TypeFunction(ParameterList(), Type.tvoid, LINK.d, sd.storage_class);
+        // Modules need a list of each imported module
 
-        /* If the static [dc]tor 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 (sd.isInstantiated() && sd.semanticRun < PASS.semantic)
+        // 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)
         {
-            /* Add this prefix to the constructor:
-             * ```
-             * static int gate;
-             * if ([--|++]gate != [0|1]) return; // dependant on ctor/dtor
-             * ```
-             * or, for shared constructor:
-             * ```
-             * shared int gate;
-             * enum op  = isDestructor ? "-=" : "+=";
-             * enum cmp = isDestructor ? 0 : 1;
-             * if (core.atomic.atomicOp!op(gate, 1) != cmp) return;
-             * ```
-             */
+            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);
 
-            auto v = new VarDeclaration(Loc.initial, Type.tint32, Id.gate, null);
-            v.storage_class = STC.temp | STC.static_ | (isShared ? STC.shared_ : STC.none);
+        if (sc.explicitVisibility)
+            imp.visibility = sc.visibility;
 
-            Statement s = new ExpStatement(Loc.initial, v);
-            auto sa = new Statements(s);
+        if (!imp.aliasId && !imp.names.length) // neither a selective nor a renamed import
+        {
+            ScopeDsymbol scopesym = sc.getScopesym();
 
-            Expression e;
-            if (isShared)
-            {
-                e = doAtomicOp(isDestructor ? "-=" : "+=", v.ident, IntegerExp.literal!(1));
-                if (e is null)
-                {
-                    .error(sd.loc, "%s `%s` shared static %s within a template require `core.atomic : atomicOp` to be present", sd.kind, sd.toPrettyChars, what);
-                    return;
-                }
-            }
-            else
+            if (!imp.isstatic)
             {
-                IntegerExp one = isDestructor ? IntegerExp.literal!(-1) : IntegerExp.literal!(1);
-                e = new AddAssignExp(
-                    Loc.initial, new IdentifierExp(Loc.initial, v.ident), one);
+                scopesym.importScope(imp.mod, imp.visibility);
             }
-            IntegerExp cmp = isDestructor ? IntegerExp.literal!0 : IntegerExp.literal!1;
-            e = new EqualExp(EXP.notEqual, Loc.initial, e, cmp);
-            s = new IfStatement(Loc.initial, null, e, new ReturnStatement(Loc.initial, null), null, Loc.initial);
 
-            sa.push(s);
-            if (sd.fbody)
-                sa.push(sd.fbody);
 
-            sd.fbody = new CompoundStatement(Loc.initial, sa);
-            if (isDestructor)
-                (cast(StaticDtorDeclaration)sd).vgate = v;
-        }
-        const LINK save = sc.linkage;
-        if (save != LINK.d)
-        {
-            const(char)* s = isShared ? "shared " : "";
-            deprecation(sd.loc, "`%sstatic` %s can only be of D linkage", s, what);
-            // Just correct it
-            sc.linkage = LINK.d;
+            imp.addPackageAccess(scopesym);
         }
-        funcDeclarationSemantic(sc, sd);
-        sc.linkage = save;
 
-        // We're going to need ModuleInfo
-        Module m = sd.getModule();
-        if (!m)
-            m = sc._module;
-        if (m)
+        // if a module has errors it means that parsing has failed.
+        if (!imp.mod.errors)
+            imp.mod.dsymbolSemantic(null);
+
+        if (imp.mod.needmoduleinfo)
         {
-            m.needmoduleinfo = 1;
-            //printf("module2 %s needs moduleinfo\n", m.toChars());
+            //printf("module4 %s because of %s\n", importer.toChars(), imp.mod.toChars());
+            importer.needmoduleinfo = 1;
         }
-    }
-    override void visit(StaticCtorDeclaration scd)
-    {
-        //printf("StaticCtorDeclaration::semantic()\n");
-        visitStaticCDtorDeclaration(scd, false);
-
-        foreachUda(scd, sc, (Expression e) {
-            import dmd.attrib : isEnumAttribute;
-            if (!isEnumAttribute(e, Id.udaStandalone))
-                return 0;
 
-            if (auto sharedCtor = scd.isSharedStaticCtorDeclaration())
+        sc = sc.push(imp.mod);
+        sc.visibility = imp.visibility;
+        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(), aliases[i].toChars(), names[i].toChars(), ad._scope);
+            Dsymbol sym = imp.mod.search(imp.loc, imp.names[i], SearchOpt.ignorePrivateImports);
+            if (sym)
             {
-                auto trust = sharedCtor.type.isTypeFunction().trust;
-                if (trust != TRUST.system && trust != TRUST.trusted)
-                    error(e.loc, "a module constructor using `@%s` must be `@system` or `@trusted`", Id.udaStandalone.toChars());
-                sharedCtor.standalone = true;
+                import dmd.access : symbolIsVisible;
+                if (!symbolIsVisible(sc, sym) && !sym.errors)
+                {
+                    .error(imp.loc, "%s `%s` member `%s` is not visible from module `%s`", imp.mod.kind, imp.mod.toPrettyChars,
+                        imp.names[i].toChars(), sc._module.toChars());
+                    sym.errors = true;
+                }
+                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
-                .error(e.loc, "`@%s` can only be used on shared static constructors", Id.udaStandalone.toChars());
-
-            return 1;
-        });
-    }
+            {
+                Dsymbol s = imp.mod.search_correct(imp.names[i]);
+                // https://issues.dlang.org/show_bug.cgi?id=23908
+                // Don't suggest symbols from the importer's module
+                if (s && s.parent != importer)
+                    .error(imp.loc, "%s `%s` import `%s` not found, did you mean %s `%s`?", imp.mod.kind, imp.mod.toPrettyChars, imp.names[i].toChars(), s.kind(), s.toPrettyChars());
+                else
+                    .error(imp.loc, "%s `%s` import `%s` not found", imp.mod.kind, imp.mod.toPrettyChars, imp.names[i].toChars());
+                ad.type = Type.terror;
+            }
+        }
+        sc = sc.pop();
 
-    override void visit(StaticDtorDeclaration sdd)
-    {
-        visitStaticCDtorDeclaration(sdd, true);
+        imp.semanticRun = PASS.semanticdone;
+        addImportDep(global.params.moduleDeps, imp, sc._module);
     }
 
-    override void visit(InvariantDeclaration invd)
+    void attribSemantic(AttribDeclaration ad)
     {
-        if (invd.semanticRun >= PASS.semanticdone)
+        if (ad.semanticRun != PASS.initial)
             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)
+        ad.semanticRun = PASS.semantic;
+        Dsymbols* d = ad.include(sc);
+        //printf("\tAttribDeclaration::semantic '%s', d = %p\n",toChars(), d);
+        if (!d)
         {
-            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;
+            ad.semanticRun = PASS.semanticdone;
             return;
         }
-        if (invd.ident != Id.classInvariant &&
-             invd.semanticRun < PASS.semantic &&
-             !ad.isUnionDeclaration()           // users are on their own with union fields
-           )
+
+        Scope* sc2 = ad.newScope(sc);
+        bool errors;
+        for (size_t i = 0; i < d.length; i++)
         {
-            invd.fixupInvariantIdent(ad.invs.length);
-            ad.invs.push(invd);
+            Dsymbol s = (*d)[i];
+            s.dsymbolSemantic(sc2);
+            errors |= s.errors;
         }
-        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.contract = Contract.invariant_;
-        sc.linkage = LINK.d;
-
-        funcDeclarationSemantic(sc, invd);
+        if (errors)
+            ad.errors = true;
+        if (sc2 != sc)
+            sc2.pop();
 
-        sc.pop();
+        ad.semanticRun = PASS.semanticdone;
     }
 
-    override void visit(UnitTestDeclaration utd)
-    {
-        if (utd.semanticRun >= PASS.semanticdone)
-            return;
-        if (utd._scope)
-        {
-            sc = utd._scope;
-            utd._scope = null;
-        }
-
-        utd.visibility = sc.visibility;
+    override void visit(AttribDeclaration atd)
+    {
+        attribSemantic(atd);
+    }
 
-        utd.parent = sc.parent;
-        Dsymbol p = utd.parent.pastMixin();
-        if (!p.isScopeDsymbol())
+    override void visit(AnonDeclaration scd)
+    {
+        //printf("\tAnonDeclaration::semantic isunion:%d ptr:%p\n", scd.isunion, scd);
+        assert(sc.parent);
+        auto p = sc.parent.pastMixin();
+        auto ad = p.isAggregateDeclaration();
+        if (!ad)
         {
-            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;
+            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 (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(sc, utd);
-            sc2.pop();
-        }
+        if (!scd.decl)
+            return;
 
-        version (none)
+        sc = sc.push();
+        sc.stc &= ~(STC.auto_ | STC.scope_ | STC.static_ | STC.gshared);
+        sc.inunion = scd.isunion ? scd : null;
+        sc.resetAllFlags();
+        for (size_t i = 0; i < scd.decl.length; i++)
         {
-            // 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)
+            Dsymbol s = (*scd.decl)[i];
+            if (auto var = s.isVarDeclaration)
             {
-                //printf("module3 %s needs moduleinfo\n", m.toChars());
-                m.needmoduleinfo = 1;
+                if (scd.isunion)
+                    var.overlapped = true;
             }
+            s.dsymbolSemantic(sc);
         }
+        sc = sc.pop();
     }
 
-    override void visit(NewDeclaration nd)
+    override void visit(PragmaDeclaration pd)
     {
-        //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);
+        import dmd.pragmasem : pragmaDeclSemantic;
+        pragmaDeclSemantic(pd, sc);
+    }
 
-        funcDeclarationSemantic(sc, nd);
+    override void visit(StaticIfDeclaration sid)
+    {
+        attribSemantic(sid);
     }
 
-    override void visit(StructDeclaration sd)
+    override void visit(StaticForeachDeclaration sfd)
     {
-        enum log = false;
-        if (log) printf("+StructDeclaration::semantic(this=%p, '%s', sizeok = %d)\n", sd, sd.toPrettyChars(), sd.sizeok);
+        attribSemantic(sfd);
+    }
 
-        //static int count; if (++count == 20) assert(0);
+    private Dsymbols* compileIt(MixinDeclaration cd)
+    {
+        //printf("MixinDeclaration::compileIt(loc = %d) %s\n", cd.loc.linnum, cd.exp.toChars());
+        OutBuffer buf;
+        if (expressionsToString(buf, sc, cd.exps, cd.loc, null, true))
+            return null;
 
-        if (sd.semanticRun >= PASS.semanticdone)
-            return;
         const errors = global.errors;
+        const len = buf.length;
+        buf.writeByte(0);
+        const str = buf.extractSlice()[0 .. len];
+        const bool doUnittests = global.params.parsingUnittestsRequired();
+        scope p = new Parser!ASTCodegen(sc._module, str, false, global.errorSink, &global.compileEnv, doUnittests);
+        adjustLocForMixin(str, cd.loc, *p.baseLoc, global.params.mixinOut);
+        p.linnum = p.baseLoc.startLine;
+        p.nextToken();
 
-        //printf("+StructDeclaration::semantic(this=%p, '%s', sizeok = %d)\n", sd, sd.toPrettyChars(), sd.sizeok);
-        Scope* scx = null;
-        if (sd._scope)
+        auto d = p.parseDeclDefs(0);
+        if (global.errors != errors)
+            return null;
+
+        if (p.token.value != TOK.endOfFile)
         {
-            sc = sd._scope;
-            scx = sd._scope; // save so we don't make redundant copies
-            sd._scope = null;
+            .error(cd.loc, "%s `%s` incomplete mixin declaration `%s`", cd.kind, cd.toPrettyChars, str.ptr);
+            return null;
         }
+        return d;
+    }
 
-        if (!sd.parent)
+    /***********************************************************
+     * https://dlang.org/spec/module.html#mixin-declaration
+     */
+    override void visit(MixinDeclaration cd)
+    {
+        //printf("MixinDeclaration::semantic()\n");
+        if (!cd.compiled)
         {
-            assert(sc.parent && sc.func);
-            sd.parent = sc.parent;
+            cd.decl = compileIt(cd);
+            attribAddMember(cd, 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);
+                }
+            }
         }
-        assert(sd.parent && !sd.isAnonymous());
+        attribSemantic(cd);
+    }
 
-        if (sd.errors)
-            sd.type = Type.terror;
-        if (sd.semanticRun == PASS.initial)
-            sd.type = sd.type.addSTC(sc.stc | sd.storage_class);
-        sd.type = sd.type.typeSemantic(sd.loc, sc);
-        auto ts = sd.type.isTypeStruct();
-        if (ts)
+    override void visit(CPPNamespaceDeclaration ns)
+    {
+        Identifier identFromSE (StringExp se)
         {
-            if (ts.sym != sd)
+            const sident = se.toStringz();
+            if (!sident.length || !Identifier.isValidIdentifier(sident))
             {
-                TemplateInstance ti = ts.sym.isInstantiated();
-                if (ti && isError(ti))
-                    ts.sym = sd;
-                /* For C modules, if module A contains `struct S;` and
-                 * module B contains `struct S { members...}` then replace
-                 * the former with the latter
-                 */
-                else if (!ts.sym.members && sd.members)
-                    ts.sym = sd;
+                error(ns.exp.loc, "expected valid identifier for C++ namespace but got `%s`", se.toErrMsg());
+                return null;
             }
+            else
+                return Identifier.idPool(sident);
         }
 
-        // Ungag errors when not speculative
-        Ungag ungag = sd.ungagSpeculative();
+        if (ns.ident !is null)
+            return attribSemantic(ns);
 
-        if (sd.semanticRun == PASS.initial)
+        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())
         {
-            sd.visibility = sc.visibility;
+            expandTuples(te.exps);
+            CPPNamespaceDeclaration current = ns.cppnamespace;
+            for (size_t d = 0; d < te.exps.length; ++d)
+            {
+                auto exp = (*te.exps)[d];
+                auto prev = d ? current : ns.cppnamespace;
+                current = (d + 1) != te.exps.length
+                    ? 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
+                    error(ns.exp.loc, "`%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 if (!ns.exp.type.isTypeError())
+            error(ns.exp.loc, "compile time string constant (or sequence) expected, not `%s`",
+                         ns.exp.toChars());
+        attribSemantic(ns);
+    }
 
-            if (sd.alignment.isUnknown())       // can be set already by `struct __declspec(align(N)) Tag { ... }`
-                sd.alignment = sc.alignment();
+    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.peekSlice(), sc, true);
+        return attribSemantic(uad);
+    }
 
-            sd.storage_class |= sc.stc;
-            if (sd.storage_class & STC.abstract_)
-                .error(sd.loc, "%s `%s` structs, unions cannot be `abstract`", sd.kind, sd.toPrettyChars);
+    override void visit(StaticAssert sa)
+    {
+        if (sa.semanticRun < PASS.semanticdone)
+            sa.semanticRun = PASS.semanticdone;
+        else
+            return;
 
-            sd.userAttribDecl = sc.userAttribDecl;
+        // https://issues.dlang.org/show_bug.cgi?id=24645
+        // This is a short-circuit. Usually, static assert conditions are evaluated
+        // in semantic2, but it's not uncommon to use this pattern:
+        // ---
+        // version(X)
+        // {}
+        // else
+        //   static assert(false, "unsupported platform");
+        // ---
+        // However, without this short-circuit, the static assert error may get drowned
+        // out by subsequent semantic1 (import) errors. Only short-circuit at module scope though,
+        // inside mixin templates you want an instantiation trace (which you don't get here).
+        if (sc.parent && sc.parent.isModule())
+            if (auto i = sa.exp.isIntegerExp())
+                if (i.toInteger() == 0)
+                    staticAssertFail(sa, sc);
+    }
+
+    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;
+    }
 
-            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)
+    override void visit(Module m)
+    {
+        if (m.semanticRun != PASS.initial)
             return;
 
-        sd.semanticRun = PASS.semantic;
-        checkGNUABITag(sd, sc.linkage);
+        timeTraceBeginEvent(TimeTraceEventType.sema1Module);
+        scope (exit) timeTraceEndEvent(TimeTraceEventType.sema1Module, m);
 
-        if (!sd.members) // if opaque declaration
-        {
-            if (log) printf("\topaque declaration %s\n", sd.toChars());
-            sd.semanticRun = PASS.semanticdone;
-            return;
-        }
-        if (!sd.symtab)
+        //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)
         {
-            sd.symtab = new DsymbolTable();
-
-            sd.members.foreachDsymbol( s => s.addMember(sc, sd) );
+            sc = scopeCreateGlobal(m, global.errorSink); // create root scope
         }
 
-        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); if (sd.errors) s.errors = true; } );
-
-        if (sd.errors)
-            sd.type = Type.terror;
-
-        if (!sd.determineFields())
+        //printf("Module = %p, linkage = %d\n", sc.scopesym, sc.linkage);
+        // Pass 1 semantic routines: do public side of the definition
+        m.members.foreachDsymbol( (s)
         {
-            if (sd.type.ty != Terror)
-            {
-                .error(sd.loc, "%s `%s` circular or forward reference", sd.kind, sd.toPrettyChars);
-                sd.errors = true;
-                sd.type = Type.terror;
-            }
+            //printf("\tModule('%s'): '%s'.dsymbolSemantic()\n", toChars(), s.toChars());
+            s.dsymbolSemantic(sc);
+            runDeferredSemantic();
+        });
 
-            sc2.pop();
-            sd.semanticRun = PASS.semanticdone;
-            return;
+        if (m.userAttribDecl)
+        {
+            m.userAttribDecl.dsymbolSemantic(sc);
         }
-        /* 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)
+        if (!m._scope)
         {
-            Type tb = v.type.baseElemOf();
-            if (tb.ty != Tstruct)
-                continue;
-            auto sdec = (cast(TypeStruct)tb).sym;
-            if (sdec.semanticRun >= PASS.semanticdone)
-                continue;
-
-            sc2.pop();
-
-            if (log) printf("\tdeferring %s\n", sd.toChars());
-            return deferDsymbolSemantic(sc, sd, scx);
+            sc = sc.pop();
+            sc.pop(); // 2 pops because scopeCreateGlobal() created 2
         }
+        m.semanticRun = PASS.semanticdone;
+        //printf("-Module::semantic(this = %p, '%s'): parent = %p\n", this, toChars(), parent);
+    }
 
-        /* Look for special member functions.
-         */
-        sd.disableNew = sd.search(Loc.initial, Id.classNew) !is null;
+    override void visit(EnumDeclaration ed)
+    {
+        enumSemantic(sc, ed);
+    }
 
-        // Look for the constructor
-        sd.ctor = sd.searchCtor();
+    override void visit(EnumMember em)
+    {
+        enumMemberSemantic(sc, em);
+    }
 
-        buildDtors(sd, sc2);
+    override void visit(TemplateDeclaration tempdecl)
+    {
+        templateDeclarationSemantic(sc, tempdecl);
+    }
 
-        bool hasCopyCtor;
-        bool hasMoveCtor;
-        bool needCopyCtor;
-        bool needMoveCtor;
-        needCopyOrMoveCtor(sd, hasCopyCtor, hasMoveCtor, needCopyCtor, needMoveCtor);
-        //printf("%s hasCopy %d hasMove %d needCopy %d needMove %d\n", sd.toChars(), hasCopyCtor, hasMoveCtor, needCopyCtor, needMoveCtor);
+    override void visit(TemplateInstance ti)
+    {
+        templateInstanceSemantic(ti, sc, ArgumentList());
+    }
 
-        /* When generating a move ctor, generate a copy ctor too, otherwise
-         *  https://github.com/s-ludwig/taggedalgebraic/issues/75
-         */
-        if (0 && needMoveCtor && !hasCopyCtor)
+    override void visit(TemplateMixin tm)
+    {
+        static if (LOG)
         {
-            needCopyCtor = true;
+            printf("+TemplateMixin.dsymbolSemantic('%s', this=%p)\n", tm.toChars(), tm);
+            fflush(stdout);
         }
-
-        if (needCopyCtor)
+        if (tm.semanticRun != PASS.initial)
         {
-            assert(hasCopyCtor == false);
-            buildCopyOrMoveCtor(sd, sc2, false); // build copy constructor
-            hasCopyCtor = true;
+            // 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.initial.
+            static if (LOG)
+            {
+                printf("\tsemantic done\n");
+            }
+            return;
         }
-        if (needMoveCtor)
+        tm.semanticRun = PASS.semantic;
+        static if (LOG)
         {
-            assert(hasMoveCtor == false);
-            buildCopyOrMoveCtor(sd, sc2, true); // build move constructor
-            hasMoveCtor = true;
+            printf("\tdo semantic\n");
         }
-        sd.hasCopyCtor = hasCopyCtor;
-        sd.hasMoveCtor = hasMoveCtor;
-
-        sd.postblit = buildPostBlit(sd, sc2);
-
-        buildOpAssign(sd, sc2);
-        buildOpEquals(sd, sc2);
 
-        if (!sc2.inCfile &&
-            global.params.useTypeInfo && Type.dtypeinfo)  // these functions are used for TypeInfo
+        Scope* scx = null;
+        if (tm._scope)
         {
-            sd.xeq = buildXopEquals(sd, sc2);
-            sd.xcmp = buildXopCmp(sd, sc2);
-            sd.xhash = buildXtoHash(sd, sc2);
+            sc = tm._scope;
+            scx = tm._scope; // save so we don't make redundant copies
+            tm._scope = null;
         }
 
-        sd.inv = buildInv(sd, sc2);
+        /* Run semantic on each argument, place results in tiargs[],
+         * then find best match template with tiargs
+         */
+        if (!tm.findMixinTempDecl(sc) || !tm.semanticTiargs(sc) || !tm.findBestMatch(sc, ArgumentList()))
+        {
+            if (tm.semanticRun == PASS.initial) // forward reference had occurred
+            {
+                //printf("forward reference - deferring\n");
+                return deferDsymbolSemantic(sc, tm, scx);
+            }
 
-        sd.semanticRun = PASS.semanticdone;
-        if (log) printf("-StructDeclaration::semantic(this=%p, '%s', sizeok = %d)\n", sd, sd.toPrettyChars(), sd.sizeok);
+            tm.inst = tm;
+            tm.errors = true;
+            return; // error recovery
+        }
 
-        sc2.pop();
+        auto tempdecl = tm.tempdecl.isTemplateDeclaration();
+        assert(tempdecl);
 
-        if (sd.ctor)
+        if (!tm.ident)
         {
-            Dsymbol scall = sd.search(Loc.initial, Id.opCall);
-            if (scall)
-            {
-                const xerrors = global.startGagging();
-                sc = sc.push();
-                sc.tinst = null;
-                sc.minst = null;
-                auto fcall = resolveFuncCall(sd.loc, sc, scall, null, null, ArgumentList(), FuncResolveFlag.quiet);
-                sc = sc.pop();
-                global.endGagging(xerrors);
+            /* Assign scope local unique identifier, as same as lambdas.
+             */
+            const(char)[] s = "__mixin";
 
-                if (fcall && fcall.isStatic())
+            if (FuncDeclaration func = sc.parent.isFuncDeclaration())
+            {
+                tm.symtab = func.localsymtab;
+                if (tm.symtab)
                 {
-                    .error(fcall.loc, "%s `%s` `static opCall` is hidden by constructors and can never be called", sd.kind, sd.toPrettyChars);
-                    errorSupplemental(fcall.loc, "Please use a factory method instead, or replace all constructors with `static opCall`.");
+                    // 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);
+            }
         }
 
-        if (ts && ts.sym != sd)
+        tm.inst = tm;
+        tm.parent = sc.parent;
+
+        /* Detect recursive mixin instantiations.
+         */
+        for (Dsymbol s = tm.parent; s; s = s.parent)
         {
-            StructDeclaration sym = ts.sym;
-            if (sd.isCsymbol() && sym.isCsymbol())
-            {
+            //printf("\ts = '%s'\n", s.toChars());
+            TemplateMixin tmix = s.isTemplateMixin();
+            if (!tmix || tempdecl != tmix.tempdecl)
+                continue;
 
-                if (!isCCompatible(sd, sym))
+            /* 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)
                 {
-                    // Already issued an error.
-                    errorSupplemental(sd.loc, "C %ss with the same name from different imports are merged", sd.kind);
+                    Type tmta = isType(tmo);
+                    if (!tmta)
+                        goto Lcontinue;
+                    if (!ta.equals(tmta))
+                        goto Lcontinue;
                 }
-                else {
-                    /* This is two structs imported from different C files.
-                     * Just ignore sd, the second one. The first one will always
-                     * be found when going through the type.
-                     */
+                else if (ea)
+                {
+                    Expression tme = isExpression(tmo);
+                    if (!tme || !dmd.expressionsem.equals(ea, tme))
+                        goto Lcontinue;
                 }
-            }
-            else
-            {
-                version (none)
+                else if (sa)
                 {
-                    printf("this = %p %s\n", sd, sd.toChars());
-                    printf("type = %d sym = %p, %s\n", sd.type.ty, sym, sym.toPrettyChars());
+                    Dsymbol tmsa = isDsymbol(tmo);
+                    if (sa != tmsa)
+                        goto Lcontinue;
                 }
-                // https://issues.dlang.org/show_bug.cgi?id=19024
-                .error(sd.loc, "%s `%s` already exists at %s. Perhaps in another function with the same name?", sd.kind, sd.toPrettyChars, sym.loc.toChars());
+                else
+                    assert(0);
             }
-        }
+            .error(tm.loc, "%s `%s` recursive mixin instantiation", tm.kind, tm.toPrettyChars);
+            return;
 
-        if (global.errors != errors)
-        {
-            // The type is no good.
-            sd.type = Type.terror;
-            sd.errors = true;
-            if (sd.deferred)
-                sd.deferred.errors = true;
+        Lcontinue:
+            continue;
         }
 
-        if (sd.deferred && !global.gag)
-        {
-            sd.deferred.semantic2(sc);
-            sd.deferred.semantic3(sc);
-        }
+        // Copy the syntax trees from the TemplateDeclaration
+        tm.members = Dsymbol.arraySyntaxCopy(tempdecl.members);
+        if (!tm.members)
+            return;
 
-        version (none)
+        tm.symtab = new DsymbolTable();
+
+        sc.getScopesym().importScope(tm, Visibility(Visibility.Kind.public_));
+
+        static if (LOG)
         {
-            // @@@DEPRECATED_2.110@@@ https://dlang.org/deprecate.html#scope%20as%20a%20type%20constraint
-            // Deprecated in 2.100
-            // Make an error in 2.110
-            if (sd.storage_class & STC.scope_)
-                deprecation(sd.loc, "`scope` as a type constraint is deprecated.  Use `scope` at the usage site.");
+            printf("\tcreate scope for template parameters '%s'\n", tm.toChars());
         }
-        //printf("-StructDeclaration::semantic(this=%p, '%s', sizeok = %d)\n", sd, sd.toPrettyChars(), sd.sizeok);
-    }
+        Scope* scy = sc.push(tm);
+        scy.parent = tm;
 
-    //
-    // Checks if two structs are compatible
-    // Implements the rules according to C23 6.2.7
-    //
-    static bool isCCompatible(StructDeclaration a, StructDeclaration b)
-    {
-        // Get the name of a type, while avoiding exposing "__tagXXX" anonymous structs
-        static const(char)* typeName(Type t)
+        /* 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);
+
+        const 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)
         {
-            if (TypeStruct ts = t.isTypeStruct())
-            {
-                if (ts.sym.ident.toString().startsWith("__tag"))
-                    return ts.sym.isUnionDeclaration() ? "(anonymous union)".ptr: "(anonymous struct)".ptr;
-            }
-            return t.toChars();
+            printf("\tdo semantic() on template instance members '%s'\n", tm.toChars());
         }
+        Scope* sc2 = argscope.push(tm);
+        //size_t deferred_dim = Module.deferred.length;
 
-        void incompatError()
+        __gshared int nest;
+        //printf("%d\n", nest);
+        if (++nest > global.recursionLimit)
         {
-            .error(a.loc, "%s `%s` already exists with an incompatible definition.",
-                    a.kind, typeName(a.type));
-            errorSupplemental(b.loc, "previously declared here");
+            global.gag = 0; // ensure error message gets printed
+            .error(tm.loc, "%s `%s` recursive expansion", tm.kind, tm.toPrettyChars);
+            fatal();
         }
 
+        tm.members.foreachDsymbol( s => s.setScope(sc2) );
 
-        // For recursive calls into unnamed structs (so Type.equals() doesn't work).
-        static bool isCCompatibleUnnamedStruct(Type a, Type b)
-        {
-            TypeStruct ats = a.isTypeStruct();
-            if (!ats) return false;
-            TypeStruct bts = b.isTypeStruct();
-            if (!bts) return false;
-            // Hack, anonymous structs within a struct are given
-            // an anonymous id starting with __tag.
-            if (!ats.sym.ident.toString().startsWith("__tag"))
-                return false;
-            if (!bts.sym.ident.toString().startsWith("__tag"))
-                return false;
-            return isCCompatible(ats.sym, bts.sym);
-        }
+        tm.members.foreachDsymbol( s => s.importAll(sc2) );
 
-        if (a.fields.length != b.fields.length)
+        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 addDeferredSemantic() for themselves.
+         * See Struct, Class, Interface, and EnumDeclaration.dsymbolSemantic().
+         */
+        //if (!sc.func && Module.deferred.length > deferred_dim) {}
+
+        AggregateDeclaration ad = tm.isMember();
+        if (sc.func && !ad)
         {
-            incompatError();
-            errorSupplemental(a.loc, "`%s` has %zu field(s) while `%s` has %zu field(s)",
-                    a.toPrettyChars(), a.fields.length, b.toPrettyChars(), b.fields.length);
-            return false;
+            tm.semantic2(sc2);
+            tm.semantic3(sc2);
         }
-        // both are structs or both are unions
-        if ((a.isUnionDeclaration() is null) != (b.isUnionDeclaration() is null))
+
+        // Give additional context info if error occurred during instantiation
+        if (global.errors != errorsave)
         {
-            incompatError();
-            errorSupplemental(a.loc, "`%s` is a %s while `%s` is a %s",
-                    a.toPrettyChars(), a.kind, b.toPrettyChars(), b.kind);
-            return false;
+            .error(tm.loc, "%s `%s` error instantiating", tm.kind, tm.toPrettyChars);
+            tm.errors = true;
         }
-        if (a.alignment != b.alignment)
+
+        sc2.pop();
+        argscope.pop();
+        scy.pop();
+
+        static if (LOG)
         {
-            incompatError();
-            errorSupplemental(a.loc, "`%s` has different alignment or packing", a.toPrettyChars());
-            if (a.alignment.isDefault() && ! b.alignment.isDefault())
-            {
-                errorSupplemental(a.loc, "`%s` alignment: default", a.toPrettyChars());
-                errorSupplemental(b.loc, "`%s` alignment: %u",
-                        b.toPrettyChars(), cast(uint)b.alignment.get());
-            }
-            else if (!a.alignment.isDefault() && b.alignment.isDefault())
-            {
-                errorSupplemental(a.loc, "`%s` alignment: %u",
-                        a.toPrettyChars(), cast(uint)a.alignment.get());
-                errorSupplemental(b.loc, "`%s` alignment: default",
-                        b.toPrettyChars());
-            }
-            else if (a.alignment.get() != b.alignment.get())
-            {
-                errorSupplemental(a.loc, "`%s` alignment: %u",
-                        a.toPrettyChars(), cast(uint)a.alignment.get());
-                errorSupplemental(b.loc, "`%s` alignment: %u",
-                        b.toPrettyChars(), cast(uint)b.alignment.get());
-            }
-            if (a.alignment.isPack() != b.alignment.isPack())
-            {
-                errorSupplemental(a.loc, "`%s` packed: %s",
-                        a.toPrettyChars(), a.alignment.isPack()?"true".ptr:"false".ptr);
-                errorSupplemental(b.loc, "`%s` packed: %s",
-                        b.toPrettyChars(), b.alignment.isPack()?"true".ptr:"false".ptr);
-            }
-            return false;
+            printf("-TemplateMixin.dsymbolSemantic('%s', this=%p)\n", tm.toChars(), tm);
         }
-        foreach (size_t i, VarDeclaration a_field; a.fields[])
-        {
-            VarDeclaration b_field = b.fields[i];
-            //
-            // — there shall be a one-to-one correspondence between
-            //   their members such that each pair of corresponding
-            //   members are declared with compatible types;
-            //
-            if (!a_field.type.equals(b_field.type) && !isCCompatibleUnnamedStruct(a_field.type, b_field.type))
-            {
-                // Already errored, just bail
-                incompatError();
-                if (a_field.type.isTypeError()) return false;
-                if (b_field.type.isTypeError()) return false;
+    }
 
-                errorSupplemental(a_field.loc, "Field %zu differs in type", i);
-                errorSupplemental(a_field.loc, "typeof(%s): %s",
-                        a_field.toChars(), typeName(a_field.type));
-                errorSupplemental(b_field.loc, "typeof(%s): %s",
-                        b_field.toChars(), typeName(b_field.type));
-                return false;
-            }
-            //
-            // — if one member of the pair is declared with an
-            //   alignment specifier, the second is declared with an
-            //   equivalent alignment specifier;
-            //
-            if (a_field.alignment != b_field.alignment)
-            {
-                incompatError();
-                errorSupplemental(a_field.loc, "Field %zu differs in alignment or packing", i);
-                if (a_field.alignment.isDefault() && ! b_field.alignment.isDefault())
-                {
-                    errorSupplemental(a_field.loc, "`%s.%s` alignment: default",
-                            a.toPrettyChars(),a_field.toChars());
-                    errorSupplemental(b_field.loc, "`%s.%s` alignment: %u",
-                            b.toPrettyChars(), b_field.toChars(), cast(uint)b_field.alignment.get());
-                }
-                else if (!a_field.alignment.isDefault() && b_field.alignment.isDefault())
-                {
-                    errorSupplemental(a_field.loc, "`%s.%s` alignment: %u",
-                            a.toPrettyChars(), a_field.toChars(), cast(uint)a_field.alignment.get());
-                    errorSupplemental(b_field.loc, "`%s.%s` alignment: default",
-                            b.toPrettyChars(), b_field.toChars());
-                }
-                else if (a_field.alignment.get() != b_field.alignment.get())
-                {
-                    errorSupplemental(a_field.loc, "`%s.%s` alignment: %u",
-                            a.toPrettyChars(), a_field.toChars(),
-                            cast(uint)a_field.alignment.get());
-                    errorSupplemental(b_field.loc, "`%s.%s` alignment: %u",
-                            b.toPrettyChars(), b_field.toChars(),
-                            cast(uint)b_field.alignment.get());
-                }
-                if (a_field.alignment.isPack() != b_field.alignment.isPack())
-                {
-                    errorSupplemental(a_field.loc, "`%s.%s` packed: %s",
-                            a.toPrettyChars(), a_field.toChars(),
-                            a_field.alignment.isPack()?"true".ptr:"false".ptr);
-                    errorSupplemental(b_field.loc, "`%s.%s` packed: %s",
-                            b.toPrettyChars(), b_field.toChars(),
-                            b_field.alignment.isPack()?"true".ptr:"false".ptr);
-                }
-                return false;
+    override void visit(Nspace ns)
+    {
+        if (ns.semanticRun != PASS.initial)
+            return;
+        static if (LOG)
+        {
+            printf("+Nspace::semantic('%s')\n", ns.toChars());
+            scope(exit) 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.isTupleExp();
+            if (!tup && !name)
+            {
+                error(ns.loc, "expected string expression for namespace name, got `%s`", ns.identExp.toChars());
+                return;
             }
-            //
-            // - and, if one member of the pair is declared with a
-            //   name, the second is declared with the same name.
-            //
-            if (a_field.ident.isAnonymous())
+            ns.identExp = resolved; // we don't need to keep the old AST around
+            if (name)
             {
-                if (!b_field.ident.isAnonymous())
+                const(char)[] ident = name.toStringz();
+                if (ident.length == 0 || !Identifier.isValidIdentifier(ident))
                 {
-                    incompatError();
-                    errorSupplemental(a_field.loc, "Field %zu differs in name", i);
-                    errorSupplemental(a_field.loc, "(anonymous)", a_field.ident.toChars());
-                    errorSupplemental(b_field.loc, "%s", b_field.ident.toChars());
-                    return false;
+                    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 if (b_field.ident.isAnonymous())
-            {
-                incompatError();
-                errorSupplemental(a_field.loc, "Field %zu differs in name", i);
-                errorSupplemental(a_field.loc, "%s", a_field.ident.toChars());
-                errorSupplemental(b_field.loc, "(anonymous)");
-                return false;
-            }
-            else if (a_field.ident != b_field.ident)
+            else
             {
-                incompatError();
-                errorSupplemental(a_field.loc, "Field %zu differs in name", i);
-                errorSupplemental(a_field.loc, "%s", a_field.ident.toChars());
-                errorSupplemental(b_field.loc, "%s", b_field.ident.toChars());
-                return false;
+                // 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;
+                    }
+                }
             }
+        }
 
-            //
-            // For two structures or unions, corresponding bitfields shall have the same widths.
-            //
-            BitFieldDeclaration bfa = a_field.isBitFieldDeclaration();
-            BitFieldDeclaration bfb = b_field.isBitFieldDeclaration();
-            if ((bfa is null) != (bfb is null))
+        ns.semanticRun = PASS.semantic;
+        ns.parent = sc.parent;
+        // Link does not matter here, if the UDA is present it will error
+        checkGNUABITag(ns, LINK.cpp);
+
+        if (!ns.members)
+        {
+            ns.semanticRun = PASS.semanticdone;
+            return;
+        }
+        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)
             {
-                incompatError();
-                errorSupplemental(a_field.loc, "Field %zu differs in being a bitfield", i);
-                if (bfa is null)
-                {
-                    errorSupplemental(a_field.loc, "`%s.%s` is not a bitfield",
-                            a.toPrettyChars(), a_field.toChars());
-                    errorSupplemental(b_field.loc, "`%s.%s` is a bitfield",
-                            b.toPrettyChars(), b_field.toChars());
-                }
-                else if (bfb is null)
-                {
-                    errorSupplemental(a_field.loc, "`%s.%s` *is a bitfield",
-                            a.toPrettyChars(), a_field.toChars());
-                    errorSupplemental(b_field.loc, "`%s.%s` is not a bitfield",
-                            b.toPrettyChars(), b_field.toChars());
-                }
-                return false;
+                s.addMember(sc, sc.scopesym);
+                s.setScope(sc);
             }
-            if (bfa !is null && bfb !is null)
+            s.importAll(sc);
+        }
+        foreach (s; *ns.members)
+        {
+            static if (LOG)
             {
-                if (bfa.fieldWidth != bfb.fieldWidth)
-                {
-                    incompatError();
-                    errorSupplemental(a_field.loc, "Field %zu differs in bitfield width", i);
-                    errorSupplemental(a_field.loc, "`%s.%s`: %u",
-                            a.toPrettyChars(), a_field.toChars(), bfa.fieldWidth);
-                    errorSupplemental(b_field.loc, "`%s.%s`: %u",
-                            b.toPrettyChars(), b_field.toChars(), bfb.fieldWidth);
-                    return false;
-                }
+                printf("\tmember '%s', kind = '%s'\n", s.toChars(), s.kind());
             }
+            s.dsymbolSemantic(sc);
         }
-        return true;
+        sc.pop();
+        ns.semanticRun = PASS.semanticdone;
     }
 
-    void interfaceSemantic(ClassDeclaration cd)
+     /// Do the semantic analysis on the external interface to the function.
+    override void visit(FuncDeclaration funcdecl)
     {
-        cd.vtblInterfaces = new BaseClasses();
-        cd.vtblInterfaces.reserve(cd.interfaces.length);
-        foreach (b; cd.interfaces)
-        {
-            cd.vtblInterfaces.push(b);
-            b.copyBaseInterfaces(cd.vtblInterfaces);
-        }
+        funcDeclarationSemantic(sc, funcdecl);
     }
 
-    override void visit(ClassDeclaration cldec)
+    override void visit(CtorDeclaration ctd)
     {
-        //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)
+        //printf("CtorDeclaration::semantic() %p %s\n", ctd, ctd.toChars());
+        if (ctd.semanticRun >= PASS.semanticdone)
             return;
-        const errors = global.errors;
-
-        //printf("+ClassDeclaration.dsymbolSemantic(%s), type = %p, sizeok = %d, this = %p\n", toChars(), type, sizeok, this);
-
-        Scope* scx = null;
-        if (cldec._scope)
+        if (ctd._scope)
         {
-            sc = cldec._scope;
-            scx = cldec._scope; // save so we don't make redundant copies
-            cldec._scope = null;
+            sc = ctd._scope;
+            ctd._scope = null;
         }
 
-        if (!cldec.parent)
+        ctd.parent = sc.parent;
+        Dsymbol p = ctd.toParentDecl();
+        AggregateDeclaration ad = p.isAggregateDeclaration();
+        if (!ad)
         {
-            assert(sc.parent);
-            cldec.parent = sc.parent;
+            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;
         }
 
-        if (cldec.errors)
-            cldec.type = Type.terror;
-        if (cldec.semanticRun == PASS.initial)
-            cldec.type = cldec.type.addSTC(sc.stc | cldec.storage_class);
-        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();
+        sc = sc.push();
 
-        if (cldec.semanticRun == PASS.initial)
+        if (sc.stc & STC.static_)
         {
-            cldec.visibility = sc.visibility;
+            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()`");
+        }
 
-            cldec.storage_class |= sc.stc;
-            if (cldec.storage_class & STC.auto_)
-                .error(cldec.loc, "%s `%s` storage class `auto` is invalid when declaring a class, did you mean to use `scope`?", cldec.kind, cldec.toPrettyChars);
-            if (cldec.storage_class & STC.scope_)
-                cldec.stack = true;
-            if (cldec.storage_class & STC.abstract_)
-                cldec.isabstract = ThreeState.yes;
+        sc.stc &= ~STC.static_; // not a static constructor
 
-            cldec.userAttribDecl = sc.userAttribDecl;
+        funcDeclarationSemantic(sc, ctd);
 
-            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)
-        {
+        sc.pop();
+
+        if (ctd.errors)
             return;
-        }
-        cldec.semanticRun = PASS.semantic;
-        checkGNUABITag(cldec, sc.linkage);
-        checkMustUseReserved(cldec);
 
-        if (cldec.baseok < Baseok.done)
+        TypeFunction tf = ctd.type.toTypeFunction();
+        immutable dim = tf.parameterList.length;
+        auto sd = ad.isStructDeclaration();
+
+        /* See if it's the default constructor
+         * But, template constructor should not become a default constructor.
+         */
+        if (ad && (!ctd.parent.isTemplateInstance() || ctd.parent.isTemplateMixin()))
         {
-            /* 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 (!sd)
             {
-                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;
-                }
+                if (dim == 0 && tf.parameterList.varargs == VarArg.none)
+                    ad.defaultCtor = ctd;
+                return;
             }
 
-            cldec.baseok = Baseok.start;
-
-            // Expand any tuples in baseclasses[]
-            for (size_t i = 0; i < cldec.baseclasses.length;)
+            if (dim == 0 && tf.parameterList.varargs == VarArg.none) // empty default ctor w/o any varargs
             {
-                auto b = (*cldec.baseclasses)[i];
-                b.type = resolveBase(b.type.typeSemantic(cldec.loc, sc));
-
-                Type tb = b.type.toBasetype();
-                if (auto tup = tb.isTypeTuple())
+                if (ctd.fbody || !(ctd.storage_class & STC.disable))
                 {
-                    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);
-                    }
+                    .error(ctd.loc, "%s `%s` default constructor for structs only allowed " ~
+                        "with `@disable`, no body, and no parameters", ctd.kind, ctd.toPrettyChars);
+                    ctd.storage_class |= STC.disable;
+                    ctd.fbody = null;
                 }
-                else
-                    i++;
+                sd.noDefaultCtor = true;
             }
-
-            if (cldec.baseok >= Baseok.done)
+            else if (dim == 0 && tf.parameterList.varargs != VarArg.none) // allow varargs only ctor
             {
-                //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.length)
+            else if (dim && !tf.parameterList.hasArgsWithoutDefault)
             {
-                BaseClass* b = (*cldec.baseclasses)[0];
-                Type tb = b.type.toBasetype();
-                TypeClass tc = tb.isTypeClass();
-                if (!tc)
+                if (ctd.storage_class & STC.disable)
                 {
-                    if (b.type != Type.terror)
-                        .error(cldec.loc, "%s `%s` base type must be `class` or `interface`, not `%s`", cldec.kind, cldec.toPrettyChars, b.type.toChars());
-                    cldec.baseclasses.remove(0);
-                    goto L7;
+                    .error(ctd.loc, "%s `%s` is marked `@disable`, so it cannot have default "~
+                              "arguments for all parameters.", ctd.kind, ctd.toPrettyChars);
+                    errorSupplemental(ctd.loc, "Use `@disable this();` if you want to disable default initialization.");
                 }
-                if (tc.sym.isDeprecated())
+                else
+                    .error(ctd.loc, "%s `%s` all parameters have default arguments, "~
+                              "but structs cannot have default constructors.", ctd.kind, ctd.toPrettyChars);
+            }
+            else if ((dim == 1 || (dim > 1 && tf.parameterList[1].defaultArg)))
+            {
+                //printf("tf: %s\n", toChars(tf));
+                auto param = tf.parameterList[0];
+                if (param.type.mutableOf().unSharedOf() == sd.type.mutableOf().unSharedOf())
                 {
-                    if (!cldec.isDeprecated())
-                    {
-                        // Deriving from deprecated class makes this one deprecated too
-                        cldec.setDeprecated();
-                        tc.checkDeprecated(cldec.loc, sc);
-                    }
+                    //printf("copy constructor %p\n", ctd);
+                    assert(!ctd.isCpCtor && !ctd.isMoveCtor);
+                    if (param.storageClass & STC.ref_)
+                        ctd.isCpCtor = true;            // copy constructor
+                    else
+                        ctd.isMoveCtor = true;          // move constructor
+                    assert(!(ctd.isCpCtor && ctd.isMoveCtor));
                 }
-                if (tc.sym.isInterfaceDeclaration())
-                    goto L7;
+            }
+        }
+        // https://issues.dlang.org/show_bug.cgi?id=22593
+        else if (auto ti = ctd.parent.isTemplateInstance())
+        {
+            checkHasBothRvalueAndCpCtor(sd, ctd, ti);
+        }
+    }
 
-                for (ClassDeclaration cdb = tc.sym; cdb; cdb = cdb.baseClass)
-                {
-                    if (cdb == cldec)
-                    {
-                        .error(cldec.loc, "%s `%s` circular inheritance", cldec.kind, cldec.toPrettyChars);
-                        cldec.baseclasses.remove(0);
-                        goto L7;
-                    }
-                }
+    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;
+        }
 
-                /* 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;
+        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);
 
-                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:
-            }
+        sc = sc.push();
+        sc.stc &= ~STC.static_; // not static
+        sc.linkage = LINK.d;
 
-            // Treat the remaining entries in baseclasses as interfaces
-            // Check for errors, handle forward references
-            int multiClassError = cldec.baseClass is null ? 0 : 1;
+        funcDeclarationSemantic(sc, pbd);
 
-            BCLoop:
-            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.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;
+        sc.pop();
+    }
 
-                                if (tc.sym.fields.length)
-                                    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;
-                }
+    override 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 >= PASS.semanticdone)
+            return;
+        if (dd._scope)
+        {
+            sc = dd._scope;
+            dd._scope = null;
+        }
 
-                // Check for duplicate interfaces
-                for (size_t j = (cldec.baseClass ? 1 : 0); j < i; j++)
+        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 (ad.isClassDeclaration() && ad.classKind == ClassKind.d)
+        {
+            // Class destructors are implicitly `scope`
+            dd.storage_class |= STC.scope_;
+        }
+
+        if (dd.ident == Id.dtor && dd.semanticRun < PASS.semantic)
+            ad.userDtors.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())
                 {
-                    BaseClass* b2 = (*cldec.baseclasses)[j];
-                    if (b2.sym == tc.sym)
+                    assert (cldec.cppDtorVtblIndex == -1); // double-call check already by dd.type
+                    if (cldec.baseClass && cldec.baseClass.cppDtorVtblIndex != -1)
                     {
-                        .error(cldec.loc, "%s `%s` inherits from duplicate interface `%s`", cldec.kind, cldec.toPrettyChars, b2.sym.toChars());
-                        cldec.baseclasses.remove(i);
-                        continue BCLoop;
+                        // override the base virtual
+                        cldec.cppDtorVtblIndex = cldec.baseClass.cppDtorVtblIndex;
                     }
-                }
-                if (tc.sym.isDeprecated())
-                {
-                    if (!cldec.isDeprecated())
+                    else if (!dd.isFinal())
                     {
-                        // Deriving from deprecated class makes this one deprecated too
-                        cldec.setDeprecated();
-                        tc.checkDeprecated(cldec.loc, sc);
+                        // reserve the dtor slot for the destructor (which we'll create later)
+                        cldec.cppDtorVtblIndex = cast(int)cldec.vtbl.length;
+                        cldec.vtbl.push(dd);
+                        if (target.cpp.twoDtorInVtable)
+                            cldec.vtbl.push(dd); // deleting destructor uses a second slot
                     }
                 }
+            }
+        }
 
-                b.sym = tc.sym;
+        sc = sc.push();
+        sc.stc &= ~STC.static_; // not a static destructor
+        if (sc.linkage != LINK.cpp)
+            sc.linkage = LINK.d;
 
-                if (tc.sym.baseok < Baseok.done)
-                    resolveBase(tc.sym.dsymbolSemantic(null)); // Try to resolve forward reference
-                if (tc.sym.baseok < Baseok.done)
+        funcDeclarationSemantic(sc, dd);
+
+        sc.pop();
+    }
+
+    void visitStaticCDtorDeclaration(FuncDeclaration sd, bool isDestructor)
+    {
+        if (sd.semanticRun >= PASS.semanticdone)
+            return;
+        if (sd._scope)
+        {
+            sc = sd._scope;
+            sd._scope = null;
+        }
+        sd.parent = sc.parent;
+        Dsymbol p = sd.parent.pastMixin();
+        const bool isShared = !!(sd.isSharedStaticDtorDeclaration() || sd.isSharedStaticCtorDeclaration());
+        const(char)* what = isDestructor ? "destructor" : "constructor";
+        if (!p.isScopeDsymbol())
+        {
+            const(char)* s = isShared ? "shared " : "";
+            error(sd.loc, "`%sstatic` %s can only be member of module/aggregate/template, not %s `%s`", s, what, p.kind(), p.toChars());
+            sd.type = Type.terror;
+            sd.errors = true;
+            return;
+        }
+
+        if (!sd.type)
+            sd.type = new TypeFunction(ParameterList(), Type.tvoid, LINK.d, sd.storage_class);
+
+        /* If the static [dc]tor 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 (sd.isInstantiated() && sd.semanticRun < PASS.semantic)
+        {
+            /* Add this prefix to the constructor:
+             * ```
+             * static int gate;
+             * if ([--|++]gate != [0|1]) return; // dependant on ctor/dtor
+             * ```
+             * or, for shared constructor:
+             * ```
+             * shared int gate;
+             * enum op  = isDestructor ? "-=" : "+=";
+             * enum cmp = isDestructor ? 0 : 1;
+             * if (core.atomic.atomicOp!op(gate, 1) != cmp) return;
+             * ```
+             */
+
+            auto v = new VarDeclaration(Loc.initial, Type.tint32, Id.gate, null);
+            v.storage_class = STC.temp | STC.static_ | (isShared ? STC.shared_ : STC.none);
+
+            Statement s = new ExpStatement(Loc.initial, v);
+            auto sa = new Statements(s);
+
+            Expression e;
+            if (isShared)
+            {
+                e = doAtomicOp(isDestructor ? "-=" : "+=", v.ident, IntegerExp.literal!(1));
+                if (e is null)
                 {
-                    //printf("\ttry later, forward reference of base %s\n", tc.sym.toChars());
-                    if (tc.sym._scope)
-                        Module.addDeferredSemantic(tc.sym);
-                    cldec.baseok = Baseok.none;
+                    .error(sd.loc, "%s `%s` shared static %s within a template require `core.atomic : atomicOp` to be present", sd.kind, sd.toPrettyChars, what);
+                    return;
                 }
-                i++;
             }
-            if (cldec.baseok == Baseok.none)
+            else
             {
-                // 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(sc, cldec, scx);
+                IntegerExp one = isDestructor ? IntegerExp.literal!(-1) : IntegerExp.literal!(1);
+                e = new AddAssignExp(
+                    Loc.initial, new IdentifierExp(Loc.initial, v.ident), one);
             }
-            cldec.baseok = Baseok.done;
+            IntegerExp cmp = isDestructor ? IntegerExp.literal!0 : IntegerExp.literal!1;
+            e = new EqualExp(EXP.notEqual, Loc.initial, e, cmp);
+            s = new IfStatement(Loc.initial, null, e, new ReturnStatement(Loc.initial, null), null, Loc.initial);
 
-            if (cldec.classKind == ClassKind.objc || (cldec.baseClass && cldec.baseClass.classKind == ClassKind.objc))
-                cldec.classKind = ClassKind.objc; // Objective-C classes do not inherit from Object
+            sa.push(s);
+            if (sd.fbody)
+                sa.push(sd.fbody);
 
-            // 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)
+            sd.fbody = new CompoundStatement(Loc.initial, sa);
+            if (isDestructor)
+                (cast(StaticDtorDeclaration)sd).vgate = v;
+        }
+        const LINK save = sc.linkage;
+        if (save != LINK.d)
+        {
+            const(char)* s = isShared ? "shared " : "";
+            deprecation(sd.loc, "`%sstatic` %s can only be of D linkage", s, what);
+            // Just correct it
+            sc.linkage = LINK.d;
+        }
+        funcDeclarationSemantic(sc, sd);
+        sc.linkage = save;
+
+        // We're going to need ModuleInfo
+        Module m = sd.getModule();
+        if (!m)
+            m = sc._module;
+        if (m)
+        {
+            m.needmoduleinfo = 1;
+            //printf("module2 %s needs moduleinfo\n", m.toChars());
+        }
+    }
+    override void visit(StaticCtorDeclaration scd)
+    {
+        //printf("StaticCtorDeclaration::semantic()\n");
+        visitStaticCDtorDeclaration(scd, false);
+
+        foreachUda(scd, sc, (Expression e) {
+            import dmd.attrib : isEnumAttribute;
+            if (!isEnumAttribute(e, Id.udaStandalone))
+                return 0;
+
+            if (auto sharedCtor = scd.isSharedStaticCtorDeclaration())
             {
-                void badObjectDotD()
-                {
-                    ObjectNotFound(cldec.loc, cldec.ident);
-                }
+                auto trust = sharedCtor.type.isTypeFunction().trust;
+                if (trust != TRUST.system && trust != TRUST.trusted)
+                    error(e.loc, "a module constructor using `@%s` must be `@system` or `@trusted`", Id.udaStandalone.toChars());
+                sharedCtor.standalone = true;
+            }
+            else
+                .error(e.loc, "`@%s` can only be used on shared static constructors", Id.udaStandalone.toChars());
 
-                if (!cldec.object || cldec.object.errors)
-                    badObjectDotD();
+            return 1;
+        });
+    }
 
-                Type t = cldec.object.type;
-                t = t.typeSemantic(cldec.loc, sc).toBasetype();
-                if (t.ty == Terror)
-                    badObjectDotD();
-                TypeClass tc = t.isTypeClass();
-                assert(tc);
+    override void visit(StaticDtorDeclaration sdd)
+    {
+        visitStaticCDtorDeclaration(sdd, true);
+    }
 
-                auto b = new BaseClass(tc);
-                cldec.baseclasses.shift(b);
+    override void visit(InvariantDeclaration invd)
+    {
+        if (invd.semanticRun >= PASS.semanticdone)
+            return;
+        if (invd._scope)
+        {
+            sc = invd._scope;
+            invd._scope = null;
+        }
 
-                cldec.baseClass = tc.sym;
-                assert(!cldec.baseClass.isInterfaceDeclaration());
-                b.sym = cldec.baseClass;
-            }
-            if (cldec.baseClass)
-            {
-                if (cldec.baseClass.storage_class & STC.final_)
-                    .error(cldec.loc, "%s `%s` cannot inherit from class `%s` because it is `final`", cldec.kind, cldec.toPrettyChars, cldec.baseClass.toChars());
+        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
+           )
+        {
+            invd.fixupInvariantIdent(ad.invs.length);
+            ad.invs.push(invd);
+        }
+        if (!invd.type)
+            invd.type = new TypeFunction(ParameterList(), Type.tvoid, LINK.d, invd.storage_class);
 
-                // Inherit properties from base class
-                if (cldec.baseClass.isCOMclass())
-                    cldec.com = true;
-                if (cldec.baseClass.isCPPclass())
-                    cldec.classKind = ClassKind.cpp;
-                if (cldec.classKind != cldec.baseClass.classKind)
-                    .error(cldec.loc, "%s `%s` with %s linkage cannot inherit from class `%s` with %s linkage", cldec.kind, cldec.toPrettyChars,
-                        ClassKindToChars(cldec.classKind), cldec.baseClass.toChars(), ClassKindToChars(cldec.baseClass.classKind));
+        sc = sc.push();
+        sc.stc &= ~STC.static_; // not a static invariant
+        sc.stc |= STC.const_; // invariant() is always const
+        sc.contract = Contract.invariant_;
+        sc.linkage = LINK.d;
+
+        funcDeclarationSemantic(sc, 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 (cldec.baseClass.stack)
-                    cldec.stack = true;
-                cldec.enclosing = cldec.baseClass.enclosing;
-                cldec.storage_class |= cldec.baseClass.storage_class & STC.TYPECTOR;
-            }
+        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(sc, utd);
+            sc2.pop();
+        }
 
-            cldec.interfaces = cldec.baseclasses.tdata()[(cldec.baseClass ? 1 : 0) .. cldec.baseclasses.length];
-            foreach (b; cldec.interfaces)
+        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)
             {
-                // 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());
-                }
+                //printf("module3 %s needs moduleinfo\n", m.toChars());
+                m.needmoduleinfo = 1;
             }
-            interfaceSemantic(cldec);
         }
-    Lancestorsdone:
-        //printf("\tClassDeclaration.dsymbolSemantic(%s) baseok = %d\n", toChars(), baseok);
+    }
 
-        if (!cldec.members) // if opaque declaration
-        {
-            cldec.semanticRun = PASS.semanticdone;
+    override void visit(NewDeclaration nd)
+    {
+        //printf("NewDeclaration::semantic()\n");
+        if (nd.semanticRun >= PASS.semanticdone)
             return;
-        }
-        if (!cldec.symtab)
-        {
-            cldec.symtab = new DsymbolTable();
+        if (!nd.type)
+            nd.type = new TypeFunction(ParameterList(), Type.tvoid.pointerTo(), LINK.d, nd.storage_class);
 
-            /* 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) );
+        funcDeclarationSemantic(sc, nd);
+    }
 
-            auto sc2 = cldec.newScope(sc);
+    override void visit(StructDeclaration sd)
+    {
+        enum log = false;
+        if (log) printf("+StructDeclaration::semantic(this=%p, '%s', sizeok = %d)\n", sd, sd.toPrettyChars(), sd.sizeok);
 
-            /* 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) );
+        //static int count; if (++count == 20) assert(0);
 
-            sc2.pop();
+        if (sd.semanticRun >= PASS.semanticdone)
+            return;
+        const errors = global.errors;
+
+        //printf("+StructDeclaration::semantic(this=%p, '%s', sizeok = %d)\n", sd, 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;
         }
 
-        for (size_t i = 0; i < cldec.baseclasses.length; i++)
+        if (!sd.parent)
         {
-            BaseClass* b = (*cldec.baseclasses)[i];
-            Type tb = b.type.toBasetype();
-            TypeClass tc = tb.isTypeClass();
-            if (tc.sym.semanticRun < PASS.semanticdone)
+            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.initial)
+            sd.type = sd.type.addSTC(sc.stc | sd.storage_class);
+        sd.type = sd.type.typeSemantic(sd.loc, sc);
+        auto ts = sd.type.isTypeStruct();
+        if (ts)
+        {
+            if (ts.sym != sd)
             {
-                // 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(sc, cldec, scx);
+                TemplateInstance ti = ts.sym.isInstantiated();
+                if (ti && isError(ti))
+                    ts.sym = sd;
+                /* For C modules, if module A contains `struct S;` and
+                 * module B contains `struct S { members...}` then replace
+                 * the former with the latter
+                 */
+                else if (!ts.sym.members && sd.members)
+                    ts.sym = sd;
             }
         }
 
-        if (cldec.baseok == Baseok.done)
+        // Ungag errors when not speculative
+        Ungag ungag = sd.ungagSpeculative();
+
+        if (sd.semanticRun == PASS.initial)
         {
-            cldec.baseok = Baseok.semanticdone;
-            objc.setMetaclass(cldec, sc);
+            sd.visibility = sc.visibility;
 
-            // initialize vtbl
-            if (cldec.baseClass)
-            {
-                if (cldec.classKind == ClassKind.cpp && cldec.baseClass.vtbl.length == 0)
-                {
-                    .error(cldec.loc, "%s `%s` C++ base class `%s` needs at least one virtual function", cldec.kind, cldec.toPrettyChars, cldec.baseClass.toChars());
-                }
+            if (sd.alignment.isUnknown())       // can be set already by `struct __declspec(align(N)) Tag { ... }`
+                sd.alignment = sc.alignment();
 
-                // Copy vtbl[] from base class
-                assert(cldec.vtbl.length == 0);
-                cldec.vtbl.setDim(cldec.baseClass.vtbl.length);
-                memcpy(cldec.vtbl.tdata(), cldec.baseClass.vtbl.tdata(), (void*).sizeof * cldec.vtbl.length);
+            sd.storage_class |= sc.stc;
+            if (sd.storage_class & STC.abstract_)
+                .error(sd.loc, "%s `%s` structs, unions cannot be `abstract`", sd.kind, sd.toPrettyChars);
 
-                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
-            }
+            sd.userAttribDecl = sc.userAttribDecl;
 
-            /* 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_)
-                    .error(cldec.loc, "%s `%s` static class cannot inherit from nested class `%s`", cldec.kind, cldec.toPrettyChars, 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())
-                    {
-                        .error(cldec.loc, "%s `%s` is nested within `%s`, but super class `%s` is nested within `%s`", cldec.kind, cldec.toPrettyChars,
-                            cldec.toParentLocal().toChars(),
-                            cldec.baseClass.toChars(),
-                            cldec.baseClass.toParentLocal().toChars());
-                    }
-                    else
-                    {
-                        .error(cldec.loc, "%s `%s` is not nested, but super class `%s` is nested within `%s`", cldec.kind, cldec.toPrettyChars,
-                            cldec.baseClass.toChars(),
-                            cldec.baseClass.toParentLocal().toChars());
-                    }
-                }
-                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())
-                        {
-                            .error(cldec.loc, "%s `%s` needs the frame pointer of `%s`, but super class `%s` needs the frame pointer of `%s`", cldec.kind, cldec.toPrettyChars,
-                                cldec.toParent2().toChars(),
-                                cldec.baseClass.toChars(),
-                                cldec.baseClass.toParent2().toChars());
-                        }
-                        else
-                        {
-                            .error(cldec.loc, "%s `%s` doesn't need a frame pointer, but super class `%s` needs the frame pointer of `%s`", cldec.kind, cldec.toPrettyChars,
-                                cldec.baseClass.toChars(),
-                                cldec.baseClass.toParent2().toChars());
-                        }
-                    }
-                }
-                else
-                    cldec.makeNested2();
-            }
-            else
-                cldec.makeNested();
+            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;
 
-        auto sc2 = cldec.newScope(sc);
+        sd.semanticRun = PASS.semantic;
+        checkGNUABITag(sd, sc.linkage);
 
-        cldec.members.foreachDsymbol( s => s.importAll(sc2) );
+        if (!sd.members) // if opaque declaration
+        {
+            if (log) printf("\topaque declaration %s\n", sd.toChars());
+            sd.semanticRun = PASS.semanticdone;
+            return;
+        }
+        if (!sd.symtab)
+        {
+            sd.symtab = new DsymbolTable();
 
-        // Note that members.length can grow due to tuple expansion during semantic()
-        cldec.members.foreachDsymbol( s => s.dsymbolSemantic(sc2) );
+            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); if (sd.errors) s.errors = true; } );
+
+        if (sd.errors)
+            sd.type = Type.terror;
+
+        if (!sd.determineFields())
+        {
+            if (sd.type.ty != Terror)
+            {
+                .error(sd.loc, "%s `%s` circular or forward reference", sd.kind, sd.toPrettyChars);
+                sd.errors = true;
+                sd.type = Type.terror;
+            }
 
-        if (!cldec.determineFields())
-        {
-            assert(cldec.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.
+         * 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; cldec.fields)
+        foreach (v; sd.fields)
         {
             Type tb = v.type.baseElemOf();
             if (tb.ty != Tstruct)
                 continue;
-            auto sd = (cast(TypeStruct)tb).sym;
-            if (sd.semanticRun >= PASS.semanticdone)
+            auto sdec = (cast(TypeStruct)tb).sym;
+            if (sdec.semanticRun >= PASS.semanticdone)
                 continue;
 
             sc2.pop();
 
-            //printf("\tdeferring %s\n", toChars());
-            return deferDsymbolSemantic(sc, cldec, scx);
+            if (log) printf("\tdeferring %s\n", sd.toChars());
+            return deferDsymbolSemantic(sc, sd, 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;
+        sd.disableNew = sd.search(Loc.initial, Id.classNew) !is null;
 
         // Look for the constructor
-        cldec.ctor = cldec.searchCtor();
+        sd.ctor = sd.searchCtor();
 
-        if (!cldec.ctor && cldec.noDefaultCtor)
+        buildDtors(sd, sc2);
+
+        bool hasCopyCtor;
+        bool hasMoveCtor;
+        bool needCopyCtor;
+        bool needMoveCtor;
+        needCopyOrMoveCtor(sd, hasCopyCtor, hasMoveCtor, needCopyCtor, needMoveCtor);
+        //printf("%s hasCopy %d hasMove %d needCopy %d needMove %d\n", sd.toChars(), hasCopyCtor, hasMoveCtor, needCopyCtor, needMoveCtor);
+
+        /* When generating a move ctor, generate a copy ctor too, otherwise
+         *  https://github.com/s-ludwig/taggedalgebraic/issues/75
+         */
+        if (0 && needMoveCtor && !hasCopyCtor)
         {
-            // A class object is always created by constructor, so this check is legitimate.
-            foreach (v; cldec.fields)
+            needCopyCtor = true;
+        }
+
+        if (needCopyCtor)
+        {
+            assert(hasCopyCtor == false);
+            buildCopyOrMoveCtor(sd, sc2, false); // build copy constructor
+            hasCopyCtor = true;
+        }
+        if (needMoveCtor)
+        {
+            assert(hasMoveCtor == false);
+            buildCopyOrMoveCtor(sd, sc2, true); // build move constructor
+            hasMoveCtor = true;
+        }
+        sd.hasCopyCtor = hasCopyCtor;
+        sd.hasMoveCtor = hasMoveCtor;
+
+        sd.postblit = buildPostBlit(sd, sc2);
+
+        buildOpAssign(sd, sc2);
+        buildOpEquals(sd, sc2);
+
+        if (!sc2.inCfile &&
+            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);
+
+        sd.rtInfoScope = sc2;
+        sc2.setNoFree();
+
+        sd.semanticRun = PASS.semanticdone;
+        if (log) printf("-StructDeclaration::semantic(this=%p, '%s', sizeok = %d)\n", sd, sd.toPrettyChars(), sd.sizeok);
+
+        sc2.pop();
+
+        if (sd.ctor)
+        {
+            Dsymbol scall = sd.search(Loc.initial, Id.opCall);
+            if (scall)
             {
-                if (v.storage_class & STC.nodefaultctor)
-                    error(v.loc, "field `%s` must be initialized in constructor", v.toChars());
+                const xerrors = global.startGagging();
+                sc = sc.push();
+                sc.tinst = null;
+                sc.minst = null;
+                auto fcall = resolveFuncCall(sd.loc, sc, scall, null, null, ArgumentList(), FuncResolveFlag.quiet);
+                sc = sc.pop();
+                global.endGagging(xerrors);
+
+                if (fcall && fcall.isStatic())
+                {
+                    .error(fcall.loc, "%s `%s` `static opCall` is hidden by constructors and can never be called", sd.kind, sd.toPrettyChars);
+                    errorSupplemental(fcall.loc, "Please use a factory method instead, or replace all constructors with `static opCall`.");
+                }
             }
         }
 
-        // 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)
+        if (ts && ts.sym != sd)
         {
-            auto fd = resolveFuncCall(cldec.loc, sc2, cldec.baseClass.ctor, null, cldec.type, ArgumentList(), FuncResolveFlag.quiet);
-            if (!fd) // try shared base ctor instead
-                fd = resolveFuncCall(cldec.loc, sc2, cldec.baseClass.ctor, null, cldec.type.sharedOf, ArgumentList(), FuncResolveFlag.quiet);
-            if (fd && !fd.errors)
+            StructDeclaration sym = ts.sym;
+            if (sd.isCsymbol() && sym.isCsymbol())
             {
-                //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, STC.none, tf);
-                ctor.storage_class |= STC.inference | (fd.storage_class & STC.scope_);
-                ctor.isGenerated = true;
-                ctor.fbody = new CompoundStatement(Loc.initial, new Statements());
+                if (!isCCompatible(sd, sym))
+                {
+                    // Already issued an error.
+                    errorSupplemental(sd.loc, "C %ss with the same name from different imports are merged", sd.kind);
+                }
+                else {
+                    /* This is two structs imported from different C files.
+                     * Just ignore sd, the second one. The first one will always
+                     * be found when going through the type.
+                     */
+                }
+            }
+            else
+            {
+                version (none)
+                {
+                    printf("this = %p %s\n", sd, sd.toChars());
+                    printf("type = %d sym = %p, %s\n", sd.type.ty, sym, sym.toPrettyChars());
+                }
+                // https://issues.dlang.org/show_bug.cgi?id=19024
+                .error(sd.loc, "%s `%s` already exists at %s. Perhaps in another function with the same name?", sd.kind, sd.toPrettyChars, sym.loc.toChars());
+            }
+        }
 
-                cldec.members.push(ctor);
-                ctor.addMember(sc, cldec);
-                ctor.dsymbolSemantic(sc2);
+        if (global.errors != errors)
+        {
+            // The type is no good.
+            sd.type = Type.terror;
+            sd.errors = true;
+            if (sd.deferred)
+                sd.deferred.errors = true;
+        }
 
-                cldec.ctor = ctor;
-                cldec.defaultCtor = ctor;
+        if (sd.deferred && !global.gag)
+        {
+            sd.deferred.semantic2(sc);
+            sd.deferred.semantic3(sc);
+        }
+
+        version (none)
+        {
+            // @@@DEPRECATED_2.110@@@ https://dlang.org/deprecate.html#scope%20as%20a%20type%20constraint
+            // Deprecated in 2.100
+            // Make an error in 2.110
+            if (sd.storage_class & STC.scope_)
+                deprecation(sd.loc, "`scope` as a type constraint is deprecated.  Use `scope` at the usage site.");
+        }
+        //printf("-StructDeclaration::semantic(this=%p, '%s', sizeok = %d)\n", sd, sd.toPrettyChars(), sd.sizeok);
+    }
+
+    //
+    // Checks if two structs are compatible
+    // Implements the rules according to C23 6.2.7
+    //
+    static bool isCCompatible(StructDeclaration a, StructDeclaration b)
+    {
+        // Get the name of a type, while avoiding exposing "__tagXXX" anonymous structs
+        static const(char)* typeName(Type t)
+        {
+            if (TypeStruct ts = t.isTypeStruct())
+            {
+                if (ts.sym.ident.toString().startsWith("__tag"))
+                    return ts.sym.isUnionDeclaration() ? "(anonymous union)".ptr: "(anonymous struct)".ptr;
             }
-            else
+            return t.toChars();
+        }
+
+        void incompatError()
+        {
+            .error(a.loc, "%s `%s` already exists with an incompatible definition.",
+                    a.kind, typeName(a.type));
+            errorSupplemental(b.loc, "previously declared here");
+        }
+
+
+        // For recursive calls into unnamed structs (so Type.equals() doesn't work).
+        static bool isCCompatibleUnnamedStruct(Type a, Type b)
+        {
+            TypeStruct ats = a.isTypeStruct();
+            if (!ats) return false;
+            TypeStruct bts = b.isTypeStruct();
+            if (!bts) return false;
+            // Hack, anonymous structs within a struct are given
+            // an anonymous id starting with __tag.
+            if (!ats.sym.ident.toString().startsWith("__tag"))
+                return false;
+            if (!bts.sym.ident.toString().startsWith("__tag"))
+                return false;
+            return isCCompatible(ats.sym, bts.sym);
+        }
+
+        if (a.fields.length != b.fields.length)
+        {
+            incompatError();
+            errorSupplemental(a.loc, "`%s` has %zu field(s) while `%s` has %zu field(s)",
+                    a.toPrettyChars(), a.fields.length, b.toPrettyChars(), b.fields.length);
+            return false;
+        }
+        // both are structs or both are unions
+        if ((a.isUnionDeclaration() is null) != (b.isUnionDeclaration() is null))
+        {
+            incompatError();
+            errorSupplemental(a.loc, "`%s` is a %s while `%s` is a %s",
+                    a.toPrettyChars(), a.kind, b.toPrettyChars(), b.kind);
+            return false;
+        }
+        if (a.alignment != b.alignment)
+        {
+            incompatError();
+            errorSupplemental(a.loc, "`%s` has different alignment or packing", a.toPrettyChars());
+            if (a.alignment.isDefault() && ! b.alignment.isDefault())
             {
-                .error(cldec.loc, "%s `%s` cannot implicitly generate a default constructor when base class `%s` is missing a default constructor", cldec.kind, cldec.toPrettyChars,
-                    cldec.baseClass.toPrettyChars());
+                errorSupplemental(a.loc, "`%s` alignment: default", a.toPrettyChars());
+                errorSupplemental(b.loc, "`%s` alignment: %u",
+                        b.toPrettyChars(), cast(uint)b.alignment.get());
+            }
+            else if (!a.alignment.isDefault() && b.alignment.isDefault())
+            {
+                errorSupplemental(a.loc, "`%s` alignment: %u",
+                        a.toPrettyChars(), cast(uint)a.alignment.get());
+                errorSupplemental(b.loc, "`%s` alignment: default",
+                        b.toPrettyChars());
+            }
+            else if (a.alignment.get() != b.alignment.get())
+            {
+                errorSupplemental(a.loc, "`%s` alignment: %u",
+                        a.toPrettyChars(), cast(uint)a.alignment.get());
+                errorSupplemental(b.loc, "`%s` alignment: %u",
+                        b.toPrettyChars(), cast(uint)b.alignment.get());
+            }
+            if (a.alignment.isPack() != b.alignment.isPack())
+            {
+                errorSupplemental(a.loc, "`%s` packed: %s",
+                        a.toPrettyChars(), a.alignment.isPack()?"true".ptr:"false".ptr);
+                errorSupplemental(b.loc, "`%s` packed: %s",
+                        b.toPrettyChars(), b.alignment.isPack()?"true".ptr:"false".ptr);
             }
+            return false;
         }
-
-        buildDtors(cldec, sc2);
-
-        if (cldec.classKind == ClassKind.cpp && cldec.cppDtorVtblIndex != -1)
+        foreach (size_t i, VarDeclaration a_field; a.fields[])
         {
-            // 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;
+            VarDeclaration b_field = b.fields[i];
+            //
+            // — there shall be a one-to-one correspondence between
+            //   their members such that each pair of corresponding
+            //   members are declared with compatible types;
+            //
+            if (!a_field.type.equals(b_field.type) && !isCCompatibleUnnamedStruct(a_field.type, b_field.type))
+            {
+                // Already errored, just bail
+                incompatError();
+                if (a_field.type.isTypeError()) return false;
+                if (b_field.type.isTypeError()) return false;
 
-            if (target.cpp.twoDtorInVtable)
+                errorSupplemental(a_field.loc, "Field %zu differs in type", i);
+                errorSupplemental(a_field.loc, "typeof(%s): %s",
+                        a_field.toChars(), typeName(a_field.type));
+                errorSupplemental(b_field.loc, "typeof(%s): %s",
+                        b_field.toChars(), typeName(b_field.type));
+                return false;
+            }
+            //
+            // — if one member of the pair is declared with an
+            //   alignment specifier, the second is declared with an
+            //   equivalent alignment specifier;
+            //
+            if (a_field.alignment != b_field.alignment)
             {
-                // 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;
+                incompatError();
+                errorSupplemental(a_field.loc, "Field %zu differs in alignment or packing", i);
+                if (a_field.alignment.isDefault() && ! b_field.alignment.isDefault())
+                {
+                    errorSupplemental(a_field.loc, "`%s.%s` alignment: default",
+                            a.toPrettyChars(),a_field.toChars());
+                    errorSupplemental(b_field.loc, "`%s.%s` alignment: %u",
+                            b.toPrettyChars(), b_field.toChars(), cast(uint)b_field.alignment.get());
+                }
+                else if (!a_field.alignment.isDefault() && b_field.alignment.isDefault())
+                {
+                    errorSupplemental(a_field.loc, "`%s.%s` alignment: %u",
+                            a.toPrettyChars(), a_field.toChars(), cast(uint)a_field.alignment.get());
+                    errorSupplemental(b_field.loc, "`%s.%s` alignment: default",
+                            b.toPrettyChars(), b_field.toChars());
+                }
+                else if (a_field.alignment.get() != b_field.alignment.get())
+                {
+                    errorSupplemental(a_field.loc, "`%s.%s` alignment: %u",
+                            a.toPrettyChars(), a_field.toChars(),
+                            cast(uint)a_field.alignment.get());
+                    errorSupplemental(b_field.loc, "`%s.%s` alignment: %u",
+                            b.toPrettyChars(), b_field.toChars(),
+                            cast(uint)b_field.alignment.get());
+                }
+                if (a_field.alignment.isPack() != b_field.alignment.isPack())
+                {
+                    errorSupplemental(a_field.loc, "`%s.%s` packed: %s",
+                            a.toPrettyChars(), a_field.toChars(),
+                            a_field.alignment.isPack()?"true".ptr:"false".ptr);
+                    errorSupplemental(b_field.loc, "`%s.%s` packed: %s",
+                            b.toPrettyChars(), b_field.toChars(),
+                            b_field.alignment.isPack()?"true".ptr:"false".ptr);
+                }
+                return false;
             }
-        }
-
-        if (auto f = hasIdentityOpAssign(cldec, sc2))
-        {
-            if (!(f.storage_class & STC.disable))
-                .error(f.loc, "%s `%s` identity assignment operator overload is illegal", cldec.kind, cldec.toPrettyChars);
-        }
-
-        cldec.inv = buildInv(cldec, sc2);
-
-        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)
+            //
+            // - and, if one member of the pair is declared with a
+            //   name, the second is declared with the same name.
+            //
+            if (a_field.ident.isAnonymous())
             {
-                .error(cldec.loc, "%s `%s` cannot infer `abstract` attribute due to circular dependencies", cldec.kind, cldec.toPrettyChars);
+                if (!b_field.ident.isAnonymous())
+                {
+                    incompatError();
+                    errorSupplemental(a_field.loc, "Field %zu differs in name", i);
+                    errorSupplemental(a_field.loc, "(anonymous)", a_field.ident.toChars());
+                    errorSupplemental(b_field.loc, "%s", b_field.ident.toChars());
+                    return false;
+                }
             }
-        }
-
-        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)
+            else if (b_field.ident.isAnonymous())
             {
-                printf("this = %p %s\n", cldec, cldec.toPrettyChars());
-                printf("type = %d sym = %p, %s\n", cldec.type.ty, cd, cd.toPrettyChars());
+                incompatError();
+                errorSupplemental(a_field.loc, "Field %zu differs in name", i);
+                errorSupplemental(a_field.loc, "%s", a_field.ident.toChars());
+                errorSupplemental(b_field.loc, "(anonymous)");
+                return false;
+            }
+            else if (a_field.ident != b_field.ident)
+            {
+                incompatError();
+                errorSupplemental(a_field.loc, "Field %zu differs in name", i);
+                errorSupplemental(a_field.loc, "%s", a_field.ident.toChars());
+                errorSupplemental(b_field.loc, "%s", b_field.ident.toChars());
+                return false;
             }
-            .error(cldec.loc, "%s `%s` already exists at %s. Perhaps in another function with the same name?", cldec.kind, cldec.toPrettyChars, cd.loc.toChars());
-        }
-
-        if (global.errors != errors || (cldec.baseClass && cldec.baseClass.errors))
-        {
-            // The type is no good, but we should keep the
-            // the type so that we have more accurate error messages
-            // See: https://issues.dlang.org/show_bug.cgi?id=23552
-            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)
+            //
+            // For two structures or unions, corresponding bitfields shall have the same widths.
+            //
+            BitFieldDeclaration bfa = a_field.isBitFieldDeclaration();
+            BitFieldDeclaration bfb = b_field.isBitFieldDeclaration();
+            if ((bfa is null) != (bfb is null))
             {
-                if (!vd.isThisDeclaration() &&
-                    vd.visible() >= Visibility(Visibility.Kind.public_))
+                incompatError();
+                errorSupplemental(a_field.loc, "Field %zu differs in being a bitfield", i);
+                if (bfa is null)
                 {
-                    .error(vd.loc, "%s `%s` Field members of a `synchronized` class cannot be `%s`", vd.kind, vd.toPrettyChars,
-                        visibilityToChars(vd.visible().kind));
+                    errorSupplemental(a_field.loc, "`%s.%s` is not a bitfield",
+                            a.toPrettyChars(), a_field.toChars());
+                    errorSupplemental(b_field.loc, "`%s.%s` is a bitfield",
+                            b.toPrettyChars(), b_field.toChars());
+                }
+                else if (bfb is null)
+                {
+                    errorSupplemental(a_field.loc, "`%s.%s` *is a bitfield",
+                            a.toPrettyChars(), a_field.toChars());
+                    errorSupplemental(b_field.loc, "`%s.%s` is not a bitfield",
+                            b.toPrettyChars(), b_field.toChars());
+                }
+                return false;
+            }
+            if (bfa !is null && bfb !is null)
+            {
+                if (bfa.fieldWidth != bfb.fieldWidth)
+                {
+                    incompatError();
+                    errorSupplemental(a_field.loc, "Field %zu differs in bitfield width", i);
+                    errorSupplemental(a_field.loc, "`%s.%s`: %u",
+                            a.toPrettyChars(), a_field.toChars(), bfa.fieldWidth);
+                    errorSupplemental(b_field.loc, "`%s.%s`: %u",
+                            b.toPrettyChars(), b_field.toChars(), bfb.fieldWidth);
+                    return false;
                 }
             }
         }
+        return true;
+    }
 
-        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);
-
-        version (none)
+    void interfaceSemantic(ClassDeclaration cd)
+    {
+        cd.vtblInterfaces = new BaseClasses();
+        cd.vtblInterfaces.reserve(cd.interfaces.length);
+        foreach (b; cd.interfaces)
         {
-            // @@@DEPRECATED_2.110@@@ https://dlang.org/deprecate.html#scope%20as%20a%20type%20constraint
-            // Deprecated in 2.100
-            // Make an error in 2.110
-            // Don't forget to remove code at https://github.com/dlang/dmd/blob/b2f8274ba76358607fc3297a1e9f361480f9bcf9/src/dmd/dsymbolsem.d#L1032-L1036
-            if (cldec.storage_class & STC.scope_)
-                deprecation(cldec.loc, "`scope` as a type constraint is deprecated.  Use `scope` at the usage site.");
+            cd.vtblInterfaces.push(b);
+            b.copyBaseInterfaces(cd.vtblInterfaces);
         }
     }
 
-    override void visit(InterfaceDeclaration idec)
+    override void visit(ClassDeclaration cldec)
     {
-        /// 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("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);
 
-        //printf("InterfaceDeclaration.dsymbolSemantic(%s), type = %p\n", toChars(), type);
-        if (idec.semanticRun >= PASS.semanticdone)
+        //{ static int n;  if (++n == 20) *(char*)0=0; }
+
+        if (cldec.semanticRun >= PASS.semanticdone)
             return;
         const errors = global.errors;
 
-        //printf("+InterfaceDeclaration.dsymbolSemantic(%s), type = %p\n", toChars(), type);
+        //printf("+ClassDeclaration.dsymbolSemantic(%s), type = %p, sizeok = %d, this = %p\n", toChars(), type, sizeok, this);
 
         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)
+        if (cldec._scope)
         {
-            assert(sc.parent && sc.func);
-            idec.parent = sc.parent;
+            sc = cldec._scope;
+            scx = cldec._scope; // save so we don't make redundant copies
+            cldec._scope = null;
         }
-        // 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)
+
+        if (!cldec.parent)
         {
-            auto ti = (cast(TypeClass)idec.type).sym.isInstantiated();
-            if (ti && isError(ti))
-                (cast(TypeClass)idec.type).sym = idec;
+            assert(sc.parent);
+            cldec.parent = sc.parent;
         }
 
+        if (cldec.errors)
+            cldec.type = Type.terror;
+        if (cldec.semanticRun == PASS.initial)
+            cldec.type = cldec.type.addSTC(sc.stc | cldec.storage_class);
+        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 = idec.ungagSpeculative();
+        Ungag ungag = cldec.ungagSpeculative();
 
-        if (idec.semanticRun == PASS.initial)
+        if (cldec.semanticRun == PASS.initial)
         {
-            idec.visibility = sc.visibility;
+            cldec.visibility = sc.visibility;
 
-            idec.storage_class |= sc.stc;
-            idec.userAttribDecl = sc.userAttribDecl;
+            cldec.storage_class |= sc.stc;
+            if (cldec.storage_class & STC.auto_)
+                .error(cldec.loc, "%s `%s` storage class `auto` is invalid when declaring a class, did you mean to use `scope`?", cldec.kind, cldec.toPrettyChars);
+            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 (idec.symtab)
+        else if (cldec.symtab && !scx)
         {
-            if (idec.sizeok == Sizeok.done || !scx)
-            {
-                idec.semanticRun = PASS.semanticdone;
-                return;
-            }
+            return;
         }
-        idec.semanticRun = PASS.semantic;
+        cldec.rtInfoScope = sc;
+        sc.setNoFree();
 
-        if (idec.baseok < Baseok.done)
+        cldec.semanticRun = PASS.semantic;
+        checkGNUABITag(cldec, sc.linkage);
+        checkMustUseReserved(cldec);
+
+        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)
@@ -4433,99 +5054,171 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
                 }
                 static if (!is(T == void))
                 {
-                    idec._scope = scx;
+                    cldec._scope = scx;
                     auto r = exp();
-                    idec._scope = null;
+                    cldec._scope = null;
                     return r;
                 }
                 else
                 {
-                    idec._scope = scx;
+                    cldec._scope = scx;
                     exp();
-                    idec._scope = null;
+                    cldec._scope = null;
                 }
             }
 
-            idec.baseok = Baseok.start;
+            cldec.baseok = Baseok.start;
 
             // Expand any tuples in baseclasses[]
-            for (size_t i = 0; i < idec.baseclasses.length;)
+            for (size_t i = 0; i < cldec.baseclasses.length;)
             {
-                auto b = (*idec.baseclasses)[i];
-                b.type = resolveBase(b.type.typeSemantic(idec.loc, sc));
+                auto b = (*cldec.baseclasses)[i];
+                b.type = resolveBase(b.type.typeSemantic(cldec.loc, sc));
 
                 Type tb = b.type.toBasetype();
                 if (auto tup = tb.isTypeTuple())
                 {
-                    idec.baseclasses.remove(i);
+                    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);
-                        idec.baseclasses.insert(i + j, b);
+                        cldec.baseclasses.insert(i + j, b);
                     }
                 }
                 else
                     i++;
             }
 
-            if (idec.baseok >= Baseok.done)
+            if (cldec.baseok >= Baseok.done)
             {
                 //printf("%s already semantic analyzed, semanticRun = %d\n", toChars(), semanticRun);
-                if (idec.semanticRun >= PASS.semanticdone)
+                if (cldec.semanticRun >= PASS.semanticdone)
                     return;
                 goto Lancestorsdone;
             }
 
-            if (!idec.baseclasses.length && sc.linkage == LINK.cpp)
-                idec.classKind = ClassKind.cpp;
-            idec.cppnamespace = sc.namespace;
-            checkGNUABITag(idec, sc.linkage);
-            checkMustUseReserved(idec);
+            // 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.isTypeClass();
+                if (!tc)
+                {
+                    if (b.type != Type.terror)
+                        .error(cldec.loc, "%s `%s` base type must be `class` or `interface`, not `%s`", cldec.kind, cldec.toPrettyChars, 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;
 
-            if (sc.linkage == LINK.objc)
-                objc.setObjc(idec);
+                for (ClassDeclaration cdb = tc.sym; cdb; cdb = cdb.baseClass)
+                {
+                    if (cdb == cldec)
+                    {
+                        .error(cldec.loc, "%s `%s` circular inheritance", cldec.kind, cldec.toPrettyChars);
+                        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)
+                        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 = 0; i < idec.baseclasses.length;)
+            for (size_t i = (cldec.baseClass ? 1 : 0); i < cldec.baseclasses.length;)
             {
-                BaseClass* b = (*idec.baseclasses)[i];
+                BaseClass* b = (*cldec.baseclasses)[i];
                 Type tb = b.type.toBasetype();
-                TypeClass tc = (tb.ty == Tclass) ? cast(TypeClass)tb : null;
+                TypeClass tc = tb.isTypeClass();
                 if (!tc || !tc.sym.isInterfaceDeclaration())
                 {
-                    if (b.type != Type.terror)
-                        .error(idec.loc, "%s `%s` base type must be `interface`, not `%s`", idec.kind, idec.toPrettyChars, b.type.toChars());
-                    idec.baseclasses.remove(i);
+                    // 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.length)
+                                    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 = 0; j < i; j++)
+                for (size_t j = (cldec.baseClass ? 1 : 0); j < i; j++)
                 {
-                    BaseClass* b2 = (*idec.baseclasses)[j];
+                    BaseClass* b2 = (*cldec.baseclasses)[j];
                     if (b2.sym == tc.sym)
                     {
-                        .error(idec.loc, "%s `%s` inherits from duplicate interface `%s`", idec.kind, idec.toPrettyChars, b2.sym.toChars());
-                        idec.baseclasses.remove(i);
+                        .error(cldec.loc, "%s `%s` inherits from duplicate interface `%s`", cldec.kind, cldec.toPrettyChars, b2.sym.toChars());
+                        cldec.baseclasses.remove(i);
                         continue BCLoop;
                     }
                 }
-                if (tc.sym == idec || idec.isBaseOf2(tc.sym))
-                {
-                    .error(idec.loc, "%s `%s` circular inheritance of interface", idec.kind, idec.toPrettyChars);
-                    idec.baseclasses.remove(i);
-                    continue;
-                }
                 if (tc.sym.isDeprecated())
                 {
-                    if (!idec.isDeprecated())
+                    if (!cldec.isDeprecated())
                     {
-                        // Deriving from deprecated interface makes this one deprecated too
-                        idec.setDeprecated();
-                        tc.checkDeprecated(idec.loc, sc);
+                        // Deriving from deprecated class makes this one deprecated too
+                        cldec.setDeprecated();
+                        tc.checkDeprecated(cldec.loc, sc);
                     }
                 }
 
@@ -4537,1454 +5230,1192 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
                 {
                     //printf("\ttry later, forward reference of base %s\n", tc.sym.toChars());
                     if (tc.sym._scope)
-                        Module.addDeferredSemantic(tc.sym);
-                    idec.baseok = Baseok.none;
+                        addDeferredSemantic(tc.sym);
+                    cldec.baseok = Baseok.none;
                 }
                 i++;
             }
-            if (idec.baseok == Baseok.none)
+            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(sc, 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()
+                {
+                    ObjectNotFound(cldec.loc, cldec.ident);
+                }
+
+                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)
             {
-                // Forward referencee of one or more bases, try again later
-                return deferDsymbolSemantic(sc, idec, scx);
+                if (cldec.baseClass.storage_class & STC.final_)
+                    .error(cldec.loc, "%s `%s` cannot inherit from class `%s` because it is `final`", cldec.kind, cldec.toPrettyChars, 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.classKind != cldec.baseClass.classKind)
+                    .error(cldec.loc, "%s `%s` with %s linkage cannot inherit from class `%s` with %s linkage", cldec.kind, cldec.toPrettyChars,
+                        ClassKindToChars(cldec.classKind), cldec.baseClass.toChars(), ClassKindToChars(cldec.baseClass.classKind));
+
+                if (cldec.baseClass.stack)
+                    cldec.stack = true;
+                cldec.enclosing = cldec.baseClass.enclosing;
+                cldec.storage_class |= cldec.baseClass.storage_class & STC.TYPECTOR;
             }
-            idec.baseok = Baseok.done;
 
-            idec.interfaces = idec.baseclasses.tdata()[0 .. idec.baseclasses.length];
-            foreach (b; idec.interfaces)
+            cldec.interfaces = cldec.baseclasses.tdata()[(cldec.baseClass ? 1 : 0) .. cldec.baseclasses.length];
+            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())
-                    idec.com = true;
-                if (b.sym.isCPPinterface())
-                    idec.classKind = ClassKind.cpp;
+                    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(idec);
+            interfaceSemantic(cldec);
         }
     Lancestorsdone:
+        //printf("\tClassDeclaration.dsymbolSemantic(%s) baseok = %d\n", toChars(), baseok);
 
-        if (!idec.members) // if opaque declaration
+        if (!cldec.members) // if opaque declaration
         {
-            idec.semanticRun = PASS.semanticdone;
+            cldec.semanticRun = PASS.semanticdone;
             return;
         }
-        if (!idec.symtab)
-            idec.symtab = new DsymbolTable();
+        if (!cldec.symtab)
+        {
+            cldec.symtab = new DsymbolTable();
 
-        for (size_t i = 0; i < idec.baseclasses.length; i++)
+            /* 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.length; i++)
         {
-            BaseClass* b = (*idec.baseclasses)[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);
-                return deferDsymbolSemantic(sc, idec, scx);
+                    addDeferredSemantic(tc.sym);
+                //printf("\tL%d semantic('%s') failed due to forward references\n", __LINE__, toChars());
+                return deferDsymbolSemantic(sc, cldec, scx);
             }
         }
 
-        if (idec.baseok == Baseok.done)
+        if (cldec.baseok == Baseok.done)
         {
-            idec.baseok = Baseok.semanticdone;
-            objc.setMetaclass(idec, sc);
+            cldec.baseok = Baseok.semanticdone;
+            objc.setMetaclass(cldec, 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)
+            if (cldec.baseClass)
             {
-                // Skip if b has already appeared
-                for (size_t k = 0; k < i; k++)
+                if (cldec.classKind == ClassKind.cpp && cldec.baseClass.vtbl.length == 0)
                 {
-                    if (b == idec.interfaces[k])
-                        goto Lcontinue;
+                    .error(cldec.loc, "%s `%s` C++ base class `%s` needs at least one virtual function", cldec.kind, cldec.toPrettyChars, cldec.baseClass.toChars());
                 }
 
                 // Copy vtbl[] from base class
-                if (b.sym.vtblOffset())
+                assert(cldec.vtbl.length == 0);
+                cldec.vtbl.setDim(cldec.baseClass.vtbl.length);
+                memcpy(cldec.vtbl.tdata(), cldec.baseClass.vtbl.tdata(), (void*).sizeof * cldec.vtbl.length);
+
+                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_)
+                    .error(cldec.loc, "%s `%s` static class cannot inherit from nested class `%s`", cldec.kind, cldec.toPrettyChars, cldec.baseClass.toChars());
+                if (cldec.toParentLocal() != cldec.baseClass.toParentLocal() &&
+                    (!cldec.toParentLocal() ||
+                     !cldec.baseClass.toParentLocal().getType() ||
+                     !cldec.baseClass.toParentLocal().getType().isBaseOf(cldec.toParentLocal().getType(), null)))
                 {
-                    size_t d = b.sym.vtbl.length;
-                    if (d > 1)
+                    if (cldec.toParentLocal())
                     {
-                        idec.vtbl.pushSlice(b.sym.vtbl[1 .. d]);
+                        .error(cldec.loc, "%s `%s` is nested within `%s`, but super class `%s` is nested within `%s`", cldec.kind, cldec.toPrettyChars,
+                            cldec.toParentLocal().toChars(),
+                            cldec.baseClass.toChars(),
+                            cldec.baseClass.toParentLocal().toChars());
+                    }
+                    else
+                    {
+                        .error(cldec.loc, "%s `%s` is not nested, but super class `%s` is nested within `%s`", cldec.kind, cldec.toPrettyChars,
+                            cldec.baseClass.toChars(),
+                            cldec.baseClass.toParentLocal().toChars());
                     }
                 }
-                else
+                if (cldec.vthis2)
                 {
-                    idec.vtbl.append(&b.sym.vtbl);
+                    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())
+                        {
+                            .error(cldec.loc, "%s `%s` needs the frame pointer of `%s`, but super class `%s` needs the frame pointer of `%s`", cldec.kind, cldec.toPrettyChars,
+                                cldec.toParent2().toChars(),
+                                cldec.baseClass.toChars(),
+                                cldec.baseClass.toParent2().toChars());
+                        }
+                        else
+                        {
+                            .error(cldec.loc, "%s `%s` doesn't need a frame pointer, but super class `%s` needs the frame pointer of `%s`", cldec.kind, cldec.toPrettyChars,
+                                cldec.baseClass.toChars(),
+                                cldec.baseClass.toParent2().toChars());
+                        }
+                    }
                 }
-
-            Lcontinue:
+                else
+                    cldec.makeNested2();
             }
+            else
+                cldec.makeNested();
         }
 
-        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) );
-
-        idec.semanticRun = PASS.semanticdone;
-        //printf("-InterfaceDeclaration.dsymbolSemantic(%s), type = %p\n", toChars(), type);
+        auto sc2 = cldec.newScope(sc);
 
-        sc2.pop();
+        cldec.members.foreachDsymbol( s => s.importAll(sc2) );
 
-        if (global.errors != errors)
-        {
-            // The type is no good.
-            idec.type = Type.terror;
-        }
+        // Note that members.length can grow due to tuple expansion during semantic()
+        cldec.members.foreachDsymbol( s => s.dsymbolSemantic(sc2) );
 
-        version (none)
+        if (!cldec.determineFields())
         {
-            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(cldec.type == Type.terror);
+            sc2.pop();
+            return;
         }
-        assert(idec.type.ty != Tclass || (cast(TypeClass)idec.type).sym == idec);
-
-        version (none)
+        /* Following special member functions creation needs semantic analysis
+         * completion of sub-structs in each field types.
+         */
+        foreach (v; cldec.fields)
         {
-            // @@@DEPRECATED_2.120@@@ https://dlang.org/deprecate.html#scope%20as%20a%20type%20constraint
-            // Deprecated in 2.087
-            // Made an error in 2.100, but removal depends on `scope class` being removed too
-            // 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_)
-                error(idec.loc, "`scope` as a type constraint is obsolete.  Use `scope` at the usage site.");
-        }
-    }
-}
-
-/*
-Adds dsym as a member of scope sds.
-
-Params:
-    dsym = dsymbol to inserted
-    sc = scope where the dsymbol is declared
-    sds = ScopeDsymbol where dsym is inserted
-*/
-void addMember(Dsymbol dsym, Scope* sc, ScopeDsymbol sds)
-{
-    auto addMemberVisitor = new AddMemberVisitor(sc, sds);
-    dsym.accept(addMemberVisitor);
-}
+            Type tb = v.type.baseElemOf();
+            if (tb.ty != Tstruct)
+                continue;
+            auto sd = (cast(TypeStruct)tb).sym;
+            if (sd.semanticRun >= PASS.semanticdone)
+                continue;
 
-private void attribAddMember(AttribDeclaration atb, Scope* sc, ScopeDsymbol sds)
-{
-    Dsymbols* d = atb.include(sc);
-    if (d)
-    {
-        Scope* sc2 = atb.newScope(sc);
-        d.foreachDsymbol( s => s.addMember(sc2, sds) );
-        if (sc2 != sc)
             sc2.pop();
-    }
-}
-
-/****************************************************
- * Declare parameters of template instance, initialize them with the
- * template instance arguments.
- */
-private void declareParameters(TemplateInstance ti, Scope* sc)
-{
-    TemplateDeclaration tempdecl = ti.tempdecl.isTemplateDeclaration();
-    assert(tempdecl);
-
-    //printf("TemplateInstance.declareParameters()\n");
-    foreach (i, o; ti.tdtypes) // initializer for tp
-    {
-        TemplateParameter tp = (*tempdecl.parameters)[i];
-        //printf("\ttdtypes[%d] = %p\n", i, o);
-        declareParameter(tempdecl, sc, tp, o);
-    }
-}
-private extern(C++) class AddMemberVisitor : Visitor
-{
-    alias visit = Visitor.visit;
 
-    Scope* sc;
-    ScopeDsymbol sds;
+            //printf("\tdeferring %s\n", toChars());
+            return deferDsymbolSemantic(sc, cldec, scx);
+        }
 
-    this(Scope* sc, ScopeDsymbol sds) @safe
-    {
-        this.sc = sc;
-        this.sds = sds;
-    }
+        /* 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;
 
-    override void visit(Dsymbol dsym)
-    {
-        //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);
-        dsym.parent = sds;
-        if (dsym.isAnonymous()) // no name, so can't add it to symbol table
-            return;
+        // Look for the constructor
+        cldec.ctor = cldec.searchCtor();
 
-        if (!sds.symtabInsert(dsym)) // if name is already defined
+        if (!cldec.ctor && cldec.noDefaultCtor)
         {
-            if (dsym.isAliasDeclaration() && !dsym._scope)
-                dsym.setScope(sc);
-            Dsymbol s2 = sds.symtabLookup(dsym, dsym.ident);
-            /* https://issues.dlang.org/show_bug.cgi?id=17434
-             *
-             * If we are trying to add an import to the symbol table
-             * that has already been introduced, then keep the one with
-             * larger visibility. This is fine for imports because if
-             * we have multiple imports of the same file, if a single one
-             * is public then the symbol is reachable.
-             */
-            if (auto i1 = dsym.isImport())
+            // A class object is always created by constructor, so this check is legitimate.
+            foreach (v; cldec.fields)
             {
-                if (auto i2 = s2.isImport())
-                {
-                    if (sc.explicitVisibility && sc.visibility > i2.visibility)
-                        sds.symtab.update(dsym);
-                }
+                if (v.storage_class & STC.nodefaultctor)
+                    error(v.loc, "field `%s` must be initialized in constructor", v.toChars());
             }
+        }
 
-            // If using C tag/prototype/forward declaration rules
-            if (sc && sc.inCfile && !dsym.isImport())
-            // When merging master, replace with: if (sc && sc.inCfile && !dsym.isImport())
+        // 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, ArgumentList(), FuncResolveFlag.quiet);
+            if (!fd) // try shared base ctor instead
+                fd = resolveFuncCall(cldec.loc, sc2, cldec.baseClass.ctor, null, cldec.type.sharedOf, ArgumentList(), FuncResolveFlag.quiet);
+            if (fd && !fd.errors)
             {
-                if (handleTagSymbols(*sc, dsym, s2, sds))
-                    return;
-                if (handleSymbolRedeclarations(*sc, dsym, s2, sds))
-                    return;
+                //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)
 
-                sds.multiplyDefined(Loc.initial, dsym, s2);  // ImportC doesn't allow overloading
-                dsym.errors = true;
-                return;
-            }
+                auto ctor = new CtorDeclaration(cldec.loc, Loc.initial, STC.none, tf);
+                ctor.storage_class |= STC.inference | (fd.storage_class & STC.scope_);
+                ctor.isGenerated = true;
+                ctor.fbody = new CompoundStatement(Loc.initial, new Statements());
 
-            if (!s2.overloadInsert(dsym))
+                cldec.members.push(ctor);
+                ctor.addMember(sc, cldec);
+                ctor.dsymbolSemantic(sc2);
+
+                cldec.ctor = ctor;
+                cldec.defaultCtor = ctor;
+            }
+            else
             {
-                sds.multiplyDefined(Loc.initial, dsym, s2);
-                dsym.errors = true;
+                .error(cldec.loc, "%s `%s` cannot implicitly generate a default constructor when base class `%s` is missing a default constructor", cldec.kind, cldec.toPrettyChars,
+                    cldec.baseClass.toPrettyChars());
             }
         }
-        if (sds.isAggregateDeclaration() || sds.isEnumDeclaration())
+
+        buildDtors(cldec, sc2);
+
+        if (cldec.classKind == ClassKind.cpp && cldec.cppDtorVtblIndex != -1)
         {
-            if (dsym.ident == Id.__sizeof ||
-                !(sc && sc.inCfile) && (dsym.ident == Id.__xalignof || dsym.ident == Id._mangleof))
+            // 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)
             {
-                .error(dsym.loc, "%s `%s` `.%s` property cannot be redefined", dsym.kind, dsym.toPrettyChars, dsym.ident.toChars());
-                dsym.errors = true;
+                // 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))
+                .error(f.loc, "%s `%s` identity assignment operator overload is illegal", cldec.kind, cldec.toPrettyChars);
+        }
 
-    override void visit(StaticAssert _)
-    {
-        // we didn't add anything
-    }
+        cldec.inv = buildInv(cldec, sc2);
 
-    /*****************************
-     * Add import to sd's symbol table.
-     */
-    override void visit(Import imp)
-    {
-        //printf("Import.addMember(this=%s, sds=%s, sc=%p)\n", imp.toChars(), sds.toChars(), sc);
-        if (imp.names.length == 0)
-            return visit(cast(Dsymbol)imp);
-        if (imp.aliasId)
-            visit(cast(Dsymbol)imp);
+        cldec.semanticRun = PASS.semanticdone;
+        //printf("-ClassDeclaration.dsymbolSemantic(%s), type = %p\n", toChars(), type);
 
-        /* Instead of adding the import to sds's symbol table,
-         * add each of the alias=name pairs
+        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.
          */
-        for (size_t i = 0; i < imp.names.length; i++)
+        if (cldec.isabstract != ThreeState.none)    // if evaluated it before completion
         {
-            Identifier name = imp.names[i];
-            Identifier _alias = imp.aliases[i];
-            if (!_alias)
-                _alias = name;
-            auto tname = new TypeIdentifier(imp.loc, name);
-            auto ad = new AliasDeclaration(imp.loc, _alias, tname);
-            ad._import = imp;
-            addMember(ad, sc, sds);
-            imp.aliasdecls.push(ad);
+            const isabstractsave = cldec.isabstract;
+            cldec.isabstract = ThreeState.none;
+            cldec.isAbstract();               // recalculate
+            if (cldec.isabstract != isabstractsave)
+            {
+                .error(cldec.loc, "%s `%s` cannot infer `abstract` attribute due to circular dependencies", cldec.kind, cldec.toPrettyChars);
+            }
         }
-    }
-
-    override void visit(AttribDeclaration atb)
-    {
-       attribAddMember(atb, sc, sds);
-    }
 
-    override void visit(StorageClassDeclaration stcd)
-    {
-        Dsymbols* d = stcd.include(sc);
-        if (d)
+        if (cldec.type.ty == Tclass && (cast(TypeClass)cldec.type).sym != cldec)
         {
-            Scope* sc2 = stcd.newScope(sc);
-
-            d.foreachDsymbol( (s)
+            // https://issues.dlang.org/show_bug.cgi?id=17492
+            ClassDeclaration cd = (cast(TypeClass)cldec.type).sym;
+            version (none)
             {
-                //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 |= stcd.stc & STC.local;
-                    if (auto sdecl = s.isStorageClassDeclaration()) // TODO: why is this not enough to deal with the nested case?
-                    {
-                        sdecl.stc |= stcd.stc & STC.local;
-                    }
-                }
-                s.addMember(sc2, sds);
-            });
-
-            if (sc2 != sc)
-                sc2.pop();
+                printf("this = %p %s\n", cldec, cldec.toPrettyChars());
+                printf("type = %d sym = %p, %s\n", cldec.type.ty, cd, cd.toPrettyChars());
+            }
+            .error(cldec.loc, "%s `%s` already exists at %s. Perhaps in another function with the same name?", cldec.kind, cldec.toPrettyChars, cd.loc.toChars());
         }
-    }
 
-    override void visit(VisibilityDeclaration visd)
-    {
-        if (visd.pkg_identifiers)
+        if (global.errors != errors || (cldec.baseClass && cldec.baseClass.errors))
         {
-            Dsymbol tmp;
-            Package.resolve(visd.pkg_identifiers, &tmp, null);
-            visd.visibility.pkg = tmp ? tmp.isPackage() : null;
-            visd.pkg_identifiers = null;
+            // The type is no good, but we should keep the
+            // the type so that we have more accurate error messages
+            // See: https://issues.dlang.org/show_bug.cgi?id=23552
+            cldec.errors = true;
+            if (cldec.deferred)
+                cldec.deferred.errors = true;
         }
-        if (visd.visibility.kind == Visibility.Kind.package_ && visd.visibility.pkg && sc._module)
-        {
-            Module m = sc._module;
 
-            // https://issues.dlang.org/show_bug.cgi?id=17441
-            // While isAncestorPackageOf does an equality check, the fix for the issue 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() || !visd.visibility.pkg.ident.equals(m.isPackage().ident))
+        // Verify fields of a synchronized class are not public
+        if (cldec.storage_class & STC.synchronized_)
+        {
+            foreach (vd; cldec.fields)
             {
-                Package pkg = m.parent ? m.parent.isPackage() : null;
-                if (!pkg || !visd.visibility.pkg.isAncestorPackageOf(pkg))
-                    .error(visd.loc, "%s `%s` does not bind to one of ancestor packages of module `%s`", visd.kind(), visd.toPrettyChars(false), m.toPrettyChars(true));
+                if (!vd.isThisDeclaration() &&
+                    vd.visible() >= Visibility(Visibility.Kind.public_))
+                {
+                    .error(vd.loc, "%s `%s` Field members of a `synchronized` class cannot be `%s`", vd.kind, vd.toPrettyChars,
+                        visibilityToChars(vd.visible().kind));
+                }
             }
         }
-        attribAddMember(visd, sc, sds);
-    }
 
-    override void visit(StaticIfDeclaration sid)
-    {
-        //printf("StaticIfDeclaration::addMember() '%s'\n", sid.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;
-         * }
-         */
-        sid.scopesym = sds;
+        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);
+
+        version (none)
+        {
+            // @@@DEPRECATED_2.110@@@ https://dlang.org/deprecate.html#scope%20as%20a%20type%20constraint
+            // Deprecated in 2.100
+            // Make an error in 2.110
+            // Don't forget to remove code at https://github.com/dlang/dmd/blob/b2f8274ba76358607fc3297a1e9f361480f9bcf9/src/dmd/dsymbolsem.d#L1032-L1036
+            if (cldec.storage_class & STC.scope_)
+                deprecation(cldec.loc, "`scope` as a type constraint is deprecated.  Use `scope` at the usage site.");
+        }
     }
 
-
-    override void visit(StaticForeachDeclaration sfd)
+    override void visit(InterfaceDeclaration idec)
     {
-        // used only for caching the enclosing symbol
-        sfd.scopesym = sds;
-    }
+        /// 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;
+        }
 
-    /***************************************
-     * Lazily initializes the scope to forward to.
-     */
-    override void visit(ForwardingAttribDeclaration fad)
-    {
-        fad.sym.parent = sds;
-        sds = fad.sym;
-        attribAddMember(fad, sc, fad.sym);
-    }
+        //printf("InterfaceDeclaration.dsymbolSemantic(%s), type = %p\n", toChars(), type);
+        if (idec.semanticRun >= PASS.semanticdone)
+            return;
+        const errors = global.errors;
 
-    override void visit(MixinDeclaration md)
-    {
-        //printf("MixinDeclaration::addMember(sc = %p, sds = %p, memnum = %d)\n", sc, sds, md.memnum);
-        md.scopesym = sds;
-    }
+        //printf("+InterfaceDeclaration.dsymbolSemantic(%s), type = %p\n", toChars(), type);
 
-    override void visit(DebugSymbol ds)
-    {
-        //printf("DebugSymbol::addMember('%s') %s\n", sds.toChars(), ds.toChars());
-        Module m = sds.isModule();
-        // Do not add the member to the symbol table,
-        // just make sure subsequent debug declarations work.
-        if (!m)
+        Scope* scx = null;
+        if (idec._scope)
         {
-            .error(ds.loc, "%s `%s` declaration must be at module level", ds.kind, ds.toPrettyChars);
-            ds.errors = true;
+            sc = idec._scope;
+            scx = idec._scope; // save so we don't make redundant copies
+            idec._scope = null;
         }
-        else
+
+        if (!idec.parent)
         {
-            if (m.debugidsNot && findCondition(*m.debugidsNot, ds.ident))
-            {
-                .error(ds.loc, "%s `%s` defined after use", ds.kind, ds.toPrettyChars);
-                ds.errors = true;
-            }
-            if (!m.debugids)
-                m.debugids = new Identifiers();
-            m.debugids.push(ds.ident);
+            assert(sc.parent && sc.func);
+            idec.parent = sc.parent;
         }
-    }
+        // Objective-C metaclasses are anonymous
+        assert(idec.parent && !idec.isAnonymous || isAnonymousMetaclass(idec));
 
-    override void visit(VersionSymbol vs)
-    {
-        //printf("VersionSymbol::addMember('%s') %s\n", sds.toChars(), vs.toChars());
-        Module m = sds.isModule();
-        // Do not add the member to the symbol table,
-        // just make sure subsequent debug declarations work.
-        VersionCondition.checkReserved(vs.loc, vs.ident.toString());
-        if (!m)
+        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)
         {
-            .error(vs.loc, "%s `%s` declaration must be at module level", vs.kind, vs.toPrettyChars);
-            vs.errors = true;
+            auto ti = (cast(TypeClass)idec.type).sym.isInstantiated();
+            if (ti && isError(ti))
+                (cast(TypeClass)idec.type).sym = idec;
         }
-        else
+
+        // Ungag errors when not speculative
+        Ungag ungag = idec.ungagSpeculative();
+
+        if (idec.semanticRun == PASS.initial)
         {
-            if (m.versionidsNot && findCondition(*m.versionidsNot, vs.ident))
+            idec.visibility = sc.visibility;
+
+            idec.storage_class |= sc.stc;
+            idec.userAttribDecl = sc.userAttribDecl;
+        }
+        else if (idec.symtab)
+        {
+            if (idec.sizeok == Sizeok.done || !scx)
             {
-                .error(vs.loc, "%s `%s` defined after use", vs.kind, vs.toPrettyChars);
-                vs.errors = true;
+                idec.semanticRun = PASS.semanticdone;
+                return;
             }
-            if (!m.versionids)
-                m.versionids = new Identifiers();
-            m.versionids.push(vs.ident);
         }
+        idec.semanticRun = PASS.semantic;
 
-    }
-
-    override void visit(Nspace ns)
-    {
-        visit(cast(Dsymbol)ns);
-
-        if (ns.members)
+        if (idec.baseok < Baseok.done)
         {
-            if (!ns.symtab)
-                ns.symtab = new DsymbolTable();
-            // The namespace becomes 'imported' into the enclosing scope
-            for (Scope* sce = sc; 1; sce = sce.enclosing)
+            T resolveBase(T)(lazy T exp)
             {
-                ScopeDsymbol sds2 = sce.scopesym;
-                if (sds2)
+                if (!scx)
                 {
-                    sds2.importScope(ns, Visibility(Visibility.Kind.public_));
-                    break;
+                    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;
                 }
             }
-            assert(sc);
-            sc = sc.push(ns);
-            sc.linkage = LINK.cpp; // namespaces default to C++ linkage
-            sc.parent = ns;
-            ns.members.foreachDsymbol(s => s.addMember(sc, ns));
-            sc.pop();
-        }
-    }
 
-    override void visit(EnumDeclaration ed)
-    {
-        version (none)
-        {
-            printf("EnumDeclaration::addMember() %s\n", ed.toChars());
-            for (size_t i = 0; i < ed.members.length; i++)
+            idec.baseok = Baseok.start;
+
+            // Expand any tuples in baseclasses[]
+            for (size_t i = 0; i < idec.baseclasses.length;)
             {
-                EnumMember em = (*ed.members)[i].isEnumMember();
-                printf("    member %s\n", em.toChars());
+                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 (!ed.isAnonymous())
-        {
-            visit(cast(Dsymbol)ed);
-        }
 
-        addEnumMembersToSymtab(ed, sc, sds);
-    }
-}
+            if (idec.baseok >= Baseok.done)
+            {
+                //printf("%s already semantic analyzed, semanticRun = %d\n", toChars(), semanticRun);
+                if (idec.semanticRun >= PASS.semanticdone)
+                    return;
+                goto Lancestorsdone;
+            }
 
-/*******************************************
- * Add members of EnumDeclaration to the symbol table(s).
- * Params:
- *      ed = EnumDeclaration
- *      sc = context of `ed`
- *      sds = symbol table that `ed` resides in
- */
-void addEnumMembersToSymtab(EnumDeclaration ed, Scope* sc, ScopeDsymbol sds)
-{
-    const bool isCEnum = sc.inCfile; // it's an ImportC enum
-    //printf("addEnumMembersToSymtab(ed: %s added: %d Cfile: %d)\n", ed.toChars(), ed.added, isCEnum);
-    if (ed.added)
-        return;
-    ed.added = true;
+            if (!idec.baseclasses.length && sc.linkage == LINK.cpp)
+                idec.classKind = ClassKind.cpp;
+            idec.cppnamespace = sc.namespace;
+            checkGNUABITag(idec, sc.linkage);
+            checkMustUseReserved(idec);
 
-    if (!ed.members)
-        return;
+            if (sc.linkage == LINK.objc)
+                objc.setObjc(idec);
 
-    const bool isAnon = ed.isAnonymous();
+            // Check for errors, handle forward references
+            BCLoop:
+            for (size_t i = 0; i < idec.baseclasses.length;)
+            {
+                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)
+                        .error(idec.loc, "%s `%s` base type must be `interface`, not `%s`", idec.kind, idec.toPrettyChars, b.type.toChars());
+                    idec.baseclasses.remove(i);
+                    continue;
+                }
 
-    if ((isCEnum || isAnon) && !sds.symtab)
-        sds.symtab = new DsymbolTable();
+                // Check for duplicate interfaces
+                for (size_t j = 0; j < i; j++)
+                {
+                    BaseClass* b2 = (*idec.baseclasses)[j];
+                    if (b2.sym == tc.sym)
+                    {
+                        .error(idec.loc, "%s `%s` inherits from duplicate interface `%s`", idec.kind, idec.toPrettyChars, b2.sym.toChars());
+                        idec.baseclasses.remove(i);
+                        continue BCLoop;
+                    }
+                }
+                if (tc.sym == idec || idec.isBaseOf2(tc.sym))
+                {
+                    .error(idec.loc, "%s `%s` circular inheritance of interface", idec.kind, idec.toPrettyChars);
+                    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);
+                    }
+                }
 
-    if ((isCEnum || !isAnon) && !ed.symtab)
-        ed.symtab = new DsymbolTable();
+                b.sym = tc.sym;
 
-    ed.members.foreachDsymbol( (s)
-    {
-        if (EnumMember em = s.isEnumMember())
-        {
-            //printf("adding EnumMember %s to %s %d\n", em.toChars(), ed.toChars(), isCEnum);
-            em.ed = ed;
-            if (isCEnum)
+                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)
+                        addDeferredSemantic(tc.sym);
+                    idec.baseok = Baseok.none;
+                }
+                i++;
+            }
+            if (idec.baseok == Baseok.none)
             {
-                /* C doesn't add the enum member to the symbol table of the enum tag, it adds
-                 * it to the symbol table that the tag is in. This is in contrast to D, where enum
-                 * members become members of the enum tag. To accommodate this, we add
-                 * the enum members to both symbol tables.
-                 */
-                em.addMember(sc, ed);   // add em to ed's symbol table
-                if (em.errors)
-                    return;
-                em.addMember(sc, sds);  // add em to symbol table that ed is in
-                em.parent = ed; // restore it after previous addMember() changed it
+                // Forward referencee of one or more bases, try again later
+                return deferDsymbolSemantic(sc, idec, scx);
             }
-            else
+            idec.baseok = Baseok.done;
+
+            idec.interfaces = idec.baseclasses.tdata()[0 .. idec.baseclasses.length];
+            foreach (b; idec.interfaces)
             {
-                em.addMember(sc, isAnon ? sds : ed);
+                // 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;
             }
-        }
-    });
-}
 
-/******************************************************
- * Verifies if the given Identifier is a DRuntime hook. It uses the hooks
- * defined in `id.d`.
- *
- * Params:
- *  id = Identifier to verify
- * Returns:
- *  true if `id` is a DRuntime hook
- *  false otherwise
- */
-private bool isDRuntimeHook(Identifier id)
-{
-    return id == Id._d_HookTraceImpl ||
-        id == Id._d_newclassT || id == Id._d_newclassTTrace ||
-        id == Id._d_arraycatnTX || id == Id._d_arraycatnTXTrace ||
-        id == Id._d_newThrowable || id == Id._d_delThrowable ||
-        id == Id._d_arrayassign_l || id == Id._d_arrayassign_r ||
-        id == Id._d_arraysetassign || id == Id._d_arraysetctor ||
-        id == Id._d_arrayctor ||
-        id == Id._d_arraysetlengthT ||
-        id == Id._d_arraysetlengthTTrace ||
-        id == Id._d_arrayappendT || id == Id._d_arrayappendTTrace ||
-        id == Id._d_arrayappendcTX;
-}
+            interfaceSemantic(idec);
+        }
+    Lancestorsdone:
 
-/*****************************************
- * Append `ti` to the specific module `ti.members[]`
- */
-private Dsymbols* appendToModuleMember(TemplateInstance ti)
-{
-    Module mi = ti.minst; // instantiated . inserted module
-
-    //printf("%s.appendToModuleMember() enclosing = %s mi = %s\n",
-    //    toPrettyChars(),
-    //    enclosing ? enclosing.toPrettyChars() : null,
-    //    mi ? mi.toPrettyChars() : null);
-    if (global.params.allInst || !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 (needsCodegen()).
-         */
-        static Dsymbol getStrictEnclosing(TemplateInstance ti)
+        if (!idec.members) // if opaque declaration
         {
-            do
-            {
-                if (ti.enclosing)
-                    return ti.enclosing;
-                ti = ti.tempdecl.isInstantiated();
-            } while (ti);
-            return null;
+            idec.semanticRun = PASS.semanticdone;
+            return;
         }
+        if (!idec.symtab)
+            idec.symtab = new DsymbolTable();
 
-        Dsymbol enc = getStrictEnclosing(ti);
-        // insert target is made stable by using the module
-        // where tempdecl is declared.
-        mi = (enc ? enc : ti.tempdecl).getModule();
-        if (!mi.isRoot())
+        for (size_t i = 0; i < idec.baseclasses.length; i++)
         {
-            if (mi.importedFrom)
+            BaseClass* b = (*idec.baseclasses)[i];
+            Type tb = b.type.toBasetype();
+            TypeClass tc = tb.isTypeClass();
+            if (tc.sym.semanticRun < PASS.semanticdone)
             {
-                mi = mi.importedFrom;
-                assert(mi.isRoot());
+                // Forward referencee of one or more bases, try again later
+                if (tc.sym._scope)
+                    addDeferredSemantic(tc.sym);
+                return deferDsymbolSemantic(sc, idec, scx);
             }
-            else
+        }
+
+        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)
             {
-                // This can happen when using the frontend as a library.
-                // Append it to the non-root module.
+                // 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.length;
+                    if (d > 1)
+                    {
+                        idec.vtbl.pushSlice(b.sym.vtbl[1 .. d]);
+                    }
+                }
+                else
+                {
+                    idec.vtbl.append(&b.sym.vtbl);
+                }
+
+            Lcontinue:
             }
         }
-    }
-    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.
-         * Unless it is re-appended to a root module later (with changed minst).
+
+        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.
          */
-    }
-    //printf("\t-. mi = %s\n", mi.toPrettyChars());
+        idec.members.foreachDsymbol( s => s.setScope(sc2) );
 
-    if (ti.memberOf) // already appended to some module
-    {
-        assert(mi.isRoot(), "can only re-append to a root module");
-        if (ti.memberOf.isRoot())
-            return null; // no need to move to another root module
-    }
+        idec.members.foreachDsymbol( s => s.importAll(sc2) );
 
-    Dsymbols* a = mi.members;
-    a.push(ti);
-    ti.memberOf = mi;
-    if (mi.semanticRun >= PASS.semantic2done && mi.isRoot())
-        Module.addDeferredSemantic2(ti);
-    if (mi.semanticRun >= PASS.semantic3done && mi.isRoot())
-        Module.addDeferredSemantic3(ti);
-    return a;
-}
+        idec.members.foreachDsymbol( s => s.dsymbolSemantic(sc2) );
 
-private void expandMembers(TemplateInstance ti,Scope* sc2)
-{
-    ti.members.foreachDsymbol( (s) { s.setScope (sc2); } );
+        idec.semanticRun = PASS.semanticdone;
+        //printf("-InterfaceDeclaration.dsymbolSemantic(%s), type = %p\n", toChars(), type);
 
-    ti.members.foreachDsymbol( (s) { s.importAll(sc2); } );
+        sc2.pop();
 
-    if (!ti.aliasdecl)
-    {
-        /* static if's are crucial to evaluating aliasdecl correctly. But
-         * evaluating the if/else bodies may require aliasdecl.
-         * So, evaluate the condition for static if's, but not their if/else bodies.
-         * Then try to set aliasdecl.
-         * Later do the if/else bodies.
-         * https://issues.dlang.org/show_bug.cgi?id=23598
-         * It might be better to do this by attaching a lambda to the StaticIfDeclaration
-         * to do the oneMembers call after the sid.include(sc2) is run as part of dsymbolSemantic().
-         */
-        bool done;
-        void staticIfDg(Dsymbol s)
+        if (global.errors != errors)
         {
-            if (done || ti.aliasdecl)
-                return;
-            //printf("\t staticIfDg on '%s %s' in '%s'\n",  s.kind(), s.toChars(), this.toChars());
-            if (!s.isStaticIfDeclaration())
-            {
-                //s.dsymbolSemantic(sc2);
-                done = true;
-                return;
-            }
-            auto sid = s.isStaticIfDeclaration();
-            sid.include(sc2);
-            if (ti.members.length)
+            // The type is no good.
+            idec.type = Type.terror;
+        }
+
+        version (none)
+        {
+            if (type.ty == Tclass && (cast(TypeClass)idec.type).sym != idec)
             {
-                Dsymbol sa;
-                if (oneMembers(ti.members, sa, ti.tempdecl.ident) && sa)
-                    ti.aliasdecl = sa;
+                printf("this = %p %s\n", idec, idec.toChars());
+                printf("type = %d sym = %p\n", idec.type.ty, (cast(TypeClass)idec.type).sym);
             }
-            done = true;
         }
+        assert(idec.type.ty != Tclass || (cast(TypeClass)idec.type).sym == idec);
 
-        ti.members.foreachDsymbol(&staticIfDg);
+        version (none)
+        {
+            // @@@DEPRECATED_2.120@@@ https://dlang.org/deprecate.html#scope%20as%20a%20type%20constraint
+            // Deprecated in 2.087
+            // Made an error in 2.100, but removal depends on `scope class` being removed too
+            // 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_)
+                error(idec.loc, "`scope` as a type constraint is obsolete.  Use `scope` at the usage site.");
+        }
     }
+}
 
-    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();
-    }
+/*
+Adds dsym as a member of scope sds.
 
-    ti.members.foreachDsymbol(&symbolDg);
+Params:
+    dsym = dsymbol to inserted
+    sc = scope where the dsymbol is declared
+    sds = ScopeDsymbol where dsym is inserted
+*/
+void addMember(Dsymbol dsym, Scope* sc, ScopeDsymbol sds)
+{
+    auto addMemberVisitor = new AddMemberVisitor(sc, sds);
+    dsym.accept(addMemberVisitor);
 }
 
-private void tryExpandMembers(TemplateInstance ti, Scope* sc2)
+private void attribAddMember(AttribDeclaration atb, Scope* sc, ScopeDsymbol sds)
 {
-    __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)
+    Dsymbols* d = atb.include(sc);
+    if (d)
     {
-        global.gag = 0; // ensure error message gets printed
-        .error(ti.loc, "%s `%s` recursive expansion exceeded allowed nesting limit", ti.kind, ti.toPrettyChars);
-        fatal();
+        Scope* sc2 = atb.newScope(sc);
+        d.foreachDsymbol( s => s.addMember(sc2, sds) );
+        if (sc2 != sc)
+            sc2.pop();
     }
-
-    ti.expandMembers(sc2);
-
-    nest--;
 }
 
-private void trySemantic3(TemplateInstance ti, Scope* sc2)
+private extern(C++) class AddMemberVisitor : Visitor
 {
-    // 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(ti.loc, "%s `%s` recursive expansion exceeded allowed nesting limit", ti.kind, ti.toPrettyChars);
-        fatal();
-    }
-
-    semantic3(ti, sc2);
+    alias visit = Visitor.visit;
 
-    --nest;
-}
+    Scope* sc;
+    ScopeDsymbol sds;
 
-void templateInstanceSemantic(TemplateInstance tempinst, Scope* sc, ArgumentList argumentList)
-{
-    //printf("[%s] TemplateInstance.dsymbolSemantic('%s', this=%p, gag = %d, sc = %p)\n", tempinst.loc.toChars(), tempinst.toChars(), tempinst, global.gag, sc);
-    version (none)
+    this(Scope* sc, ScopeDsymbol sds) @safe
     {
-        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");
-        }
+        this.sc = sc;
+        this.sds = sds;
     }
 
-    static if (LOG)
-    {
-        printf("\n+TemplateInstance.dsymbolSemantic('%s', this=%p)\n", tempinst.toChars(), tempinst);
-    }
-    if (tempinst.inst) // if semantic() was already run
+    override void visit(Dsymbol dsym)
     {
-        static if (LOG)
+        //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);
+        dsym.parent = sds;
+        if (dsym.isAnonymous()) // no name, so can't add it to symbol table
+            return;
+
+        if (!sds.symtabInsert(dsym)) // if name is already defined
         {
-            printf("-TemplateInstance.dsymbolSemantic('%s', this=%p) already run\n",
-                   tempinst.inst.toChars(), tempinst.inst);
-        }
-        return;
-    }
-    if (tempinst.semanticRun != PASS.initial)
-    {
-        static if (LOG)
+            if (dsym.isAliasDeclaration() && !dsym._scope)
+                dsym.setScope(sc);
+            Dsymbol s2 = sds.symtabLookup(dsym, dsym.ident);
+            /* https://issues.dlang.org/show_bug.cgi?id=17434
+             *
+             * If we are trying to add an import to the symbol table
+             * that has already been introduced, then keep the one with
+             * larger visibility. This is fine for imports because if
+             * we have multiple imports of the same file, if a single one
+             * is public then the symbol is reachable.
+             */
+            if (auto i1 = dsym.isImport())
+            {
+                if (auto i2 = s2.isImport())
+                {
+                    if (sc.explicitVisibility && sc.visibility > i2.visibility)
+                        sds.symtab.update(dsym);
+                }
+            }
+
+            // If using C tag/prototype/forward declaration rules
+            if (sc && sc.inCfile && !dsym.isImport())
+            // When merging master, replace with: if (sc && sc.inCfile && !dsym.isImport())
+            {
+                if (handleTagSymbols(*sc, dsym, s2, sds))
+                    return;
+                if (handleSymbolRedeclarations(*sc, dsym, s2, sds))
+                    return;
+
+                sds.multiplyDefined(Loc.initial, dsym, s2);  // ImportC doesn't allow overloading
+                dsym.errors = true;
+                return;
+            }
+
+            if (!s2.overloadInsert(dsym))
+            {
+                if (auto _td = s2.isTemplateDeclaration())
+                    _td.computeOneMember();
+                sds.multiplyDefined(Loc.initial, dsym, s2);
+                dsym.errors = true;
+            }
+        }
+        if (sds.isAggregateDeclaration() || sds.isEnumDeclaration())
         {
-            printf("Recursive template expansion\n");
+            if (dsym.ident == Id.__sizeof ||
+                !(sc && sc.inCfile) && (dsym.ident == Id.__xalignof || dsym.ident == Id._mangleof))
+            {
+                .error(dsym.loc, "%s `%s` `.%s` property cannot be redefined", dsym.kind, dsym.toPrettyChars, dsym.ident.toChars());
+                dsym.errors = true;
+            }
         }
-        auto ungag = Ungag(global.gag);
-        if (!tempinst.gagged)
-            global.gag = 0;
-        .error(tempinst.loc, "%s `%s` recursive template expansion", tempinst.kind, tempinst.toPrettyChars);
-        if (tempinst.gagged)
-            tempinst.semanticRun = PASS.initial;
-        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())
+    override void visit(StaticAssert _)
     {
-        tempinst.minst = null;
+        // we didn't add anything
     }
 
-    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.
+    /*****************************
+     * Add import to sd's symbol table.
      */
-    if (!tempinst.findTempDecl(sc, null) || !tempinst.semanticTiargs(sc) || !tempinst.findBestMatch(sc, argumentList))
+    override void visit(Import imp)
     {
-    Lerror:
-        if (tempinst.gagged)
+        //printf("Import.addMember(this=%s, sds=%s, sc=%p)\n", imp.toChars(), sds.toChars(), sc);
+        if (imp.names.length == 0)
+            return visit(cast(Dsymbol)imp);
+        if (imp.aliasId)
+            visit(cast(Dsymbol)imp);
+
+        /* Instead of adding the import to sds's symbol table,
+         * add each of the alias=name pairs
+         */
+        for (size_t i = 0; i < imp.names.length; i++)
         {
-            // https://issues.dlang.org/show_bug.cgi?id=13220
-            // Roll back status for later semantic re-running
-            tempinst.semanticRun = PASS.initial;
+            Identifier name = imp.names[i];
+            Identifier _alias = imp.aliases[i];
+            if (!_alias)
+                _alias = name;
+            auto tname = new TypeIdentifier(imp.loc, name);
+            auto ad = new AliasDeclaration(imp.loc, _alias, tname);
+            ad._import = imp;
+            addMember(ad, sc, sds);
+            imp.aliasdecls.push(ad);
         }
-        else
-            tempinst.inst = tempinst;
-        tempinst.errors = true;
-        return;
-    }
-    TemplateDeclaration tempdecl = tempinst.tempdecl.isTemplateDeclaration();
-    assert(tempdecl);
-
-    if (global.params.v.templates)
-        TemplateStats.incInstance(tempdecl, tempinst, global.params.v.templatesListInstances);
-
-    tempdecl.checkDeprecated(tempinst.loc, sc);
-
-    // If tempdecl is a mixin, disallow it
-    if (tempdecl.ismixin)
-    {
-        .error(tempinst.loc, "%s `%s` mixin templates are not regular templates", tempinst.kind, tempinst.toPrettyChars);
-        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)
+    override void visit(AttribDeclaration atb)
     {
-        tempinst.inst = tempinst;
-        return aliasSeqInstanceSemantic(tempinst, sc, tempdecl);
+       attribAddMember(atb, sc, sds);
     }
 
-    /* Greatly simplified semantic processing for Alias templates
-     */
-    else if (tempdecl.isTrivialAlias)
+    override void visit(StorageClassDeclaration stcd)
     {
-        tempinst.inst = tempinst;
-        return aliasInstanceSemantic(tempinst, sc, tempdecl);
-    }
+        Dsymbols* d = stcd.include(sc);
+        if (d)
+        {
+            Scope* sc2 = stcd.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 |= stcd.stc & STC.local;
+                    if (auto sdecl = s.isStorageClassDeclaration()) // TODO: why is this not enough to deal with the nested case?
+                    {
+                        sdecl.stc |= stcd.stc & STC.local;
+                    }
+                }
+                s.addMember(sc2, sds);
+            });
 
-    /* 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, argumentList);
-    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;
+            if (sc2 != sc)
+                sc2.pop();
+        }
     }
-    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)
+    override void visit(VisibilityDeclaration visd)
+    {
+        if (visd.pkg_identifiers)
         {
-            // It had succeeded, mark it is a non-gagged instantiation,
-            // and reuse it.
-            tempinst.inst.gagged = tempinst.gagged;
+            Dsymbol tmp;
+            Package.resolve(visd.pkg_identifiers, &tmp, null);
+            visd.visibility.pkg = tmp ? tmp.isPackage() : null;
+            visd.pkg_identifiers = null;
         }
-
-        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()))
+        if (visd.visibility.kind == Visibility.Kind.package_ && visd.visibility.pkg && sc._module)
         {
-            /* 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) scope @safe
-                {
-                    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(StructDeclaration sd)
-                {
-                    // need to visit auto-generated methods as well
-                    if (sd.xeq) visit(sd.xeq);
-                    if (sd.xcmp) visit(sd.xcmp);
-                    if (sd.xhash) visit(sd.xhash);
-                    visit(cast(ScopeDsymbol)sd);
-                }
-
-                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);
+            Module m = sc._module;
 
-            if (!global.params.allInst &&
-                tempinst.minst) // if inst was not speculative...
+            // https://issues.dlang.org/show_bug.cgi?id=17441
+            // While isAncestorPackageOf does an equality check, the fix for the issue 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() || !visd.visibility.pkg.ident.equals(m.isPackage().ident))
             {
-                assert(!tempinst.minst.isRoot()); // ... it was previously appended to a non-root module
-                // Append again to the root module members[], so that the instance will
-                // get codegen chances (depending on `tempinst.inst.needsCodegen()`).
-                tempinst.inst.appendToModuleMember();
+                Package pkg = m.parent ? m.parent.isPackage() : null;
+                if (!pkg || !visd.visibility.pkg.isAncestorPackageOf(pkg))
+                    .error(visd.loc, "%s `%s` does not bind to one of ancestor packages of module `%s`", visd.kind(), visd.toPrettyChars(false), m.toPrettyChars(true));
             }
-
-            assert(tempinst.inst.memberOf && tempinst.inst.memberOf.isRoot(), "no codegen chances");
-        }
-
-        // 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;
+        attribAddMember(visd, sc, sds);
     }
-    static if (LOG)
+
+    override void visit(StaticIfDeclaration sid)
     {
-        printf("\timplement template instance %s '%s'\n", tempdecl.parent.toChars(), tempinst.toChars());
-        printf("\ttempdecl %s\n", tempdecl.toChars());
+        //printf("StaticIfDeclaration::addMember() '%s'\n", sid.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;
+         * }
+         */
+        sid.scopesym = sds;
     }
-    const errorsave = global.errors;
-
-    tempinst.inst = tempinst;
-    tempinst.parent = tempinst.enclosing ? tempinst.enclosing : tempdecl.parent;
-    //printf("parent = '%s'\n", parent.kind());
-
-    if (global.params.v.templates)
-        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.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++)
+    override void visit(StaticForeachDeclaration sfd)
     {
-        if ((*tempdecl.parameters)[i].isTemplateThisParameter() is null)
-            continue;
-        Type t = isType((*tempinst.tiargs)[i]);
-        assert(t);
-        if (STC stc = ModToStc(t.mod))
-        {
-            //printf("t = %s, stc = x%llx\n", t.toChars(), stc);
-            auto s = new Dsymbols(new StorageClassDeclaration(stc, tempinst.members));
-            tempinst.members = s;
-        }
-        break;
+        // used only for caching the enclosing symbol
+        sfd.scopesym = sds;
     }
 
-    // Create our own scope for the template parameters
-    Scope* _scope = tempdecl._scope;
-    if (tempdecl.semanticRun == PASS.initial)
+    /***************************************
+     * Lazily initializes the scope to forward to.
+     */
+    override void visit(ForwardingAttribDeclaration fad)
     {
-        .error(tempinst.loc, "%s `%s` template instantiation `%s` forward references template declaration `%s`",
-           tempinst.kind, tempinst.toPrettyChars, tempinst.toChars(), tempdecl.toChars());
-        return;
+        fad.sym.parent = sds;
+        sds = fad.sym;
+        attribAddMember(fad, sc, fad.sym);
     }
 
-    static if (LOG)
+    override void visit(MixinDeclaration md)
     {
-        printf("\tcreate scope for template parameters '%s'\n", tempinst.toChars());
+        //printf("MixinDeclaration::addMember(sc = %p, sds = %p, memnum = %d)\n", sc, sds, md.memnum);
+        md.scopesym = sds;
     }
-    tempinst.argsym = new ScopeDsymbol();
-    tempinst.argsym.parent = _scope.parent;
-    _scope = _scope.push(tempinst.argsym);
-    _scope.tinst = tempinst;
-    _scope.minst = tempinst.minst;
-    //scope.stc = STC.none;
-
-    // Declare each template parameter as an alias for the argument type
-    Scope* paramscope = _scope.push();
-    paramscope.stc = STC.none;
-    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)
+    override void visit(DebugSymbol ds)
     {
-        static if (LOG)
+        //printf("DebugSymbol::addMember('%s') %s\n", sds.toChars(), ds.toChars());
+        Module m = sds.isModule();
+        // Do not add the member to the symbol table,
+        // just make sure subsequent debug declarations work.
+        if (!m)
         {
-            printf("\t adding member '%s' %p kind %s to '%s'\n", s.toChars(), s, s.kind(), tempinst.toChars());
+            .error(ds.loc, "%s `%s` declaration must be at module level", ds.kind, ds.toPrettyChars);
+            ds.errors = true;
         }
-        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.length = %d\n", tempinst.members.length);
-    if (tempinst.members.length)
-    {
-        Dsymbol s;
-        if (oneMembers(tempinst.members, s, tempdecl.ident) && s)
+        else
         {
-            //printf("tempdecl.ident = %s, s = `%s %s`\n", tempdecl.ident.toChars(), s.kind(), s.toPrettyChars());
-            //printf("setting aliasdecl\n");
-            tempinst.aliasdecl = s;
+            if (m.debugidsNot && findCondition(*m.debugidsNot, ds.ident))
+            {
+                .error(ds.loc, "%s `%s` defined after use", ds.kind, ds.toPrettyChars);
+                ds.errors = true;
+            }
+            if (!m.debugids)
+                m.debugids = new Identifiers();
+            m.debugids.push(ds.ident);
         }
     }
 
-    /* If function template declaration
-     */
-    if (argumentList.length > 0 && tempinst.aliasdecl)
+    override void visit(VersionSymbol vs)
     {
-        if (auto fd = tempinst.aliasdecl.isFuncDeclaration())
+        //printf("VersionSymbol::addMember('%s') %s\n", sds.toChars(), vs.toChars());
+        Module m = sds.isModule();
+        // Do not add the member to the symbol table,
+        // just make sure subsequent debug declarations work.
+        VersionCondition.checkReserved(vs.loc, vs.ident.toString());
+        if (!m)
         {
-            /* 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.inferenceArguments = argumentList;
+            .error(vs.loc, "%s `%s` declaration must be at module level", vs.kind, vs.toPrettyChars);
+            vs.errors = true;
+        }
+        else
+        {
+            if (m.versionidsNot && findCondition(*m.versionidsNot, vs.ident))
+            {
+                .error(vs.loc, "%s `%s` defined after use", vs.kind, vs.toPrettyChars);
+                vs.errors = true;
+            }
+            if (!m.versionids)
+                m.versionids = new Identifiers();
+            m.versionids.push(vs.ident);
         }
-    }
 
-    // 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.length)
+    override void visit(Nspace ns)
     {
-        Dsymbol s;
-        if (oneMembers(tempinst.members, s, tempdecl.ident) && s)
+        visit(cast(Dsymbol)ns);
+
+        if (ns.members)
         {
-            if (!tempinst.aliasdecl || tempinst.aliasdecl != s)
+            if (!ns.symtab)
+                ns.symtab = new DsymbolTable();
+            // The namespace becomes 'imported' into the enclosing scope
+            for (Scope* sce = sc; 1; sce = sce.enclosing)
             {
-                //printf("tempdecl.ident = %s, s = `%s %s`\n", tempdecl.ident.toChars(), s.kind(), s.toPrettyChars());
-                //printf("setting aliasdecl 2\n");
-                tempinst.aliasdecl = s;
+                ScopeDsymbol sds2 = sce.scopesym;
+                if (sds2)
+                {
+                    sds2.importScope(ns, Visibility(Visibility.Kind.public_));
+                    break;
+                }
             }
+            assert(sc);
+            sc = sc.push(ns);
+            sc.linkage = LINK.cpp; // namespaces default to C++ linkage
+            sc.parent = ns;
+            ns.members.foreachDsymbol(s => s.addMember(sc, ns));
+            sc.pop();
         }
     }
 
-    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.
-     */
+    override void visit(EnumDeclaration ed)
     {
-        bool found_deferred_ad = false;
-        for (size_t i = 0; i < Module.deferred.length; i++)
+        version (none)
         {
-            Dsymbol sd = Module.deferred[i];
-            AggregateDeclaration ad = sd.isAggregateDeclaration();
-            if (ad && ad.parent && ad.parent.isTemplateInstance())
+            printf("EnumDeclaration::addMember() %s\n", ed.toChars());
+            for (size_t i = 0; i < ed.members.length; i++)
             {
-                //printf("deferred template aggregate: %s %s\n",
-                //        sd.parent.toChars(), sd.toChars());
-                found_deferred_ad = true;
-                if (ad.parent == tempinst)
-                {
-                    ad.deferred = tempinst;
-                    break;
-                }
+                EnumMember em = (*ed.members)[i].isEnumMember();
+                printf("    member %s\n", em.toChars());
             }
         }
-        if (found_deferred_ad || Module.deferred.length)
-            goto Laftersemantic;
-    }
+        if (!ed.isAnonymous())
+        {
+            visit(cast(Dsymbol)ed);
+        }
 
-    /* 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);
+        addEnumMembersToSymtab(ed, sc, sds);
     }
-    if (global.errors != errorsave)
-        goto Laftersemantic;
+}
 
-    if ((sc.func || sc.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;
+/*******************************************
+ * Add members of EnumDeclaration to the symbol table(s).
+ * Params:
+ *      ed = EnumDeclaration
+ *      sc = context of `ed`
+ *      sds = symbol table that `ed` resides in
+ */
+void addEnumMembersToSymtab(EnumDeclaration ed, Scope* sc, ScopeDsymbol sds)
+{
+    const bool isCEnum = sc.inCfile; // it's an ImportC enum
+    //printf("addEnumMembersToSymtab(ed: %s added: %d Cfile: %d)\n", ed.toChars(), ed.added, isCEnum);
+    if (ed.added)
+        return;
+    ed.added = true;
 
-        //printf("Run semantic3 on %s\n", toChars());
+    if (!ed.members)
+        return;
 
-        /* https://issues.dlang.org/show_bug.cgi?id=23965
-         * DRuntime hooks are not deprecated, but may be used for deprecated
-         * types. Deprecations are disabled while analysing hooks to avoid
-         * spurious error messages.
-         */
-        auto saveUseDeprecated = global.params.useDeprecated;
-        if (sc.isDeprecated() && isDRuntimeHook(tempinst.name))
-            global.params.useDeprecated = DiagnosticReporting.off;
+    const bool isAnon = ed.isAnonymous();
 
-        tempinst.trySemantic3(sc2);
+    if ((isCEnum || isAnon) && !sds.symtab)
+        sds.symtab = new DsymbolTable();
 
-        global.params.useDeprecated = saveUseDeprecated;
+    if ((isCEnum || !isAnon) && !ed.symtab)
+        ed.symtab = new DsymbolTable();
 
-        for (size_t i = 0; i < deferred.length; i++)
+    ed.members.foreachDsymbol( (s)
+    {
+        if (EnumMember em = s.isEnumMember())
         {
-            //printf("+ run deferred semantic3 on %s\n", deferred[i].toChars());
-            deferred[i].semantic3(null);
+            //printf("adding EnumMember %s to %s %d\n", em.toChars(), ed.toChars(), isCEnum);
+            em.ed = ed;
+            if (isCEnum)
+            {
+                /* C doesn't add the enum member to the symbol table of the enum tag, it adds
+                 * it to the symbol table that the tag is in. This is in contrast to D, where enum
+                 * members become members of the enum tag. To accommodate this, we add
+                 * the enum members to both symbol tables.
+                 */
+                em.addMember(sc, ed);   // add em to ed's symbol table
+                if (em.errors)
+                    return;
+                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);
+            }
         }
+    });
+}
 
-        tempinst.deferred = null;
+private OverloadSet mergeOverloadSet(ScopeDsymbol sds, Identifier ident, OverloadSet os, Dsymbol s)
+{
+    if (!os)
+    {
+        os = new OverloadSet(ident);
+        os.parent = sds;
     }
-    else if (tempinst.tinst)
+    if (OverloadSet os2 = s.isOverloadSet())
     {
-        bool doSemantic3 = false;
-        FuncDeclaration fd;
-        if (tempinst.aliasdecl)
-            fd = tempinst.aliasdecl.toAlias2().isFuncDeclaration();
-
-        if (fd)
+        // Merge the cross-module overload set 'os2' into 'os'
+        if (os.a.length == 0)
         {
-            /* 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.length == 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);
+            os.a.setDim(os2.a.length);
+            memcpy(os.a.tdata(), os2.a.tdata(), (os.a[0]).sizeof * os2.a.length);
         }
-        if (doSemantic3)
-            tempinst.trySemantic3(sc2);
-
-        TemplateInstance ti = tempinst.tinst;
-        int nest = 0;
-        while (ti && !ti.deferred && ti.tinst)
+        else
         {
-            ti = ti.tinst;
-            if (++nest > global.recursionLimit)
+            for (size_t i = 0; i < os2.a.length; i++)
             {
-                global.gag = 0; // ensure error message gets printed
-                .error(tempinst.loc, "%s `%s` recursive expansion", tempinst.kind, tempinst.toPrettyChars);
-                fatal();
+                os = mergeOverloadSet(sds, ident, os, os2.a[i]);
             }
         }
-        if (ti && ti.deferred)
+        return os;
+    }
+
+    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())
         {
-            //printf("deferred semantic3 of %p %s, ti = %s, ti.deferred = %p\n", this, toChars(), ti.toChars());
-            for (size_t i = 0;; i++)
+            if (s2.isDeprecated() || (s2.visible() < s.visible() && s.visible().kind != Visibility.Kind.none))
             {
-                if (i == ti.deferred.length)
-                {
-                    ti.deferred.push(tempinst);
-                    break;
-                }
-                if ((*ti.deferred)[i] == tempinst)
-                    break;
+                os.a[j] = s;
             }
+            return os;
         }
     }
+    os.push(s);
+    return os;
+}
 
-    if (tempinst.aliasdecl)
+/***
+ * Returns true if any of the symbols `p1` or `p2` resides in the enclosing
+ * instantiation scope of `this`.
+ */
+bool followInstantiationContext(Dsymbol d,Dsymbol p1, Dsymbol p2 = null)
+{
+    static bool has2This(Dsymbol s)
     {
-        /* 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();
-
-        // stop AliasAssign tuple building
-        if (auto td = tempinst.aliasdecl.isTupleDeclaration())
-            td.building = false;
+        if (auto f = s.isFuncDeclaration())
+            return f.hasDualContext;
+        if (auto ad = s.isAggregateDeclaration())
+            return ad.vthis2 !is null;
+        return false;
     }
 
-Laftersemantic:
-    sc2.pop();
-    _scope.pop();
+    if (!has2This(d))
+        return false;
 
-    // Give additional context info if error occurred during instantiation
-    if (global.errors != errorsave)
+    assert(p1);
+    auto outer = d.toParent();
+    while (outer)
     {
-        if (!tempinst.errors)
+        auto ti = outer.isTemplateInstance();
+        if (!ti)
+            return false;
+        foreach (oarg; *ti.tiargs)
         {
-            if (!tempdecl.literal)
-                .error(tempinst.loc, "%s `%s` error instantiating", tempinst.kind, tempinst.toPrettyChars);
-            if (tempinst.tinst)
-                tempinst.tinst.printInstantiationTrace();
+            auto sa = getDsymbol(oarg);
+            if (!sa)
+                continue;
+            sa = sa.toAlias().toParent2();
+            if (!sa)
+                continue;
+            if (sa == p1)
+                return true;
+            if (p2 && sa == p2)
+                return true;
         }
-        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.initial;
-            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);
+        outer = ti.tempdecl.toParent();
     }
+    return false;
 }
 
-/******************************************************
- * 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 = STC.none;
-    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().
+/**
+ * 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.
  */
-private
-void aliasInstanceSemantic(TemplateInstance tempinst, Scope* sc, TemplateDeclaration tempdecl)
+Dsymbol toParentP(Dsymbol d, Dsymbol p1, Dsymbol p2 = null)
 {
-    //printf("[%s] aliasInstance.dsymbolSemantic('%s')\n", tempinst.loc.toChars(), tempinst.toChars());
-    Scope* paramscope = sc.push();
-    paramscope.stc = STC.none;
-    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;
+    return followInstantiationContext(d, p1, p2) ? d.toParent2() : d.toParentLocal();
 }
 
 // function used to perform semantic on AliasDeclaration
@@ -6178,196 +6609,6 @@ void aliasSemantic(AliasDeclaration ds, Scope* sc)
     normalRet();
 }
 
-/**********************************************
- * 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 findTempDecl(TemplateInstance ti, Scope* sc, WithScopeSymbol* pwithsym)
-{
-    if (pwithsym)
-        *pwithsym = null;
-
-    if (ti.havetempdecl)
-        return true;
-
-    //printf("TemplateInstance.findTempDecl() %s\n", toChars());
-    if (!ti.tempdecl)
-    {
-        /* Given:
-         *    foo!( ... )
-         * figure out which TemplateDeclaration foo refers to.
-         */
-        Identifier id = ti.name;
-        Dsymbol scopesym;
-        Dsymbol s = sc.search(ti.loc, id, scopesym);
-        if (!s)
-        {
-            s = sc.search_correct(id);
-            if (s)
-                .error(ti.loc, "%s `%s` template `%s` is not defined, did you mean %s?", ti.kind, ti.toPrettyChars(), id.toChars(), s.toChars());
-            else
-                .error(ti.loc, "%s `%s` template `%s` is not defined", ti.kind, ti.toPrettyChars(), 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 ti2;
-        if (s.parent && (ti2 = s.parent.isTemplateInstance()) !is null)
-        {
-            if (ti2.tempdecl && ti2.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 = ti2.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(ti.loc, sc);
-
-        if (!ti.updateTempDecl(sc, s))
-        {
-            return false;
-        }
-    }
-    assert(ti.tempdecl);
-
-    // Look for forward references
-    auto tovers = ti.tempdecl.isOverloadSet();
-    foreach (size_t oi; 0 .. tovers ? tovers.a.length : 1)
-    {
-        Dsymbol dstart = tovers ? tovers.a[oi] : ti.tempdecl;
-        int r = overloadApply(dstart, (Dsymbol s)
-        {
-            auto td = s.isTemplateDeclaration();
-            if (!td)
-                return 0;
-
-            if (td.semanticRun == PASS.initial)
-            {
-                if (td._scope)
-                {
-                    // Try to fix forward reference. Ungag errors while doing so.
-                    auto ungag = td.ungagSpeculative();
-                    td.dsymbolSemantic(td._scope);
-                }
-                if (td.semanticRun == PASS.initial)
-                {
-                    .error(ti.loc, "%s `%s` `%s` forward references template declaration `%s`",
-                           ti.kind, ti.toPrettyChars(), ti.toChars(), td.toChars());
-                    return 1;
-                }
-            }
-            return 0;
-        });
-        if (r)
-            return false;
-    }
-    return true;
-}
-
-private bool findMixinTempDecl(TemplateMixin tm, Scope* sc)
-{
-    // Follow qualifications to find the TemplateDeclaration
-    if (!tm.tempdecl)
-    {
-        Expression e;
-        Type t;
-        Dsymbol s;
-        tm.tqual.resolve(tm.loc, sc, e, t, s);
-        if (!s)
-        {
-            .error(tm.loc, "%s `%s` is not defined", tm.kind, tm.toPrettyChars);
-            return false;
-        }
-        s = s.toAlias();
-        tm.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)
-                    {
-                        tm.tempdecl = os;
-                        break;
-                    }
-                    ds = s2;
-                }
-            }
-        }
-        if (!tm.tempdecl)
-        {
-            .error(tm.loc, "%s `%s` - `%s` is a %s, not a template", tm.kind,
-                   tm.toPrettyChars, s.toChars(), s.kind());
-            return false;
-        }
-    }
-    assert(tm.tempdecl);
-
-    // Look for forward references
-    auto tovers = tm.tempdecl.isOverloadSet();
-    foreach (size_t oi; 0 .. tovers ? tovers.a.length : 1)
-    {
-        Dsymbol dstart = tovers ? tovers.a[oi] : tm.tempdecl;
-        int r = overloadApply(dstart, (Dsymbol s)
-        {
-            auto td = s.isTemplateDeclaration();
-            if (!td)
-                return 0;
-
-            if (td.semanticRun == PASS.initial)
-            {
-                if (td._scope)
-                    td.dsymbolSemantic(td._scope);
-                else
-                {
-                    tm.semanticRun = PASS.initial;
-                    return 1;
-                }
-            }
-            return 0;
-        });
-        if (r)
-            return false;
-    }
-    return true;
-}
-
 /********************
  * Perform semantic on AliasAssignment.
  * Has a lot of similarities to aliasSemantic(). Perhaps they should share code.
@@ -6483,7 +6724,8 @@ private void aliasAssignSemantic(AliasAssign ds, Scope* sc)
     // Try AliasSeq optimization
     if (auto ti = ds.type.isTypeInstance())
     {
-        if (!ti.tempinst.findTempDecl(sc, null))
+        import dmd.templatesem : findTempDecl;
+        if (!findTempDecl(ti.tempinst, sc, null))
             return errorRet();
         if (auto tempinst = isAliasSeq(sc, ti))
         {
@@ -6731,9 +6973,7 @@ private TemplateInstance isAliasSeq(Scope* sc, TypeInstance ti)
         int r = overloadApply(dstart, (Dsymbol s)
         {
             auto td = s.isTemplateDeclaration();
-            if (!td || !td.isTrivialAliasSeq)
-                return 1;
-            return 0;
+            return (td && td.isTrivialAliasSeq) ? 0 : 1;
         });
         if (r)
             return null;
@@ -7883,7 +8123,7 @@ extern(C++) class ImportAllVisitor : Visitor
          * gets imported, it is unaffected by context.
          * Ignore prevsc.
          */
-        Scope* sc = Scope.createGlobal(m, global.errorSink); // create root scope
+        Scope* sc = scopeCreateGlobal(m, global.errorSink); // create root scope
 
         if (m.md && m.md.msg)
             m.md.msg = semanticString(sc, m.md.msg, "deprecation message");
@@ -7928,7 +8168,7 @@ extern(C++) class ImportAllVisitor : Visitor
             s.importAll(sc);
         }
         sc = sc.pop();
-        sc.pop(); // 2 pops because Scope.createGlobal() created 2
+        sc.pop(); // 2 pops because scopeCreateGlobal() created 2
     }
 
     override void visit(AttribDeclaration atb)
@@ -8055,6 +8295,8 @@ void setFieldOffset(Dsymbol d, AggregateDeclaration ad, FieldState* fieldState,
 
 private extern(C++) class SetFieldOffsetVisitor : Visitor
 {
+    import dmd.typesem: size;
+
     alias visit = Visitor.visit;
 
     AggregateDeclaration ad;
@@ -8801,6 +9043,8 @@ bool isGNUABITag(Expression e)
  */
 private Expression callScopeDtor(VarDeclaration vd, Scope* sc)
 {
+    import dmd.typesem: size;
+
     //printf("VarDeclaration::callScopeDtor() %s\n", toChars());
 
     // Destruction of STC.field's is handled by buildDtor()
@@ -9352,7 +9596,7 @@ bool isAbstract(ClassDeclaration cd)
         static int virtualSemantic(Dsymbol s, void*)
         {
             auto fd = s.isFuncDeclaration();
-            if (fd && !(fd.storage_class & STC.static_) && !fd.isUnitTestDeclaration())
+            if (fd && !(fd.storage_class & STC.static_) && !fd.isUnitTestDeclaration() && !fd.isCtorDeclaration())
                 fd.dsymbolSemantic(null);
             return 0;
         }
@@ -9394,6 +9638,8 @@ void finalizeSize(AggregateDeclaration ad)
  */
 bool _isZeroInit(Expression exp)
 {
+    import dmd.typesem: size;
+
     switch (exp.op)
     {
         case EXP.int64:
@@ -9559,6 +9805,8 @@ private bool checkOverlappedFields(AggregateDeclaration ad)
 
 private extern(C++) class FinalizeSizeVisitor : Visitor
 {
+    import dmd.typesem: size;
+
     alias visit = Visitor.visit;
 
     override void visit(ClassDeclaration outerCd)
@@ -9930,7 +10178,7 @@ extern (D) bool oneMembers(Dsymbols* members, out Dsymbol ps, Identifier ident)
     for (size_t i = 0; i < members.length; i++)
     {
         Dsymbol sx = (*members)[i];
-        bool x = sx.oneMember(ps, ident); //MYTODO: this temporarily creates a new dependency to dsymbolsem, will need to extract oneMembers() later
+        bool x = sx.oneMember(ps, ident);
         //printf("\t[%d] kind %s = %d, s = %p\n", i, sx.kind(), x, *ps);
         if (!x)
         {
@@ -9938,38 +10186,38 @@ extern (D) bool oneMembers(Dsymbols* members, out Dsymbol ps, Identifier ident)
             assert(ps is null);
             return false;
         }
-        if (ps)
+        if (!ps)
+            continue;
+
+        assert(ident);
+        if (!ps.ident || !ps.ident.equals(ident))
+            continue;
+        if (!s)
+            s = ps;
+        else if (s.isOverloadable() && ps.isOverloadable())
         {
-            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)
             {
-                // 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)
                 {
-                    assert(!f1.isFuncAliasDeclaration());
-                    assert(!f2.isFuncAliasDeclaration());
-                    for (; f1 != f2; f1 = f1.overnext0)
+                    if (f1.overnext0 is null)
                     {
-                        if (f1.overnext0 is null)
-                        {
-                            f1.overnext0 = f2;
-                            break;
-                        }
+                        f1.overnext0 = f2;
+                        break;
                     }
                 }
             }
-            else // more than one symbol
-            {
-                ps = null;
-                //printf("\tfalse 2\n");
-                return false;
-            }
+        }
+        else // more than one symbol
+        {
+            ps = null;
+            //printf("\tfalse 2\n");
+            return false;
         }
     }
     ps = s; // s is the one symbol, null if none
index b2a61dda441bd4d83245074f119004a0e4a39820..f68c92811c56a28ff12cfe0124c6b9e6bc1a15f0 100644 (file)
@@ -40,41 +40,27 @@ 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.attrib;
-import dmd.dcast;
-import dmd.dclass;
 import dmd.declaration;
-import dmd.dinterpret;
 import dmd.dmodule;
 import dmd.dscope;
 import dmd.dsymbol;
-import dmd.dsymbolsem : aliasSemantic, oneMembers, toAlias;
 import dmd.errors;
 import dmd.errorsink;
 import dmd.expression;
 import dmd.func;
 import dmd.globals;
 import dmd.hdrgen;
-import dmd.id;
 import dmd.identifier;
-import dmd.impcnvtab;
-import dmd.init;
 import dmd.location;
 import dmd.mangle;
 import dmd.mtype;
-import dmd.opover;
-import dmd.optimize;
 import dmd.root.array;
 import dmd.common.outbuffer;
 import dmd.rootobject;
-import dmd.templatesem : getExpression, TemplateInstance_semanticTiargs;
 import dmd.tokens;
-import dmd.typesem : typeSemantic, isBaseOf, resolveNamedArgs;
 import dmd.visitor;
 
 //debug = FindExistingInstance; // print debug stats of findExistingInstance
@@ -195,253 +181,6 @@ inout(Type) getType(inout RootObject o)
 
 }
 
-/******************************
- * See if two objects match
- * Params:
- *      o1 = first object
- *      o2 = second object
- * Returns: true if they match
- */
-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());
-    }
-
-    bool yes()
-    {
-        static if (log)
-            printf("\t. match\n");
-        return true;
-    }
-    bool no()
-    {
-        static if (log)
-            printf("\t. nomatch\n");
-        return false;
-    }
-    /* 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)
-            return no();
-
-        static if (log)
-        {
-            printf("\tt1 = %s\n", t1.toChars());
-            printf("\tt2 = %s\n", t2.toChars());
-        }
-        if (!t1.equals(t2))
-            return no();
-
-        return yes();
-    }
-    if (auto e1 = getExpression(o1))
-    {
-        auto e2 = getExpression(o2);
-        if (!e2)
-            return no();
-
-        static if (log)
-        {
-            printf("\te1 = %s '%s' %s\n", e1.type ? e1.type.toChars() : "null", EXPtoString(e1.op).ptr, e1.toChars());
-            printf("\te2 = %s '%s' %s\n", e2.type ? e2.type.toChars() : "null", EXPtoString(e2.op).ptr, 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))
-            return no();
-
-        return yes();
-    }
-    if (auto s1 = isDsymbol(o1))
-    {
-        auto s2 = isDsymbol(o2);
-        if (!s2)
-            return no();
-
-        static if (log)
-        {
-            printf("\ts1 = %s \n", s1.kind(), s1.toChars());
-            printf("\ts2 = %s \n", s2.kind(), s2.toChars());
-        }
-        if (!s1.equals(s2))
-            return no();
-        if (s1.parent != s2.parent && !s1.isFuncDeclaration() && !s2.isFuncDeclaration())
-            return no();
-
-        return yes();
-    }
-    if (auto u1 = isTuple(o1))
-    {
-        auto u2 = isTuple(o2);
-        if (!u2)
-            return no();
-
-        static if (log)
-        {
-            printf("\tu1 = %s\n", u1.toChars());
-            printf("\tu2 = %s\n", u2.toChars());
-        }
-        if (!arrayObjectMatch(u1.objects, u2.objects))
-            return no();
-
-        return yes();
-    }
-    return yes();
-}
-
-/************************************
- * Match an array of them.
- */
-bool arrayObjectMatch(ref Objects oa1, ref Objects oa2)
-{
-    if (&oa1 == &oa2)
-        return true;
-    if (oa1.length != oa2.length)
-        return false;
-    immutable oa1dim = oa1.length;
-    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(ref 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))
-        {
-            if (auto fa1 = s1.isFuncAliasDeclaration())
-                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 EXP.int64:
-        return cast(size_t) e.isIntegerExp().getInteger();
-
-    case EXP.float64:
-        return CTFloat.hash(e.isRealExp().value);
-
-    case EXP.complex80:
-        auto ce = e.isComplexExp();
-        return mixHash(CTFloat.hash(ce.toReal), CTFloat.hash(ce.toImaginary));
-
-    case EXP.identifier:
-        return cast(size_t)cast(void*) e.isIdentifierExp().ident;
-
-    case EXP.null_:
-        return cast(size_t)cast(void*) e.isNullExp().type;
-
-    case EXP.string_:
-        return calcHash(e.isStringExp.peekData());
-
-    case EXP.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 EXP.arrayLiteral:
-    {
-        auto ae = e.isArrayLiteralExp();
-        size_t hash;
-        foreach (i; 0 .. ae.elements.length)
-            hash = mixHash(hash, expressionHash(ae[i]));
-        return hash;
-    }
-
-    case EXP.assocArrayLiteral:
-    {
-        auto ae = e.isAssocArrayLiteralExp();
-        size_t hash;
-        foreach (i; 0 .. ae.keys.length)
-            // reduction needs associative op as keys are unsorted (use XOR)
-            hash ^= mixHash(expressionHash((*ae.keys)[i]), expressionHash((*ae.values)[i]));
-        return hash;
-    }
-
-    case EXP.structLiteral:
-    {
-        auto se = e.isStructLiteralExp();
-        size_t hash;
-        foreach (elem; *se.elements)
-            hash = mixHash(hash, elem ? expressionHash(elem) : 0);
-        return hash;
-    }
-
-    case EXP.variable:
-        return cast(size_t)cast(void*) e.isVarExp().var;
-
-    case EXP.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)
@@ -502,7 +241,7 @@ extern (C++) final class TemplateDeclaration : ScopeDsymbol
     Expression constraint;
 
     // Hash table to look up TemplateInstance's of this TemplateDeclaration
-    TemplateInstance[TemplateInstanceBox] instances;
+    void* instances;
 
     TemplateDeclaration overnext;       // next overloaded TemplateDeclaration
     TemplateDeclaration overroot;       // first in overnext list
@@ -517,6 +256,7 @@ extern (C++) final class TemplateDeclaration : ScopeDsymbol
     bool isTrivialAlias;    /// matches pattern `template Alias(T) { alias Alias = qualifiers(T); }`
     bool deprecated_;       /// this template declaration is deprecated
     bool isCmacro;          /// Whether this template is a translation of a C macro
+    bool haveComputedOneMember; /// Whether computeOneMeber has been called
     Visibility visibility;
 
     // threaded list of previous instantiation attempts on stack
@@ -555,20 +295,12 @@ extern (C++) final class TemplateDeclaration : ScopeDsymbol
         this.literal = literal;
         this.ismixin = ismixin;
         this.isstatic = true;
+        this.haveComputedOneMember = false;
         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 (!oneMembers(members, s, ident) || !s)
-            return;
-
-        onemember = s;
-        s.parent = this;
-
+    extern(D) void computeIsTrivialAlias(Dsymbol s)
+    {
         /* Set isTrivialAliasSeq if this fits the pattern:
          *   template AliasSeq(T...) { alias AliasSeq = T; }
          * or set isTrivialAlias if this fits the pattern:
@@ -618,56 +350,6 @@ extern (C++) final class TemplateDeclaration : ScopeDsymbol
         return new TemplateDeclaration(loc, ident, p, constraint ? constraint.syntaxCopy() : null, Dsymbol.arraySyntaxCopy(members), ismixin, literal);
     }
 
-    /**********************************
-     * Overload existing TemplateDeclaration 'this' with the new one 's'.
-     * Params:
-     *    s = symbol to be inserted
-     * 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 const(char)* kind() const
     {
         return (onemember && onemember.isAggregateDeclaration()) ? onemember.kind() : "template";
@@ -689,67 +371,6 @@ extern (C++) final class TemplateDeclaration : ScopeDsymbol
         return visibility;
     }
 
-    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.
-     *
-     * Params:
-     *   tithis = template instance to check
-     *   argumentList = For function templates, needed because different
-     *                  `auto ref` resolutions create different instances,
-     *                  even when template parameters are identical
-     *
-     * Returns: that existing instance, or `null` when it doesn't exist
-     */
-    extern (D) TemplateInstance findExistingInstance(TemplateInstance tithis, ArgumentList argumentList)
-    {
-        //printf("findExistingInstance() %s\n", tithis.toChars());
-        tithis.fargs = argumentList.arguments;
-        tithis.fnames = argumentList.names;
-        auto tibox = TemplateInstanceBox(tithis);
-        auto p = tibox in this.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);
-    }
-
     /**
      * Check if the last template parameter is a tuple one,
      * and returns it if so, else returns `null`.
@@ -812,384 +433,6 @@ extern (C++) final class TypeDeduced : Type
     }
 }
 
-/* ======================== Type ============================================ */
-
-/***********************************************************
- * 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.length]);
-}
-
-/***********************************************************
- * 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.
- */
-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) @safe
-        {
-            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.placement)
-                e.placement.accept(this);
-            if (e.thisexp)
-                e.thisexp.accept(this);
-            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(e.isUnaExp());
-            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(e.isUnaExp());
-            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(e.isUnaExp());
-            // 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(e.isUnaExp());
-            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(e.isUnaExp());
-            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(e.isBinExp());
-        }
-    }
-
-    scope ReliesOnTemplateParameters v = new ReliesOnTemplateParameters(tparams);
-    e.accept(v);
-    return v.result;
-}
-
 /***********************************************************
  * https://dlang.org/spec/template.html#TemplateParameter
  */
@@ -1242,8 +485,6 @@ extern (C++) class TemplateParameter : ASTNode
 
     abstract TemplateParameter syntaxCopy();
 
-    abstract bool declareParameter(Scope* sc);
-
     abstract void print(RootObject oarg, RootObject oded);
 
     abstract RootObject specialization();
@@ -1295,14 +536,6 @@ extern (C++) class TemplateTypeParameter : TemplateParameter
         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());
@@ -1398,27 +631,6 @@ extern (C++) final class TemplateValueParameter : TemplateParameter
             defaultValue ? defaultValue.syntaxCopy() : null);
     }
 
-    override bool declareParameter(Scope* sc)
-    {
-        /*
-            Do type semantic earlier.
-
-            This means for certain erroneous value parameters
-            their "type" can be known earlier and thus a better
-            error message given.
-
-            For example:
-            `template test(x* x) {}`
-            now yields "undefined identifier" rather than the opaque
-            "variable `x` is used as a type".
-         */
-        if (valType)
-            valType = valType.typeSemantic(loc, 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());
@@ -1475,13 +687,6 @@ extern (C++) final class TemplateAliasParameter : TemplateParameter
         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());
@@ -1528,13 +733,6 @@ extern (C++) final class TemplateTupleParameter : TemplateParameter
         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());
@@ -1621,7 +819,6 @@ extern (C++) class TemplateInstance : ScopeDsymbol
     TemplateInstance inst;
 
     ScopeDsymbol argsym;        // argument symbol table
-    size_t hash;                // cached result of toHash()
 
     /// For function template, these are the function fnames(name and loc of it) and arguments
     /// Relevant because different resolutions of `auto ref` parameters
@@ -1860,238 +1057,6 @@ extern (C++) class TemplateInstance : ScopeDsymbol
         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.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() : "");
-            return false;
-        }
-        //printf("parent = %s, ti.parent = %s\n", parent.toPrettyChars(), ti.parent.toPrettyChars());
-
-        if (!arrayObjectMatch(tdtypes, ti.tdtypes))
-            return false;
-
-        /* Template functions may have different instantiations based on
-         * "auto ref" parameters.
-         */
-        auto fd = ti.toAlias().isFuncDeclaration();
-        if (!fd)
-            return true;
-        if (fd.errors)
-            return true;
-
-        auto resolvedArgs = fd.type.isTypeFunction().resolveNamedArgs(
-            ArgumentList(this.fargs, this.fnames), null);
-
-        // resolvedArgs can be null when there's an error: fail_compilation/fail14669.d
-        // In that case, equalsx returns true to prevent endless template instantiations
-        // However, it can also mean the function was explicitly instantiated
-        // without function arguments: fail_compilation/fail14669
-        // Hence the following check:
-        if (this.fargs && !resolvedArgs)
-            return true;
-
-        Expression[] args = resolvedArgs ? (*resolvedArgs)[] : [];
-
-        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"
-                continue;
-
-            Expression farg = (j < args.length) ? args[j] : fparam.defaultArg;
-            // resolveNamedArgs strips trailing nulls / default params
-            // when it doesn't anymore, the ternary can be replaced with:
-            // assert(j < resolvedArgs.length);
-            if (!farg)
-                farg = fparam.defaultArg;
-            if (!farg)
-                return false;
-            if (farg.isLvalue())
-            {
-                if (!(fparam.storageClass & STC.ref_))
-                    return false; // auto ref's don't match
-            }
-            else
-            {
-                if (fparam.storageClass & STC.ref_)
-                    return false; // auto ref's don't match
-            }
-        }
-        return true;
-    }
-
-    extern (D) final size_t toHash()
-    {
-        if (!hash)
-        {
-            hash = cast(size_t)cast(void*)enclosing;
-            hash += arrayObjectHash(tdtypes);
-            hash += hash == 0;
-        }
-        return hash;
-    }
-
-    /**********************************
-     * 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 (TemplateInstance_semanticTiargs(loc, sc, tiargs, 0))
-        {
-            // cache the result iff semantic analysis succeeded entirely
-            semantictiargsdone = 1;
-            return true;
-        }
-        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 (auto ve = ea.isVarExp())
-                {
-                    sa = ve.var;
-                    goto Lsa;
-                }
-                if (auto te = ea.isThisExp())
-                {
-                    sa = te.var;
-                    goto Lsa;
-                }
-                if (auto fe = ea.isFuncExp())
-                {
-                    if (fe.td)
-                        sa = fe.td;
-                    else
-                        sa = fe.fd;
-                    goto Lsa;
-                }
-                // Emulate Expression.toMangleBuffer call that had exist in TemplateInstance.genIdent.
-                if (ea.op != EXP.int64 && ea.op != EXP.float64 && ea.op != EXP.complex80 && ea.op != EXP.null_ && ea.op != EXP.string_ && ea.op != EXP.arrayLiteral && ea.op != EXP.assocArrayLiteral && ea.op != EXP.structLiteral)
-                {
-                    if (!ea.type.isTypeError())
-                        .error(ea.loc, "%s `%s` expression `%s` is not a valid template value argument", kind, toPrettyChars, 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 || dparent.isModule)
-                        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
-                            }
-                        }
-                        //https://issues.dlang.org/show_bug.cgi?id=17870
-                        if (dparent.isClassDeclaration() && enclosing.isClassDeclaration())
-                        {
-                            auto pc = dparent.isClassDeclaration();
-                            auto ec = enclosing.isClassDeclaration();
-                            if (pc.isBaseOf(ec, null))
-                                goto L1;
-                            else if (ec.isBaseOf(pc, null))
-                            {
-                                enclosing = dparent;
-                                goto L1;
-                            }
-                        }
-                        .error(loc, "%s `%s` `%s` is nested in both `%s` and `%s`", kind, toPrettyChars, toChars(), enclosing.toChars(), dparent.toChars());
-                        errors = true;
-                    }
-                L1:
-                    //printf("\tnested inside %s as it references %s\n", enclosing.toChars(), sa.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;
-    }
-
     /****************************************
      * This instance needs an identifier for name mangling purposes.
      * Create one by taking the template declaration name and adding
@@ -2210,64 +1175,6 @@ extern (C++) final class TemplateMixin : TemplateInstance
     }
 }
 
-/************************************
- * This struct is needed for TemplateInstance to be the key in an associative array.
- * Fixing 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 @safe 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.
-             */
-            static if (__VERSION__ < 2099) // https://issues.dlang.org/show_bug.cgi?id=22717
-                res = (cast()s.ti).equalsx(cast()ti);
-            else
-                res = (cast()ti).equalsx(cast()s.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);
-        }
-    }
-}
-
 /***********************************************
  * Collect and print statistics on template instantiations.
  */
@@ -2402,21 +1309,6 @@ void printTemplateStats(bool listInstances, ErrorSink eSink)
     }
 }
 
-/// Pair of MATCHes
-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;
-    }
-}
-
 void write(ref OutBuffer buf, RootObject obj)
 {
     if (obj)
index 4cb7d1ac5dda71bebd96ae0d088ca56e89fad427..839127ca42f04776dfd11d69c581688f4fadf4e7 100644 (file)
@@ -18,10 +18,11 @@ import core.stdc.ctype;
 import dmd.astcodegen;
 import dmd.astenums;
 import dmd.arraytypes;
-import dmd.attrib;
-import dmd.dsymbol;
 import dmd.dsymbolsem;
+import dmd.templatesem : computeOneMember;
+import dmd.expressionsem : toInteger;
 import dmd.errors;
+import dmd.errorsink;
 import dmd.globals;
 import dmd.hdrgen;
 import dmd.id;
@@ -46,6 +47,7 @@ import dmd.utils;
  *
  * Params:
  *   ms = the modules
+ *   eSink = where to report errors
  *
  * Notes:
  *  - the header is written to `<global.params.cxxhdrdir>/<global.params.cxxhdrfile>`
@@ -54,7 +56,7 @@ import dmd.utils;
  *  - ignored declarations are mentioned in a comment if `global.params.doCxxHdrGeneration`
  *    is set to `CxxHeaderMode.verbose`
  */
-void genCppHdrFiles(ref Modules ms)
+void genCppHdrFiles(ref Modules ms, ErrorSink eSink)
 {
     initialize();
 
@@ -68,7 +70,7 @@ void genCppHdrFiles(ref Modules ms)
     decl.doindent = true;
     decl.spaces = true;
 
-    scope v = new ToCppBuffer(&fwd, &done, &decl);
+    scope v = new ToCppBuffer(&fwd, &done, &decl, eSink);
 
     // Conditionally include another buffer for sanity checks
     debug (Debug_DtoH_Checks)
@@ -259,6 +261,9 @@ public:
     /// Default buffer for the currently visited declaration
     OutBuffer* buf;
 
+    /// Sink for error reporting
+    ErrorSink eSink;
+
     /// The generated header uses `real` emitted as `_d_real`?
     bool hasReal = false;
 
@@ -329,12 +334,13 @@ public:
     }
     mixin(generateMembers());
 
-    this(OutBuffer* fwdbuf, OutBuffer* donebuf, OutBuffer* buf) scope
+    this(OutBuffer* fwdbuf, OutBuffer* donebuf, OutBuffer* buf, ErrorSink eSink) scope
     {
         this.fwdbuf = fwdbuf;
         this.donebuf = donebuf;
         this.buf = buf;
         this.printIgnored = global.params.cxxhdr.fullOutput;
+        this.eSink = eSink;
     }
 
     /**
@@ -502,12 +508,12 @@ public:
             }
 
             __gshared bool warned = false;
-            warning(loc, "%s `%s` is a %s", kind, ident.toChars(), reason);
+            eSink.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++");
+                eSink.warningSupplemental(loc, "The generated C++ header will contain " ~
+                                          "identifiers that are keywords in C++");
                 warned = true;
             }
         }
@@ -574,7 +580,6 @@ public:
         debug (Debug_DtoH)
         {
             mixin(traceVisit!s);
-            import dmd.asttypename;
             printf("[AST.Dsymbol enter] %s\n", s.astTypeName().ptr);
         }
     }
@@ -1748,7 +1753,6 @@ public:
         // `this` but accessible via `outer`
         if (auto td = s.isThisDeclaration())
         {
-            import dmd.id;
             this.ident = Id.outer;
         }
         else
@@ -2141,6 +2145,7 @@ public:
         if (!shouldEmitAndMarkVisited(td))
             return;
 
+        td.computeOneMember();
         if (!td.parameters || !td.onemember || (!td.onemember.isStructDeclaration && !td.onemember.isClassDeclaration && !td.onemember.isFuncDeclaration))
         {
             visit(cast(AST.Dsymbol)td);
@@ -2697,7 +2702,6 @@ public:
         }
         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);
index 1561ebb0b7c46d92cad39307f4a10ca57c175815..166eb92fe37f2a32b22ba3ee239509557ae4105f 100644 (file)
 
 module dmd.dversion;
 
-import dmd.arraytypes;
-import dmd.cond;
-import dmd.dmodule;
-import dmd.dscope;
 import dmd.dsymbol;
-import dmd.dsymbolsem;
 import dmd.identifier;
 import dmd.location;
-import dmd.common.outbuffer;
 import dmd.visitor;
 
 /***********************************************************
index 2b4d3b52d89e6578e15cf91ce2421abd606ef20a..5d59534bcf6ed43a8def3e250fc76a7ffa3e2e81 100644 (file)
@@ -52,7 +52,6 @@ public:
     bool inuse(bool v);
 
     EnumDeclaration *syntaxCopy(Dsymbol *s) override;
-    Type *getType() override;
     const char *kind() const override;
     bool isDeprecated() const override;       // is Dsymbol deprecated?
     Visibility visible() override;
index cdf8a61bd88ad0418d39db567cb0e870d71de7d3..f00c69c4510e777d317a2741c4ff89e05134a962 100644 (file)
@@ -488,21 +488,30 @@ void enumMemberSemantic(Scope* sc, EnumMember em)
     else if (first)
     {
         Type t;
+        bool terror;
         if (em.ed.memtype)
+        {
             t = em.ed.memtype;
+            if (!t.isScalar())
+            {
+                t = Type.terror; // print more relevant error message later
+                terror = true;
+            }
+        }
         else
         {
             t = Type.tint32;
             if (!em.ed.isAnonymous())
                 em.ed.memtype = t;
         }
+
         const errors = global.startGagging();
         Expression e = new IntegerExp(em.loc, 0, t);
         e = e.ctfeInterpret();
-        if (global.endGagging(errors))
+        if (global.endGagging(errors) || terror)
         {
             error(em.loc, "cannot generate 0 value of type `%s` for `%s`",
-                t.toChars(), em.toChars());
+                em.ed.memtype.toChars(), em.toChars());
         }
         // save origValue for better json output
         em.origValue = e;
index 90c18c3fed6d55255ffc039ea9ebe2baaf74d508..1fcc654ad68bc8bb6656e852ee6adfb20c9d33fd 100644 (file)
@@ -41,37 +41,37 @@ class ErrorSinkCompiler : ErrorSink
 
     void verror(Loc loc, const(char)* format, va_list ap)
     {
-        verrorReport(loc, format, ap, ErrorKind.error);
+        vreportDiagnostic(loc, format, ap, ErrorKind.error);
     }
 
     void verrorSupplemental(Loc loc, const(char)* format, va_list ap)
     {
-        verrorReportSupplemental(loc, format, ap, ErrorKind.error);
+        vsupplementalDiagnostic(loc, format, ap, ErrorKind.error);
     }
 
     void vwarning(Loc loc, const(char)* format, va_list ap)
     {
-        verrorReport(loc, format, ap, ErrorKind.warning);
+        vreportDiagnostic(loc, format, ap, ErrorKind.warning);
     }
 
     void vwarningSupplemental(Loc loc, const(char)* format, va_list ap)
     {
-        verrorReportSupplemental(loc, format, ap, ErrorKind.warning);
+        vsupplementalDiagnostic(loc, format, ap, ErrorKind.warning);
     }
 
     void vdeprecation(Loc loc, const(char)* format, va_list ap)
     {
-        verrorReport(loc, format, ap, ErrorKind.deprecation);
+        vreportDiagnostic(loc, format, ap, ErrorKind.deprecation);
     }
 
     void vdeprecationSupplemental(Loc loc, const(char)* format, va_list ap)
     {
-        verrorReportSupplemental(loc, format, ap, ErrorKind.deprecation);
+        vsupplementalDiagnostic(loc, format, ap, ErrorKind.deprecation);
     }
 
     void vmessage(Loc loc, const(char)* format, va_list ap)
     {
-        verrorReport(loc, format, ap, ErrorKind.message);
+        vreportDiagnostic(loc, format, ap, ErrorKind.message);
     }
 }
 
@@ -159,7 +159,7 @@ static if (__VERSION__ < 2092)
     {
         va_list ap;
         va_start(ap, format);
-        verrorReport(loc, format, ap, ErrorKind.error);
+        vreportDiagnostic(loc, format, ap, ErrorKind.error);
         va_end(ap);
     }
 else
@@ -167,7 +167,7 @@ else
     {
         va_list ap;
         va_start(ap, format);
-        verrorReport(loc, format, ap, ErrorKind.error);
+        vreportDiagnostic(loc, format, ap, ErrorKind.error);
         va_end(ap);
     }
 
@@ -186,7 +186,7 @@ static if (__VERSION__ < 2092)
         const loc = SourceLoc(filename.toDString, linnum, charnum);
         va_list ap;
         va_start(ap, format);
-        verrorReport(loc, format, ap, ErrorKind.error);
+        vreportDiagnostic(loc, format, ap, ErrorKind.error);
         va_end(ap);
     }
 else
@@ -195,7 +195,7 @@ else
         const loc = SourceLoc(filename.toDString, linnum, charnum);
         va_list ap;
         va_start(ap, format);
-        verrorReport(loc, format, ap, ErrorKind.error);
+        vreportDiagnostic(loc, format, ap, ErrorKind.error);
         va_end(ap);
     }
 
@@ -205,7 +205,7 @@ extern(C++) void errorBackend(const(char)* filename, uint linnum, uint charnum,
     const loc = SourceLoc(filename.toDString, linnum, charnum);
     va_list ap;
     va_start(ap, format);
-    verrorReport(loc, format, ap, ErrorKind.error);
+    vreportDiagnostic(loc, format, ap, ErrorKind.error);
     va_end(ap);
 }
 
@@ -222,7 +222,7 @@ static if (__VERSION__ < 2092)
     {
         va_list ap;
         va_start(ap, format);
-        verrorReportSupplemental(loc, format, ap, ErrorKind.error);
+        vsupplementalDiagnostic(loc, format, ap, ErrorKind.error);
         va_end(ap);
     }
 else
@@ -230,7 +230,7 @@ else
     {
         va_list ap;
         va_start(ap, format);
-        verrorReportSupplemental(loc, format, ap, ErrorKind.error);
+        vsupplementalDiagnostic(loc, format, ap, ErrorKind.error);
         va_end(ap);
     }
 
@@ -246,7 +246,7 @@ static if (__VERSION__ < 2092)
     {
         va_list ap;
         va_start(ap, format);
-        verrorReport(loc, format, ap, ErrorKind.warning);
+        vreportDiagnostic(loc, format, ap, ErrorKind.warning);
         va_end(ap);
     }
 else
@@ -254,7 +254,7 @@ else
     {
         va_list ap;
         va_start(ap, format);
-        verrorReport(loc, format, ap, ErrorKind.warning);
+        vreportDiagnostic(loc, format, ap, ErrorKind.warning);
         va_end(ap);
     }
 
@@ -271,7 +271,7 @@ static if (__VERSION__ < 2092)
     {
         va_list ap;
         va_start(ap, format);
-        verrorReportSupplemental(loc, format, ap, ErrorKind.warning);
+        vsupplementalDiagnostic(loc, format, ap, ErrorKind.warning);
         va_end(ap);
     }
 else
@@ -279,7 +279,7 @@ else
     {
         va_list ap;
         va_start(ap, format);
-        verrorReportSupplemental(loc, format, ap, ErrorKind.warning);
+        vsupplementalDiagnostic(loc, format, ap, ErrorKind.warning);
         va_end(ap);
     }
 
@@ -296,7 +296,7 @@ static if (__VERSION__ < 2092)
     {
         va_list ap;
         va_start(ap, format);
-        verrorReport(loc, format, ap, ErrorKind.deprecation);
+        vreportDiagnostic(loc, format, ap, ErrorKind.deprecation);
         va_end(ap);
     }
 else
@@ -304,7 +304,7 @@ else
     {
         va_list ap;
         va_start(ap, format);
-        verrorReport(loc, format, ap, ErrorKind.deprecation);
+        vreportDiagnostic(loc, format, ap, ErrorKind.deprecation);
         va_end(ap);
     }
 
@@ -321,7 +321,7 @@ static if (__VERSION__ < 2092)
     {
         va_list ap;
         va_start(ap, format);
-        verrorReportSupplemental(loc, format, ap, ErrorKind.deprecation);
+        vsupplementalDiagnostic(loc, format, ap, ErrorKind.deprecation);
         va_end(ap);
     }
 else
@@ -329,7 +329,7 @@ else
     {
         va_list ap;
         va_start(ap, format);
-        verrorReportSupplemental(loc, format, ap, ErrorKind.deprecation);
+        vsupplementalDiagnostic(loc, format, ap, ErrorKind.deprecation);
         va_end(ap);
     }
 
@@ -346,7 +346,7 @@ static if (__VERSION__ < 2092)
     {
         va_list ap;
         va_start(ap, format);
-        verrorReport(loc, format, ap, ErrorKind.message);
+        vreportDiagnostic(loc, format, ap, ErrorKind.message);
         va_end(ap);
     }
 else
@@ -354,7 +354,7 @@ else
     {
         va_list ap;
         va_start(ap, format);
-        verrorReport(loc, format, ap, ErrorKind.message);
+        vreportDiagnostic(loc, format, ap, ErrorKind.message);
         va_end(ap);
     }
 
@@ -369,7 +369,7 @@ static if (__VERSION__ < 2092)
     {
         va_list ap;
         va_start(ap, format);
-        verrorReport(Loc.initial, format, ap, ErrorKind.message);
+        vreportDiagnostic(Loc.initial, format, ap, ErrorKind.message);
         va_end(ap);
     }
 else
@@ -377,13 +377,13 @@ else
     {
         va_list ap;
         va_start(ap, format);
-        verrorReport(Loc.initial, format, ap, ErrorKind.message);
+        vreportDiagnostic(Loc.initial, format, ap, ErrorKind.message);
         va_end(ap);
     }
 
 /**
  * The type of the diagnostic handler
- * see verrorReport for arguments
+ * see vreportDiagnostic for arguments
  * Returns: true if error handling is done, false to continue printing to stderr
  */
 alias DiagnosticHandler = bool delegate(const ref SourceLoc location, Color headerColor, const(char)* header, const(char)* messageFormat, va_list args, const(char)* prefix1, const(char)* prefix2);
@@ -406,7 +406,7 @@ static if (__VERSION__ < 2092)
     {
         va_list ap;
         va_start(ap, format);
-        verrorReport(Loc.initial, format, ap, ErrorKind.tip);
+        vreportDiagnostic(Loc.initial, format, ap, ErrorKind.tip);
         va_end(ap);
     }
 else
@@ -414,7 +414,7 @@ else
     {
         va_list ap;
         va_start(ap, format);
-        verrorReport(Loc.initial, format, ap, ErrorKind.tip);
+        vreportDiagnostic(Loc.initial, format, ap, ErrorKind.tip);
         va_end(ap);
     }
 
@@ -432,13 +432,13 @@ else
  *      p1          = additional message prefix
  *      p2          = additional message prefix
  */
-private extern(C++) void verrorReport(Loc loc, const(char)* format, va_list ap, ErrorKind kind, const(char)* p1 = null, const(char)* p2 = null)
+private extern(C++) void vreportDiagnostic(Loc loc, const(char)* format, va_list ap, ErrorKind kind, const(char)* p1 = null, const(char)* p2 = null)
 {
-    return verrorReport(loc.SourceLoc, format, ap, kind, p1, p2);
+    return vreportDiagnostic(loc.SourceLoc, format, ap, kind, p1, p2);
 }
 
 /// ditto
-private extern(C++) void verrorReport(const SourceLoc loc, const(char)* format, va_list ap, ErrorKind kind, const(char)* p1 = null, const(char)* p2 = null);
+private extern(C++) void vreportDiagnostic(const SourceLoc loc, const(char)* format, va_list ap, ErrorKind kind, const(char)* p1 = null, const(char)* p2 = null);
 
 /**
  * Implements $(D errorSupplemental), $(D warningSupplemental), and
@@ -451,13 +451,13 @@ private extern(C++) void verrorReport(const SourceLoc loc, const(char)* format,
  *      ap          = printf-style variadic arguments
  *      kind        = kind of error being printed
  */
-private extern(C++) void verrorReportSupplemental(Loc loc, const(char)* format, va_list ap, ErrorKind kind)
+private extern(C++) void vsupplementalDiagnostic(Loc loc, const(char)* format, va_list ap, ErrorKind kind)
 {
-    return verrorReportSupplemental(loc.SourceLoc, format, ap, kind);
+    return vsupplementalDiagnostic(loc.SourceLoc, format, ap, kind);
 }
 
 /// ditto
-private extern(C++) void verrorReportSupplemental(const SourceLoc loc, const(char)* format, va_list ap, ErrorKind kind);
+private extern(C++) void vsupplementalDiagnostic(const SourceLoc loc, const(char)* format, va_list ap, ErrorKind kind);
 
 /**
  * The type of the fatal error handler
index d3473f691d55ebbabe9c18da7b3d57f529a96ecb..1b50cdea5b26e9bef0f878e85f8467a3478632fb 100644 (file)
@@ -24,9 +24,11 @@ import dmd.dscope;
 import dmd.dsymbol;
 import dmd.errors;
 import dmd.expression;
+import dmd.expressionsem;
 import dmd.func;
 import dmd.funcsem;
 import dmd.globals : FeatureState;
+import dmd.hdrgen : toErrMsg;
 import dmd.id;
 import dmd.identifier;
 import dmd.init;
@@ -36,7 +38,7 @@ import dmd.printast;
 import dmd.rootobject;
 import dmd.safe;
 import dmd.tokens;
-import dmd.typesem : hasPointers, parameterStorageClass;
+import dmd.typesem;
 import dmd.visitor;
 import dmd.arraytypes;
 
@@ -46,14 +48,14 @@ private:
 package(dmd) struct EscapeState
 {
     // Maps `sequenceNumber` of a `VarDeclaration` to an object that contains the
-    // reason it failed to infer `scope`
+    // reason it inferred or didn't infer `scope` for supplemental error messages
     // https://issues.dlang.org/show_bug.cgi?id=23295
-    private __gshared RootObject[int] scopeInferFailure;
+    private __gshared RootObject[int] scopeInferReason;
 
     /// Called by `initDMD` / `deinitializeDMD` to reset global state
     static void reset()
     {
-        scopeInferFailure = null;
+        scopeInferReason = null;
     }
 }
 
@@ -282,33 +284,38 @@ bool checkAssocArrayLiteralEscape(ref Scope sc, AssocArrayLiteralExp ae, bool ga
 }
 
 /**
- * A `scope` variable was assigned to non-scope parameter `v`.
- * If applicable, print why the parameter was not inferred `scope`.
+ * An error occured due to `v` either being or not being `scope`.
+ * If applicable, print why the `v` was inferred that way.
  *
  * Params:
  *    printFunc = error/deprecation print function to use
  *    v = parameter that was not inferred
  *    recursionLimit = recursion limit for printing the reason
+ *    positive = true for telling why v is scope, false for telling why v is not scope
  */
-private
-void printScopeFailure(E)(E printFunc, VarDeclaration v, int recursionLimit)
+public
+void printScopeReason(E)(E printFunc, VarDeclaration v, int recursionLimit, bool positive)
 {
     recursionLimit--;
     if (recursionLimit < 0 || !v)
         return;
 
-    if (RootObject* o = v.sequenceNumber in EscapeState.scopeInferFailure)
+    if (RootObject* o = v.sequenceNumber in EscapeState.scopeInferReason)
     {
         switch ((*o).dyncast())
         {
             case DYNCAST.expression:
                 Expression e = cast(Expression) *o;
-                printFunc(e.loc, "which is not `scope` because of `%s`", e.toChars());
+                if (positive)
+                    printFunc(e.loc, "`%s` inferred `scope` because of `%s`", v.toErrMsg(), e.toErrMsg());
+                else
+                    printFunc(e.loc, "`%s` is not `scope` because of `%s`", v.toErrMsg(), e.toErrMsg());
                 break;
             case DYNCAST.dsymbol:
                 VarDeclaration v1 = cast(VarDeclaration) *o;
-                printFunc(v1.loc, "which is assigned to non-scope parameter `%s`", v1.toChars());
-                printScopeFailure(printFunc, v1, recursionLimit);
+                assert(!positive);
+                printFunc(v1.loc, "`%s` is assigned to non-scope parameter `%s`", v.toErrMsg(), v1.toErrMsg());
+                printScopeReason(printFunc, v1, recursionLimit, positive);
                 break;
             default:
                 assert(0);
@@ -368,11 +375,12 @@ bool checkParamArgumentEscape(ref Scope sc, FuncDeclaration fdc, Identifier parI
             (desc ~ " `%s` assigned to non-scope anonymous parameter");
 
         if (isThis ?
-            sc.setUnsafeDIP1000(gag, arg.loc, msg, arg, fdc.toParent2(), fdc) :
-            sc.setUnsafeDIP1000(gag, arg.loc, msg, v, parId ? parId : fdc, fdc))
+            sc.setUnsafeDIP1000(gag, arg.loc, vPar, msg, arg, fdc.toParent2(), fdc) :
+            sc.setUnsafeDIP1000(gag, arg.loc, vPar, msg, v, parId ? parId : fdc, fdc))
         {
             result = true;
-            printScopeFailure(previewSupplementalFunc(sc.isDeprecated(), sc.useDIP1000), vPar, 10);
+            printScopeReason(previewSupplementalFunc(sc.isDeprecated(), sc.useDIP1000), vPar, 10, false);
+            printScopeReason(previewSupplementalFunc(sc.isDeprecated(), sc.useDIP1000), v, 10, true);
         }
     }
 
@@ -714,7 +722,7 @@ bool checkAssignEscape(ref Scope sc, Expression e, bool gag, bool byRef)
             if (vaIsFirstRef && v.isParameter() && v.isReturn())
             {
                 // va=v, where v is `return scope`
-                if (inferScope(va))
+                if (inferScope(va, e))
                     return;
             }
 
@@ -748,7 +756,7 @@ bool checkAssignEscape(ref Scope sc, Expression e, bool gag, bool byRef)
 
             // v = scope, va should be scope as well
             const vaWasScope = va && va.isScope();
-            if (inferScope(va))
+            if (inferScope(va, e))
             {
                 // In case of `scope local = returnScopeParam`, do not infer return scope for `x`
                 if (!vaWasScope && v.isReturn() && !va.isReturn())
@@ -822,7 +830,7 @@ bool checkAssignEscape(ref Scope sc, Expression e, bool gag, bool byRef)
         if (p != sc.func)
             return;
 
-        if (inferScope(va))
+        if (inferScope(va, e))
         {
             if (v.isReturn() && !va.isReturn())
                 va.storage_class |= STC.return_ | STC.returninferred;
@@ -1210,8 +1218,12 @@ private bool checkReturnEscapeImpl(ref Scope sc, Expression e, bool refs, bool g
                 else
                 {
                     // https://issues.dlang.org/show_bug.cgi?id=17029
-                    result |= sc.setUnsafeDIP1000(gag, e.loc, "returning scope variable `%s`", v);
-                    return;
+                    if (sc.setUnsafeDIP1000(gag, e.loc, "returning scope variable `%s`", v))
+                    {
+                        printScopeReason(previewSupplementalFunc(sc.isDeprecated(), sc.useDIP1000), v, 10, true);
+                        result = true;
+                        return;
+                    }
                 }
             }
         }
@@ -1385,16 +1397,19 @@ private bool checkReturnEscapeImpl(ref Scope sc, Expression e, bool refs, bool g
  *
  * Params:
  *      va = variable to infer scope for
+ *      reason = optional Expression that causes `va` to infer scope, used for supplemental error message
  * Returns: `true` if succesful or already `scope`
  */
 private
-bool inferScope(VarDeclaration va)
+bool inferScope(VarDeclaration va, RootObject reason)
 {
     if (!va)
         return false;
     if (!va.isDataseg() && va.maybeScope && !va.isScope())
     {
         //printf("inferring scope for %s\n", va.toChars());
+        if (reason)
+            EscapeState.scopeInferReason[va.sequenceNumber] = reason;
         va.maybeScope = false;
         va.storage_class |= STC.scope_ | STC.scopeinferred;
         return true;
@@ -2010,7 +2025,7 @@ private void doNotInferScope(VarDeclaration v, RootObject o)
     {
         v.maybeScope = false;
         if (o && v.isParameter())
-            EscapeState.scopeInferFailure[v.sequenceNumber] = o;
+            EscapeState.scopeInferReason[v.sequenceNumber] = o;
     }
 }
 
@@ -2048,7 +2063,7 @@ void finishScopeParamInference(FuncDeclaration funcdecl, ref TypeFunction f)
         foreach (u, p; f.parameterList)
         {
             auto v = (*funcdecl.parameters)[u];
-            if (!v.isScope() && v.type.hasPointers() && inferScope(v))
+            if (!v.isScope() && v.type.hasPointers() && inferScope(v, null))
             {
                 //printf("Inferring scope for %s\n", v.toChars());
                 p.storageClass |= STC.scope_ | STC.scopeinferred;
@@ -2058,7 +2073,7 @@ void finishScopeParamInference(FuncDeclaration funcdecl, ref TypeFunction f)
 
     if (funcdecl.vthis)
     {
-        inferScope(funcdecl.vthis);
+        inferScope(funcdecl.vthis, null);
         f.isScopeQual = funcdecl.vthis.isScope();
         f.isScopeInferred = !!(funcdecl.vthis.storage_class & STC.scopeinferred);
     }
@@ -2199,6 +2214,14 @@ bool setUnsafeDIP1000(ref Scope sc, bool gag, Loc loc, const(char)* msg,
     return setUnsafePreview(&sc, sc.useDIP1000, gag, loc, msg, args);
 }
 
+/// Overload for scope violations that also stores the variable whose scope status caused the issue
+private
+bool setUnsafeDIP1000(ref Scope sc, bool gag, Loc loc, VarDeclaration scopeVar,
+    const(char)* msg, RootObject[] args...)
+{
+    return setUnsafePreview(&sc, sc.useDIP1000, gag, loc, scopeVar, msg, args);
+}
+
 /***************************************
  * Check that taking the address of `v` is `@safe`
  *
index 4d873ccecdc0b0daa621f873623094bebcf65fb0..d7bbdbd3b0c32ab5678f1fcac650b15896b9bb93 100644 (file)
@@ -17,25 +17,18 @@ import core.stdc.stdarg;
 import core.stdc.stdio;
 import core.stdc.string;
 
-import dmd.aggregate;
 import dmd.arraytypes;
 import dmd.astenums;
 import dmd.ast_node;
-import dmd.ctfeexpr : isCtfeReferenceValid;
-import dmd.dcast : implicitConvTo;
 import dmd.dclass;
 import dmd.declaration;
-import dmd.dimport;
-import dmd.dmodule;
 import dmd.dstruct;
 import dmd.dsymbol;
 import dmd.dtemplate;
 import dmd.errors;
-import dmd.errorsink;
-import dmd.expressionsem : getDsymbol;
 import dmd.func;
 import dmd.globals;
-import dmd.hdrgen;
+import dmd.hdrgen : toChars;
 import dmd.id;
 import dmd.identifier;
 import dmd.init;
@@ -43,15 +36,11 @@ import dmd.location;
 import dmd.mtype;
 import dmd.root.complex;
 import dmd.root.ctfloat;
-import dmd.common.outbuffer;
-import dmd.root.optional;
 import dmd.root.rmem;
 import dmd.rootobject;
 import dmd.root.string;
 import dmd.root.utf;
-import dmd.target;
 import dmd.tokens;
-import dmd.typesem : toHeadMutable, size, mutableOf, unSharedOf;
 import dmd.visitor;
 
 enum LOGSEMANTIC = false;
@@ -73,103 +62,6 @@ inout(Expression) lastComma(inout Expression e)
 
 }
 
-/****************************************
- * Expand tuples in-place.
- *
- * Example:
- *     When there's a call `f(10, pair: AliasSeq!(20, 30), single: 40)`, the input is:
- *         `exps =  [10, (20, 30), 40]`
- *         `names = [null, "pair", "single"]`
- *     The arrays will be modified to:
- *         `exps =  [10, 20, 30, 40]`
- *         `names = [null, "pair", null, "single"]`
- *
- * Params:
- *     exps  = array of Expressions
- *     names = optional array of names corresponding to Expressions
- */
-void expandTuples(Expressions* exps, ArgumentLabels* names = null)
-{
-    //printf("expandTuples()\n");
-    if (exps is null)
-        return;
-
-    if (names)
-    {
-        if (exps.length != names.length)
-        {
-            printf("exps.length = %d, names.length = %d\n", cast(int) exps.length, cast(int) names.length);
-            printf("exps = %s, names = %s\n", exps.toChars(), names.toChars());
-            if (exps.length > 0)
-                printf("%s\n", (*exps)[0].loc.toChars());
-            assert(0);
-        }
-    }
-
-    // At `index`, a tuple of length `length` is expanded. Insert corresponding nulls in `names`.
-    void expandNames(size_t index, size_t length)
-    {
-        if (names)
-        {
-            if (length == 0)
-            {
-                names.remove(index);
-                return;
-            }
-            foreach (i; 1 .. length)
-            {
-                names.insert(index + i, ArgumentLabel(cast(Identifier) null, Loc.init));
-            }
-        }
-    }
-
-    for (size_t i = 0; i < exps.length; 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.length == 0)
-                {
-                    exps.remove(i);
-                    expandNames(i, 0);
-                    if (i == exps.length)
-                        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);
-                    expandNames(i, texps.length);
-                }
-                i--;
-                continue;
-            }
-        }
-
-        // Inline expand all the tuples
-        while (arg.op == EXP.tuple)
-        {
-            TupleExp te = cast(TupleExp)arg;
-            exps.remove(i); // remove arg
-            exps.insert(i, te.exps); // replace with tuple contents
-            expandNames(i, te.exps.length);
-            if (i == exps.length)
-                return; // empty tuple, no more arguments
-            (*exps)[i] = Expression.combine(te.e0, (*exps)[i]);
-            arg = (*exps)[i];
-        }
-    }
-}
-
 /****************************************
  * If `s` is a function template, i.e. the only member of a template
  * and that member is a function, return that template.
@@ -210,76 +102,6 @@ DotIdExp typeDotIdExp(Loc loc, Type type, Identifier ident) @safe
     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
- *      deref = number of dereferences encountered
- * Returns:
- *      variable if there is one, null if not
- */
-VarDeclaration expToVariable(Expression e, out int deref)
-{
-    deref = 0;
-    while (1)
-    {
-        switch (e.op)
-        {
-            case EXP.variable:
-                return e.isVarExp().var.isVarDeclaration();
-
-            case EXP.dotVariable:
-                e = e.isDotVarExp().e1;
-                if (e.type.toBasetype().isTypeClass())
-                    deref++;
-
-                continue;
-
-            case EXP.index:
-            {
-                e = e.isIndexExp().e1;
-                if (!e.type.toBasetype().isTypeSArray())
-                    deref++;
-
-                continue;
-            }
-
-            case EXP.slice:
-            {
-                e = e.isSliceExp().e1;
-                if (!e.type.toBasetype().isTypeSArray())
-                    deref++;
-
-                continue;
-            }
-
-            case EXP.super_:
-                return e.isSuperExp().var.isVarDeclaration();
-            case EXP.this_:
-                return e.isThisExp().var.isVarDeclaration();
-
-            // Temporaries for rvalues that need destruction
-            // are of form: (T s = rvalue, s). For these cases
-            // we can just return var declaration of `s`. However,
-            // this is intentionally not calling `Expression.extractLast`
-            // because at this point we cannot infer the var declaration
-            // of more complex generated comma expressions such as the
-            // one for the array append hook.
-            case EXP.comma:
-            {
-                if (auto ve = e.isCommaExp().e2.isVarExp())
-                    return ve.var.isVarDeclaration();
-
-                return null;
-            }
-            default:
-                return null;
-        }
-    }
-}
-
 enum OwnedBy : ubyte
 {
     code,          // normal code expression in AST
@@ -471,64 +293,6 @@ extern (C++) abstract class Expression : ASTNode
         return a;
     }
 
-    dinteger_t toInteger()
-    {
-        //printf("Expression %s\n", EXPtoString(op).ptr);
-        if (!type || !type.isTypeError())
-            error(loc, "integer constant expression expected instead of `%s`", toChars());
-        return 0;
-    }
-
-    uinteger_t toUInteger()
-    {
-        //printf("Expression %s\n", EXPtoString(op).ptr);
-        return cast(uinteger_t)toInteger();
-    }
-
-    real_t toReal()
-    {
-        error(loc, "floating point constant expression expected instead of `%s`", toChars());
-        return CTFloat.zero;
-    }
-
-    real_t toImaginary()
-    {
-        error(loc, "floating point constant expression expected instead of `%s`", toChars());
-        return CTFloat.zero;
-    }
-
-    complex_t toComplex()
-    {
-        error(loc, "floating point constant expression expected instead of `%s`", toChars());
-        return complex_t(CTFloat.zero);
-    }
-
-    StringExp toStringExp()
-    {
-        return null;
-    }
-
-    /***************************************
-     * Return !=0 if expression is an lvalue.
-     */
-    bool isLvalue()
-    {
-        return false;
-    }
-
-    /****************************************
-     * 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;
-    }
-
     /******************************
      * If this is a reference, dereference it.
      */
@@ -564,22 +328,6 @@ extern (C++) abstract class Expression : ASTNode
         assert(0);
     }
 
-    /******
-     * Identical, not just equal. I.e. NaNs with different bit patterns are not identical
-     */
-    bool isIdentical(const Expression e) const
-    {
-        return equals(e);
-    }
-
-
-    /// Statically evaluate this expression to a `bool` if possible
-    /// Returns: an optional thath either contains the value or is empty
-    Optional!bool toBool()
-    {
-        return typeof(return)();
-    }
-
     bool hasCode()
     {
         return true;
@@ -738,27 +486,37 @@ extern (C++) abstract class Expression : ASTNode
     }
 }
 
+// Approximate Non-semantic version of the `Type.isScalar` function in `typesem`
+bool _isRoughlyScalar(Type _this)
+{
+    if (auto tb = _this.isTypeBasic())
+        return (tb.flags & TFlags.integral | TFlags.floating) != 0;
+    else if (_this.ty == Tenum || _this.ty == Tpointer) // the enum is possibly scalar
+        return true;
+    return false;
+}
+
 /***********************************************************
  * A compile-time known integer value
  */
 extern (C++) final class IntegerExp : Expression
 {
-    private dinteger_t value;
+    dinteger_t value;
 
     extern (D) this(Loc loc, dinteger_t value, Type type)
     {
         super(loc, EXP.int64);
         //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(loc, "integral constant must be scalar type, not `%s`", type.toChars());
-            type = Type.terror;
-        }
+
+        /* Verify no path to the following assert failure.
+         * Weirdly, the isScalar() includes floats - see enumsem.enumMemberSemantic() for the
+         * base type. This is possibly a bug.
+         */
+        assert(_isRoughlyScalar(type) || type.ty == Terror);
+
         this.type = type;
-        this.value = normalize(type.toBasetype().ty, value);
+        this.value = normalize(type.toBaseTypeNonSemantic().ty, value);
     }
 
     extern (D) this(dinteger_t value)
@@ -773,53 +531,6 @@ extern (C++) final class IntegerExp : Expression
         return new IntegerExp(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(ulong)val)
-            : real_t(cast(long)val);
-    }
-
-    override real_t toImaginary()
-    {
-        return CTFloat.zero;
-    }
-
-    override complex_t toComplex()
-    {
-        return complex_t(toReal());
-    }
-
-    override Optional!bool toBool()
-    {
-        bool r = toInteger() != 0;
-        return typeof(return)(r);
-    }
-
     override void accept(Visitor v)
     {
         v.visit(this);
@@ -832,7 +543,7 @@ extern (C++) final class IntegerExp : Expression
 
     extern (D) void setInteger(dinteger_t value)
     {
-        this.value = normalize(type.toBasetype().ty, value);
+        this.value = normalize(type.toBaseTypeNonSemantic().ty, value);
     }
 
     extern (D) static dinteger_t normalize(TY ty, dinteger_t value)
@@ -840,6 +551,8 @@ extern (C++) final class IntegerExp : Expression
         /* 'Normalize' the value of the integer to be in range of the type
          */
         dinteger_t result;
+        if (ty == Tpointer)
+            ty = Type.tsize_t.ty;
         switch (ty)
         {
         case Tbool:
@@ -881,15 +594,6 @@ extern (C++) final class IntegerExp : Expression
             result = cast(ulong)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;
         }
@@ -1022,72 +726,6 @@ extern (C++) final class RealExp : Expression
         return new RealExp(loc, value, type);
     }
 
-    /********************************
-     * 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
-     */
-    private static bool RealIdentical(real_t x1, real_t x2) @safe
-    {
-        return (CTFloat.isNaN(x1) && CTFloat.isNaN(x2)) || CTFloat.isIdentical(x1, x2);
-    }
-    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 bool isIdentical(const Expression e) const
-    {
-        if (!equals(e))
-            return false;
-        return CTFloat.isIdentical(value, e.isRealExp().value);
-    }
-
-    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 Optional!bool toBool()
-    {
-        return typeof(return)(!!value);
-    }
-
     override void accept(Visitor v)
     {
         v.visit(this);
@@ -1114,62 +752,6 @@ extern (C++) final class ComplexExp : Expression
         return new ComplexExp(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()) &&
-                RealExp.RealIdentical(creall(value), creall(ne.value)) &&
-                RealExp.RealIdentical(cimagl(value), cimagl(ne.value)))
-            {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    override bool isIdentical(const Expression e) const
-    {
-        if (!equals(e))
-            return false;
-        // equals() regards different NaN values as 'equals'
-        auto c = e.isComplexExp();
-        return CTFloat.isIdentical(creall(value), creall(c.value)) &&
-               CTFloat.isIdentical(cimagl(value), cimagl(c.value));
-    }
-
-    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 Optional!bool toBool()
-    {
-        return typeof(return)(!!value);
-    }
-
     override void accept(Visitor v)
     {
         v.visit(this);
@@ -1199,11 +781,6 @@ extern (C++) class IdentifierExp : Expression
         return new IdentifierExp(loc, ident);
     }
 
-    override final bool isLvalue()
-    {
-        return !this.rvalue;
-    }
-
     override void accept(Visitor v)
     {
         v.visit(this);
@@ -1243,11 +820,6 @@ extern (C++) final class DsymbolExp : Expression
         this.hasOverloads = hasOverloads;
     }
 
-    override bool isLvalue()
-    {
-        return !rvalue;
-    }
-
     override void accept(Visitor v)
     {
         v.visit(this);
@@ -1282,18 +854,6 @@ extern (C++) class ThisExp : Expression
         return r;
     }
 
-    override Optional!bool toBool()
-    {
-        // `this` is never null (what about structs?)
-        return typeof(return)(true);
-    }
-
-    override final bool isLvalue()
-    {
-        // Class `this` should be an rvalue; struct `this` should be an lvalue.
-        return !rvalue && type.toBasetype().ty != Tclass;
-    }
-
     override void accept(Visitor v)
     {
         v.visit(this);
@@ -1329,36 +889,6 @@ extern (C++) final class NullExp : Expression
         this.type = type;
     }
 
-    override bool equals(const RootObject o) const
-    {
-        if (auto e = o.isExpression())
-        {
-            if (e.op == EXP.null_ && type.equals(e.type))
-            {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    override Optional!bool toBool()
-    {
-        // null in any type is false
-        return typeof(return)(false);
-    }
-
-    override StringExp toStringExp()
-    {
-        if (this.type.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);
@@ -1427,28 +957,16 @@ extern (C++) final class StringExp : Expression
         return new StringExp(loc, string[0 .. len]);
     }
 
-    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
+     *      s = set to error message on invalid string
      * Returns:
      *      number of code units
      */
-    size_t numberOfCodeUnits(int tynto = 0) const
+    extern (D) size_t numberOfCodeUnits(int tynto, out .string s) const
     {
         int encSize;
         switch (tynto)
@@ -1471,11 +989,9 @@ extern (C++) final class StringExp : Expression
         case 1:
             for (size_t u = 0; u < len;)
             {
-                if (const s = utf_decodeChar(string[0 .. len], u, c))
-                {
-                    error(loc, "%.*s", cast(int)s.length, s.ptr);
+                s = utf_decodeChar(string[0 .. len], u, c);
+                if (s)
                     return 0;
-                }
                 result += utf_codeLength(encSize, c);
             }
             break;
@@ -1483,11 +999,9 @@ extern (C++) final class StringExp : Expression
         case 2:
             for (size_t u = 0; u < len;)
             {
-                if (const s = utf_decodeWchar(wstring[0 .. len], u, c))
-                {
-                    error(loc, "%.*s", cast(int)s.length, s.ptr);
+                s = utf_decodeWchar(wstring[0 .. len], u, c);
+                if (s)
                     return 0;
-                }
                 result += utf_codeLength(encSize, c);
             }
             break;
@@ -1596,12 +1110,6 @@ extern (C++) final class StringExp : Expression
         }
     }
 
-    override StringExp toStringExp()
-    {
-        return this;
-    }
-
-
     /**
      * Compare two `StringExp` by length, then value
      *
@@ -1631,55 +1139,39 @@ extern (C++) final class StringExp : Expression
 
         assert(this.sz == se2.sz, "Comparing string expressions of different sizes");
         //printf("sz = %d, len1 = %d, len2 = %d\n", sz, cast(int)len1, cast(int)len2);
-        if (len1 == len2)
+        if (len1 != len2)
+            return cast(int)(len1 - len2);
+        switch (sz)
         {
-            switch (sz)
-            {
-            case 1:
-                return memcmp(string, se2.string, len1);
+        case 1:
+            return memcmp(string, se2.string, len1);
 
-            case 2:
+        case 2:
+            {
+                wchar* s1 = cast(wchar*)string;
+                wchar* s2 = cast(wchar*)se2.string;
+                foreach (u; 0 .. len)
                 {
-                    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];
-                    }
+                    if (s1[u] != s2[u])
+                        return s1[u] - s2[u];
                 }
-                break;
-            case 4:
+            }
+            break;
+        case 4:
+            {
+                dchar* s1 = cast(dchar*)string;
+                dchar* s2 = cast(dchar*)se2.string;
+                foreach (u; 0 .. len)
                 {
-                    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];
-                    }
+                    if (s1[u] != s2[u])
+                        return s1[u] - s2[u];
                 }
-                break;
-            default:
-                assert(0);
             }
+            break;
+        default:
+            assert(0);
         }
-        return cast(int)(len1 - len2);
-    }
-
-    override Optional!bool toBool()
-    {
-        // Keep the old behaviour for this refactoring
-        // Should probably match language spec instead and check for length
-        return typeof(return)(true);
-    }
-
-    override bool isLvalue()
-    {
-        /* string literal is rvalue in default, but
-         * conversion to reference of static array is only allowed.
-         */
-        return !rvalue && (type && type.toBasetype().ty == Tsarray);
+        return 0;
     }
 
     /********************************
@@ -1808,34 +1300,7 @@ extern (C++) final class TupleExp : Expression
     {
         super(loc, EXP.tuple);
         this.exps = new Expressions();
-
-        this.exps.reserve(tup.objects.length);
-        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(loc, "`%s` is not an expression", o.toChars());
-            }
-        }
+        // the rest of the constructor is moved to expressionsem.d in fillTupleExpExps function
     }
 
     static TupleExp create(Loc loc, Expressions* exps) @safe
@@ -1843,31 +1308,9 @@ extern (C++) final class TupleExp : Expression
         return new TupleExp(loc, exps);
     }
 
-    override TupleExp syntaxCopy()
-    {
-        return new TupleExp(loc, e0 ? e0.syntaxCopy() : null, arraySyntaxCopy(exps));
-    }
-
-    override bool equals(const RootObject o) const
+    override TupleExp syntaxCopy()
     {
-        if (this == o)
-            return true;
-        if (auto e = o.isExpression())
-            if (auto te = e.isTupleExp())
-            {
-                if (exps.length != te.exps.length)
-                    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;
+        return new TupleExp(loc, e0 ? e0.syntaxCopy() : null, arraySyntaxCopy(exps));
     }
 
     override void accept(Visitor v)
@@ -1935,36 +1378,6 @@ extern (C++) final class ArrayLiteralExp : Expression
             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.length != ae.elements.length)
-                return false;
-            if (elements.length == 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) // use opIndex instead
     {
         return this[i];
@@ -1976,65 +1389,6 @@ extern (C++) final class ArrayLiteralExp : Expression
         return el ? el : basis;
     }
 
-    override Optional!bool toBool()
-    {
-        size_t dim = elements ? elements.length : 0;
-        return typeof(return)(dim != 0);
-    }
-
-    override StringExp toStringExp()
-    {
-        TY telem = type.nextOf().toBasetype().ty;
-        if (telem.isSomeChar || (telem == Tvoid && (!elements || elements.length == 0)))
-        {
-            ubyte sz = 1;
-            if (telem == Twchar)
-                sz = 2;
-            else if (telem == Tdchar)
-                sz = 4;
-
-            OutBuffer buf;
-            if (elements)
-            {
-                foreach (i; 0 .. elements.length)
-                {
-                    auto ch = this[i];
-                    if (ch.op != EXP.int64)
-                        return null;
-                    if (sz == 1)
-                        buf.writeByte(cast(ubyte)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);
@@ -2064,46 +1418,11 @@ extern (C++) final class AssocArrayLiteralExp : Expression
         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.length != ae.keys.length)
-                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.length;
-        }
-        return false;
-    }
-
     override AssocArrayLiteralExp syntaxCopy()
     {
         return new AssocArrayLiteralExp(loc, arraySyntaxCopy(keys), arraySyntaxCopy(values));
     }
 
-    override Optional!bool toBool()
-    {
-        size_t dim = keys.length;
-        return typeof(return)(dim != 0);
-    }
-
     override void accept(Visitor v)
     {
         v.visit(this);
@@ -2180,30 +1499,6 @@ extern (C++) final class StructLiteralExp : Expression
         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.length != se.elements.length)
-                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);
@@ -2211,37 +1506,6 @@ extern (C++) final class StructLiteralExp : Expression
         return exp;
     }
 
-    /************************************
-     * Get index of field.
-     * Returns -1 if not found.
-     */
-    extern (D) int getFieldIndex(Type type, uint offset)
-    {
-        /* Find which field offset is by looking at the field offsets
-         */
-        if (elements.length)
-        {
-            const sz = type.size();
-            if (sz == SIZE_INVALID)
-                return -1;
-            foreach (i, v; sd.fields)
-            {
-                if (offset == v.offset && sz == 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 void accept(Visitor v)
     {
         v.visit(this);
@@ -2287,12 +1551,6 @@ extern (C++) final class TypeExp : Expression
         return new TypeExp(loc, type.syntaxCopy());
     }
 
-    override bool checkType()
-    {
-        error(loc, "type `%s` is not an expression", toChars());
-        return true;
-    }
-
     override void accept(Visitor v)
     {
         v.visit(this);
@@ -2325,27 +1583,6 @@ extern (C++) final class ScopeExp : Expression
         return new ScopeExp(loc, sds.syntaxCopy(null));
     }
 
-    override bool checkType()
-    {
-        if (sds.isPackage())
-        {
-            error(loc, "%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.initial)
-            {
-                error(loc, "partial %s `%s` has no type", sds.kind(), toChars());
-                return true;
-            }
-        }
-        return false;
-    }
-
     override void accept(Visitor v)
     {
         v.visit(this);
@@ -2368,17 +1605,6 @@ extern (C++) final class TemplateExp : Expression
         this.fd = fd;
     }
 
-    override bool isLvalue()
-    {
-        return fd !is null;
-    }
-
-    override bool checkType()
-    {
-        error(loc, "%s `%s` has no type", td.kind(), toChars());
-        return true;
-    }
-
     override void accept(Visitor v)
     {
         v.visit(this);
@@ -2504,25 +1730,13 @@ extern (C++) final class SymOffExp : SymbolExp
     {
         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())
-            {
-                auto t = v.isThis();
-                assert(t);
-                .error(loc, "taking the address of non-static variable `%s` requires an instance of `%s`", v.toChars(), t.toChars());
-            }
+            assert(!v.needThis()); // make sure the error message is no longer necessary
             hasOverloads = false;
         }
         super(loc, EXP.symbolOffset, var, hasOverloads);
         this.offset = offset;
     }
 
-    override Optional!bool toBool()
-    {
-        return typeof(return)(true);
-    }
-
     override void accept(Visitor v)
     {
         v.visit(this);
@@ -2551,27 +1765,6 @@ extern (C++) final class VarExp : SymbolExp
         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 (rvalue || var.storage_class & (STC.lazy_ | STC.rvalue | STC.manifest))
-            return false;
-        return true;
-    }
-
     override void accept(Visitor v)
     {
         v.visit(this);
@@ -2593,11 +1786,6 @@ extern (C++) final class OverExp : Expression
         type = Type.tvoid;
     }
 
-    override bool isLvalue()
-    {
-        return true;
-    }
-
     override void accept(Visitor v)
     {
         v.visit(this);
@@ -2629,20 +1817,6 @@ extern (C++) final class FuncExp : Expression
         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;
-    }
-
     override FuncExp syntaxCopy()
     {
         if (td)
@@ -2654,16 +1828,6 @@ extern (C++) final class FuncExp : Expression
         return new FuncExp(loc, fd);
     }
 
-    override bool checkType()
-    {
-        if (td)
-        {
-            error(loc, "template lambda has no type");
-            return true;
-        }
-        return false;
-    }
-
     override void accept(Visitor v)
     {
         v.visit(this);
@@ -2913,11 +2077,6 @@ extern (C++) class BinAssignExp : BinExp
         super(loc, op, e1, e2);
     }
 
-    override final bool isLvalue()
-    {
-        return !rvalue;
-    }
-
     override void accept(Visitor v)
     {
         v.visit(this);
@@ -2944,28 +2103,6 @@ extern (C++) final class MixinExp : Expression
         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.length != ce.exps.length)
-                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);
@@ -3081,12 +2218,6 @@ extern (C++) final class DotTemplateExp : UnaExp
         this.td = td;
     }
 
-    override bool checkType()
-    {
-        error(loc, "%s `%s` has no type", td.kind(), toChars());
-        return true;
-    }
-
     override void accept(Visitor v)
     {
         v.visit(this);
@@ -3111,16 +2242,6 @@ extern (C++) final class DotVarExp : UnaExp
         this.hasOverloads = hasOverloads;
     }
 
-    override bool isLvalue()
-    {
-        if (rvalue)
-            return false;
-        if (e1.op != EXP.structLiteral)
-            return true;
-        auto vd = var.isVarDeclaration();
-        return !(vd && vd.isField());
-    }
-
     override void accept(Visitor v)
     {
         v.visit(this);
@@ -3134,35 +2255,21 @@ extern (C++) final class DotTemplateInstanceExp : UnaExp
 {
     TemplateInstance ti;
 
-    extern (D) this(Loc loc, Expression e, Identifier name, Objects* tiargs)
-    {
-        super(loc, EXP.dotTemplateInstance, e);
-        //printf("DotTemplateInstanceExp()\n");
-        this.ti = new TemplateInstance(loc, name, tiargs);
-    }
-
     extern (D) this(Loc loc, Expression e, TemplateInstance ti) @safe
     {
         super(loc, EXP.dotTemplateInstance, e);
         this.ti = ti;
     }
 
-    override DotTemplateInstanceExp syntaxCopy()
+    extern (D) this(Loc loc, Expression e, Identifier name, Objects* tiargs)
     {
-        return new DotTemplateInstanceExp(loc, e1.syntaxCopy(), ti.name, TemplateInstance.arraySyntaxCopy(ti.tiargs));
+        //printf("DotTemplateInstanceExp()\n");
+        this(loc, e, new TemplateInstance(loc, name, tiargs));
     }
 
-    override bool checkType()
+    override DotTemplateInstanceExp syntaxCopy()
     {
-        // Same logic as ScopeExp.checkType()
-        if (ti.tempdecl &&
-            ti.semantictiargsdone &&
-            ti.semanticRun == PASS.initial)
-        {
-            error(loc, "partial %s `%s` has no type", ti.kind(), toChars());
-            return true;
-        }
-        return false;
+        return new DotTemplateInstanceExp(loc, e1.syntaxCopy(), ti.name, TemplateInstance.arraySyntaxCopy(ti.tiargs));
     }
 
     override void accept(Visitor v)
@@ -3336,49 +2443,12 @@ extern (C++) final class CallExp : UnaExp
         return new CallExp(loc, e1.syntaxCopy(), arraySyntaxCopy(arguments), names ? names.copy() : null);
     }
 
-    override bool isLvalue()
-    {
-        if (rvalue)
-            return false;
-        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 void accept(Visitor v)
     {
         v.visit(this);
     }
 }
 
-/**
- * Get the called function type from a call expression
- * Params:
- *   ce = function call expression. Must have had semantic analysis done.
- * Returns: called function type, or `null` if error / no semantic analysis done
- */
-TypeFunction calledFunctionType(CallExp ce)
-{
-    Type t = ce.e1.type;
-    if (!t)
-        return null;
-    t = t.toBasetype();
-    if (auto tf = t.isTypeFunction())
-        return tf;
-    if (auto td = t.isTypeDelegate())
-        return td.nextOf().isTypeFunction();
-    return null;
-}
-
 FuncDeclaration isFuncAddress(Expression e, bool* hasOverloads = null) @safe
 {
     if (auto ae = e.isAddrExp())
@@ -3431,13 +2501,6 @@ extern (C++) final class AddrExp : UnaExp
         type = t;
     }
 
-    override Optional!bool toBool()
-    {
-        if (isCtfeReferenceValid(e1))
-            return typeof(return)(true);
-        return UnaExp.toBool();
-    }
-
     override void accept(Visitor v)
     {
         v.visit(this);
@@ -3462,11 +2525,6 @@ extern (C++) final class PtrExp : UnaExp
         type = t;
     }
 
-    override bool isLvalue()
-    {
-        return !rvalue;
-    }
-
     override void accept(Visitor v)
     {
         v.visit(this);
@@ -3593,16 +2651,6 @@ extern (C++) final class CastExp : UnaExp
         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 (rvalue || !e1.isLvalue())
-            return false;
-        return (to.ty == Tsarray && (e1.type.ty == Tvector || e1.type.ty == Tsarray)) ||
-            (to.ty == Taarray && e1.type.ty == Taarray) ||
-            e1.type.mutableOf.unSharedOf().equals(to.mutableOf().unSharedOf());
-    }
-
     override void accept(Visitor v)
     {
         v.visit(this);
@@ -3652,11 +2700,6 @@ extern (C++) final class VectorArrayExp : UnaExp
         super(loc, EXP.vectorArray, e1);
     }
 
-    override bool isLvalue()
-    {
-        return !rvalue && e1.isLvalue();
-    }
-
     override void accept(Visitor v)
     {
         v.visit(this);
@@ -3706,19 +2749,6 @@ extern (C++) final class SliceExp : UnaExp
         return se;
     }
 
-    override bool isLvalue()
-    {
-        /* slice expression is rvalue in default, but
-         * conversion to reference of static array is only allowed.
-         */
-        return !rvalue && (type && type.toBasetype().ty == Tsarray);
-    }
-
-    override Optional!bool toBool()
-    {
-        return e1.toBool();
-    }
-
     override void accept(Visitor v)
     {
         v.visit(this);
@@ -3775,15 +2805,6 @@ extern (C++) final class ArrayExp : UnaExp
         return ae;
     }
 
-    override bool isLvalue()
-    {
-        if (rvalue)
-            return false;
-        if (type && type.toBasetype().ty == Tvoid)
-            return false;
-        return true;
-    }
-
     override void accept(Visitor v)
     {
         v.visit(this);
@@ -3835,16 +2856,6 @@ extern (C++) final class CommaExp : BinExp
         originalExp = oe;
     }
 
-    override bool isLvalue()
-    {
-        return !rvalue && e2.isLvalue();
-    }
-
-    override Optional!bool toBool()
-    {
-        return e2.toBool();
-    }
-
     override void accept(Visitor v)
     {
         v.visit(this);
@@ -3910,11 +2921,6 @@ extern (C++) final class DelegatePtrExp : UnaExp
         super(loc, EXP.delegatePointer, e1);
     }
 
-    override bool isLvalue()
-    {
-        return !rvalue && e1.isLvalue();
-    }
-
     override void accept(Visitor v)
     {
         v.visit(this);
@@ -3933,11 +2939,6 @@ extern (C++) final class DelegateFuncptrExp : UnaExp
         super(loc, EXP.delegateFunctionPointer, e1);
     }
 
-    override bool isLvalue()
-    {
-        return !rvalue && e1.isLvalue();
-    }
-
     override void accept(Visitor v)
     {
         v.visit(this);
@@ -3974,19 +2975,6 @@ extern (C++) final class IndexExp : BinExp
         return ie;
     }
 
-    override bool isLvalue()
-    {
-        if (rvalue)
-            return false;
-        auto t1b = e1.type.toBasetype();
-        if (t1b.isTypeAArray() || t1b.isTypeSArray() ||
-            (e1.isIndexExp() && t1b != t1b.isTypeDArray()))
-        {
-            return e1.isLvalue();
-        }
-        return true;
-    }
-
     override void accept(Visitor v)
     {
         v.visit(this);
@@ -4055,17 +3043,6 @@ extern (C++) class AssignExp : BinExp
         super(loc, tok, 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 == EXP.slice || e1.op == EXP.arrayLength)
-        {
-            return false;
-        }
-        return !rvalue;
-    }
-
     override void accept(Visitor v)
     {
         v.visit(this);
@@ -4098,6 +3075,8 @@ extern (C++) final class LoweredAssignExp : AssignExp
  */
 extern (C++) final class ConstructExp : AssignExp
 {
+    Expression lowering;
+
     extern (D) this(Loc loc, Expression e1, Expression e2) @safe
     {
         super(loc, EXP.construct, e1, e2);
@@ -4788,11 +3767,6 @@ extern (C++) final class CondExp : BinExp
         return new CondExp(loc, econd.syntaxCopy(), e1.syntaxCopy(), e2.syntaxCopy());
     }
 
-    override bool isLvalue()
-    {
-        return !rvalue && e1.isLvalue() && e2.isLvalue();
-    }
-
     override void accept(Visitor v)
     {
         v.visit(this);
@@ -4933,27 +3907,6 @@ extern (C++) final class ClassReferenceExp : Expression
         return value.sd.isClassDeclaration();
     }
 
-    // Return index of the field, or -1 if not found
-    int getFieldIndex(Type fieldtype, uint fieldoffset)
-    {
-        ClassDeclaration cd = originalClass();
-        uint 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 cast(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 findFieldIndexByName(VarDeclaration v)
index 7c92ebe66c44827c9d0d271740bb4da27695f6bb..7af0992ef8bfa0f2429702d04b14576a45932815 100644 (file)
@@ -59,6 +59,18 @@ namespace dmd
     Expression *ctfeInterpret(Expression *e);
     void expandTuples(Expressions *exps, ArgumentLabels *names = nullptr);
     Expression *optimize(Expression *exp, int result, bool keepLvalue = false);
+    bool isIdentical(const Expression *exp, const Expression *e);
+    bool equals(const Expression *exp, const Expression *e);
+    bool isLvalue(Expression *exp);
+    int32_t getFieldIndex(ClassReferenceExp *cre, Type *fieldtype, uint32_t fieldoffset);
+    void fillTupleExpExps(TupleExp *te, TupleDeclaration *tup);
+    Optional<bool> toBool(Expression *exp);
+    StringExp *toStringExp(Expression *exp);
+    dinteger_t toInteger(Expression *exp);
+    uinteger_t toUInteger(Expression *exp);
+    real_t toReal(Expression *exp);
+    real_t toImaginary(Expression *exp);
+    complex_t toComplex(Expression *exp);
 }
 
 typedef unsigned char OwnedBy;
@@ -94,20 +106,10 @@ public:
 
     const char* toChars() const final override;
 
-    virtual dinteger_t toInteger();
-    virtual uinteger_t toUInteger();
-    virtual real_t toReal();
-    virtual real_t toImaginary();
-    virtual complex_t toComplex();
-    virtual StringExp *toStringExp();
-    virtual bool isLvalue();
-    virtual bool checkType();
     Expression *addressOf();
     Expression *deref();
 
     int isConst();
-    virtual bool isIdentical(const Expression *e) const;
-    virtual Optional<bool> toBool();
     virtual bool hasCode()
     {
         return true;
@@ -235,12 +237,6 @@ public:
     dinteger_t value;
 
     static IntegerExp *create(Loc loc, dinteger_t value, Type *type);
-    bool equals(const RootObject * const o) const override;
-    dinteger_t toInteger() override;
-    real_t toReal() override;
-    real_t toImaginary() override;
-    complex_t toComplex() override;
-    Optional<bool> toBool() override;
     void accept(Visitor *v) override { v->visit(this); }
     dinteger_t getInteger() { return value; }
     template<int v>
@@ -261,14 +257,6 @@ public:
     real_t value;
 
     static RealExp *create(Loc loc, real_t value, Type *type);
-    bool equals(const RootObject * const o) const override;
-    bool isIdentical(const Expression *e) const override;
-    dinteger_t toInteger() override;
-    uinteger_t toUInteger() override;
-    real_t toReal() override;
-    real_t toImaginary() override;
-    complex_t toComplex() override;
-    Optional<bool> toBool() override;
     void accept(Visitor *v) override { v->visit(this); }
 };
 
@@ -278,14 +266,6 @@ public:
     complex_t value;
 
     static ComplexExp *create(Loc loc, complex_t value, Type *type);
-    bool equals(const RootObject * const o) const override;
-    bool isIdentical(const Expression *e) const override;
-    dinteger_t toInteger() override;
-    uinteger_t toUInteger() override;
-    real_t toReal() override;
-    real_t toImaginary() override;
-    complex_t toComplex() override;
-    Optional<bool> toBool() override;
     void accept(Visitor *v) override { v->visit(this); }
 };
 
@@ -295,7 +275,6 @@ public:
     Identifier *ident;
 
     static IdentifierExp *create(Loc loc, Identifier *ident);
-    bool isLvalue() override final;
     void accept(Visitor *v) override { v->visit(this); }
 };
 
@@ -312,7 +291,6 @@ public:
     d_bool hasOverloads;
 
     DsymbolExp *syntaxCopy() override;
-    bool isLvalue() override;
     void accept(Visitor *v) override { v->visit(this); }
 };
 
@@ -322,8 +300,6 @@ public:
     VarDeclaration *var;
 
     ThisExp *syntaxCopy() override;
-    Optional<bool> toBool() override;
-    bool isLvalue() override;
 
     void accept(Visitor *v) override { v->visit(this); }
 };
@@ -337,9 +313,6 @@ public:
 class NullExp final : public Expression
 {
 public:
-    bool equals(const RootObject * const o) const override;
-    Optional<bool> toBool() override;
-    StringExp *toStringExp() override;
     void accept(Visitor *v) override { v->visit(this); }
 };
 
@@ -357,12 +330,8 @@ public:
 
     static StringExp *create(Loc loc, const char *s);
     static StringExp *create(Loc loc, const void *s, d_size_t len);
-    bool equals(const RootObject * const o) const override;
     char32_t getCodeUnit(d_size_t i) const;
     dinteger_t getIndex(d_size_t i) const;
-    StringExp *toStringExp() override;
-    Optional<bool> toBool() override;
-    bool isLvalue() override;
     void accept(Visitor *v) override { v->visit(this); }
     size_t numberOfCodeUnits(int tynto = 0) const;
     void writeTo(void* dest, bool zero, int tyto = 0) const;
@@ -395,7 +364,6 @@ public:
 
     static TupleExp *create(Loc loc, Expressions *exps);
     TupleExp *syntaxCopy() override;
-    bool equals(const RootObject * const o) const override;
 
     void accept(Visitor *v) override { v->visit(this); }
 };
@@ -412,10 +380,7 @@ public:
 
     static ArrayLiteralExp *create(Loc loc, Expressions *elements);
     ArrayLiteralExp *syntaxCopy() override;
-    bool equals(const RootObject * const o) const override;
     Expression *getElement(d_size_t i);
-    Optional<bool> toBool() override;
-    StringExp *toStringExp() override;
 
     void accept(Visitor *v) override { v->visit(this); }
 };
@@ -429,9 +394,7 @@ public:
     Expression* lowering;
     Expression* loweringCtfe;
 
-    bool equals(const RootObject * const o) const override;
     AssocArrayLiteralExp *syntaxCopy() override;
-    Optional<bool> toBool() override;
 
     void accept(Visitor *v) override { v->visit(this); }
 };
@@ -478,7 +441,6 @@ public:
 
 
     static StructLiteralExp *create(Loc loc, StructDeclaration *sd, void *elements, Type *stype = nullptr);
-    bool equals(const RootObject * const o) const override;
     StructLiteralExp *syntaxCopy() override;
 
     void accept(Visitor *v) override { v->visit(this); }
@@ -488,7 +450,6 @@ class TypeExp final : public Expression
 {
 public:
     TypeExp *syntaxCopy() override;
-    bool checkType() override;
     void accept(Visitor *v) override { v->visit(this); }
 };
 
@@ -498,7 +459,6 @@ public:
     ScopeDsymbol *sds;
 
     ScopeExp *syntaxCopy() override;
-    bool checkType() override;
     void accept(Visitor *v) override { v->visit(this); }
 };
 
@@ -508,8 +468,6 @@ public:
     TemplateDeclaration *td;
     FuncDeclaration *fd;
 
-    bool isLvalue() override;
-    bool checkType() override;
     void accept(Visitor *v) override { v->visit(this); }
 };
 
@@ -569,8 +527,6 @@ class SymOffExp final : public SymbolExp
 public:
     dinteger_t offset;
 
-    Optional<bool> toBool() override;
-
     void accept(Visitor *v) override { v->visit(this); }
 };
 
@@ -581,8 +537,6 @@ class VarExp final : public SymbolExp
 public:
     d_bool delegateWasExtracted;
     static VarExp *create(Loc loc, Declaration *var, bool hasOverloads = true);
-    bool equals(const RootObject * const o) const override;
-    bool isLvalue() override;
 
     void accept(Visitor *v) override { v->visit(this); }
 };
@@ -594,7 +548,6 @@ class OverExp final : public Expression
 public:
     OverloadSet *vars;
 
-    bool isLvalue() override;
     void accept(Visitor *v) override { v->visit(this); }
 };
 
@@ -607,9 +560,7 @@ public:
     TemplateDeclaration *td;
     TOK tok;
 
-    bool equals(const RootObject * const o) const override;
     FuncExp *syntaxCopy() override;
-    bool checkType() override;
 
     void accept(Visitor *v) override { v->visit(this); }
 };
@@ -699,7 +650,6 @@ public:
 class BinAssignExp : public BinExp
 {
 public:
-    bool isLvalue() override final;
     void accept(Visitor *v) override { v->visit(this); }
 };
 
@@ -752,7 +702,6 @@ class DotTemplateExp final : public UnaExp
 public:
     TemplateDeclaration *td;
 
-    bool checkType() override;
     void accept(Visitor *v) override { v->visit(this); }
 };
 
@@ -762,7 +711,6 @@ public:
     Declaration *var;
     d_bool hasOverloads;
 
-    bool isLvalue() override;
     void accept(Visitor *v) override { v->visit(this); }
 };
 
@@ -772,7 +720,6 @@ public:
     TemplateInstance *ti;
 
     DotTemplateInstanceExp *syntaxCopy() override;
-    bool checkType() override;
     void accept(Visitor *v) override { v->visit(this); }
 };
 
@@ -844,7 +791,6 @@ public:
     static CallExp *create(Loc loc, FuncDeclaration *fd, Expression *earg1);
 
     CallExp *syntaxCopy() override;
-    bool isLvalue() override;
 
     void accept(Visitor *v) override { v->visit(this); }
 };
@@ -852,15 +798,12 @@ public:
 class AddrExp final : public UnaExp
 {
 public:
-    Optional<bool> toBool() override;
     void accept(Visitor *v) override { v->visit(this); }
 };
 
 class PtrExp final : public UnaExp
 {
 public:
-    bool isLvalue() override;
-
     void accept(Visitor *v) override { v->visit(this); }
 };
 
@@ -906,7 +849,6 @@ public:
     Expression* lowering;
 
     CastExp *syntaxCopy() override;
-    bool isLvalue() override;
 
     void accept(Visitor *v) override { v->visit(this); }
 };
@@ -926,7 +868,6 @@ public:
 class VectorArrayExp final : public UnaExp
 {
 public:
-    bool isLvalue() override;
     void accept(Visitor *v) override { v->visit(this); }
 };
 
@@ -948,8 +889,6 @@ private:
 
 public:
     SliceExp *syntaxCopy() override;
-    bool isLvalue() override;
-    Optional<bool> toBool() override;
 
     void accept(Visitor *v) override { v->visit(this); }
 };
@@ -974,14 +913,12 @@ public:
 class DelegatePtrExp final : public UnaExp
 {
 public:
-    bool isLvalue() override;
     void accept(Visitor *v) override { v->visit(this); }
 };
 
 class DelegateFuncptrExp final : public UnaExp
 {
 public:
-    bool isLvalue() override;
     void accept(Visitor *v) override { v->visit(this); }
 };
 
@@ -996,7 +933,6 @@ public:
     d_bool modifiable;
 
     ArrayExp *syntaxCopy() override;
-    bool isLvalue() override;
 
     void accept(Visitor *v) override { v->visit(this); }
 };
@@ -1015,8 +951,6 @@ public:
     d_bool isGenerated;
     d_bool allowCommaExp;
     Expression* originalExp;
-    bool isLvalue() override;
-    Optional<bool> toBool() override;
     void accept(Visitor *v) override { v->visit(this); }
 };
 
@@ -1029,7 +963,6 @@ public:
     d_bool indexIsInBounds;       // true if 0 <= e2 && e2 <= e1.length - 1
 
     IndexExp *syntaxCopy() override;
-    bool isLvalue() override;
 
     void accept(Visitor *v) override { v->visit(this); }
 };
@@ -1062,14 +995,13 @@ class AssignExp : public BinExp
 public:
     MemorySet memset;
 
-    bool isLvalue() override final;
-
     void accept(Visitor *v) override { v->visit(this); }
 };
 
 class ConstructExp final : public AssignExp
 {
 public:
+    Expression *lowering;
     void accept(Visitor *v) override { v->visit(this); }
 };
 
@@ -1309,7 +1241,6 @@ public:
     Expression *econd;
 
     CondExp *syntaxCopy() override;
-    bool isLvalue() override;
 
     void accept(Visitor *v) override { v->visit(this); }
 };
index 68fe3dca41c0621837797944e34c689910d2a2dc..23b77fc571723a955252ffeb9f3263cad635c81e 100644 (file)
@@ -27,9 +27,11 @@ import dmd.canthrow;
 import dmd.chkformat;
 import dmd.cond;
 import dmd.ctorflow;
+import dmd.ctfeexpr : isCtfeReferenceValid;
 import dmd.dscope;
 import dmd.dsymbol;
 import dmd.dsymbolsem;
+import dmd.templatesem : computeOneMember;
 import dmd.declaration;
 import dmd.dclass;
 import dmd.dcast;
@@ -40,7 +42,6 @@ import dmd.dimport;
 import dmd.dinterpret;
 import dmd.dmodule;
 import dmd.dstruct;
-import dmd.dsymbolsem : findTempDecl;
 import dmd.dtemplate;
 import dmd.errors;
 import dmd.errorsink;
@@ -72,18 +73,22 @@ import dmd.optimize;
 import dmd.parse;
 import dmd.printast;
 import dmd.root.array;
+import dmd.root.complex;
 import dmd.root.ctfloat;
 import dmd.root.filename;
+import dmd.root.optional;
 import dmd.common.outbuffer;
 import dmd.rootobject;
 import dmd.root.string;
 import dmd.root.utf;
+import dmd.root.rmem;
 import dmd.semantic2;
 import dmd.semantic3;
 import dmd.sideeffect;
 import dmd.safe;
 import dmd.target;
-import dmd.templatesem : matchWithInstance, deduceType, matchArg, updateTempDecl;
+import dmd.targetcompiler;
+import dmd.templatesem : matchWithInstance, deduceType, matchArg, updateTempDecl, semanticTiargs, findTempDecl;
 import dmd.tokens;
 import dmd.traits;
 import dmd.typesem;
@@ -95,6 +100,971 @@ import dmd.visitor.postorder;
 
 enum LOGSEMANTIC = false;
 
+/*******************************
+ * Merge results of `ctorflow` into `_this`.
+ * Params:
+ *   _this = scope to merge the results into
+ *   loc = for error messages
+ *   ctorflow = flow results to merge in
+ */
+void merge(Scope* _this, 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)
+        return;
+    FuncDeclaration f = _this.func;
+    if (_this.fes)
+        f = _this.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());
+        }
+    }
+}
+
+real_t toImaginary(Expression _this)
+{
+    if (auto ie = _this.isIntegerExp())
+        return CTFloat.zero;
+    else if (auto re = _this.isRealExp)
+        return re.type.isReal() ? CTFloat.zero : re.value;
+    else if (auto ce = _this.isComplexExp())
+        return cimagl(ce.value);
+
+    error(_this.loc, "floating point constant expression expected instead of `%s`", _this.toChars());
+    return CTFloat.zero;
+}
+
+real_t toReal(Expression _this)
+{
+    if (auto iexp = _this.isIntegerExp())
+    {
+        // normalize() is necessary until we fix all the paints of 'type'
+        const ty = iexp.type.toBasetype().ty;
+        const val = iexp.normalize(ty, iexp.value);
+        iexp.value = val;
+        return (ty == Tuns64)
+            ? real_t(cast(ulong)val)
+            : real_t(cast(long)val);
+    }
+    else if (auto rexp = _this.isRealExp())
+    {
+        return rexp.type.isReal() ? rexp.value : CTFloat.zero;
+    }
+    else if (auto cexp = _this.isComplexExp())
+    {
+        return creall(cexp.value);
+    }
+    error(_this.loc, "floating point constant expression expected instead of `%s`", _this.toChars());
+    return CTFloat.zero;
+}
+
+complex_t toComplex(Expression _this)
+{
+    if (auto iexp = _this.isIntegerExp())
+    {
+        return complex_t(iexp.toReal());
+    }
+    else if (auto rexp = _this.isRealExp())
+    {
+        return complex_t(rexp.toReal(), rexp.toImaginary());
+    }
+    else if (auto cexp = _this.isComplexExp())
+    {
+        return cexp.value;
+    }
+    error(_this.loc, "floating point constant expression expected instead of `%s`", _this.toChars());
+    return complex_t(CTFloat.zero);
+}
+
+dinteger_t toInteger(Expression _this)
+{
+    if (auto iexp = _this.isIntegerExp())
+    {
+        // normalize() is necessary until we fix all the paints of 'type'
+        return iexp.value = IntegerExp.normalize(iexp.type.toBasetype().ty, iexp.value);
+    }
+    else if (auto rexp = _this.isRealExp())
+    {
+        return cast(sinteger_t)rexp.toReal();
+    }
+
+    else if (auto cexp = _this.isComplexExp())
+    {
+        return cast(sinteger_t)cexp.toReal();
+    }
+
+    // import dmd.hdrgen : EXPtoString;
+    //printf("Expression %s\n", EXPtoString(op).ptr);
+    if (!_this.type || !_this.type.isTypeError())
+        error(_this.loc, "integer constant expression expected instead of `%s`", _this.toChars());
+    return 0;
+}
+
+uinteger_t toUInteger(Expression _this)
+{
+    if (auto rexp = _this.isRealExp())
+    {
+        return cast(uinteger_t)rexp.toReal();
+    }
+    else if (auto cexp = _this.isComplexExp())
+    {
+        return cast(uinteger_t)cexp.toReal();
+    }
+    // import dmd.hdrgen : EXPtoString;
+    //printf("Expression %s\n", EXPtoString(op).ptr);
+    return cast(uinteger_t)_this.toInteger();
+}
+
+
+/***************************************************
+ * 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
+ *      deref = number of dereferences encountered
+ * Returns:
+ *      variable if there is one, null if not
+ */
+VarDeclaration expToVariable(Expression e, out int deref)
+{
+    deref = 0;
+    while (1)
+    {
+        switch (e.op)
+        {
+            case EXP.variable:
+                return e.isVarExp().var.isVarDeclaration();
+
+            case EXP.dotVariable:
+                e = e.isDotVarExp().e1;
+                if (e.type.toBasetype().isTypeClass())
+                    deref++;
+
+                continue;
+
+            case EXP.index:
+            {
+                e = e.isIndexExp().e1;
+                if (!e.type.toBasetype().isTypeSArray())
+                    deref++;
+
+                continue;
+            }
+
+            case EXP.slice:
+            {
+                e = e.isSliceExp().e1;
+                if (!e.type.toBasetype().isTypeSArray())
+                    deref++;
+
+                continue;
+            }
+
+            case EXP.super_:
+                return e.isSuperExp().var.isVarDeclaration();
+            case EXP.this_:
+                return e.isThisExp().var.isVarDeclaration();
+
+            // Temporaries for rvalues that need destruction
+            // are of form: (T s = rvalue, s). For these cases
+            // we can just return var declaration of `s`. However,
+            // this is intentionally not calling `Expression.extractLast`
+            // because at this point we cannot infer the var declaration
+            // of more complex generated comma expressions such as the
+            // one for the array append hook.
+            case EXP.comma:
+            {
+                if (auto ve = e.isCommaExp().e2.isVarExp())
+                    return ve.var.isVarDeclaration();
+
+                return null;
+            }
+            default:
+                return null;
+        }
+    }
+}
+
+/**
+ * Get the called function type from a call expression
+ * Params:
+ *   ce = function call expression. Must have had semantic analysis done.
+ * Returns: called function type, or `null` if error / no semantic analysis done
+ */
+TypeFunction calledFunctionType(CallExp ce)
+{
+    Type t = ce.e1.type;
+    if (!t)
+        return null;
+    t = t.toBasetype();
+    if (auto tf = t.isTypeFunction())
+        return tf;
+    if (auto td = t.isTypeDelegate())
+        return td.nextOf().isTypeFunction();
+    return null;
+}
+
+/****************************************
+ * Expand tuples in-place.
+ *
+ * Example:
+ *     When there's a call `f(10, pair: AliasSeq!(20, 30), single: 40)`, the input is:
+ *         `exps =  [10, (20, 30), 40]`
+ *         `names = [null, "pair", "single"]`
+ *     The arrays will be modified to:
+ *         `exps =  [10, 20, 30, 40]`
+ *         `names = [null, "pair", null, "single"]`
+ *
+ * Params:
+ *     exps  = array of Expressions
+ *     names = optional array of names corresponding to Expressions
+ */
+void expandTuples(Expressions* exps, ArgumentLabels* names = null)
+{
+    //printf("expandTuples()\n");
+    if (exps is null)
+        return;
+
+    if (names)
+    {
+        if (exps.length != names.length)
+        {
+            printf("exps.length = %d, names.length = %d\n", cast(int) exps.length, cast(int) names.length);
+            printf("exps = %s, names = %s\n", exps.toChars(), names.toChars());
+            if (exps.length > 0)
+                printf("%s\n", (*exps)[0].loc.toChars());
+            assert(0);
+        }
+    }
+
+    // At `index`, a tuple of length `length` is expanded. Insert corresponding nulls in `names`.
+    void expandNames(size_t index, size_t length)
+    {
+        if (names)
+        {
+            if (length == 0)
+            {
+                names.remove(index);
+                return;
+            }
+            foreach (i; 1 .. length)
+            {
+                names.insert(index + i, ArgumentLabel(cast(Identifier) null, Loc.init));
+            }
+        }
+    }
+
+    for (size_t i = 0; i < exps.length; 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.length == 0)
+                {
+                    exps.remove(i);
+                    expandNames(i, 0);
+                    if (i == exps.length)
+                        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);
+                    expandNames(i, texps.length);
+                }
+                i--;
+                continue;
+            }
+        }
+
+        // Inline expand all the tuples
+        while (arg.op == EXP.tuple)
+        {
+            TupleExp te = cast(TupleExp)arg;
+            exps.remove(i); // remove arg
+            exps.insert(i, te.exps); // replace with tuple contents
+            expandNames(i, te.exps.length);
+            if (i == exps.length)
+                return; // empty tuple, no more arguments
+            (*exps)[i] = Expression.combine(te.e0, (*exps)[i]);
+            arg = (*exps)[i];
+        }
+    }
+}
+
+StringExp toStringExp(Expression _this)
+{
+    static StringExp nullExpToStringExp(NullExp _this)
+    {
+        if (_this.type.implicitConvTo(Type.tstring))
+        {
+            auto se = new StringExp(_this.loc, (cast(char*).mem.xcalloc(1, 1))[0 .. 0]);
+            se.type = Type.tstring;
+            return se;
+        }
+        return null;
+    }
+
+    static StringExp arrayLiteralToStringExp(ArrayLiteralExp _this)
+    {
+        TY telem = _this.type.nextOf().toBasetype().ty;
+        if (!(telem.isSomeChar || (telem == Tvoid && (!_this.elements || _this.elements.length == 0))))
+            return null;
+
+        ubyte sz = 1;
+        if (telem == Twchar)
+            sz = 2;
+        else if (telem == Tdchar)
+            sz = 4;
+
+        OutBuffer buf;
+        if (_this.elements)
+        {
+            foreach (i; 0 .. _this.elements.length)
+            {
+                auto ch = _this[i];
+                if (ch.op != EXP.int64)
+                    return null;
+                if (sz == 1)
+                    buf.writeByte(cast(ubyte)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(_this.loc, buf.extractSlice()[0 .. len * sz], len, sz, prefix);
+        se.sz = sz;
+        se.type = _this.type;
+        return se;
+    }
+
+    switch(_this.op)
+    {
+        case EXP.null_: return nullExpToStringExp(_this.isNullExp());
+        case EXP.string_: return _this.isStringExp();
+        case EXP.arrayLiteral: return arrayLiteralToStringExp(_this.isArrayLiteralExp());
+        default: return null;
+    }
+}
+
+Optional!bool toBool(Expression _this)
+{
+    static Optional!bool integerToBool(IntegerExp _this)
+    {
+        bool r = _this.toInteger() != 0;
+        return typeof(return)(r);
+    }
+
+    static Optional!bool arrayLiteralToBool(ArrayLiteralExp _this)
+    {
+        size_t dim = _this.elements ? _this.elements.length : 0;
+        return typeof(return)(dim != 0);
+    }
+
+    static Optional!bool assocArrayLiteralToBool(AssocArrayLiteralExp _this)
+    {
+        size_t dim = _this.keys.length;
+        return typeof(return)(dim != 0);
+    }
+
+    static Optional!bool addrToBool(AddrExp _this)
+    {
+        if (isCtfeReferenceValid(_this.e1))
+            return typeof(return)(true);
+        return typeof(return)();
+    }
+
+    switch(_this.op)
+    {
+        case EXP.int64: return integerToBool(_this.isIntegerExp());
+        case EXP.float64: return typeof(return)(!!_this.isRealExp().value);
+        case EXP.complex80: return typeof(return)(!!_this.isComplexExp().value);
+        // `this` is never null (what about structs?)
+        case EXP.this_, EXP.super_: return typeof(return)(true);
+        // null in any type is false
+        case EXP.null_: return typeof(return)(false);
+        // Keep the old behaviour for this refactoring
+        // Should probably match language spec instead and check for length
+        case EXP.string_: return typeof(return)(true);
+        case EXP.arrayLiteral: return arrayLiteralToBool(_this.isArrayLiteralExp());
+        case EXP.assocArrayLiteral: return assocArrayLiteralToBool(_this.isAssocArrayLiteralExp());
+        case EXP.symbolOffset: return typeof(return)(true);
+        case EXP.address: return addrToBool(_this.isAddrExp());
+        case EXP.slice: return _this.isSliceExp().e1.toBool();
+        case EXP.comma: return _this.isCommaExp().e2.toBool();
+        // Statically evaluate this expression to a `bool` if possible
+        // Returns: an optional that either contains the value or is empty
+        default: return typeof(return)();
+    }
+}
+
+/****************************************
+ * Check that the expression `e` has a valid type.
+ * If not, generates an error "... has no type".
+ * Params:
+ *      e = Expression to check
+ * Returns:
+ *      true if the expression has a valid type.
+ * Note:
+ *      When this function returns false, `checkValue()` should also return true.
+ */
+bool hasValidType(Expression e)
+{
+    static bool visitTypeExp(TypeExp e)
+    {
+        error(e.loc, "type `%s` is not an expression", e.toChars());
+        return false;
+    }
+
+    static bool visitScopeExp(ScopeExp e)
+    {
+        if (e.sds.isPackage())
+        {
+            error(e.loc, "%s `%s` has no type", e.sds.kind(), e.sds.toChars());
+            return false;
+        }
+        auto ti = e.sds.isTemplateInstance();
+        if (!ti)
+            return true;
+        //assert(ti.needsTypeInference(sc));
+        if (ti.tempdecl &&
+            ti.semantictiargsdone &&
+            ti.semanticRun == PASS.initial)
+        {
+            error(e.loc, "partial %s `%s` has no type", e.sds.kind(), e.toChars());
+            return false;
+        }
+        return true;
+    }
+
+    static bool visitTemplateExp(TemplateExp e)
+    {
+        error(e.loc, "%s `%s` has no type", e.td.kind(), e.toChars());
+        return false;
+    }
+
+    static bool visitFuncExp(FuncExp e)
+    {
+        if (e.td)
+        {
+            error(e.loc, "template lambda has no type");
+            return false;
+        }
+        return true;
+    }
+
+    static bool visitDotTemplateExp(DotTemplateExp e)
+    {
+        error(e.loc, "%s `%s` has no type", e.td.kind(), e.toChars());
+        return false;
+    }
+
+    static bool visitDotTemplateInstanceExp(DotTemplateInstanceExp e)
+    {
+        // Same logic as ScopeExp.hasValidType()
+        if (e.ti.tempdecl &&
+            e.ti.semantictiargsdone &&
+            e.ti.semanticRun == PASS.initial)
+        {
+            error(e.loc, "partial %s `%s` has no type", e.ti.kind(), e.toChars());
+            return false;
+        }
+        return true;
+    }
+
+    switch (e.op)
+    {
+        case EXP.type:                   return visitTypeExp(e.isTypeExp());
+        case EXP.scope_:                 return visitScopeExp(e.isScopeExp());
+        case EXP.template_:              return visitTemplateExp(e.isTemplateExp());
+        case EXP.function_:              return visitFuncExp(e.isFuncExp());
+        case EXP.dotTemplateDeclaration: return visitDotTemplateExp(e.isDotTemplateExp());
+        case EXP.dotTemplateInstance:    return visitDotTemplateInstanceExp(e.isDotTemplateInstanceExp());
+
+        default: return true;
+    }
+}
+
+void fillTupleExpExps(TupleExp _this, TupleDeclaration tup)
+{
+    _this.exps.reserve(tup.objects.length);
+    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(_this.loc, s);
+            _this.exps.push(e);
+        }
+        else if (auto eo = o.isExpression())
+        {
+            auto e = eo.copy();
+            e.loc = _this.loc;    // https://issues.dlang.org/show_bug.cgi?id=15669
+            _this.exps.push(e);
+        }
+        else if (auto t = o.isType())
+        {
+            Expression e = new TypeExp(_this.loc, t);
+            _this.exps.push(e);
+        }
+        else
+        {
+            error(_this.loc, "`%s` is not an expression", o.toChars());
+        }
+    }
+}
+
+bool isLvalue(Expression _this)
+{
+    static bool dotVarExpIsLvalue(DotVarExp _this)
+    {
+        if (_this.rvalue)
+            return false;
+        if (_this.e1.op != EXP.structLiteral)
+            return true;
+        auto vd = _this.var.isVarDeclaration();
+        return !(vd && vd.isField());
+    }
+
+    static bool callExpIsLvalue(CallExp _this)
+    {
+        if (_this.rvalue)
+            return false;
+        Type tb = _this.e1.type.toBasetype();
+        if (tb.ty == Tdelegate || tb.ty == Tpointer)
+            tb = tb.nextOf();
+        auto tf = tb.isTypeFunction();
+        if (tf && tf.isRef)
+        {
+            if (auto dve = _this.e1.isDotVarExp())
+                if (dve.var.isCtorDeclaration())
+                    return false;
+            return true; // function returns a reference
+        }
+        return false;
+    }
+
+    static bool castExpIsLvalue(CastExp _this)
+    {
+        //printf("e1.type = %s, to.type = %s\n", e1.type.toChars(), to.toChars());
+        if (_this.rvalue || !_this.e1.isLvalue())
+            return false;
+        return (_this.to.ty == Tsarray && (_this.e1.type.ty == Tvector || _this.e1.type.ty == Tsarray)) ||
+            (_this.to.ty == Taarray && _this.e1.type.ty == Taarray) ||
+            _this.e1.type.mutableOf.unSharedOf().equals(_this.to.mutableOf().unSharedOf());
+    }
+
+    static bool indexExpIsLvalue(IndexExp _this)
+    {
+        if (_this.rvalue)
+            return false;
+        auto t1b = _this.e1.type.toBasetype();
+        if (t1b.isTypeAArray() || t1b.isTypeSArray() ||
+            (_this.e1.isIndexExp() && t1b != t1b.isTypeDArray()))
+        {
+            return _this.e1.isLvalue();
+        }
+        return true;
+    }
+
+    static bool assignExpIsLvalue(AssignExp _this)
+    {
+        // Array-op 'x[] = y[]' should make an rvalue.
+        // Setting array length 'x.length = v' should make an rvalue.
+        if (_this.e1.op == EXP.slice || _this.e1.op == EXP.arrayLength)
+        {
+            return false;
+        }
+        return !_this.rvalue;
+    }
+
+    bool not_rvalue = _this.rvalue == false;
+
+    if (_this.isBinAssignExp())
+        return not_rvalue;
+
+    switch(_this.op)
+    {
+        case EXP.overloadSet: return true;
+        case EXP.identifier, EXP.dollar, EXP.dSymbol, EXP.star: return not_rvalue;
+        // Class `this` should be an rvalue; struct `this` should be an lvalue.
+        case EXP.this_, EXP.super_: return not_rvalue && _this.type.toBasetype().ty != Tclass;
+        /* string literal/slice is rvalue in default, but
+         * conversion to reference of static array is only allowed.
+         */
+        case EXP.string_, EXP.slice: return not_rvalue && (_this.type && _this.type.toBasetype().ty == Tsarray);
+        case EXP.question: return not_rvalue && _this.isCondExp().e1.isLvalue() && _this.isCondExp().e2.isLvalue();
+        case EXP.array: return !( _this.rvalue || (_this.type && _this.type.toBasetype().ty == Tvoid));
+
+        case EXP.vectorArray: return not_rvalue && _this.isVectorArrayExp().e1.isLvalue();
+        case EXP.delegatePointer: return not_rvalue && _this.isDelegatePtrExp().e1.isLvalue();
+        case EXP.delegateFunctionPointer: return not_rvalue && _this.isDelegateFuncptrExp().e1.isLvalue();
+
+        case EXP.comma: return not_rvalue && _this.isCommaExp().e2.isLvalue();
+        case EXP.variable: return !(_this.rvalue || (_this.isVarExp().var.storage_class & (STC.lazy_ | STC.rvalue | STC.manifest)));
+        case EXP.template_: return _this.isTemplateExp().fd !is null;
+
+        case EXP.dotVariable: return dotVarExpIsLvalue(_this.isDotVarExp());
+        case EXP.call: return callExpIsLvalue(_this.isCallExp());
+        case EXP.cast_: return castExpIsLvalue(_this.isCastExp());
+        case EXP.index: return indexExpIsLvalue(_this.isIndexExp());
+        case EXP.assign, EXP.loweredAssignExp, EXP.construct, EXP.blit: return assignExpIsLvalue(cast(AssignExp) _this);
+        default: return false;
+    }
+}
+
+/***********************************************************
+ * Determine if copy elision is allowed when copying an expression to
+ * a typed storage. This basically elides a restricted subset of so-called
+ * "pure" rvalues, i.e. expressions with no reference semantics.
+ */
+bool canElideCopy(Expression e, Type to, bool checkMod = true)
+{
+    if (checkMod && !MODimplicitConv(e.type.mod, to.mod))
+        return false;
+
+    static bool visitCallExp(CallExp e)
+    {
+        if (auto dve = e.e1.isDotVarExp())
+            if (dve.var.isCtorDeclaration())
+                return true;
+
+        auto tb = e.e1.type.toBasetype();
+        if (tb.ty == Tdelegate || tb.ty == Tpointer)
+            tb = tb.nextOf();
+        auto tf = tb.isTypeFunction();
+        return tf && !tf.isRef;
+    }
+
+    static bool visitDotVarExp(DotVarExp e)
+    {
+        auto vd = e.var.isVarDeclaration();
+        if (!vd || !vd.isField())
+            return false;
+
+        auto sd = vd.type.isTypeStruct();
+        if (!sd || sd.needsCopyOrPostblit() || sd.sym.hasMoveCtor)
+            return false;
+
+        // If an aggregate can be elided, so are its fields
+        return canElideCopy(e.e1, e.e1.type, false);
+    }
+
+    switch (e.op)
+    {
+        case EXP.comma:
+            auto ce = e.isCommaExp();
+            return canElideCopy(ce.e2, to, checkMod);
+        case EXP.question:
+            auto ce = e.isCondExp();
+            return canElideCopy(ce.e1, to, checkMod) && canElideCopy(ce.e2, to, checkMod);
+
+        case EXP.call:
+            return visitCallExp(e.isCallExp());
+        case EXP.dotVariable:
+            return visitDotVarExp(e.isDotVarExp());
+        case EXP.structLiteral:
+            auto sle = e.isStructLiteralExp();
+            return !(checkMod && sle.useStaticInit && to.isMutable());
+        case EXP.variable:
+            return (e.isVarExp().var.storage_class & STC.rvalue) != 0;
+        default:
+            return false;
+    }
+}
+
+// Return index of the field, or -1 if not found
+int getFieldIndex(ClassReferenceExp _this, Type fieldtype, uint fieldoffset)
+{
+    ClassDeclaration cd = _this.originalClass();
+    uint fieldsSoFar = 0;
+    for (size_t j = 0; j <  _this.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 cast(int)( _this.value.elements.length - fieldsSoFar - cd.fields.length + (j - fieldsSoFar));
+        }
+    }
+    return -1;
+}
+
+/************************************
+ * Get index of field.
+ * Returns -1 if not found.
+ */
+int getFieldIndex(StructLiteralExp _this, Type type, uint offset)
+{
+    /* Find which field offset is by looking at the field offsets
+     */
+    if (!_this.elements.length)
+        return -1;
+
+    const sz = type.size();
+    if (sz == SIZE_INVALID)
+        return -1;
+    foreach (i, v; _this.sd.fields)
+    {
+        if (offset != v.offset)
+            continue;
+        if (sz != v.type.size())
+            continue;
+        /* context fields might not be filled. */
+        if (i >= _this.sd.nonHiddenFields())
+            return cast(int)i;
+        if (auto e = (*_this.elements)[i])
+        {
+            return cast(int)i;
+        }
+        return -1;
+    }
+    assert(0);
+}
+
+bool equals(const Expression _this, const Expression e)
+{
+    static bool intExpEquals(const IntegerExp _this, const IntegerExp e)
+    {
+        return _this.type.toHeadMutable().equals(e.type.toHeadMutable()) && _this.value == e.value;
+    }
+
+    static bool realExpEquals(const RealExp _this, const RealExp e)
+    {
+        return _this.type.toHeadMutable().equals(e.type.toHeadMutable()) && RealIdentical(_this.value, e.value);
+    }
+
+    static bool complexExpEquals(const ComplexExp _this, const ComplexExp e)
+    {
+        return _this.type.toHeadMutable().equals(e.type.toHeadMutable()) &&
+            RealIdentical(creall(_this.value), creall(e.value)) &&
+            RealIdentical(cimagl(_this.value), cimagl(e.value));
+    }
+
+    static bool nullExpEquals(const NullExp _this, const NullExp e)
+    {
+        return e.op == EXP.null_ && _this.type.equals(e.type);
+    }
+
+    static bool stringExpEquals(const StringExp _this, const StringExp e)
+    {
+        //printf("StringExp::equals('%s') %s\n", o.toChars(), toChars());
+        return _this.compare(e) == 0;
+    }
+
+    static bool tupleExpEquals(const TupleExp _this, const TupleExp e)
+    {
+        if (_this.exps.length != e.exps.length)
+            return false;
+        if (_this.e0 && !_this.e0.equals(e.e0) || !_this.e0 && e.e0)
+            return false;
+        foreach (i, e1; *_this.exps)
+        {
+            auto e2 = (*e.exps)[i];
+            if (!e1.equals(e2))
+                return false;
+        }
+        return true;
+    }
+
+    static bool arrayLiteralExpEquals(const ArrayLiteralExp _this, const ArrayLiteralExp e)
+    {
+        if (_this.elements.length != e.elements.length)
+            return false;
+        if (_this.elements.length == 0 && !_this.type.equals(e.type))
+        {
+            return false;
+        }
+
+        foreach (i, e1; *_this.elements)
+        {
+            auto e2 = (*e.elements)[i];
+            auto e1x = e1 ? e1 : _this.basis;
+            auto e2x = e2 ? e2 : e.basis;
+
+            if (e1x != e2x && (!e1x || !e2x || !e1x.equals(e2x)))
+                return false;
+        }
+        return true;
+    }
+
+    static bool assocArrayLiteralExpEquals(const AssocArrayLiteralExp _this, const AssocArrayLiteralExp e)
+    {
+        if (_this.keys.length != e.keys.length)
+            return false;
+        size_t count = 0;
+        foreach (i, key; *_this.keys)
+        {
+            foreach (j, akey; *e.keys)
+            {
+                if (key.equals(akey))
+                {
+                    if (!(*_this.values)[i].equals((*e.values)[j]))
+                        return false;
+                    ++count;
+                }
+            }
+        }
+        return count == _this.keys.length;
+    }
+
+    static bool structLiteralExpEquals(const StructLiteralExp _this, const StructLiteralExp e)
+    {
+        if (!_this.type.equals(e.type))
+            return false;
+        if (_this.elements.length != e.elements.length)
+            return false;
+        foreach (i, e1; *_this.elements)
+        {
+            auto e2 = (*e.elements)[i];
+            if (e1 != e2 && (!e1 || !e2 || !e1.equals(e2)))
+                return false;
+        }
+        return true;
+    }
+
+    static bool varExpEquals(const VarExp _this, const VarExp e)
+    {
+        return _this.type.toHeadMutable().equals(e.type.toHeadMutable()) && _this.var == e.var;
+    }
+
+    static bool funcExpEquals(const FuncExp _this, const FuncExp e)
+    {
+        return _this.fd == e.fd;
+    }
+
+    static bool mixinExpEquals(const MixinExp _this, const MixinExp e)
+    {
+        if (_this.exps.length != e.exps.length)
+            return false;
+        foreach (i, e1; *_this.exps)
+        {
+            auto e2 = (*e.exps)[i];
+            if (e1 != e2 && (!e1 || !e2 || !e1.equals(e2)))
+                return false;
+        }
+        return true;
+    }
+
+    if (_this == e)
+        return true;
+
+    if (_this.op != e.op)
+        return false;
+
+    switch(_this.op)
+    {
+        case EXP.int64: return intExpEquals(_this.isIntegerExp(), e.isIntegerExp());
+        case EXP.float64: return realExpEquals(_this.isRealExp(), e.isRealExp());
+        case EXP.complex80: return complexExpEquals(_this.isComplexExp(), e.isComplexExp());
+        case EXP.null_: return nullExpEquals(_this.isNullExp(), e.isNullExp());
+        case EXP.string_: return stringExpEquals(_this.isStringExp(), e.isStringExp());
+        case EXP.tuple: return tupleExpEquals(_this.isTupleExp(), e.isTupleExp());
+        case EXP.arrayLiteral: return arrayLiteralExpEquals(_this.isArrayLiteralExp(), e.isArrayLiteralExp());
+        case EXP.assocArrayLiteral: return assocArrayLiteralExpEquals(_this.isAssocArrayLiteralExp(), e.isAssocArrayLiteralExp());
+        case EXP.structLiteral: return structLiteralExpEquals(_this.isStructLiteralExp(), e.isStructLiteralExp());
+        case EXP.variable: return varExpEquals(_this.isVarExp(), e.isVarExp());
+        case EXP.function_: return funcExpEquals(_this.isFuncExp(), e.isFuncExp());
+        case EXP.mixin_: return mixinExpEquals(_this.isMixinExp(), e.isMixinExp());
+
+        default: return _this is e;
+    }
+}
+
+
+/********************************
+ * 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
+ */
+@safe private bool RealIdentical(real_t x1, real_t x2)
+{
+    return (CTFloat.isNaN(x1) && CTFloat.isNaN(x2)) || CTFloat.isIdentical(x1, x2);
+}
+
+private bool realExpIsIdentical(const RealExp _this, const Expression e)
+{
+    if (!equals(_this, e))
+        return false;
+    return CTFloat.isIdentical(_this.value, e.isRealExp().value);
+}
+
+private bool complexExpIsIdentical(const ComplexExp _this, const Expression e)
+{
+    if (!equals(_this, e))
+        return false;
+    // equals() regards different NaN values as 'equals'
+    auto c = e.isComplexExp();
+    return CTFloat.isIdentical(creall(_this.value), creall(c.value)) &&
+           CTFloat.isIdentical(cimagl(_this.value), cimagl(c.value));
+}
+
+/******
+ * Identical, not just equal. I.e. NaNs with different bit patterns are not identical
+ */
+bool isIdentical(const Expression _this, const Expression e)
+{
+    if (auto re = _this.isRealExp())
+        return realExpIsIdentical(re, e);
+    else if (auto ce = _this.isComplexExp())
+        return complexExpIsIdentical(ce, e);
+    return equals(_this, e);
+}
+
 /***********************************
  * Determine if a `this` is needed to access `d`.
  * Params:
@@ -227,6 +1197,7 @@ bool hasRegularCtor(StructDeclaration sd, bool ignoreDisabled)
     {
         if (auto td = s.isTemplateDeclaration())
         {
+            td.computeOneMember();
             if (ignoreDisabled && td.onemember)
             {
                 if (auto ctorDecl = td.onemember.isCtorDeclaration())
@@ -251,6 +1222,42 @@ bool hasRegularCtor(StructDeclaration sd, bool ignoreDisabled)
     return result;
 }
 
+/// Returns: whether `s` is a method which can possibly be called without a struct instance.
+/// Used to check whether S() should try to call `S.opCall()` rather than construct a struct literal
+bool hasStaticOverload(Dsymbol s)
+{
+    bool result = false;
+    overloadApply(s, (Dsymbol sym) {
+        if (auto fd = sym.isFuncDeclaration())
+        {
+            if (fd.isStatic)
+            {
+                result = true;
+                return 1;
+            }
+        }
+        else if (auto td = sym.isTemplateDeclaration())
+        {
+            // Consider both `template opCall { static opCall() {} }` and `static opCall()() {}`
+            if (td._scope.stc & STC.static_)
+            {
+                result = true;
+                return 1;
+            }
+            if (auto fd = td.onemember.isFuncDeclaration())
+            {
+                if (fd.isStatic)
+                {
+                    result = true;
+                    return 1;
+                }
+            }
+        }
+        return 0;
+    });
+    return result;
+}
+
 /*****************************************
  * Determine if `this` is available by walking up the enclosing
  * scopes until a function is found.
@@ -471,16 +1478,19 @@ private Expression checkOpAssignTypes(BinExp binExp, Scope* sc)
     Type t1 = e1.type;
     Type t2 = e2.type;
 
+    // @@@DEPRECATED_2.122@@@
+    // Deprecated in 2.112, make it an error in 2.122
     // T opAssign floating yields a floating. Prevent truncating conversions (float to int).
     // See https://issues.dlang.org/show_bug.cgi?id=3841.
     // Should we also prevent double to float (type.isFloating() && type.size() < t2.size()) ?
-    if (op == EXP.addAssign || op == EXP.minAssign ||
-        op == EXP.mulAssign || op == EXP.divAssign || op == EXP.modAssign ||
-        op == EXP.powAssign)
+    if (!sc.inCfile &&
+        (op == EXP.addAssign || op == EXP.minAssign ||
+         op == EXP.mulAssign || op == EXP.divAssign || op == EXP.modAssign ||
+         op == EXP.powAssign))
     {
         if ((type.isIntegral() && t2.isFloating()))
         {
-            warning(loc, "`%s %s %s` is performing truncating conversion", type.toChars(), EXPtoString(op).ptr, t2.toChars());
+            deprecation(loc, "`%s %s %s` is performing truncating conversion", type.toChars(), EXPtoString(op).ptr, t2.toChars());
         }
     }
 
@@ -907,7 +1917,7 @@ extern (D) Expression doCopyOrMove(Scope* sc, Expression e, Type t, bool nrvo, b
     {
         e = callCpCtor(sc, e, t, nrvo);
     }
-    else if (move && sd && sd.hasMoveCtor && !e.isCallExp() && !e.isStructLiteralExp())
+    else if (move && sd && sd.hasMoveCtor && !canElideCopy(e, t ? t : e.type, false))
     {
         // #move
         /* Rewrite as:
@@ -918,7 +1928,7 @@ extern (D) Expression doCopyOrMove(Scope* sc, Expression e, Type t, bool nrvo, b
         VarDeclaration vd = new VarDeclaration(e.loc, e.type, Identifier.generateId("__copyrvalue"), null);
         if (nrvo)
             vd.nrvo = true;
-        vd.storage_class |= STC.nodtor;
+        vd.storage_class |= STC.temp | STC.rvalue | STC.nodtor;
         vd.dsymbolSemantic(sc);
         Expression de = new DeclarationExp(e.loc, vd);
         Expression ve = new VarExp(e.loc, vd);
@@ -1524,41 +2534,19 @@ 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)
+        td.computeOneMember();
+        if (!td.onemember)
+            return e1;
+
+        if (auto fd = td.onemember.isFuncDeclaration())
         {
-            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);
-            }
+            if (fd.type.isTypeFunction().isProperty ||
+                (fd.storage_class2 & STC.property) ||
+                (td._scope.stc & STC.property))
+                return resolveProperties(sc, e1);
         }
         return e1;
     }
@@ -1571,6 +2559,20 @@ Expression resolvePropertiesOnly(Scope* sc, Expression e1)
         return e1;
     }
 
+    Expression handleOverloadSet(OverloadSet os)
+    {
+        assert(os);
+        foreach (s; os.a)
+        {
+            if (auto fd = s.isFuncDeclaration())
+                return handleFuncDecl(fd);
+
+            if (auto td = s.isTemplateDeclaration())
+                return handleTemplateDecl(td);
+        }
+        return e1;
+    }
+
     if (auto de = e1.isDotExp())
     {
         if (auto os = de.e2.isOverExp())
@@ -1788,7 +2790,7 @@ Lagain:
         return ie.expressionSemantic(sc);
     }
 
-    if (Type t = s.getType())
+    if (Type t = dmd.dsymbolsem.getType(s))
     {
         return (new TypeExp(loc, t)).expressionSemantic(sc);
     }
@@ -1798,7 +2800,10 @@ Lagain:
         if (tup.needThis() && hasThis(sc))
             e = new DotVarExp(loc, new ThisExp(loc), tup);
         else
+        {
             e = new TupleExp(loc, tup);
+            fillTupleExpExps(e.isTupleExp(), tup);
+        }
         e = e.expressionSemantic(sc);
         return e;
     }
@@ -1866,7 +2871,7 @@ L1:
     {
         auto cls = ad.isClassDeclaration();
         auto classObj = new ObjcClassReferenceExp(e1.loc, cls);
-        classObj.type = objc.getRuntimeMetaclass(cls).getType();
+        classObj.type = dmd.dsymbolsem.getType(objc.getRuntimeMetaclass(cls));
         return classObj;
     }
 
@@ -2159,6 +3164,12 @@ public void errorSupplementalInferredAttr(FuncDeclaration fd, int maxDepth, bool
     if (s.action.length > 0)
     {
         errorFunc(s.loc, "and %.*s makes it fail to infer `%.*s`", s.action.fTuple.expand, attr.fTuple.expand);
+        // For scope violations, also print why the target parameter is not scope
+        if (s.scopeVar)
+        {
+            import dmd.escape : printScopeReason;
+            printScopeReason(errorFunc, s.scopeVar, 10, false);
+        }
     }
     else if (s.fd)
     {
@@ -2304,12 +3315,18 @@ private bool checkImpure(Scope* sc, Loc loc, const(char)* fmt, RootObject arg0)
 
 /*********************************************
  * Calling function f.
- * Check the safety, i.e. if we're in a @safe function
+ * Check the safety, i.e. if we are in a @safe function
  * we can only call @safe or @trusted functions.
- * Returns true if error occurs.
+ * Params:
+ *      f = function being called
+ *      loc = location for error messages
+ *      sc = context
+ *      arguments = array of actual arguments to function call, null for none
+ * Returns: true if unsafe (and diagnostic is generated)
  */
-private bool checkSafety(FuncDeclaration f, ref Loc loc, Scope* sc)
+private bool checkSafety(FuncDeclaration f, ref Loc loc, Scope* sc, Expressions* arguments)
 {
+    //printf("checkSafety() %s\n", f.toChars());
     if (sc.func == f)
         return false;
     if (sc.intypeof == 1)
@@ -2338,6 +3355,23 @@ private bool checkSafety(FuncDeclaration f, ref Loc loc, Scope* sc)
         return false;
     }
 
+    if (f.printf)
+    {
+        TypeFunction tf = f.type.isTypeFunction();
+        assert(tf);
+        const isVa_list = tf.parameterList.varargs == VarArg.none;
+        const nparams = tf.parameterList.length;
+        const nargs = arguments ? arguments.length : 0;
+        if (nparams == 1 && nargs)
+        {
+            if (auto se = (*arguments)[nparams - 1 - isVa_list].isStringExp())
+            {
+                if (isFormatSafe(se.peekString()))
+                    return false;
+            }
+        }
+    }
+
     if (!f.isSafe() && !f.isTrusted())
     {
         if (isRootTraitsCompilesScope(sc) ? sc.func.isSafeBypassingInference() : sc.func.setUnsafeCall(f))
@@ -2401,7 +3435,7 @@ private bool checkNogc(FuncDeclaration f, ref Loc loc, Scope* sc)
     if (f.isNogc())
         return false;
 
-    if (isRootTraitsCompilesScope(sc) ? !sc.func.isNogcBypassingInference() : !sc.func.setGCCall(f))
+    if (isRootTraitsCompilesScope(sc) ? !sc.func.isNogcBypassingInference() : !sc.setGCCall(sc.func, f))
         return false;
 
     if (loc == Loc.initial) // e.g. implicitly generated dtor
@@ -2461,7 +3495,7 @@ private bool checkPostblit(Type t, ref Loc loc, Scope* sc)
     //checkAccess(sd, loc, sc, sd.postblit);   // necessary?
     bool result = false;
     result |= sd.postblit.checkPurity(loc, sc);
-    result |= sd.postblit.checkSafety(loc, sc);
+    result |= sd.postblit.checkSafety(loc, sc, null);
     result |= sd.postblit.checkNogc(loc, sc);
     return result;
 }
@@ -3036,7 +4070,7 @@ private bool checkDefCtor(Loc loc, Type t)
  */
 private bool functionParameters(Loc loc, Scope* sc,
     TypeFunction tf, Expression ethis, Type tthis, ArgumentList argumentList, FuncDeclaration fd,
-    Type* prettype, Expression* peprefix)
+    out Type prettype, out Expression peprefix)
 {
     Expressions* arguments = argumentList.arguments;
     //printf("functionParameters() fd: %s tf: %s\n", fd ? fd.ident.toChars() : "", toChars(tf));
@@ -3046,7 +4080,6 @@ private bool functionParameters(Loc loc, Scope* sc,
     const olderrors = global.errors;
     bool err = false;
     Expression eprefix = null;
-    *peprefix = null;
 
     if (argumentList.names)
     {
@@ -3109,7 +4142,7 @@ private bool functionParameters(Loc loc, Scope* sc,
      * 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;
+    MOD wildmatch = (tthis && !isCtorCall) ? tthis.typeDeduceWild(tf, false) : 0;
 
     bool done = false;
     foreach (const i; 0 .. n)
@@ -3832,8 +4865,8 @@ private bool functionParameters(Loc loc, Scope* sc,
         tret = tret.substWildTo(wildmatch);
     }
 
-    *prettype = tret;
-    *peprefix = eprefix;
+    prettype = tret;
+    peprefix = eprefix;
     return (err || olderrors != global.errors);
 }
 
@@ -3930,9 +4963,28 @@ private extern(C++) final class IsMemcmpableVisitor : Visitor
     public:
     bool result = false;
 
+    static Type loweredBaseElemOf(Type t)
+    {
+        t = t.baseElemOf(); // skip over static-array parents
+        switch (t.ty)
+        {
+            case Tvoid:
+                return Type.tuns8;
+            case Tpointer:
+                return Type.tsize_t;
+            default:
+                return t;
+        }
+    }
+
+    static bool isTriviallyMemcmpable(Type t)
+    {
+        return loweredBaseElemOf(t).isIntegral();
+    }
+
     override void visit(Type t)
     {
-        result = t.ty == Tvoid || (t.isScalar() && !t.isFloating());
+        result = isTriviallyMemcmpable(t);
     }
 
     override void visit(TypeStruct ts)
@@ -4061,7 +5113,7 @@ private bool checkNestedFuncReference(FuncDeclaration fd, Scope* sc, Loc loc)
             }
         }
         const lv = fdthis.getLevelAndCheck(loc, sc, fdv, fd);
-        if (lv == fd.LevelError)
+        if (lv == LevelError)
             return true; // error
         if (lv == -1)
             return false; // downlevel call
@@ -4159,7 +5211,7 @@ private void unSpeculative(Scope* sc, RootObject o)
             o = vd.type;
         else if (AliasDeclaration ad = d.isAliasDeclaration())
         {
-            o = ad.getType();
+            o = dmd.dsymbolsem.getType(ad);
             if (!o)
                 o = ad.toAlias();
         }
@@ -4254,37 +5306,10 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
     {
         if (!e.type)
             e.type = Type.tfloat64;
-        else if (!e.type.isImaginary || !sc.inCfile)
-        {
-            e.type = e.type.typeSemantic(e.loc, sc);
-            result = e;
-            return;
-        }
-
-        /* Convert to core.stdc.config.complex
-         */
-        Type t = getComplexLibraryType(e.loc, sc, e.type.ty);
-        if (t.ty == Terror)
-            return setError();
-
-        Type tf;
-        switch (e.type.ty)
-        {
-            case Timaginary32: tf = Type.tfloat32; break;
-            case Timaginary64: tf = Type.tfloat64; break;
-            case Timaginary80: tf = Type.tfloat80; break;
-            default:
-                assert(0);
-        }
 
-        /* Construct ts{re : 0.0, im : e}
-         */
-        TypeStruct ts = t.isTypeStruct;
-        Expressions* elements = new Expressions(2);
-        (*elements)[0] = new RealExp(e.loc,    CTFloat.zero, tf);
-        (*elements)[1] = new RealExp(e.loc, e.toImaginary(), tf);
-        Expression sle = new StructLiteralExp(e.loc, ts.sym, elements);
-        result = sle.expressionSemantic(sc);
+        e.type = e.type.typeSemantic(e.loc, sc);
+        result = e;
+        return;
     }
 
     override void visit(ComplexExp e)
@@ -4310,7 +5335,11 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                 setError();
             }
         }
-        scope (exit) result.rvalue = exp.rvalue;
+        scope (exit)
+        {
+            if (result !is null)
+                result.rvalue = exp.rvalue;
+        }
 
         Dsymbol scopesym;
         Dsymbol s = sc.search(exp.loc, exp.ident, scopesym);
@@ -4510,7 +5539,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                 error(exp.loc, "`%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))
                 error(exp.loc, "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))
+            else if (const p = search_correct_C(exp.ident))
                 error(exp.loc, "undefined identifier `%s`, did you mean `%s`?", exp.ident.toChars(), p);
             else if (exp.ident == Id.dollar)
                 error(exp.loc, "undefined identifier `$`");
@@ -5072,8 +6101,47 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             error(cle.loc, "cannot convert initializer `%s` to expression", toChars(init));
             return setError();
         }
-        result = e;
-        return;
+
+        // leave these to be handled by their respective functions
+        if (e.isStringExp() || e.isStructLiteralExp() || e.isArrayLiteralExp() || e.isAssocArrayLiteralExp())
+        {
+            result = e;
+            return;
+        }
+
+        if (sc.func)
+        {
+            Expression e0;
+            auto ve = extractSideEffect(sc, "__cl", e0, e, true);
+
+            // export (int a = 5, a) so a doesn't forward ref
+            result = new CommaExp(cle.loc, e0, ve).expressionSemantic(sc);
+            return;
+        }
+        else //global variables
+        {
+            Identifier ident = Identifier.generateId("__cl");
+            auto tmp = new VarDeclaration(cle.loc, t, ident, new ExpInitializer(cle.loc, e));
+
+            // static const type ???
+            tmp.storage_class = STC.static_ | STC.const_ | STC.ctfe;
+
+            // for our global variables, we need them in our symbol table
+            if (sc._module)
+            {
+                sc._module.members.push(tmp);
+            }
+            else if (sc.minst)
+            {
+                sc.minst.members.push(tmp);
+            }
+
+            dsymbolSemantic(tmp, sc);
+
+            auto ve = new VarExp(cle.loc, tmp).expressionSemantic(sc);
+            result = ve;
+            return;
+        }
     }
 
     override void visit(TypeExp exp)
@@ -5243,7 +6311,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         sds2.dsymbolSemantic(sc);
 
         // (Aggregate|Enum)Declaration
-        if (auto t = sds2.getType())
+        if (auto t = dmd.dsymbolsem.getType(sds2))
         {
             result = (new TypeExp(exp.loc, t)).expressionSemantic(sc);
             return;
@@ -5350,13 +6418,18 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                 error(p.loc, "PlacementExpression `%s` is an rvalue, but must be an lvalue", p.toChars());
                 return setError();
             }
-            if (sc.setUnsafe(false, p.loc, "`@safe` function `%s` cannot use placement `new`", sc.func))
+            if (sc.setUnsafe(false, p.loc, "placement `new`", sc.func))
+            {
+                return setError();
+            }
+            if (!p.type.isNaked)
             {
+                error(p.loc, "PlacementExpression `%s` of type `%s` must be unshared and mutable", p.toChars(), toChars(p.type));
                 return setError();
             }
-            if (!exp.placement.type.isNaked)
+            if (p.type.ty == Tarray)
             {
-                error(p.loc, "PlacementExpression `%s` of type `%s` be unshared and mutable", p.toChars(), toChars(p.type));
+                error(p.loc, "PlacementExpression cannot be a dynamic array");
                 return setError();
             }
             checkModifiable(exp.placement, sc);
@@ -5629,7 +6702,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                 TypeFunction tf = f.type.isTypeFunction();
                 if (!exp.arguments)
                     exp.arguments = new Expressions();
-                if (functionParameters(exp.loc, sc, tf, null, exp.type, exp.argumentList, f, &exp.type, &exp.argprefix))
+                if (functionParameters(exp.loc, sc, tf, null, exp.type, exp.argumentList, f, exp.type, exp.argprefix))
                     return setError();
 
                 exp.member = f.isCtorDeclaration();
@@ -5744,7 +6817,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                 TypeFunction tf = f.type.isTypeFunction();
                 if (!exp.arguments)
                     exp.arguments = new Expressions();
-                if (functionParameters(exp.loc, sc, tf, null, exp.type, exp.argumentList, f, &exp.type, &exp.argprefix))
+                if (functionParameters(exp.loc, sc, tf, null, exp.type, exp.argumentList, f, exp.type, exp.argprefix))
                     return setError();
 
                 exp.member = f.isCtorDeclaration();
@@ -5848,10 +6921,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
 
             if (!global.params.useGC && sc.needsCodegen())
             {
-                version(IN_GCC)
-                    error(exp.loc, "expression `%s` allocates with the GC and cannot be used with switch `-fno-rtti`", exp.toErrMsg());
-                else
-                    error(exp.loc, "expression `%s` allocates with the GC and cannot be used with switch `-betterC`", exp.toErrMsg());
+                error(exp.loc, "expression `%s` allocates with the GC and cannot be used with switch `-%s`", exp.toErrMsg(), SwitchVariadic.ptr);
                 return setError();
             }
 
@@ -6196,7 +7266,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         //printf("td = %p, treq = %p\n", td, fd.treq);
         if (exp.td)
         {
-            assert(exp.td.parameters && exp.td.parameters.length);
+            assert(exp.td.parameters);
             exp.td.dsymbolSemantic(sc);
             exp.type = Type.tvoid; // temporary type
 
@@ -6366,7 +7436,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                     {
                         setError();
                     }
-                    else
+                    else if (result !is null)
                     {
                         result.rvalue = true;
                     }
@@ -6415,17 +7485,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                 return;
             }
         }
-        if (sc.inCfile)
-        {
-            /* See if need to rewrite the AST because of cast/call ambiguity
-             */
-            if (auto e = castCallAmbiguity(exp, sc))
-            {
-                result = expressionSemantic(e, sc);
-                return;
-            }
-        }
-
         if (Expression ex = resolveUFCS(sc, exp))
         {
             result = ex;
@@ -6725,8 +7784,13 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                     return;
                 }
                 // No constructor, look for overload of opCall
-                if (search_function(sd, Id.opCall))
-                    goto L1;
+                if (auto sym = search_function(sd, Id.opCall))
+                {
+                    // Don't consider opCall on a type with no instance
+                    // if there's no static overload for it
+                    if (!exp.e1.isTypeExp() || hasStaticOverload(sym))
+                        goto L1;
+                }
                 // overload of opCall, therefore it's a call
                 if (exp.e1.op != EXP.type)
                 {
@@ -7210,7 +8274,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             if (exp.f)
             {
                 exp.f.checkPurity(exp.loc, sc);
-                exp.f.checkSafety(exp.loc, sc);
+                exp.f.checkSafety(exp.loc, sc, exp.arguments);
                 exp.f.checkNogc(exp.loc, sc);
                 if (exp.f.checkNestedFuncReference(sc, exp.loc))
                     return setError();
@@ -7224,7 +8288,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                         sc.func.kind(), sc.func.toPrettyChars(), p, exp.e1.toErrMsg());
                     err = true;
                 }
-                if (!tf.isNogc && sc.func.setGC(exp.loc, "calling non-@nogc `%s`", exp.e1))
+                if (!tf.isNogc && sc.setGC(sc.func, exp.loc, "calling non-@nogc `%s`", exp.e1))
                 {
                     error(exp.loc, "`@nogc` %s `%s` cannot call non-@nogc %s `%s`",
                         sc.func.kind(), sc.func.toPrettyChars(), p, exp.e1.toErrMsg());
@@ -7348,7 +8412,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         Expression argprefix;
         if (!exp.arguments)
             exp.arguments = new Expressions();
-        if (functionParameters(exp.loc, sc, cast(TypeFunction)t1, ethis, tthis, exp.argumentList, exp.f, &exp.type, &argprefix))
+        if (functionParameters(exp.loc, sc, cast(TypeFunction)t1, ethis, tthis, exp.argumentList, exp.f, exp.type, argprefix))
             return setError();
 
         if (!exp.type)
@@ -7481,7 +8545,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
 
         //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.
+        // Must be unique in both (except for importC).
         if (s.ident)
         {
             VarDeclaration v = s.isVarDeclaration();
@@ -8667,14 +9731,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
 
         if (sc.inCfile)
         {
-            /* See if need to rewrite the AST because of cast/call ambiguity
-             */
-            if (auto e = castCallAmbiguity(exp, sc))
-            {
-                result = expressionSemantic(e, sc);
-                return;
-            }
-
             if (exp.arrow) // ImportC only
                 exp.e1 = exp.e1.expressionSemantic(sc).arrayFuncConv(sc);
 
@@ -8794,7 +9850,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         }
         else if (auto ad = exp.var.isAliasDeclaration())
         {
-            if (auto t = ad.getType())
+            if (auto t = dmd.dsymbolsem.getType(ad))
             {
                 result = new TypeExp(exp.loc, t).expressionSemantic(sc);
                 return;
@@ -8981,7 +10037,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             return;
         }
 
-        exp.type = exp.sym.getType().addMod(exp.e1.type.mod);
+        exp.type = dmd.dsymbolsem.getType(exp.sym).addMod(exp.e1.type.mod);
         result = exp;
     }
 
@@ -9004,7 +10060,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
              * since C regards string/array literals as lvalues
              */
             auto e = exp.e1;
-            if(e.isStringExp() || e.isArrayLiteralExp())
+            if (e.isStringExp() || e.isArrayLiteralExp())
             {
                 e.type = typeSemantic(e.type, Loc.initial, sc);
                 // if type is already a pointer exp is an illegal expression of the form `&(&"")`
@@ -9516,7 +10572,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         {
             err |= !functionSemantic(cd.dtor);
             err |= cd.dtor.checkPurity(exp.loc, sc);
-            err |= cd.dtor.checkSafety(exp.loc, sc);
+            err |= cd.dtor.checkSafety(exp.loc, sc, null);
             err |= cd.dtor.checkNogc(exp.loc, sc);
         }
         if (err)
@@ -9608,7 +10664,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             result = e1x;
             return;
         }
-        if (e1x.checkType())
+        if (!e1x.hasValidType())
             return setError();
         exp.e1 = e1x;
 
@@ -10188,17 +11244,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             printf("ArrayExp::semantic('%s')\n", exp.toChars());
         }
 
-        if (sc.inCfile)
-        {
-            /* See if need to rewrite the AST because of cast/call ambiguity
-             */
-            if (auto e = castCallAmbiguity(exp, sc))
-            {
-                result = expressionSemantic(e, sc);
-                return;
-            }
-        }
-
         result = exp.carraySemantic(sc);  // C semantics
         if (result)
             return;
@@ -10527,7 +11572,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                  * 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))
+                if (!keyCompatibleWithoutCasting(exp.e2, taa.index))
                 {
                     exp.e2 = exp.e2.implicitCastTo(sc, taa.index); // type checking
                     if (exp.e2.type == Type.terror)
@@ -10639,17 +11684,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             printf("PostExp::semantic('%s')\n", exp.toChars());
         }
 
-        if (sc.inCfile)
-        {
-            /* See if need to rewrite the AST because of cast/call ambiguity
-             */
-            if (auto e = castCallAmbiguity(exp, sc))
-            {
-                result = expressionSemantic(e, sc);
-                return;
-            }
-        }
-
         if (auto ae = exp.e1.isArrayExp())
             markArrayExpModifiable(ae);
 
@@ -12043,16 +13077,16 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                     return setError();
 
                 // Lower to object._d_array{,set}ctor(e1, e2)
-                Expression id = new IdentifierExp(exp.loc, Id.empty);
-                id = new DotIdExp(exp.loc, id, Id.object);
-                id = new DotIdExp(exp.loc, id, func);
+                Expression lowering = new IdentifierExp(ae.loc, Id.empty);
+                lowering = new DotIdExp(ae.loc, lowering, Id.object);
+                lowering = new DotIdExp(ae.loc, lowering, func);
 
                 auto arguments = new Expressions(new CastExp(ae.loc, ae.e1, t1e.arrayOf).expressionSemantic(sc));
                 if (lowerToArrayCtor)
                 {
                     arguments.push(new CastExp(ae.loc, rhs, t2b.nextOf.arrayOf).expressionSemantic(sc));
-                    Expression ce = new CallExp(exp.loc, id, arguments);
-                    res = ce.expressionSemantic(sc);
+                    lowering = new CallExp(ae.loc, lowering, arguments);
+                    lowering = lowering.expressionSemantic(sc);
                 }
                 else
                 {
@@ -12067,12 +13101,14 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                     else
                         arguments.push(ae.e2);
 
-                    Expression ce = new CallExp(exp.loc, id, arguments);
-                    res = Expression.combine(e0, ce).expressionSemantic(sc);
+                    lowering = new CallExp(ae.loc, lowering, arguments);
+                    lowering = Expression.combine(e0, lowering).expressionSemantic(sc);
                 }
 
+                ae.lowering = lowering;
+
                 if (global.params.v.verbose)
-                    message("lowered   %s =>\n          %s", exp.toChars(), res.toChars());
+                    message("lowered   %s =>\n          %s", exp.toChars(), lowering.toChars());
             }
         }
         else if (auto ae = res.isAssignExp())
@@ -13634,7 +14670,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                 TypeAArray ta = cast(TypeAArray)t2b;
 
                 // Special handling for array keys
-                if (!arrayTypeCompatibleWithoutCasting(exp.e1.type, ta.index))
+                if (!keyCompatibleWithoutCasting(exp.e1, ta.index))
                 {
                     // Convert key to type of key
                     exp.e1 = exp.e1.implicitCastTo(sc, ta.index);
@@ -13829,15 +14865,20 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         {
             Type t1n = t1.nextOf().toBasetype();
             Type t2n = t2.nextOf().toBasetype();
-            const t1nsz = t1n.size();
-            const t2nsz = t2n.size();
 
-            if ((t1n.ty == Tvoid || (t1n.isScalar() && !t1n.isFloating())) &&
-                (t2n.ty == Tvoid || (t2n.isScalar() && !t1n.isFloating())) &&
-                t1nsz == t2nsz && t1n.isUnsigned() == t2n.isUnsigned())
+            if (t1n.size() != t2n.size())
+                return false;
+
+            if (IsMemcmpableVisitor.isTriviallyMemcmpable(t1n) &&
+                IsMemcmpableVisitor.isTriviallyMemcmpable(t2n))
             {
-                return true;
+                // due to int promotion, disallow small integers of diverging signed-ness
+                Type e1 = IsMemcmpableVisitor.loweredBaseElemOf(t1n);
+                Type e2 = IsMemcmpableVisitor.loweredBaseElemOf(t2n);
+                if ((e1.size() >= 4 && e2.size() >= 4) || e1.isUnsigned() == e2.isUnsigned())
+                    return true;
             }
+
             if (t1n.constOf() != t2n.constOf())
             {
                 return false;
@@ -13852,7 +14893,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                 if (global.params.useTypeInfo && Type.dtypeinfo)
                     semanticTypeInfo(sc, ts);
 
-                auto v = new IsMemcmpableVisitor();
+                scope v = new IsMemcmpableVisitor();
                 ts.accept(v);
                 return v.result;
             }
@@ -14854,7 +15895,7 @@ Expression dotIdSemanticProp(DotIdExp exp, Scope* sc, bool gag)
                 return new OverExp(exp.loc, o);
             }
 
-            if (auto t = s.getType())
+            if (auto t = dmd.dsymbolsem.getType(s))
             {
                 return (new TypeExp(exp.loc, t)).expressionSemantic(sc);
             }
@@ -14868,6 +15909,7 @@ Expression dotIdSemanticProp(DotIdExp exp, Scope* sc, bool gag)
                     return e;
                 }
                 Expression e = new TupleExp(exp.loc, tup);
+                fillTupleExpExps(e.isTupleExp(), tup);
                 e = e.expressionSemantic(sc);
                 return e;
             }
@@ -15183,6 +16225,8 @@ Expression dotTemplateSemanticProp(DotTemplateInstanceExp exp, Scope* sc, bool g
 
 MATCH matchType(FuncExp funcExp, Type to, Scope* sc, FuncExp* presult, ErrorSink eSink)
 {
+    import dmd.typesem : merge;
+
     auto loc = funcExp.loc;
     auto tok = funcExp.tok;
     auto td = funcExp.td;
@@ -16220,7 +17264,7 @@ Expression toLvalue(Expression _this, Scope* sc, const(char)* action, Expression
         }
         if (!_this.isLvalue())
             return visit(_this);
-        if (e1.op == EXP.this_ && sc.ctorflow.fieldinit.length && !(sc.ctorflow.callSuper & CSX.any_ctor))
+        if (e1.op == EXP.this_ && sc && sc.ctorflow.fieldinit.length && !(sc.ctorflow.callSuper & CSX.any_ctor))
         {
             if (VarDeclaration vd = var.isVarDeclaration())
             {
@@ -16709,7 +17753,7 @@ private bool checkAddressVar(Scope* sc, Expression exp, VarDeclaration v)
             "taking the address of local variable `%s`" :
             "taking the address of stack-allocated local variable `%s`";
         if (sc.useDIP1000 != FeatureState.enabled &&
-            !(v.storage_class & STC.temp) &&
+            (!(v.storage_class & STC.temp) || v.storage_class & STC.result) &&
             sc.setUnsafe(false, exp.loc, msg.ptr, v))
         {
             return false;
@@ -16787,7 +17831,7 @@ bool checkAddressable(Expression e, Scope* sc, const(char)* action)
  * and usage of `deprecated` and `@disabled`-ed symbols are checked.
  *
  * Params:
- *  exp = expression to check attributes for
+ *  exp = expression to check attributes for (CallExp or NewExp)
  *  sc  = scope of the function
  *  f   = function to be checked
  * Returns: `true` if error occur.
@@ -16797,7 +17841,8 @@ private bool checkFunctionAttributes(Expression exp, Scope* sc, FuncDeclaration
     bool error = f.checkDisabled(exp.loc, sc);
     error |= f.checkDeprecated(exp.loc, sc);
     error |= f.checkPurity(exp.loc, sc);
-    error |= f.checkSafety(exp.loc, sc);
+    Expressions* arguments = exp.isCallExp() ? exp.isCallExp().arguments : null;
+    error |= f.checkSafety(exp.loc, sc, arguments);
     error |= f.checkNogc(exp.loc, sc);
     return error;
 }
@@ -16907,7 +17952,7 @@ private VarDeclaration makeThis2Argument(Loc loc, Scope* sc, FuncDeclaration fd)
 bool verifyHookExist(Loc loc, ref Scope sc, Identifier id, string description, Identifier module_ = Id.object)
 {
     Dsymbol pscopesym;
-    auto rootSymbol = sc.search(loc, Id.empty, pscopesym);
+    auto rootSymbol = search(&sc, loc, Id.empty, pscopesym);
     if (auto moduleSymbol = rootSymbol.search(loc, module_))
         if (moduleSymbol.search(loc, id))
           return true;
@@ -17010,11 +18055,17 @@ private bool fit(StructDeclaration sd, Loc loc, Scope* sc, Expressions* elements
             Type typeb = se.type.toBasetype();
             TY tynto = tb.nextOf().ty;
             if (!se.committed &&
-                typeb.isStaticOrDynamicArray() && tynto.isSomeChar &&
-                se.numberOfCodeUnits(tynto) < (cast(TypeSArray)tb).dim.toInteger())
+                typeb.isStaticOrDynamicArray() && tynto.isSomeChar)
             {
-                e = se.castTo(sc, t);
-                goto L1;
+                string s;
+                size_t len = se.numberOfCodeUnits(tynto, s);
+                if (s)
+                    error(se.loc, "%.*s", cast(int)s.length, s.ptr);
+                if (len < (cast(TypeSArray)tb).dim.toInteger())
+                {
+                    e = se.castTo(sc, t);
+                    goto L1;
+                }
             }
         }
 
@@ -17624,14 +18675,14 @@ void semanticTypeInfo(Scope* sc, Type t)
         else if (TemplateInstance ti = sd.isInstantiated())
         {
             if (ti.minst && !ti.minst.isRoot())
-                Module.addDeferredSemantic3(sd);
+                addDeferredSemantic3(sd);
         }
         else
         {
             if (sd.inNonRoot())
             {
                 //printf("deferred sem3 for TypeInfo - sd = %s, inNonRoot = %d\n", sd.toChars(), sd.inNonRoot());
-                Module.addDeferredSemantic3(sd);
+                addDeferredSemantic3(sd);
             }
         }
     }
@@ -17832,14 +18883,15 @@ private bool needsTypeInference(TemplateInstance ti, Scope* sc, int flag = 0)
             auto td = s.isTemplateDeclaration();
             if (!td)
                 return 0;
-
             /* If any of the overloaded template declarations need inference,
              * then return true
              */
+            td.computeOneMember();
             if (!td.onemember)
                 return 0;
             if (auto td2 = td.onemember.isTemplateDeclaration())
             {
+                td2.computeOneMember();
                 if (!td2.onemember || !td2.onemember.isFuncDeclaration())
                     return 0;
                 if (ti.tiargs.length >= td.parameters.length - (td.isVariadic() ? 1 : 0))
@@ -18260,8 +19312,9 @@ void lowerNonArrayAggregate(StaticForeach sfe, Scope* sc)
 */
 extern(D) void prepare(StaticForeach sfe, Scope* sc)
 {
-    assert(sc);
+    import dmd.statementsem : ready;
 
+    assert(sc);
     if (sfe.aggrfe)
     {
         sc = sc.startCTFE();
@@ -18692,7 +19745,7 @@ private Expression rewriteAAIndexAssign(BinExp exp, Scope* sc, ref Type[2] alias
     foreach (ekey; ekeys)
     {
         Type tidx = ekey.type.toBasetype();
-        if (tidx.ty == Tarray && tidx.nextOf().isMutable())
+        if (tidx.ty == Tarray && tidx.nextOf().isMutable() && !ekey.isArrayLiteralExp())
         {
             error(loc, "associative arrays can only be assigned values with immutable keys, not `%s`", tidx.toChars());
             return ErrorExp.get();
@@ -18701,12 +19754,16 @@ private Expression rewriteAAIndexAssign(BinExp exp, Scope* sc, ref Type[2] alias
     // extract side effects in lexical order
     for (size_t i = ekeys.length; i > 0; --i)
         ekeys[i-1] = extractSideEffect(sc, "__aakey", e0, ekeys[i-1]);
-    Expression ev = extractSideEffect(sc, "__aaval", e0, exp.e2);
+    // some implicit conversions are lost when assigning to a temporary, e.g. from array literal
+    auto taa = eaa.type.isTypeAArray();
+    auto match = exp.e2.implicitConvTo(taa.next);
+    auto e2 = match == MATCH.exact || match == MATCH.nomatch ? exp.e2 : exp.e2.implicitCastTo(sc, taa.next);
+    Expression ev = extractSideEffect(sc, "__aaval", e0, e2); // must be evaluated before the insertion
 
     // generate series of calls to _d_aaGetY
     for (size_t i = ekeys.length; i > 0; --i)
     {
-        auto taa = eaa.type.isTypeAArray();
+        taa = eaa.type.isTypeAArray();
         assert (taa); // type must not have changed during rewrite
         Expression func = new IdentifierExp(loc, Id.empty);
         func = new DotIdExp(loc, func, Id.object);
index 8116dda79d74f504ea1d6badb234372ac834940d..c63dbc66996bac01746ceb4bb13d25b472d2fc37 100644 (file)
@@ -13,7 +13,7 @@ module dmd.file_manager;
 import core.stdc.stdio;
 import dmd.common.outbuffer;
 import dmd.root.stringtable : StringTable;
-import dmd.root.file : File, Buffer;
+import dmd.root.file : File;
 import dmd.root.filename : FileName, isDirSeparator;
 import dmd.root.string : toDString;
 import dmd.globals;
index 17044233ca8576bf9f68a5be7483b0f2dba38c3d..b7e60aa2a1d29e677917e0b08bbc642ffaab9260 100644 (file)
@@ -23,39 +23,24 @@ import core.stdc.string;
 import dmd.aggregate;
 import dmd.arraytypes;
 import dmd.astenums;
-import dmd.blockexit;
-import dmd.dcast;
 import dmd.dclass;
 import dmd.declaration;
-import dmd.delegatize;
-import dmd.dmodule;
-import dmd.dscope;
-import dmd.dstruct;
 import dmd.dsymbol;
 import dmd.dtemplate;
-import dmd.escape;
-import dmd.expression;
-import dmd.funcsem : isUnique;
 import dmd.globals;
 import dmd.hdrgen;
 import dmd.id;
 import dmd.identifier;
-import dmd.init;
 import dmd.location;
 import dmd.mtype;
 import dmd.objc;
 import dmd.common.outbuffer;
 import dmd.rootobject;
-import dmd.root.string;
-import dmd.root.stringtable;
 import dmd.statement;
+import dmd.targetcompiler;
 import dmd.tokens;
 import dmd.visitor;
 
-version (IN_GCC) {}
-else version (IN_LLVM) {}
-else version = MARS;
-
 /// Inline Status
 enum ILS : ubyte
 {
@@ -189,6 +174,40 @@ private struct ContractInfo
     Expressions* fdensureParams;        /// argument list for __ensure
 }
 
+/// Information for data flow analysis regarding parameters
+extern(D) struct ParametersDFAInfo
+{
+    ParameterDFAInfo thisPointer;
+    ParameterDFAInfo returnValue;
+    ParameterDFAInfo[] parameters;
+}
+
+/// Information for data flow analysis per parameter
+extern(D) struct ParameterDFAInfo
+{
+    /// Parameter id: -1 this, -2 return, otherwise it is FuncDeclaration.parameters index
+    int parameterId;
+
+    /// Is the parameter non-null upon input, only applies to pointers.
+    Fact notNullIn;
+    /// Is the parameter non-null, applies only for by-ref parameters that are pointers.
+    Fact notNullOut;
+
+    /// Was the attributes for this parameter specified by the user?
+    bool specifiedByUser;
+
+    /// Given a property, has it been specificed and is it guaranteed?
+    enum Fact : ubyte
+    {
+        ///
+        Unspecified,
+        ///
+        NotGuaranteed,
+        ///
+        Guaranteed
+    }
+}
+
 /***********************************************************
  */
 extern (C++) class FuncDeclaration : Declaration
@@ -245,11 +264,7 @@ extern (C++) class FuncDeclaration : Declaration
 
     GotoStatements* gotos;              /// Gotos with forward references
 
-    version (MARS)
-    {
-        VarDeclarations* alignSectionVars;  /// local variables with alignment needs larger than stackAlign
-        void* salignSection;              /// pointer to aligned section, if any
-    }
+    mixin FuncDeclarationExtra;
 
     /// set if this is a known, builtin function we can evaluate at compile time
     BUILTIN builtin = BUILTIN.unknown;
@@ -285,6 +300,8 @@ extern (C++) class FuncDeclaration : Declaration
     AttributeViolation* pureViolation;
     AttributeViolation* nothrowViolation;
 
+    ParametersDFAInfo* parametersDFAInfo;
+
     /// See the `FUNCFLAG` struct
     import dmd.common.bitfields;
     mixin(generateBitFields!(FUNCFLAG, uint));
@@ -315,8 +332,13 @@ extern (C++) class FuncDeclaration : Declaration
         /* The type given for "infer the return type" is a TypeFunction with
          * NULL for the return type.
          */
-        if (type && type.nextOf() is null)
-            this.inferRetType = true;
+        if (type)
+        {
+            if (auto tf = type.isTypeFunction())
+                this.inferRetType = tf.next is null;
+            else
+                assert(0); // unreachable
+        }
     }
 
     static FuncDeclaration create(Loc loc, Loc endloc, Identifier id, StorageClass storage_class, Type type, bool noreturn = false)
@@ -373,93 +395,6 @@ extern (C++) class FuncDeclaration : Declaration
         return f;
     }
 
-    override final bool equals(const RootObject o) const
-    {
-        if (this == o)
-            return true;
-
-        auto s = isDsymbol(o);
-        if (!s)
-            return false;
-
-        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);
-    }
-
-    /****************************************************
-     * 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);
-        if (AliasDeclaration ad = s.isAliasDeclaration())
-        {
-            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;
-
-        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 template root in overload list
      */
@@ -518,64 +453,6 @@ extern (C++) class FuncDeclaration : Declaration
         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`
-     */
-    extern (D) 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;
-    }
-
-    enum LevelError = -2;
-
     override const(char)* toPrettyChars(bool QualifyTypes = false)
     {
         if (isMain())
@@ -833,99 +710,6 @@ extern (C++) class FuncDeclaration : Declaration
         return this.isGenerated ? "generated function" : "function";
     }
 
-    /*******************************
-     * 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", toPrettyChars());
-
-        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);
-
-                /* __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:
-        return true;
-    }
-
     /***********************************************
      * Determine if function's variables are referenced by a function
      * nested within it.
@@ -973,46 +757,6 @@ extern (C++) class FuncDeclaration : Declaration
         return ParameterList(null, VarArg.none);
     }
 
-    /**********************************
-     * Generate a FuncDeclaration for a runtime library function.
-     */
-    extern(D) static FuncDeclaration genCfunc(Parameters* fparams, Type treturn, const(char)* name, STC stc = STC.none)
-    {
-        return genCfunc(fparams, treturn, Identifier.idPool(name[0 .. strlen(name)]), stc);
-    }
-
-    extern(D) static FuncDeclaration genCfunc(Parameters* fparams, Type treturn, Identifier id, STC stc = STC.none)
-    {
-        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;
-    }
-
     inout(FuncDeclaration) toAliasFunc() inout @safe
     {
         return this;
@@ -1105,96 +849,6 @@ unittest
     assert(mismatches.isMutable);
 }
 
-/* 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.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.
- * 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
- */
-bool checkEscapingSiblings(FuncDeclaration f, FuncDeclaration outerFunc, void* p = null)
-{
-    static struct PrevSibling
-    {
-        PrevSibling* p;
-        FuncDeclaration f;
-    }
-
-    if (f.computedEscapingSiblings)
-        return f.hasEscapingSiblings;
-
-    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.length; ++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;
-        }
-    }
-    f.hasEscapingSiblings = bAnyClosures;
-    f.computedEscapingSiblings = true;
-    //printf("\t%d\n", bAnyClosures);
-    return bAnyClosures;
-}
-
 /***********************************************************
  * Used as a way to import a set of functions from another scope into this one.
  */
@@ -1403,11 +1057,6 @@ extern (C++) final class PostBlitDeclaration : FuncDeclaration
         return (isThis() && vthis && global.params.useInvariants == CHECKENABLE.on);
     }
 
-    override bool overloadInsert(Dsymbol s)
-    {
-        return false; // cannot overload postblits
-    }
-
     override void accept(Visitor v)
     {
         v.visit(this);
@@ -1460,11 +1109,6 @@ extern (C++) final class DtorDeclaration : FuncDeclaration
         return false;
     }
 
-    override bool overloadInsert(Dsymbol s)
-    {
-        return false; // cannot overload destructors
-    }
-
     override void accept(Visitor v)
     {
         v.visit(this);
@@ -1786,6 +1430,8 @@ struct AttributeViolation
 
     string action;   /// Action that made the attribute fail to get inferred
 
+    VarDeclaration scopeVar;  /// For scope violations: the parameter whose scope status caused the issue
+
     this(Loc loc, FuncDeclaration fd) { this.loc = loc; this.fd = fd; }
 
     this(Loc loc, const(char)* fmt, RootObject[] args)
@@ -1801,4 +1447,10 @@ struct AttributeViolation
         );
         this.action = buf.extractSlice();
     }
+
+    this(Loc loc, const(char)* fmt, VarDeclaration scopeVar, RootObject[] args)
+    {
+        this(loc, fmt, args);
+        this.scopeVar = scopeVar;
+    }
 }
index 0d147dd0ddcde29e9fc58421a34d4bb98e581578..d4e660ce667fecc7ed04dfad48486dbfbfd5dde6 100644 (file)
@@ -14,6 +14,7 @@
 module dmd.funcsem;
 
 import core.stdc.stdio;
+import core.stdc.string;
 
 import dmd.aggregate;
 import dmd.arraytypes;
@@ -59,15 +60,53 @@ import dmd.semantic3;
 import dmd.statement;
 import dmd.statementsem;
 import dmd.target;
+import dmd.targetcompiler;
 import dmd.templatesem;
 import dmd.tokens;
 import dmd.typesem;
 import dmd.visitor;
 import dmd.visitor.statement_rewrite_walker;
 
-version (IN_GCC) {}
-else version (IN_LLVM) {}
-else version = MARS;
+
+/**********************************
+ * Generate a FuncDeclaration for a runtime library function.
+ */
+FuncDeclaration genCfunc(Parameters* fparams, Type treturn, const(char)* name, STC stc = STC.none)
+{
+    return genCfunc(fparams, treturn, Identifier.idPool(name[0 .. strlen(name)]), stc);
+}
+
+FuncDeclaration genCfunc(Parameters* fparams, Type treturn, Identifier id, STC stc = STC.none)
+{
+    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;
+}
 
 /* Tweak all return statements and dtor call for nrvo_var, for correct NRVO.
  */
@@ -215,6 +254,10 @@ void funcDeclarationSemantic(Scope* sc, FuncDeclaration funcdecl)
         printf("type: %p, %s\n", funcdecl.type, funcdecl.type.toChars());
     }
 
+    import dmd.timetrace;
+    timeTraceBeginEvent(TimeTraceEventType.sema1Function);
+    scope (exit) timeTraceEndEvent(TimeTraceEventType.sema1Function, funcdecl);
+
     if (funcdecl.semanticRun != PASS.initial && funcdecl.isFuncLiteralDeclaration())
     {
         /* Member functions that have return types that are
@@ -335,6 +378,9 @@ void funcDeclarationSemantic(Scope* sc, FuncDeclaration funcdecl)
         sc = sc.push();
         sc.stc |= funcdecl.storage_class & (STC.disable | STC.deprecated_); // forward to function type
 
+        // Parameters don't inherit UDAs from outside, https://github.com/dlang/dmd/issues/19788
+        sc.userAttribDecl = null;
+
         if (sc.func)
         {
             /* If the nesting parent is pure without inference,
@@ -461,6 +507,16 @@ void funcDeclarationSemantic(Scope* sc, FuncDeclaration funcdecl)
         funcdecl.type = funcdecl.type.addSTC(stc);
 
         funcdecl.type = funcdecl.type.typeSemantic(funcdecl.loc, sc);
+
+        // semantic for parameters' UDAs
+        if (auto f = getFunctionType(funcdecl))
+            foreach (i, param; f.parameterList)
+            {
+                sc.userAttribDecl = null;
+                if (param && param.userAttribDecl)
+                    param.userAttribDecl.dsymbolSemantic(sc);
+            }
+
         sc = sc.pop();
     }
 
@@ -507,7 +563,7 @@ void funcDeclarationSemantic(Scope* sc, FuncDeclaration funcdecl)
         auto fnext = funcdecl.overnext.isFuncDeclaration();
         funcDeclarationSemantic(sc, fnext);
         auto fn = fnext.type.isTypeFunction();
-        if (!fn || !cFuncEquivalence(f, fn))
+        if (!fn || !cFuncEquivalence(f, fn) || !cTypeEquivalence(f.next, fn.next))
         {
             .error(funcdecl.loc, "%s `%s` redeclaration with different type", funcdecl.kind, funcdecl.toPrettyChars);
             //printf("t1: %s\n", f.toChars());
@@ -694,13 +750,6 @@ Ldone:
     }
 
     assert(funcdecl.type.ty != Terror || funcdecl.errors);
-
-    // semantic for parameters' UDAs
-    foreach (i, param; f.parameterList)
-    {
-        if (param && param.userAttribDecl)
-            param.userAttribDecl.dsymbolSemantic(sc);
-    }
 }
 
 /**
@@ -1171,8 +1220,8 @@ Linterfaces:
                     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());
+                    error(funcdecl.loc, "function `%s` does not override any function", funcdeclToChars);
+                    errorSupplemental(fd.loc, "did you mean to override `%s`?", buf1.peekChars());
 
                     // Supplemental error for parameter scope differences
                     auto tf1 = cast(TypeFunction)funcdecl.type;
@@ -1185,8 +1234,6 @@ Linterfaces:
 
                         if (params1.length == params2.length)
                         {
-                            bool hasScopeDifference = false;
-
                             for (size_t i = 0; i < params1.length; i++)
                             {
                                 auto p1 = params1[i];
@@ -1198,14 +1245,7 @@ Linterfaces:
                                 if (!(p2.storageClass & STC.scope_))
                                     continue;
 
-                                if (!hasScopeDifference)
-                                {
-                                    // Intended signature
-                                    errorSupplemental(funcdecl.loc, "Did you intend to override:");
-                                    errorSupplemental(funcdecl.loc, "`%s`", buf1.peekChars());
-                                    hasScopeDifference = true;
-                                }
-                                errorSupplemental(funcdecl.loc, "Parameter %d is missing `scope`",
+                                errorSupplemental(funcdecl.loc, "parameter %d is missing `scope`",
                                 cast(int)(i + 1));
                             }
                         }
@@ -1962,7 +2002,7 @@ private void checkNamedArgErrorAndReport(TemplateDeclaration td, ArgumentList ar
 {
     if (!argumentList.hasArgNames())
         return;
-
+    td.computeOneMember();
     auto tf = td.onemember ? td.onemember.isFuncDeclaration() : null;
     if (tf && tf.type && tf.type.ty == Tfunction)
     {
@@ -1992,8 +2032,11 @@ private void checkNamedArgErrorAndReportOverload(Dsymbol od, ArgumentList argume
             if (auto fd = s.isFuncDeclaration())
                 tf = fd;
             else if (auto td = s.isTemplateDeclaration())
+            {
+                td.computeOneMember();
                 if (td.onemember)
                     tf = td.onemember.isFuncDeclaration();
+            }
         }
         return 0;
     });
@@ -2073,6 +2116,7 @@ private void printCandidates(Decl)(Loc loc, Decl declaration, bool showDeprecate
             // td.onemember may not have overloads set
             // (see fail_compilation/onemember_overloads.d)
             // assume if more than one member it is overloaded internally
+            td.computeOneMember();
             bool recurse = td.onemember && (!td.onemember.isFuncDeclaration ||
                 td.members.length > 1);
             OutBuffer buf;
@@ -2456,7 +2500,7 @@ int getLevelAndCheck(FuncDeclaration fd, Loc loc, Scope* sc, FuncDeclaration tar
                      Declaration decl)
 {
     int level = fd.getLevel(target, sc.intypeof);
-    if (level != fd.LevelError)
+    if (level != LevelError)
         return level;
     // Don't give error if in template constraint
     if (!sc.inTemplateConstraint)
@@ -2467,7 +2511,7 @@ int getLevelAndCheck(FuncDeclaration fd, Loc loc, Scope* sc, FuncDeclaration tar
                xstatic, fd.kind(), fd.toPrettyChars(), decl.kind(), decl.toChars(),
                target.toPrettyChars());
             .errorSupplemental(decl.loc, "`%s` declared here", decl.toChars());
-        return fd.LevelError;
+        return LevelError;
     }
     return 1;
 }
@@ -2549,6 +2593,8 @@ void buildResultVar(FuncDeclaration fd, Scope* sc, Type tret)
         TypeFunction tf = fd.type.toTypeFunction();
         if (tf.isRef)
             fd.vresult.storage_class |= STC.ref_;
+        else if (target.isReturnOnStack(tf, fd.needThis()))
+            fd.vresult.nrvo = true;
         fd.vresult.type = tret;
         fd.vresult.dsymbolSemantic(sc);
         if (!sc.insert(fd.vresult))
@@ -3093,6 +3139,248 @@ extern (D) void checkMain(FuncDeclaration fd)
         .error(fd.loc, "%s `%s` must return `int`, `void` or `noreturn`, not `%s`", fd.kind, fd.toPrettyChars, tf.nextOf().toChars());
 }
 
+enum LevelError = -2;
+
+/*****************************************
+ * Determine lexical level difference from `fd1` to nested function `fd2`.
+ * Params:
+ *      fd1 = function
+ *      fd2 = target of call
+ *      intypeof = !=0 if inside typeof
+ * Returns:
+ *      0       same level
+ *      >0      decrease nesting by number
+ *      -1      increase nesting by 1 (`fd2` is nested within `fd1`)
+ *      LevelError  error, `this` cannot call `fd2`
+ */
+extern (D) int getLevel(FuncDeclaration fd1, FuncDeclaration fd2, int intypeof)
+{
+    //printf("FuncDeclaration::getLevel(fd2 = '%s')\n", fd2.toChars());
+    Dsymbol fd2parent = fd2.toParent2();
+    if (fd2parent == fd1)
+        return -1;
+
+    Dsymbol s = fd1;
+    int level = 0;
+    while (fd2 != s && fd2parent != 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(fd2);
+        assert(s);
+        level++;
+    }
+    return level;
+}
+
+/* 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.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.
+ * 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
+ */
+bool checkEscapingSiblings(FuncDeclaration f, FuncDeclaration outerFunc, void* p = null)
+{
+    static struct PrevSibling
+    {
+        PrevSibling* p;
+        FuncDeclaration f;
+    }
+
+    if (f.computedEscapingSiblings)
+        return f.hasEscapingSiblings;
+
+    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.length; ++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;
+        }
+    }
+    f.hasEscapingSiblings = bAnyClosures;
+    f.computedEscapingSiblings = true;
+    //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 needsClosure(FuncDeclaration fd)
+{
+    /* 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", toPrettyChars());
+
+    if (fd.requiresClosure)
+        goto Lyes;
+
+    for (size_t i = 0; i < fd.closureVars.length; i++)
+    {
+        VarDeclaration v = fd.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 != fd);
+
+            /* __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 != fd; s = s.toParentP(fd))
+            {
+                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(fd) : fx, fd);
+
+                    fd.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, fd))
+                    fd.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 (fd.requiresClosure)
+        goto Lyes;
+
+    return false;
+
+Lyes:
+    return true;
+}
+
 /***********************************************
  * Check all return statements for a function to verify that returning
  * using NRVO is possible.
@@ -3125,12 +3413,12 @@ extern (D) bool checkNRVO(FuncDeclaration fd)
                     return false;
                 if (v.nestedrefs.length && fd.needsClosure())
                     return false;
+
                 // don't know if the return storage is aligned
-                version (MARS)
-                {
-                    if (fd.alignSectionVars && (*fd.alignSectionVars).contains(v))
-                        return false;
-                }
+                mixin alignSectionVarsContains;
+                if (isAlignSectionVar(v))
+                    return false;
+
                 // The variable type needs to be equivalent to the return type.
                 if (!v.type.equivalent(tf.next))
                     return false;
@@ -3411,7 +3699,7 @@ private bool isTypeIsolatedIndirect(FuncDeclaration fd, Type t)
     // The 'this' reference is a parameter, too
     if (AggregateDeclaration ad = fd.isCtorDeclaration() ? null : fd.isThis())
     {
-        Type tthis = ad.getType().addMod(tf.mod);
+        Type tthis = dmd.dsymbolsem.getType(ad).addMod(tf.mod);
         //printf("\ttthis = %s\n", tthis.toChars());
         if (!traverseIndirections(tthis, t))
             return false;
@@ -3594,14 +3882,14 @@ extern (D) int overloadApply(Dsymbol fstart, scope int delegate(Dsymbol) dg, Sco
 
     int overloadApplyRecurse(Dsymbol fstart, scope int delegate(Dsymbol) dg, Scope* sc)
     {
-        // Detect cyclic calls.
-        if (visited.contains(fstart))
-            return 0;
-        visited.push(fstart);
-
         Dsymbol next;
         for (auto d = fstart; d; d = next)
         {
+            // Detect cyclic calls.
+            if (visited.contains(d))
+                return 0;
+            visited.push(d);
+
             import dmd.access : checkSymbolAccess;
             if (auto od = d.isOverDeclaration())
             {
@@ -3658,6 +3946,13 @@ extern (D) int overloadApply(Dsymbol fstart, scope int delegate(Dsymbol) dg, Sco
             }
             else if (auto td = d.isTemplateDeclaration())
             {
+                // Sometimes funcroot is not in the .overnext chain
+                // in such case we have to recurse into it
+                if(td.funcroot)
+                {
+                    if (int r = overloadApplyRecurse(td.funcroot, dg, sc))
+                        return r;
+                }
                 if (int r = dg(td))
                     return r;
                 next = td.overnext;
@@ -3736,23 +4031,23 @@ extern (D) bool checkNestedReference(VarDeclaration vd, Scope* sc, Loc loc)
     if (!fdv || fdv == fdthis)
         return false;
 
-    // Add fdthis to nestedrefs[] if not already there
-    if (!vd.nestedrefs.contains(fdthis))
-        vd.nestedrefs.push(fdthis);
-
     //printf("\tfdv = %s\n", fdv.toChars());
     //printf("\tfdthis = %s\n", fdthis.toChars());
     if (loc.isValid())
     {
-        if (fdthis.getLevelAndCheck(loc, sc, fdv, vd) == fdthis.LevelError)
+        if (fdthis.getLevelAndCheck(loc, sc, fdv, vd) == LevelError)
             return true;
     }
 
-    // Add this VarDeclaration to fdv.closureVars[] if not already there
     if (!sc.intypeof && !sc.traitsCompiles &&
         // https://issues.dlang.org/show_bug.cgi?id=17605
         (fdv.skipCodegen || !fdthis.skipCodegen))
     {
+        // Add fdthis to nestedrefs[] if not already there
+        if (!vd.nestedrefs.contains(fdthis))
+            vd.nestedrefs.push(fdthis);
+
+        // Add this VarDeclaration to fdv.closureVars[] if not already there
         if (!fdv.closureVars.contains(vd))
             fdv.closureVars.push(vd);
     }
index eb8d05533193ad61e216e5ff11d84127e38b6583..54a0228c4f214fc5b19d1ed8a31184ed3b2ba56d 100644 (file)
@@ -26,12 +26,9 @@ import dmd.file_manager;
 import dmd.identifier;
 import dmd.location;
 import dmd.lexer : CompileEnv;
+import dmd.targetcompiler;
 import dmd.utils;
 
-version (IN_GCC) {}
-else version (IN_LLVM) {}
-else version = MARS;
-
 /// Defines a setting for how compiler warnings and deprecations are handled
 enum DiagnosticReporting : ubyte
 {
@@ -183,7 +180,9 @@ extern (C++) struct Param
     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 bitfields;         // support C style bit fields
+    bool bitfields = true;  // support C style bit fields
+    bool rewriteNoExceptionToSeq; // Allow finally statements that do not throw an Exception
+                                  // in try body to rewrite to a sequence.
 
     CppStdRevision cplusplus = CppStdRevision.cpp11;    // version of C++ standard to support
 
@@ -317,7 +316,7 @@ extern (C++) struct Global
     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
+    uint gaggedDeprecations; /// number of deprecations reported while gagged
 
     void* console;         /// opaque pointer to console for controlling text attributes
 
@@ -353,7 +352,7 @@ extern (C++) struct Global
     extern (C++) uint startGagging() @safe
     {
         ++gag;
-        gaggedWarnings = 0;
+        gaggedDeprecations = 0;
         return gaggedErrors;
     }
 
@@ -393,21 +392,11 @@ extern (C++) struct Global
         errorSinkNull = new ErrorSinkNull;
 
         this.fileManager = new FileManager();
+        compileEnv.vendor = TargetCompiler;
+        compileEnv.switchPrefix = SwitchPrefix;
 
-        version (MARS)
-        {
-            compileEnv.vendor = "Digital Mars D";
-        }
-        else version (IN_GCC)
-        {
-            compileEnv.vendor = "GNU D";
-        }
-        else version (IN_LLVM)
-        {
-            compileEnv.vendor = "LDC";
-        }
-        else
-            static assert(0, "unknown vendor");
+        mixin UseAnsiColors;
+        params.v.color = useAnsiColors();
 
         compileEnv.versionNumber = parseVersionNumber(versionString());
 
index 0427347e487b7fbb2db51176168ce4303d64cdc3..be44f025fc25c2ea88446793105cf05b80b5e956 100644 (file)
@@ -66,6 +66,13 @@ enum JsonFieldFlags
     semantics    = (1 << 3)
 };
 
+enum class Edition : uint16_t
+{
+    v2023 = 2023,
+    v2024,
+    v2025,
+};
+
 enum CppStdRevision
 {
     CppStdRevisionCpp98 = 199711,
@@ -198,12 +205,13 @@ struct Param
     d_bool addMain;       // add a default main() function
     d_bool allInst;       // generate code for all template instantiations
     d_bool bitfields;         // support C style bit fields
+    d_bool rewriteNoExceptionToSeq;
     CppStdRevision cplusplus;  // version of C++ name mangling to support
 
     Help help;
     Verbose v;
 
-    unsigned short edition;      // edition year
+    Edition edition;             // edition year
     void* editionFiles;          // Edition corresponding to a filespec
 
     // Options for `-preview=/-revert=`
@@ -288,9 +296,10 @@ struct Param
 
 struct structalign_t
 {
-    unsigned short value;
-    d_bool pack;
-
+private:
+    uint16_t value;
+    uint8_t flags;
+public:
     bool isDefault() const;
     void setDefault();
     bool isUnknown() const;
@@ -298,7 +307,9 @@ struct structalign_t
     void set(unsigned value);
     unsigned get() const;
     bool isPack() const;
-    void setPack(bool pack);
+    void setPack();
+    bool fromAlignas() const;
+    void setAlignas();
 };
 
 // magic value means "match whatever the underlying C compiler does"
@@ -324,6 +335,7 @@ struct CompileEnv
     d_bool transitionIn;
     d_bool ddocOutput;
     d_bool masm;
+    DString switchPrefix;
     IdentifierCharLookup cCharLookupTable;
     IdentifierCharLookup dCharLookupTable;
 };
@@ -347,7 +359,7 @@ struct Global
     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
+    unsigned gaggedDeprecations; // number of deprecations reported while gagged
 
     void* console;         // opaque pointer to console for controlling text attributes
 
index 73f430e3db8795b16d729fba64fb3e34197d6389..a5d9f0ff3fae4a73e8de20133b70739ef4d0eb44 100644 (file)
@@ -22,7 +22,6 @@ import dmd.arraytypes;
 import dmd.astenums;
 import dmd.attrib;
 import dmd.cond;
-import dmd.ctfeexpr;
 import dmd.dclass;
 import dmd.declaration;
 import dmd.denum;
@@ -50,7 +49,6 @@ import dmd.root.string;
 import dmd.statement;
 import dmd.staticassert;
 import dmd.tokens;
-import dmd.typesem;
 import dmd.visitor;
 
 struct HdrGenState
@@ -2209,7 +2207,7 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt
 
     void visitInteger(IntegerExp e)
     {
-        const ulong v = e.toInteger();
+        const ulong v = e.value;
         if (e.type)
         {
             Type t = e.type;
@@ -2224,7 +2222,7 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt
                     {
                         foreach (em; *sym.members)
                         {
-                            if ((cast(EnumMember)em).value.toInteger == v)
+                            if ((cast(EnumMember)em).value.isIntegerExp().value == v)
                             {
                                 const id = em.ident.toString();
                                 buf.printf("%s.%.*s", sym.toChars(), cast(int)id.length, id.ptr);
@@ -3186,7 +3184,8 @@ void floatToBuffer(Type type, const real_t value, ref OutBuffer buf, const bool
 
     if (type)
     {
-        Type t = type.toBasetype();
+        Type t = type.toBaseTypeNonSemantic();
+
         switch (t.ty)
         {
         case Tfloat32:
@@ -3202,7 +3201,7 @@ void floatToBuffer(Type type, const real_t value, ref OutBuffer buf, const bool
         default:
             break;
         }
-        if (t.isImaginary())
+        if (t.isImaginaryNonSemantic())
             buf.put('i');
     }
 }
@@ -3349,7 +3348,7 @@ void toCBufferInstance(const TemplateInstance ti, ref OutBuffer buf, bool qualif
     HdrGenState hgs;
     hgs.fullQual = qualifyTypes;
 
-    buf.put(ti.name.toChars());
+    buf.put(ti.name == Id.ctor ? "this" : ti.name.toChars());
     tiargsToBuffer(cast() ti, buf, hgs);
 }
 
@@ -3815,7 +3814,7 @@ private void sizeToBuffer(Expression e, ref OutBuffer buf, ref HdrGenState hgs)
     {
         Expression ex = (e.op == EXP.cast_ ? (cast(CastExp)e).e1 : e);
         ex = ex.optimize(WANTvalue);
-        const ulong uval = ex.op == EXP.int64 ? ex.toInteger() : cast(ulong)-1;
+        const ulong uval = ex.op == EXP.int64 ? ex.isIntegerExp().value : cast(ulong)-1;
         if (cast(long)uval >= 0)
         {
             if (uval <= 0xFFFFU)
@@ -4342,14 +4341,14 @@ private void typeToBufferx(Type t, ref OutBuffer buf, ref HdrGenState hgs)
 
     void visitDArray(TypeDArray t)
     {
-        Type ut = t.castMod(0);
+        auto basetype = t.next;
         if (hgs.declstring)
             goto L1;
-        if (ut.equals(Type.tstring))
+        if (basetype.ty == Tchar && basetype.isImmutable())
             buf.put("string");
-        else if (ut.equals(Type.twstring))
+        else if (basetype.ty == Twchar && basetype.isImmutable())
             buf.put("wstring");
-        else if (ut.equals(Type.tdstring))
+        else if (basetype.ty == Tdchar && basetype.isImmutable())
             buf.put("dstring");
         else
         {
index 2104b98f75d7db4bdad7f750d1c5975823aabb15..2bc355908d0c3086d8016e9fec57996cb5c5307d 100644 (file)
@@ -21,7 +21,7 @@ class Statement;
 namespace dmd
 {
     void genhdrfile(Module *m, bool doFuncBodies, OutBuffer &buf);
-    void genCppHdrFiles(Modules &ms);
+    void genCppHdrFiles(Modules &ms, ErrorSink *eSink);
     void moduleToBuffer(OutBuffer& buf, bool vcg_ast, Module *m);
     const char *parametersTypeToChars(ParameterList pl);
 
index 4be94d76ad43fd086a6d8ff84560c1903eea3df0..f7216f3575cd601bf883d75fa26b6820c205fd68 100644 (file)
@@ -30,6 +30,7 @@ import dmd.target;
 import dmd.tokens;
 import dmd.statement;
 import dmd.statementsem;
+import dmd.typesem;
 
 /***********************************
  * Parse and run semantic analysis on a GccAsmStatement.
@@ -188,7 +189,8 @@ Expression semanticAsmString(Scope* sc, Expression exp, const char *s)
 {
     import dmd.dcast : implicitCastTo;
     import dmd.dsymbolsem : resolveAliasThis;
-    import dmd.mtype : isAggregate, Type;
+    import dmd.mtype : Type;
+    import dmd.typesem : isAggregate;
 
     exp = expressionSemantic(exp, sc);
 
@@ -553,6 +555,7 @@ GccAsmStatement parseGccAsm(Parser)(Parser p, GccAsmStatement s)
 unittest
 {
     import dmd.mtype : TypeBasic;
+    import dmd.typesem : merge;
 
     if (!global.errorSink)
         global.errorSink = new ErrorSinkCompiler;
@@ -560,7 +563,7 @@ unittest
     const errors = global.startGagging();
     scope(exit) global.endGagging(errors);
 
-    // If this check fails, then Type._init() was called before reaching here,
+    // 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
@@ -573,8 +576,10 @@ unittest
         ASTCodegen.Type.tchar = null;
     }
     scope tint32 = new TypeBasic(ASTCodegen.Tint32);
+    tint32.merge();
     ASTCodegen.Type.tint32 = tint32;
     scope tchar = new TypeBasic(ASTCodegen.Tchar);
+    tchar.merge();
     ASTCodegen.Type.tchar = tchar;
 
     // Imitates asmSemantic if version = IN_GCC.
index c5988203d1b2314488b3f716699414cba6dc1547..ed7423d433d0013766532c03f0b5a0c1499a2012 100644 (file)
@@ -283,6 +283,8 @@ immutable Msgtable[] msgtable =
     { "_d_newitemTTrace" },
     { "_d_newarrayT" },
     { "_d_newarrayTTrace" },
+    { "_d_newarrayU" },
+    { "_d_newarrayUTrace" },
     { "_d_newarraymTX" },
     { "_d_newarraymTXTrace" },
     { "_d_arrayliteralTX" },
index 92d3a8c79505b7d5aa42c174c65f6e1048e32740..5f2deb9233d3495eded73d19d3e6c8864b71e8ed 100644 (file)
@@ -78,6 +78,11 @@ nothrow:
         isAnonymous_ = isAnonymous;
     }
 
+    bool equals(const Identifier i) const
+    {
+        return this is i;
+    }
+
     static Identifier create(const(char)* name)
     {
         return new Identifier(name);
index eb06c25c724fbe7b4bc4932e294eb7bafefa215e..02b55b6502db1a6e3957ca1f2697a97aa7d996bf 100644 (file)
@@ -28,6 +28,7 @@ public:
     const char *toHChars2() const;
     DYNCAST dyncast() const override;
 
+    bool equals(const Identifier * const i) { return this == i; };
     static Identifier *generateId(const char *prefix, size_t length, size_t suffix);
     static Identifier *idPool(const char *s, unsigned len);
 
index e22bcc8b123f223fb0d1bdf8b7cda3bcebdd0322..1b2d51d79434976abc466d2dac3e0b4694d175fb 100644 (file)
@@ -91,21 +91,89 @@ shared static this()
         "InterpolationFooter": "core.interpolation",
     ];
     cHints = [
+        "va_list": "<stdarg.h>",
+
+        "bool": "<stdbool.h>",
+        "false": "<stdbool.h>",
+        "true": "<stdbool.h>",
+
         "NULL": "<stddef.h>",
-        "calloc": "<stdlib.h>",
+        "nullptr_t": "<stddef.h>",
+        "offsetof": "<stddef.h>",
+        "ptrdiff_t": "<stddef.h>",
+        "size_t": "<stddef.h>",
+        "wchar_t": "<stddef.h>",
+
+        "INT8_MAX": "<stdint.h>",
+        "INT16_MAX": "<stdint.h>",
+        "INT32_MAX": "<stdint.h>",
+        "INT64_MAX": "<stdint.h>",
+        "INTPTR_MAX": "<stdint.h>",
+        "PTRDIFF_MAX": "<stdint.h>",
+        "PTRDIFF_MIN": "<stdint.h>",
+        "SIZE_MAX": "<stdint.h>",
+        "UINT8_MAX": "<stdint.h>",
+        "UINT16_MAX": "<stdint.h>",
+        "UINT32_MAX": "<stdint.h>",
+        "UINT64_MAX": "<stdint.h>",
+        "UINTPTR_MAX": "<stdint.h>",
+        "WINT_MAX": "<stdint.h>",
+        "WINT_MIN": "<stdint.h>",
+        "int8_t": "<stdint.h>",
+        "int16_t": "<stdint.h>",
+        "int32_t": "<stdint.h>",
+        "int64_t": "<stdint.h>",
+        "intptr_t": "<stdint.h>",
+        "uint8_t": "<stdint.h>",
+        "uint16_t": "<stdint.h>",
+        "uint32_t": "<stdint.h>",
+        "uint64_t": "<stdint.h>",
+        "uintptr_t": "<stdint.h>",
+
+        "EOF": "<stdio.h>",
+        "FILE": "<stdio.h>",
         "fopen": "<stdio.h>",
+        "fpos_t": "<stdio.h>",
         "fprintf": "<stdio.h>",
+        "getchar": "<stdio.h>",
+        "printf": "<stdio.h>",
+        "snprintf": "<stdio.h>",
+        "sprintf": "<stdio.h>",
+        "stderr": "<stdio.h>",
+        "stdin": "<stdio.h>",
+        "stdout": "<stdio.h>",
+
+        "EXIT_FAILURE": "<stdlib.h>",
+        "EXIT_SUCCESS": "<stdlib.h>",
+        "abort": "<stdlib.h>",
+        "atexit": "<stdlib.h>",
+        "calloc": "<stdlib.h>",
+        "exit": "<stdlib.h>",
         "free": "<stdlib.h>",
+        "getenv": "<stdlib.h>",
         "malloc": "<stdlib.h>",
+        "realloc": "<stdlib.h>",
+
+        "memchr": "<string.h>",
+        "memcmp": "<string.h>",
         "memcpy": "<string.h>",
         "memmove": "<string.h>",
         "memset": "<string.h>",
-        "printf": "<stdio.h>",
-        "ptrdiff_t": "<stddef.h>",
-        "size_t": "<stddef.h>",
-        "stderr": "<stdio.h>",
-        "stdin": "<stdio.h>",
-        "stdout": "<stdio.h>",
+        "strcat": "<string.h>",
+        "strchr": "<string.h>",
+        "strcmp": "<string.h>",
+        "strcpy": "<string.h>",
+        "strerror": "<string.h>",
+        "strlen": "<string.h>",
+        "strncat": "<string.h>",
+        "strncmp": "<string.h>",
+        "strncpy": "<string.h>",
+        "strrchr": "<string.h>",
+        "strspn": "<string.h>",
+        "strstr": "<string.h>",
+
+        "WCHAR_MAX": "<wchar.h>",
+        "WCHAR_MIN": "<wchar.h>",
     ];
 }
 
index 54efbd24cc020d062e6f79a2dd2de3ef34498e4e..06b212f0546547ece5cf490505f0f6b60f186313 100644 (file)
@@ -41,7 +41,6 @@ public:
     const char *kind() const override;
     Visibility visible() override;
     Import *syntaxCopy(Dsymbol *s) override; // copy only syntax trees
-    bool overloadInsert(Dsymbol *s) override;
 
     void accept(Visitor *v) override { v->visit(this); }
 };
index 3f5fe86dc9770c26d62e9182b6d45a7a42dc625e..db16ce763d28f998f6eeeca5205ceb49f67757bc 100644 (file)
@@ -223,66 +223,6 @@ void addDefaultCInitializer(VarDeclaration dsym)
     dsym._init = new ExpInitializer(dsym.loc, e);
 }
 
-/********************************************
- * Resolve cast/call grammar ambiguity.
- * Params:
- *      e = expression that might be a cast, might be a call
- *      sc = context
- * Returns:
- *      null means leave as is, !=null means rewritten AST
- */
-Expression castCallAmbiguity(Expression e, Scope* sc)
-{
-    Expression* pe = &e;
-
-    while (1)
-    {
-        // Walk down the postfix expressions till we find a CallExp or something else
-        switch ((*pe).op)
-        {
-            case EXP.dotIdentifier:
-                pe = &(*pe).isDotIdExp().e1;
-                continue;
-
-            case EXP.plusPlus:
-            case EXP.minusMinus:
-                pe = &(*pe).isPostExp().e1;
-                continue;
-
-            case EXP.array:
-                pe = &(*pe).isArrayExp().e1;
-                continue;
-
-            case EXP.call:
-                auto ce = (*pe).isCallExp();
-                if (ce.e1.parens)
-                {
-                    ce.e1 = expressionSemantic(ce.e1, sc);
-                    if (ce.e1.op == EXP.type)
-                    {
-                        const numArgs = ce.arguments ? ce.arguments.length : 0;
-                        if (numArgs >= 1)
-                        {
-                            ce.e1.parens = false;
-                            Expression arg;
-                            foreach (a; (*ce.arguments)[])
-                            {
-                                arg = arg ? new CommaExp(a.loc, arg, a) : a;
-                            }
-                            auto t = ce.e1.isTypeExp().type;
-                            *pe = arg;
-                            return new CastExp(ce.loc, e, t);
-                        }
-                    }
-                }
-                return null;
-
-            default:
-                return null;
-        }
-    }
-}
-
 /********************************************
  * Implement the C11 notion of function equivalence,
  * which allows prototyped functions to match K+R functions,
@@ -580,6 +520,10 @@ Dsymbol handleSymbolRedeclarations(ref Scope sc, Dsymbol s, Dsymbol s2, ScopeDsy
             sds.symtab.update(vd);      // replace vd2 with the definition
             return vd;
         }
+        else if (!i1 && !(vd2.storage_class & STC.extern_)) /* incoming has void void definition */
+        {
+            vd.storage_class |= STC.extern_;
+        }
 
         /* BUG: the types should match, which needs semantic() to be run on it
          *    extern int x;
@@ -588,7 +532,14 @@ Dsymbol handleSymbolRedeclarations(ref Scope sc, Dsymbol s, Dsymbol s2, ScopeDsy
          *    INT x;  // match
          *    long x; // collision
          * We incorrectly ignore these collisions
+         * when their types are not matching, err on type differences
          */
+
+        if (!cTypeEquivalence(vd.type, vd2.type))
+        {
+            .error(vd.loc, "redefinition of `%s` with different type: `%s` vs `%s`",
+                vd2.ident.toChars(), vd2.type.toChars(), vd.type.toChars());
+        }
         return vd2;
     }
 
@@ -641,6 +592,15 @@ Dsymbol handleSymbolRedeclarations(ref Scope sc, Dsymbol s, Dsymbol s2, ScopeDsy
          */
         fd2.overloadInsert(fd);
 
+        //for the sake of functions declared in function scope.
+        // check for return type equivalence also
+        auto tf1 = fd.type.isTypeFunction();
+        auto tf2 = fd2.type.isTypeFunction();
+        if (sc.func &&  !cTypeEquivalence(tf1.next, tf2.next) )
+        {
+            .error(fd.loc, "%s `%s` redeclaration with different type", fd.kind, fd.toPrettyChars);
+        }
+
         return fd2;
     }
 
index 7247d747974610728d733cb9bf021ebd25ec1207..00de258451360125a4cdd2069e82210251873cd5 100644 (file)
@@ -14,8 +14,6 @@ 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;
@@ -168,7 +166,7 @@ Initializer initializerSemantic(Initializer init, Scope* sc, ref Type tx, NeedIn
                 // Convert initializer to Expression `ex`
                 auto tm = fieldType.addMod(t.mod);
                 auto iz = i.value[j].initializerSemantic(sc, tm, needInterpret);
-                auto ex = iz.initializerToExpression(null, sc.inCfile);
+                auto ex = iz.initializerToExpression(fieldType, sc.inCfile);
                 if (ex.op != EXP.error)
                     i.value[j] = iz;
                 return ex;
@@ -449,11 +447,17 @@ Initializer initializerSemantic(Initializer init, Scope* sc, ref Type tx, NeedIn
             Type typeb = se.type.toBasetype();
             TY tynto = tb.nextOf().ty;
             if (!se.committed &&
-                typeb.isStaticOrDynamicArray() && tynto.isSomeChar &&
-                se.numberOfCodeUnits(tynto) < tb.isTypeSArray().dim.toInteger())
+                typeb.isStaticOrDynamicArray() && tynto.isSomeChar)
             {
-                i.exp = se.castTo(sc, t);
-                goto L1;
+                string s;
+                size_t len = se.numberOfCodeUnits(tynto, s);
+                if (s)
+                    error(se.loc, "%.*s", cast(int)s.length, s.ptr);
+                if (len < tb.isTypeSArray().dim.toInteger())
+                {
+                    i.exp = se.castTo(sc, t);
+                    goto L1;
+                }
             }
 
             /* Lop off terminating 0 of initializer for:
@@ -617,7 +621,6 @@ Initializer initializerSemantic(Initializer init, Scope* sc, ref Type tx, NeedIn
         static if (0)
             if (auto ts = tx.isTypeStruct())
             {
-                import dmd.common.outbuffer;
                 OutBuffer buf;
                 HdrGenState hgs;
                 toCBuffer(ts.sym, buf, hgs);
@@ -628,6 +631,19 @@ Initializer initializerSemantic(Initializer init, Scope* sc, ref Type tx, NeedIn
          */
         t = t.toBasetype();
 
+        bool isComplexInitilaizer()
+        {
+            switch (t.ty)
+            {
+                case Tcomplex32:
+                case Tcomplex64:
+                case Tcomplex80:
+                    return true;
+                default:
+                    return false;
+            }
+        }
+
         if (auto tv = t.isTypeVector())
             t = tv.basetype;
 
@@ -820,20 +836,105 @@ Initializer initializerSemantic(Initializer init, Scope* sc, ref Type tx, NeedIn
             {
                 DesigInit di = ci.initializerList[index];
                 Designators* dlist = di.designatorList;
+                VarDeclaration field;
                 if (dlist)
                 {
                     const length = (*dlist).length;
+                    auto id = (*dlist)[0].ident;
                     if (length == 0 || !(*dlist)[0].ident)
                     {
                         error(ci.loc, "`.identifier` expected for C struct field initializer `%s`", toChars(ci));
                         return err();
                     }
+
                     if (length > 1)
                     {
-                        error(ci.loc, "only 1 designator currently allowed for C struct field initializer `%s`", toChars(ci));
-                        return err();
+                        StructDeclaration nstsd = sd; // use this for member structs we wish to traverse
+                        auto subsi = si;
+                        /*
+                         * run this for each designator in the chain until you hit the last
+                         * then perform semantic analysis on the last field in the chain using the previous struct initializer
+                         */
+                        for (size_t i = 0; i < length; i++)
+                        {
+                            int found;
+                            id = (*dlist)[i].ident;
+                            foreach (f; nstsd.fields[])
+                            {
+                                if (f.ident == id)
+                                {
+                                    field = f;
+                                    ++found;
+                                    break;
+                                }
+                            }
+                            if (!found)
+                            {
+                                error(ci.loc, "`.%s` is not a field of `%s`\n", id.toChars(), nstsd.toChars());
+                                return err();
+                            }
+
+                            auto base = field.type.toBasetype();
+
+                            if (i >= length -1)
+                            {
+                                subsi.addInit(id, di.initializer);
+                                ++index;
+                                continue Loop1;
+                            }
+
+                            auto tstr = base.isTypeStruct();
+                            auto tarr = base.isTypeSArray();
+
+                            if (tstr)
+                            {
+                                if (!overlaps(field, nstsd.fields[], subsi))
+                                {
+                                    auto innersi = new StructInitializer(ci.loc);
+                                    subsi.addInit(id, innersi);
+                                    subsi = innersi;
+                                }
+                                else {
+                                    foreach(k, ident; subsi.field[])
+                                    {
+                                        if (ident == id && subsi.value[k])
+                                            subsi = subsi.value[k].isStructInitializer();
+                                    }
+                                }
+                                nstsd = tstr.sym;
+                            }
+                            /*
+                             * once we hit an array, check & attach the array initializer to the struct initializer
+                             * move to the next initializer id and run initializer semantics on it
+                             */
+                            else if (tarr)
+                            {
+                                /*
+                                 * so tempting to check for null cases for field._init.
+                                 * but if your object is set to null on decl, you can't use designators anymore
+                                 * and D does well to default initialize for us
+                                 */
+                                auto ai = field._init.isArrayInitializer();
+
+                                if (ai is null)
+                                {
+                                    ai = new ArrayInitializer(ci.loc);
+                                    subsi.addInit(id, ai);
+                                    field._init = ai;
+                                }
+
+                                auto ndx = (*dlist)[i+1].exp;
+                                ai.addInit(ndx, di.initializer);
+                                ++index;
+                                continue Loop1;
+                            }
+                            else
+                            {
+                                error(ci.loc, "only 1 designated initializer allowed for C struct field of type `%s`", toChars(base));
+                                return err();
+                            }
+                        }
                     }
-                    auto id = (*dlist)[0].ident;
                     foreach (k, f; sd.fields[])         // linear search for now
                     {
                         if (f.ident == id)
@@ -909,7 +1010,6 @@ Initializer initializerSemantic(Initializer init, Scope* sc, ref Type tx, NeedIn
                         continue Loop1;
                 }
 
-                VarDeclaration field;
                 while (1)   // skip field if it overlaps with previously seen fields
                 {
                     field = sd.fields[fieldi];
@@ -1059,6 +1159,24 @@ Initializer initializerSemantic(Initializer init, Scope* sc, ref Type tx, NeedIn
         {
             return visitExp(ei);
         }
+        else if (isComplexInitilaizer())
+        {
+            /* just convert _Complex = { a, b} to _Complex =. a + b*i */
+            if (ci.initializerList[].length != 2)
+            {
+                error(ci.loc, "only two initializers required for complex type `%s`", t.toChars());
+                return err();
+            }
+            auto rexp = ci.initializerList[0].initializer.initializerToExpression();
+            auto imexp = ci.initializerList[1].initializer.initializerToExpression();
+
+            import dmd.root.ctfloat;
+            auto newExpr = new AddExp(ci.loc, rexp,
+            new MulExp(ci.loc, imexp, new RealExp(ci.loc, CTFloat.one, Type.timaginary64)));
+
+            auto ce = new ExpInitializer(ci.loc, newExpr);
+            return ce.initializerSemantic(sc, t, needInterpret);
+        }
         else
         {
             error(ci.loc, "unrecognized C initializer `%s` for type `%s`", toChars(ci), t.toChars());
@@ -1267,6 +1385,11 @@ Expression initializerToExpression(Initializer init, Type itype = null, const bo
     {
         //printf("ArrayInitializer::toExpression(), dim = %d\n", dim);
         //static int i; if (++i == 2) assert(0);
+        if (!itype || itype.toBasetype().isTypeAArray())
+            if (!init.type || init.type.isTypeAArray())
+                if (init.isAssociativeArray())
+                    return init.toAssocArrayLiteral();
+
         uint edim;      // the length of the resulting array literal
         const(uint) amax = 0x80000000;
         Type t = null;  // type of the array literal being initialized
index f68e302f741d52401c8bd684b5c70c984fe46a3f..9f4cd955bdf4f982826b515412c849a2e1273e11 100644 (file)
@@ -16,6 +16,7 @@ import core.stdc.stdio;
 import dmd.astenums : Tdchar;
 import dmd.mtype : Type;
 import dmd.globals : uinteger_t;
+import dmd.typesem;
 
 private uinteger_t copySign(uinteger_t x, bool sign) @safe
 {
index 080870aa47087b509b1724c2a44bdc25c0c08564..839ac81bb5aa90fee03e21fab0528407c7092fa0 100644 (file)
@@ -25,6 +25,7 @@ import dmd.dimport;
 import dmd.dmodule;
 import dmd.dsymbol;
 import dmd.dsymbolsem : include;
+import dmd.templatesem : computeOneMember;
 import dmd.dtemplate;
 import dmd.errors;
 import dmd.expression;
@@ -474,6 +475,7 @@ public:
     void jsonProperties(TemplateDeclaration td)
     {
         jsonProperties(cast(Dsymbol)td);
+        td.computeOneMember();
         if (td.onemember && td.onemember.isCtorDeclaration())
             property("name", "this"); // __ctor -> this
         else
index 2b8a8ed4987ac3f1d5b85750979d69a03979e7d9..445ddd69d7e03a26ad7c0a6f9d499661e24c4c1b 100644 (file)
@@ -25,7 +25,7 @@ import dmd.dsymbol;
 import dmd.dsymbolsem;
 import dmd.dtemplate;
 import dmd.expression;
-import dmd.expressionsem : getConstInitializer;
+import dmd.expressionsem : getConstInitializer, toInteger;
 import dmd.func;
 import dmd.hdrgen;
 import dmd.mangle;
index 62855fe93f9340888f5fe8fbd7d3b1e2ef7afd4e..fe8b0c8158a3a7dd4dc8431904ab5c40721476f2 100644 (file)
@@ -51,6 +51,7 @@ struct CompileEnv
     bool transitionIn;       /// `-transition=in` is active, `in` parameters are listed
     bool ddocOutput;         /// collect embedded documentation comments
     bool masm;               /// use MASM inline asm syntax
+    const(char)[] switchPrefix;
 
     // these need a default otherwise tests won't work.
     IdentifierCharLookup cCharLookupTable; /// C identifier table (set to the lexer by the C parser)
@@ -324,10 +325,9 @@ class Lexer
         t.blockComment = null;
         t.lineComment = null;
 
-        size_t universalCharacterName4, universalCharacterName8;
-
         while (1)
         {
+            bool startsUCN;
             t.ptr = p;
             //printf("p = %p, *p = '%c'\n",p,*p);
             t.loc = loc();
@@ -427,10 +427,7 @@ class Lexer
                         // Universal Character Name (C) 2 byte
                         // \uXXXX
                         // let the main case handling for identifiers process this
-
-                        // case_indent will always increment, so subtract to prevent branching on the fast path
-                        p--;
-
+                        startsUCN = true;
                         goto case_ident;
                     }
                     else if (p[1] == 'U')
@@ -438,10 +435,7 @@ class Lexer
                         // Universal Character Name (C) 4 byte
                         // \UXXXXXXXX
                         // let the main case handling for identifiers process this
-
-                        // case_indent will always increment, so subtract to prevent branching on the fast path
-                        p--;
-
+                        startsUCN = true;
                         goto case_ident;
                     }
                 }
@@ -629,164 +623,8 @@ class Lexer
             case '_':
             case_ident:
                 {
-        IdentLoop: while (1)
-                    {
-                        // If this is changed, change the decrement in C's universal character name code above
-                        // For syntax \uXXXX and \UXXXXXXXX
-                        const c = *++p;
-
-                        // Is this the first character of the identifier
-                        // For the universal character name this will line up,
-                        //  for the main switch it won't since it wasn't the first,
-                        //  for the default it won't either because a decode increments.
-                        const isStartCharacter = t.ptr is p;
-
-                        if (isidchar(c))
-                            continue;
-                        if (c & 0x80)
-                        {
-                            const s = p;
-                            const u = decodeUTF();
-
-                            if (isStartCharacter)
-                            {
-                                if (charLookup.isStart(u))
-                                    continue;
-                                error(t.loc, "character 0x%04x is not allowed as a start character in an identifier", u);
-                            }
-                            else
-                            {
-                                if (charLookup.isContinue(u))
-                                    continue;
-                                error(t.loc, "character 0x%04x is not allowed as a continue character in an identifier", u);
-                            }
-
-                            p = s;
-                        }
-                        else if (Ccompile && c == '\\')
-                        {
-                            uint times;
-                            const s = p;
-                            p++;
-
-                            if (*p == 'u')
-                            {
-                                // Universal Character Name (C) 2 byte
-                                // \uXXXX
-                                p++;
-                                times = 4;
-                            }
-                            else if (*p == 'U')
-                            {
-                                // Universal Character Name (C) 4 byte
-                                // \UXXXXXXXX
-                                p++;
-                                times = 8;
-                            }
-                            else
-                            {
-                                error(t.loc, "char 0x%x is not allowed to follow '\\' expecting a C universal character name in format \\uXXXX or \\UXXXXXXXX with hex digits instead of X with invalid u/U", *p);
-                                p = s;
-                                break;
-                            }
-
-                            foreach(_; 0 .. times)
-                            {
-                                const hc = *p;
-                                p++;
-
-                                if ((hc >= '0' && hc <= '9') || (hc >= 'a' && hc <= 'f') || (hc >= 'A' && hc <= 'F'))
-                                    continue;
-
-                                error(t.loc, "char 0x%x is not allowed to follow '\\' expecting a C universal character name in format \\uXXXX or \\UXXXXXXXX with hex digits instead of X with invalid hex digit", hc);
-                                p = s;
-                                break IdentLoop;
-                            }
-
-                            continue;
-                        }
-                        break;
-                    }
-
-                    Identifier id;
-
-                    if (universalCharacterName4 > 0 || universalCharacterName8 > 0)
-                    {
-                        auto priorValidation = t.ptr[0 .. p - t.ptr];
-                        const(char)* priorVPtr = priorValidation.ptr;
-                        const possibleLength = (
-                            priorValidation.length - (
-                                (universalCharacterName4 * 6) +
-                                (universalCharacterName8 * 10)
-                            )) + (
-                                (universalCharacterName4 * 3) +
-                                (universalCharacterName8 * 4)
-                            );
-
-                        char[64] buffer = void;
-                        SmallBuffer!char sb = SmallBuffer!char(possibleLength, buffer[]);
-
-                        char[] storage = sb.extent;
-                        size_t offset;
-
-                        while(priorVPtr < &priorValidation[$-1] + 1)
-                        {
-                            if (*priorVPtr == '\\')
-                            {
-                                dchar tempDchar = 0;
-                                uint times;
-
-                                // universal character name (C)
-                                if (priorVPtr[1] == 'u')
-                                    times = 4;
-                                else if (priorVPtr[1] == 'U')
-                                    times = 8;
-                                else
-                                    assert(0, "ICE: Universal character name is 2 or 4 bytes only");
-                                priorVPtr += 2;
-
-                                foreach(_; 0 .. times)
-                                {
-                                    char c = *++priorVPtr;
-                                    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;
-
-                                    tempDchar <<= 4;
-                                    tempDchar |= c;
-                                }
-
-                                utf_encodeChar(&storage[offset], tempDchar);
-                                offset += utf_codeLengthChar(tempDchar);
-
-                                // Could be an error instead of a warning,
-                                //  but hey it was written specifically so why worry?
-                                if (priorVPtr is priorValidation.ptr)
-                                {
-                                    if (!charLookup.isStart(tempDchar))
-                                        warning(t.loc, "char 0x%x is not allowed start character for an identifier", tempDchar);
-                                }
-                                else
-                                {
-                                    if (!charLookup.isContinue(tempDchar))
-                                        warning(t.loc, "char 0x%x is not allowed continue character for an identifier", tempDchar);
-                                }
-                            }
-                            else
-                                storage[offset++] = *++priorVPtr;
-                        }
-
-                        id = Identifier.idPool(storage[0 .. offset], false);
-                    }
-                    else
-                        id = Identifier.idPool((cast(char*)t.ptr)[0 .. p - t.ptr], false);
-
-                    t.ident = id;
-                    t.value = cast(TOK)id.getValue();
-
+                    if (!lexIdentifier(t, startsUCN))
+                        goto default;
                     anyToken = 1;
 
                     /* Different keywords for C and D
@@ -811,20 +649,20 @@ class Lexer
                             t.postfix = 0;
                         }
 
-                        if (id == Id.DATE)
+                        if (t.ident == Id.DATE)
                             toToken(compileEnv.date);
-                        else if (id == Id.TIME)
+                        else if (t.ident == Id.TIME)
                             toToken(compileEnv.time);
-                        else if (id == Id.VENDOR)
+                        else if (t.ident == Id.VENDOR)
                             toToken(compileEnv.vendor);
-                        else if (id == Id.TIMESTAMP)
+                        else if (t.ident == Id.TIMESTAMP)
                             toToken(compileEnv.timestamp);
-                        else if (id == Id.VERSIONX)
+                        else if (t.ident == Id.VERSIONX)
                         {
                             t.value = TOK.int64Literal;
                             t.unsvalue = compileEnv.versionNumber;
                         }
-                        else if (id == Id.EOFX)
+                        else if (t.ident == Id.EOFX)
                         {
                             t.value = TOK.endOfFile;
                             // Advance scanner to end of file
@@ -1358,8 +1196,10 @@ class Lexer
 
                         // Check for start of an identifier
                         if (charLookup.isStart(c))
+                        {
+                            p++;
                             goto case_ident;
-
+                        }
                         if (c == PS || c == LS)
                         {
                             endOfLine();
@@ -1630,6 +1470,118 @@ class Lexer
         return c;
     }
 
+    /**
+    Lex an identifier
+    Params:
+        t = pointer to the token that starts the identifier, and accepts the result
+        startsUCN = if the identifier s
+    Returns:
+        true if an identifier was set
+     */
+    private bool lexIdentifier(Token* t, bool startsUCN)
+    {
+        Identifier id;
+
+        if (!startsUCN)
+        {
+            while (isidchar(*p))
+                p++;
+        }
+
+        if (!startsUCN && !(*p & 0x80) && (!Ccompile || *p != '\\'))
+        {
+            // Fast path for ascii identifiers
+            id = Identifier.idPool((cast(char*)t.ptr)[0 .. p - t.ptr], false);
+        }
+        else
+        {
+            // Slow path for identifiers with UCNs and UTF8 characters
+            stringbuffer.setsize(0);
+            stringbuffer.writestring(t.ptr[0 .. p - t.ptr]);
+
+        IdentLoop:
+            while (1)
+            {
+                // Is this the first character of the identifier?
+                const isStartCharacter = t.ptr is p;
+                uint u;
+
+                if (isidchar(*p))
+                {
+                    const c = *p++;
+                    stringbuffer.writeByte(c);
+                    continue;
+                }
+                else if (*p & 0x80)
+                {
+                    u = decodeUTF();
+                    p++;
+                }
+                else if (Ccompile && *p == '\\')
+                {
+                    uint times;
+                    if (p[1] == 'u')
+                    {
+                        // Universal Character Name (C) 2 byte
+                        // \uXXXX
+                        times = 4;
+                    }
+                    else if (p[1] == 'U')
+                    {
+                        // Universal Character Name (C) 4 byte
+                        // \UXXXXXXXX
+                        times = 8;
+                    }
+                    else
+                        break;  // end of identifier
+
+                    p += 2;
+
+                    foreach(i; 0 .. times)
+                    {
+                        char c = p[i];
+
+                        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
+                        {
+                            // Not a valid ucn, if this is the first character, return false now.
+                            // Otherwise break to return the identifier lexed so far.
+                            p -= 2;
+                            if (isStartCharacter)
+                            {
+                                return false;
+                            }
+                            break IdentLoop;
+                        }
+                        u = (u << 4) | c;
+                    }
+                    if (isBidiControl(u))
+                        error(t.loc, "Bidirectional control characters in universal character names are disallowed for security reasons");
+                    p += times;
+                }
+                else
+                    break;  // end of identifier
+
+                if (isStartCharacter && !charLookup.isStart(u))
+                    error(t.loc, "character 0x%04x is not allowed as a start character in an identifier", u);
+                else if (!charLookup.isContinue(u))
+                    error(t.loc, "character 0x%04x is not allowed as a continue character in an identifier", u);
+                stringbuffer.writeUTF8(u);
+            }
+
+            id = Identifier.idPool(stringbuffer[], false);
+        }
+
+        t.ident = id;
+        t.value = cast(TOK)id.getValue();
+        return true;
+    }
+
     /**
     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
@@ -2283,7 +2235,13 @@ class Lexer
 
             if (*p == '\'')
             {
-                error("character constant has multiple characters");
+                const(char)* s = p - 1;
+                while(*s != '\'')
+                {
+                    s--;
+                }
+                s++;
+                error("character constant has multiple characters - did you mean \"%.*s\"?", cast(int) (p - s), s);
                 p++;
             }
             else
index 8a429037dfce6b17aee5933a207635d96ba8b04e..ae0e60bd24fdfff16b9063374f92c8153ad6c9e8 100644 (file)
@@ -26,7 +26,9 @@ import dmd.astenums;
 import dmd.attrib;
 import dmd.declaration;
 import dmd.dsymbol;
-import dmd.dsymbolsem : isGNUABITag, toAlias;
+import dmd.dsymbolsem : isGNUABITag, toAlias, equals;
+import dmd.expressionsem : toStringExp, toInteger, toUInteger;
+import dmd.templatesem : computeOneMember;
 import dmd.dtemplate;
 import dmd.errors;
 import dmd.expression;
@@ -509,6 +511,10 @@ private final class CppMangleVisitor : Visitor
             // `&function`
             Dsymbol d = isDsymbol(o);
             Expression e = isExpression(o);
+
+            if (d && d.isTemplateDeclaration())
+                d.isTemplateDeclaration().computeOneMember();
+
             if (d && d.isFuncDeclaration())
             {
                 // X .. E => template parameter is an expression
index a6a80b7be7be4a01df9a77a33f62d4826a3b01a5..8b68ac87a4996202efb8d433f056bd6920e2c71e 100644 (file)
@@ -146,6 +146,7 @@ import dmd.dinterpret;
 import dmd.dmodule;
 import dmd.dsymbol;
 import dmd.dsymbolsem : toAlias;
+import dmd.expressionsem : toInteger, toReal, toImaginary;
 import dmd.dtemplate;
 import dmd.errors;
 import dmd.expression;
index c81b6d7531ef0555cb6cc6e7392ebe6b0c7a10ea..15101c20395037a2280602deda821dc7b325acca 100644 (file)
@@ -30,13 +30,6 @@ enum PKG
     PKGpackage  // already determined that's an actual package
 };
 
-enum class Edition : uint16_t
-{
-    v2023 = 2023,
-    v2024,
-    v2025,
-};
-
 class Package : public ScopeDsymbol
 {
 public:
@@ -46,8 +39,6 @@ public:
 
     const char *kind() const override;
 
-    bool equals(const RootObject * const o) const override;
-
     bool isAncestorPackageOf(const Package * const pkg) const;
 
     void accept(Visitor *v) override { v->visit(this); }
@@ -131,9 +122,6 @@ public:
     int needModuleInfo();
     bool isPackageAccessible(Package *p, Visibility visibility, SearchOptFlags flags = (SearchOptFlags)SearchOpt::all) override;
     Dsymbol *symtabInsert(Dsymbol *s) override;
-    static void runDeferredSemantic();
-    static void runDeferredSemantic2();
-    static void runDeferredSemantic3();
     int imports(Module *m);
 
     bool isRoot() { return this->importedFrom == this; }
index c11ce9d390054d3f35921453c314686769273e42..d562c103d008f7c4bce7d915403bf6e0c542e84c 100644 (file)
@@ -17,7 +17,6 @@ import core.stdc.stdio;
 import core.stdc.stdlib;
 import core.stdc.string;
 
-import dmd.aggregate;
 import dmd.arraytypes;
 import dmd.astenums;
 import dmd.ast_node;
@@ -27,21 +26,16 @@ import dmd.denum;
 import dmd.dstruct;
 import dmd.dsymbol;
 import dmd.dtemplate;
-import dmd.enumsem;
-import dmd.errors;
 import dmd.expression;
 import dmd.hdrgen;
 import dmd.id;
 import dmd.identifier;
 import dmd.location;
-import dmd.root.ctfloat;
 import dmd.common.outbuffer;
 import dmd.root.rmem;
 import dmd.rootobject;
 import dmd.root.stringtable;
-import dmd.target;
 import dmd.tokens;
-import dmd.typesem;
 import dmd.visitor;
 
 enum LOGDOTEXP = 0;         // log ::dotExp()
@@ -55,11 +49,133 @@ static if (__VERSION__ < 2095)
     private alias StringValueType = StringValue!Type;
 }
 
-private auto X(T, U)(T m, U n)
+private auto X(T, U)(T m, U n) nothrow
 {
     return (m << 4) | n;
 }
 
+bool isImaginaryNonSemantic(Type _this)
+{
+    assert(_this.isTypeEnum() is null);
+    if (auto tb = _this.isTypeBasic())
+        return (tb.flags & TFlags.imaginary) != 0;
+    return false;
+}
+
+Type toBaseTypeNonSemantic(Type _this)
+{
+    if (auto te = _this.isTypeEnum())
+    {
+        if (!te.sym.members && !te.sym.memtype)
+            return te;
+        return te.sym.memtype.toBaseTypeNonSemantic();
+    }
+    return _this;
+}
+
+/* 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", EXPtoString(e.op).ptr, e.toChars());
+    foreach (id; t.idents[i .. t.idents.length])
+    {
+        //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;
+}
+
+/******************************************
+ * 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(t.isTypeSArray());
+        case Taarray:   return visitAArray(t.isTypeAArray());
+        case Tident:    return visitIdentifier(t.isTypeIdentifier());
+        case Tinstance: return visitInstance(t.isTypeInstance());
+        case Tmixin:    return visitMixin(t.isTypeMixin());
+        default:        return null;
+    }
+}
+
 /***************************
  * Return !=0 if modfrom can be implicitly converted to modto
  */
@@ -273,6 +389,7 @@ enum Covariant
  */
 extern (C++) abstract class Type : ASTNode
 {
+
     TY ty;
     MOD mod; // modifiers MODxxxx
     char* deco;
@@ -401,7 +518,7 @@ extern (C++) abstract class Type : ASTNode
             return sizeTy;
         }();
 
-    final extern (D) this(TY ty) scope @safe
+    final extern (D) this(TY ty) scope @safe nothrow
     {
         this.ty = ty;
     }
@@ -424,13 +541,31 @@ extern (C++) abstract class Type : ASTNode
         assert(0);
     }
 
-    override bool equals(const RootObject o) const
+    final bool equals(const Type t) const nothrow
     {
-        Type t = cast(Type)o;
         //printf("Type::equals(%s, %s)\n", toChars(), t.toChars());
+        if (this == t)
+            return true;
+        if (ty == Ttuple)
+        {
+            if (t.ty != Ttuple)
+                return false;
+            auto t1 = this.isTypeTuple();
+            auto t2 = t.isTypeTuple();
+            if (t1.arguments.length != t2.arguments.length)
+                return false;
+            for (size_t i = 0; i < t1.arguments.length; i++)
+            {
+                const Parameter arg1 = (*t1.arguments)[i];
+                const Parameter arg2 = (*t2.arguments)[i];
+                if (!arg1.type.equals(arg2.type))
+                    return false;
+            }
+            return true;
+        }
         // deco strings are unique
         // and semantic() has been run
-        if (this == o || ((t && deco == t.deco) && deco !is null))
+        if ((t && deco == t.deco) && deco !is null)
         {
             //printf("deco = '%s', t.deco = '%s'\n", deco, t.deco);
             return true;
@@ -440,20 +575,20 @@ extern (C++) abstract class Type : ASTNode
     }
 
     // kludge for template.isType()
-    override final DYNCAST dyncast() const
+    override final DYNCAST dyncast() const nothrow
     {
         return DYNCAST.type;
     }
 
     /// Returns a non-zero unique ID for this Type, or returns 0 if the Type does not (yet) have a unique ID.
     /// If `semantic()` has not been run, 0 is returned.
-    final size_t getUniqueID() const
+    final size_t getUniqueID() const nothrow
     {
         return cast(size_t) deco;
     }
 
     extern (D)
-    final Mcache* getMcache()
+    final Mcache* getMcache() nothrow
     {
         if (!mcache)
             mcache = cast(Mcache*) mem.xcalloc(Mcache.sizeof, 1);
@@ -480,142 +615,17 @@ extern (C++) abstract class Type : ASTNode
         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
-        ];
-
-        static Type merge(Type t)
-        {
-            import dmd.mangle.basic : tyToDecoBuffer;
-
-            OutBuffer buf;
-            buf.reserve(3);
-
-            if (t.ty == Tnoreturn)
-                buf.writestring("Nn");
-            else
-                tyToDecoBuffer(buf, t.ty);
-
-            auto sv = t.stringtable.update(buf[]);
-            if (sv.value)
-                return sv.value;
-            t.deco = cast(char*)sv.toDchars();
-            sv.value = t;
-            return t;
-        }
-
-        for (size_t i = 0; basetab[i] != Terror; i++)
-        {
-            Type t = new TypeBasic(basetab[i]);
-            t = merge(t);
-            basic[basetab[i]] = t;
-        }
-        basic[Terror] = new TypeError();
-
-        tnoreturn = new TypeNoreturn();
-        tnoreturn.deco = merge(tnoreturn).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 = merge(tnull).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;
-
-        static if (__VERSION__ == 2081)
-        {
-            // Related issue: https://issues.dlang.org/show_bug.cgi?id=19134
-            // D 2.081.x regressed initializing class objects at compile time.
-            // As a workaround initialize this global at run-time instead.
-            TypeTuple.empty = new TypeTuple();
-        }
-    }
-
     /**
      * 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()
+    static void deinitialize() nothrow
     {
         stringtable = stringtable.init;
     }
 
-    uint alignsize()
-    {
-        return cast(uint)size(this, Loc.initial);
-    }
-
     /*********************************
      * Store this type's modifier name into buf.
      */
@@ -639,60 +649,11 @@ extern (C++) abstract class Type : ASTNode
         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 isScopeClass()
-    {
-        return false;
-    }
-
-    bool isString()
+    bool isScopeClass() nothrow
     {
         return false;
     }
 
-    /**************************
-     * Returns true if T can be converted to boolean value.
-     */
-    bool isBoolean()
-    {
-        return isScalar();
-    }
-
     final bool isConst() const nothrow pure @nogc @safe
     {
         return (mod & MODFlags.const_) != 0;
@@ -759,785 +720,128 @@ extern (C++) abstract class Type : ASTNode
         return t;
     }
 
-    /**********************************
-     * For our new type 'this', which is type-constructed from t,
-     * fill in the cto, ito, sto, scto, wto shortcuts.
-     */
-    extern (D) final void fixTo(Type t)
+    inout(ClassDeclaration) isClassHandle() inout
     {
-        // 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;
+        return null;
+    }
 
-            case MODFlags.immutable_:
-                getMcache();
-                mcache.ito = t;
-                break;
+    /***************************************
+     * Return !=0 if the type or any of its subtypes is wild.
+     */
+    int hasWild() const
+    {
+        return mod & MODFlags.wild;
+    }
 
-            default:
-                break;
-            }
-        }
-        assert(mod != t.mod);
+    // For eliminating dynamic_cast
+    TypeBasic isTypeBasic() nothrow
+    {
+        return null;
+    }
 
-        if (mod)
+    final pure inout nothrow @nogc
+    {
+        /****************
+         * Is this type a pointer to a function?
+         * Returns:
+         *  the function type if it is
+         */
+        inout(TypeFunction) isPtrToFunction()
         {
-            getMcache();
-            t.getMcache();
+            return (ty == Tpointer && (cast(TypePointer)this).next.ty == Tfunction)
+                ? cast(typeof(return))(cast(TypePointer)this).next
+                : null;
         }
-        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;
+        /*****************
+         * 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 :
 
-        case MODFlags.wildconst:
-            mcache.wcto = mto;
-            t.mcache.wcto = this;
-            break;
+                   ty == Tdelegate ? cast(typeof(return))(cast(TypePointer)this).next :
 
-        case MODFlags.shared_:
-            mcache.sto = mto;
-            t.mcache.sto = this;
-            break;
+                   ty == Tpointer && (cast(TypePointer)this).next.ty == Tfunction ?
+                        cast(typeof(return))(cast(TypePointer)this).next :
 
-        case MODFlags.shared_ | MODFlags.const_:
-            mcache.scto = mto;
-            t.mcache.scto = this;
-            break;
+                   null;
+        }
+    }
 
-        case MODFlags.shared_ | MODFlags.wild:
-            mcache.swto = mto;
-            t.mcache.swto = this;
-            break;
+    final pure inout nothrow @nogc @trusted
+    {
+        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; }
 
-        case MODFlags.shared_ | MODFlags.wildconst:
-            mcache.swcto = mto;
-            t.mcache.swcto = this;
-            break;
+        extern (D) bool isStaticOrDynamicArray() const { return ty == Tarray || ty == Tsarray; }
+    }
 
-        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;
+    override void accept(Visitor v)
+    {
+        v.visit(this);
+    }
 
-        default:
+    final TypeFunction toTypeFunction() nothrow
+    {
+        if (ty != Tfunction)
             assert(0);
-        }
-
-        check();
-        t.check();
-        //printf("fixTo: %s, %s\n", toChars(), t.toChars());
+        return cast(TypeFunction)this;
     }
 
-    /***************************
-     * Look for bugs in constructing types.
-     */
-    extern (D) final void check()
+    extern (D) static Types* arraySyntaxCopy(Types* types)
     {
-        if (mcache)
-        with (mcache)
-        switch (mod)
+        Types* a = null;
+        if (types)
         {
-        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;
+            a = new Types(types.length);
+            foreach (i, t; *types)
+            {
+                (*a)[i] = t ? t.syntaxCopy() : null;
+            }
+        }
+        return a;
+    }
+}
 
-        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;
+/***********************************************************
+ */
+extern (C++) final class TypeError : Type
+{
+    extern (D) this() @safe
+    {
+        super(Terror);
+    }
 
-        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;
+    override const(char)* kind() const nothrow
+    {
+        return "error";
+    }
 
-        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.
-     */
-    extern (D) final Type addSTC(STC stc)
-    {
-        Type t = this;
-        if (t.isImmutable())
-        {
-            return t;
-        }
-        else if (stc & STC.immutable_)
-        {
-            t = t.makeImmutable();
-            return t;
-        }
-
-        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;
-    }
-
-    final bool hasDeprecatedAliasThis()
-    {
-        auto ad = isAggregate(this);
-        return ad && ad.aliasthis && (ad.aliasthis.isDeprecated || ad.aliasthis.sym.isDeprecated);
-    }
-
-    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;
-    }
-
-    /*******************************
-     * 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;
-    }
-
-    /***************************************
-     * 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_;
-            if (isWildConst())
-            {
-                if (t.isWildConst())
-                    return MODFlags.wild;
-                return MODFlags.wildconst;
-            }
-            if (isWild())
-                return MODFlags.wild;
-            if (isConst())
-                return MODFlags.const_;
-            if (isMutable())
-                return MODFlags.mutable;
-            assert(0);
-        }
-        return 0;
-    }
-
-    inout(ClassDeclaration) isClassHandle() inout
-    {
-        return null;
-    }
-
-    /************************************
-     * Return alignment to use for this type.
-     */
-    structalign_t alignment()
-    {
-        structalign_t s;
-        s.setDefault();
-        return s;
-    }
-
-    /***************************************
-     * Return !=0 if the type or any of its subtypes is wild.
-     */
-    int hasWild() const
-    {
-        return mod & MODFlags.wild;
-    }
-
-    /*************************************
-     * 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;
-    }
-
-    /*************************************
-     * Detect if this is an unsafe type because of the presence of `@system` members
-     * Returns:
-     *  true if so
-     */
-    bool hasUnsafeBitpatterns()
-    {
-        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;
-    }
-
-    /****************************************
-     * Return the mask that an integral type will
-     * fit into.
-     */
-    extern (D) final ulong sizemask()
-    {
-        ulong 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;
-    }
-
-    // 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 @trusted
-    {
-        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; }
-
-        extern (D) bool isStaticOrDynamicArray() const { return ty == Tarray || ty == Tsarray; }
-    }
-
-    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() @safe
-    {
-        super(Terror);
-    }
-
-    override const(char)* kind() const
-    {
-        return "error";
-    }
-
-    override TypeError syntaxCopy()
+    override TypeError syntaxCopy() nothrow
     {
         // No semantic analysis done, no need to copy
         return this;
@@ -1570,242 +874,6 @@ extern (C++) abstract class TypeNext : Type
         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 been run
-     * on the function. After semantic(), it must no longer be NULL.
-     */
-    override final Type nextOf() @safe
-    {
-        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 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);
@@ -1940,74 +1008,27 @@ extern (C++) final class TypeBasic : Type
             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 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()
-    {
-        return (flags & TFlags.floating) != 0;
-    }
-
-    override bool isReal()
-    {
-        return (flags & TFlags.real_) != 0;
-    }
-
-    override bool isImaginary()
-    {
-        return (flags & TFlags.imaginary) != 0;
-    }
-
-    override bool isComplex()
-    {
-        return (flags & TFlags.complex) != 0;
-    }
+        case Tdchar:
+            d = Token.toChars(TOK.dchar_);
+            flags |= TFlags.integral | TFlags.unsigned;
+            break;
 
-    override bool isScalar()
-    {
-        return (flags & (TFlags.integral | TFlags.floating)) != 0;
+        default:
+            assert(0);
+        }
+        this.dstring = d;
+        this.flags = flags;
     }
 
-    override bool isUnsigned()
+    override const(char)* kind() const
     {
-        return (flags & TFlags.unsigned) != 0;
+        return dstring;
     }
 
-    override bool hasUnsafeBitpatterns()
+    override TypeBasic syntaxCopy()
     {
-        return ty == Tbool;
+        // No semantic analysis done on basic types, no need to copy
+        return this;
     }
 
     // For eliminating dynamic_cast
@@ -2053,42 +1074,11 @@ extern (C++) final class TypeVector : Type
         return new TypeVector(basetype.syntaxCopy());
     }
 
-    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()
-    {
-        return false;
-    }
-
     TypeBasic elementType()
     {
         assert(basetype.ty == Tsarray);
         TypeSArray t = cast(TypeSArray)basetype;
-        TypeBasic tb = t.nextOf().isTypeBasic();
+        TypeBasic tb = t.next.isTypeBasic();
         assert(tb);
         return tb;
     }
@@ -2158,55 +1148,6 @@ extern (C++) final class TypeSArray : TypeArray
         return dim.isIntegerExp() && dim.isIntegerExp().getInteger() == 0;
     }
 
-    override uint alignsize()
-    {
-        return next.alignsize();
-    }
-
-    override bool isString()
-    {
-        TY nty = next.toBasetype().ty;
-        return nty.isSomeChar;
-    }
-
-    override structalign_t alignment()
-    {
-        return next.alignment();
-    }
-
-    override bool hasUnsafeBitpatterns()
-    {
-        return next.hasUnsafeBitpatterns();
-    }
-
-    override bool hasVoidInitPointers()
-    {
-        return next.hasVoidInitPointers();
-    }
-
-    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);
@@ -2240,24 +1181,6 @@ extern (C++) final class TypeDArray : TypeArray
         return result;
     }
 
-    override uint alignsize()
-    {
-        // 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 isBoolean()
-    {
-        return true;
-    }
-
     override void accept(Visitor v)
     {
         v.visit(this);
@@ -2299,11 +1222,6 @@ extern (C++) final class TypeAArray : TypeArray
         return result;
     }
 
-    override bool isBoolean()
-    {
-        return true;
-    }
-
     override void accept(Visitor v)
     {
         v.visit(this);
@@ -2340,11 +1258,6 @@ extern (C++) final class TypePointer : TypeNext
         return result;
     }
 
-    override bool isScalar()
-    {
-        return true;
-    }
-
     override void accept(Visitor v)
     {
         v.visit(this);
@@ -2603,16 +1516,6 @@ extern (C++) final class TypeDelegate : TypeNext
         return result;
     }
 
-    override uint alignsize()
-    {
-        return target.ptrsize;
-    }
-
-    override bool isBoolean()
-    {
-        return true;
-    }
-
     override void accept(Visitor v)
     {
         v.visit(this);
@@ -2926,99 +1829,11 @@ extern (C++) final class TypeStruct : Type
         return "struct";
     }
 
-    override uint alignsize()
-    {
-        sym.size(Loc.initial); // give error for forward references
-        return sym.alignsize;
-    }
-
     override TypeStruct syntaxCopy()
     {
         return this;
     }
 
-    override structalign_t alignment()
-    {
-        if (sym.alignment.isUnknown())
-            sym.size(sym.loc);
-        return sym.alignment;
-    }
-
-    override bool isBoolean()
-    {
-        return false;
-    }
-
-    override bool needsDestruction()
-    {
-        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.length; i++)
-        {
-            VarDeclaration v = sym.fields[i];
-            if (!v.isDataseg() && v.type.needsNested())
-                return true;
-        }
-        return false;
-    }
-
-    override bool hasVoidInitPointers()
-    {
-        sym.size(Loc.initial); // give error for forward references
-        sym.determineTypeProperties();
-        return sym.hasVoidInitPointers;
-    }
-
-    override bool hasUnsafeBitpatterns()
-    {
-        sym.size(Loc.initial); // give error for forward references
-        sym.determineTypeProperties();
-        return sym.hasUnsafeBitpatterns;
-    }
-
-    override bool hasInvariant()
-    {
-        sym.size(Loc.initial); // give error for forward references
-        sym.determineTypeProperties();
-        return sym.hasInvariant() || sym.hasFieldWithInvariant;
-    }
-
-    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(this))
-            {
-                att = cast(AliasThisRec)(att | AliasThisRec.tracing);
-                wm = ato.deduceWild(t, isRef);
-                att = cast(AliasThisRec)(att & ~AliasThisRec.tracing);
-            }
-        }
-
-        return wm;
-    }
-
     override void accept(Visitor v)
     {
         v.visit(this);
@@ -3047,107 +1862,6 @@ extern (C++) final class TypeEnum : Type
         return this;
     }
 
-    Type memType()
-    {
-        return sym.getMemtype(Loc.initial);
-    }
-
-    override uint alignsize()
-    {
-        Type t = memType();
-        if (t.ty == Terror)
-            return 4;
-        return t.alignsize();
-    }
-
-    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 needsDestruction()
-    {
-        return memType().needsDestruction();
-    }
-
-    override bool needsCopyOrPostblit()
-    {
-        return memType().needsCopyOrPostblit();
-    }
-
-    override bool needsNested()
-    {
-        return memType().needsNested();
-    }
-
-    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 hasVoidInitPointers()
-    {
-        return memType().hasVoidInitPointers();
-    }
-
-    override bool hasUnsafeBitpatterns()
-    {
-        return memType().hasUnsafeBitpatterns();
-    }
-
-    override bool hasInvariant()
-    {
-        return memType().hasInvariant();
-    }
-
-    override Type nextOf()
-    {
-        return memType().nextOf();
-    }
-
     override void accept(Visitor v)
     {
         v.visit(this);
@@ -3183,37 +1897,11 @@ extern (C++) final class TypeClass : Type
         return sym;
     }
 
-    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(this))
-            {
-                att = cast(AliasThisRec)(att | AliasThisRec.tracing);
-                wm = ato.deduceWild(t, isRef);
-                att = cast(AliasThisRec)(att & ~AliasThisRec.tracing);
-            }
-        }
-
-        return wm;
-    }
-
     override bool isScopeClass()
     {
         return sym.stack;
     }
 
-    override bool isBoolean()
-    {
-        return true;
-    }
-
     override void accept(Visitor v)
     {
         v.visit(this);
@@ -3331,29 +2019,6 @@ extern (C++) final class TypeTuple : Type
         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.length == tt.arguments.length)
-            {
-                for (size_t i = 0; i < tt.arguments.length; 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);
@@ -3415,11 +2080,6 @@ extern (C++) final class TypeNull : Type
         return this;
     }
 
-    override bool isBoolean()
-    {
-        return true;
-    }
-
     override void accept(Visitor v)
     {
         v.visit(this);
@@ -3447,16 +2107,6 @@ extern (C++) final class TypeNoreturn : Type
         return this;
     }
 
-    override bool isBoolean()
-    {
-        return true;  // bottom type can be implicitly converted to any other type
-    }
-
-    override uint alignsize()
-    {
-        return 0;
-    }
-
     override void accept(Visitor v)
     {
         v.visit(this);
@@ -3656,33 +2306,6 @@ extern (C++) final class Parameter : ASTNode
         return new Parameter(loc, 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.isStaticOrDynamicArray())
-        {
-            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 lazy
     bool isLazy() const @safe pure nothrow @nogc
     {
@@ -3815,8 +2438,7 @@ extern (C++) final class Parameter : ASTNode
         if (eparam is null)
             return 0;
 
-        Type t = eparam.type.toBasetype();
-        if (auto tu = t.isTypeTuple())
+        if (auto tu = eparam.type.isTypeTuple())
         {
             // Check for empty tuples
             if (tu.arguments is null)
@@ -4050,35 +2672,6 @@ void attributesApply(const TypeFunction tf, void delegate(string) dg, TRUSTforma
     dg(trustToString(trustAttrib));
 }
 
-/**
- * If the type is a class or struct, returns the symbol for it,
- * else null.
- */
-AggregateDeclaration isAggregate(Type t)
-{
-    t = t.toBasetype();
-    if (auto tc = t.isTypeClass())
-        return tc.sym;
-    if (auto ts = t.isTypeStruct())
-        return ts.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.isStaticOrDynamicArray() || t.ty == Taarray ||
-            t.ty == Ttuple || t.ty == Tvector);
-}
-
 /***************************************
  * Computes how a parameter may be returned.
  * Shrinking the representation is necessary because STC is so wide
index 7f9c026918f62ed3b41fa3910860a33807a62b0b..3a78228f7f94cbdfa83feca679d81424f442463b 100644 (file)
@@ -42,9 +42,13 @@ typedef struct TYPE type;
 
 namespace dmd
 {
+    void Type_init();
     Type *typeSemantic(Type *t, Loc loc, Scope *sc);
     Type *merge(Type *type);
     Expression *defaultInitLiteral(Type *t, Loc loc);
+    Type *toBasetype(Type *type);
+    Type *nextOf(Type* type);
+    Type *baseElemOf(Type* type);
 }
 
 enum class TY : uint8_t
@@ -215,31 +219,23 @@ public:
 
     static Type *basic[(int)TY::TMAX];
 
+    static void _init() { return dmd::Type_init(); }
+
     virtual const char *kind();
     Type *copy() const;
     virtual Type *syntaxCopy();
-    bool equals(const RootObject * const o) const override;
+    bool equals(const Type * const t) const;
     // kludge for template.isType()
     DYNCAST dyncast() const override final { return DYNCAST_TYPE; }
     size_t getUniqueID() const;
     const char *toChars() const override;
     char *toPrettyChars(bool QualifyTypes = false);
-    static void _init();
 
-    virtual unsigned alignsize();
     void modToBuffer(OutBuffer& buf) const;
     char *modToChars() const;
 
-    virtual bool isIntegral();
-    virtual bool isFloating();   // real, imaginary, or complex
-    virtual bool isReal();
-    virtual bool isImaginary();
-    virtual bool isComplex();
-    virtual bool isScalar();
-    virtual bool isUnsigned();
     virtual bool isScopeClass();
-    virtual bool isString();
-    virtual bool isBoolean();
+
     bool isConst() const       { return (mod & MODconst) != 0; }
     bool isImmutable() const   { return (mod & MODimmutable) != 0; }
     bool isMutable() const     { return (mod & (MODconst | MODimmutable | MODwild)) == 0; }
@@ -250,30 +246,13 @@ public:
     bool isSharedWild() const  { return (mod & (MODshared | MODwild)) == (MODshared | MODwild); }
     bool isNaked() const       { return mod == 0; }
     Type *nullAttributes() const;
-    bool hasDeprecatedAliasThis();
-    virtual Type *makeConst();
-    virtual Type *makeImmutable();
-    virtual Type *makeShared();
-    virtual Type *makeSharedConst();
-    virtual Type *makeWild();
-    virtual Type *makeWildConst();
-    virtual Type *makeSharedWild();
-    virtual Type *makeSharedWildConst();
-    virtual Type *makeMutable();
-    Type *toBasetype();
-    virtual unsigned char deduceWild(Type *t, bool isRef);
+
+    Type *toBasetype() { return dmd::toBasetype(this); }
+    Type *nextOf()     { return dmd::nextOf(this); }
+    Type *baseElemOf() { return dmd::baseElemOf(this); }
 
     virtual ClassDeclaration *isClassHandle();
-    virtual structalign_t alignment();
     virtual int hasWild() const;
-    virtual bool hasVoidInitPointers();
-    virtual bool hasUnsafeBitpatterns();
-    virtual bool hasInvariant();
-    virtual Type *nextOf();
-    Type *baseElemOf();
-    virtual bool needsDestruction();
-    virtual bool needsCopyOrPostblit();
-    virtual bool needsNested();
 
     TypeFunction *toTypeFunction();
 
@@ -323,18 +302,6 @@ public:
     Type *next;
 
     int hasWild() const override final;
-    Type *nextOf() override final;
-    Type *makeConst() override final;
-    Type *makeImmutable() override final;
-    Type *makeShared() override final;
-    Type *makeSharedConst() override final;
-    Type *makeWild() override final;
-    Type *makeWildConst() override final;
-    Type *makeSharedWild() override final;
-    Type *makeSharedWildConst() override final;
-    Type *makeMutable() override final;
-    unsigned char deduceWild(Type *t, bool isRef) override final;
-    void transitive();
     void accept(Visitor *v) override { v->visit(this); }
 };
 
@@ -346,14 +313,6 @@ public:
 
     const char *kind() override;
     TypeBasic *syntaxCopy() override;
-    unsigned alignsize() override;
-    bool isIntegral() override;
-    bool isFloating() override;
-    bool isReal() override;
-    bool isImaginary() override;
-    bool isComplex() override;
-    bool isScalar() override;
-    bool isUnsigned() override;
 
     // For eliminating dynamic_cast
     TypeBasic *isTypeBasic() override;
@@ -368,12 +327,6 @@ public:
     static TypeVector *create(Type *basetype);
     const char *kind() override;
     TypeVector *syntaxCopy() override;
-    unsigned alignsize() override;
-    bool isIntegral() override;
-    bool isFloating() override;
-    bool isScalar() override;
-    bool isUnsigned() override;
-    bool isBoolean() override;
     TypeBasic *elementType();
 
     void accept(Visitor *v) override { v->visit(this); }
@@ -394,15 +347,6 @@ public:
     const char *kind() override;
     TypeSArray *syntaxCopy() override;
     bool isIncomplete();
-    unsigned alignsize() override;
-    bool isString() override;
-    structalign_t alignment() override;
-    bool hasUnsafeBitpatterns() override;
-    bool hasVoidInitPointers() override;
-    bool hasInvariant() override;
-    bool needsDestruction() override;
-    bool needsCopyOrPostblit() override;
-    bool needsNested() override;
 
     void accept(Visitor *v) override { v->visit(this); }
 };
@@ -413,9 +357,6 @@ class TypeDArray final : public TypeArray
 public:
     const char *kind() override;
     TypeDArray *syntaxCopy() override;
-    unsigned alignsize() override;
-    bool isString() override;
-    bool isBoolean() override;
 
     void accept(Visitor *v) override { v->visit(this); }
 };
@@ -429,7 +370,6 @@ public:
     static TypeAArray *create(Type *t, Type *index);
     const char *kind() override;
     TypeAArray *syntaxCopy() override;
-    bool isBoolean() override;
 
     void accept(Visitor *v) override { v->visit(this); }
 };
@@ -440,7 +380,6 @@ public:
     static TypePointer *create(Type *t);
     const char *kind() override;
     TypePointer *syntaxCopy() override;
-    bool isScalar() override;
 
     void accept(Visitor *v) override { v->visit(this); }
 };
@@ -494,7 +433,6 @@ public:
     static Parameter *create(Loc loc, StorageClass storageClass, Type *type, Identifier *ident,
                              Expression *defaultArg, UserAttributeDeclaration *userAttribDecl);
     Parameter *syntaxCopy();
-    Type *isLazyArray();
     bool isLazy() const;
     bool isReference() const;
     // kludge for template.isType()
@@ -579,8 +517,6 @@ public:
     static TypeDelegate *create(TypeFunction *t);
     const char *kind() override;
     TypeDelegate *syntaxCopy() override;
-    unsigned alignsize() override;
-    bool isBoolean() override;
 
     void accept(Visitor *v) override { v->visit(this); }
 };
@@ -684,17 +620,7 @@ public:
 
     static TypeStruct *create(StructDeclaration *sym);
     const char *kind() override;
-    unsigned alignsize() override;
     TypeStruct *syntaxCopy() override;
-    structalign_t alignment() override;
-    bool isBoolean() override;
-    bool needsDestruction() override;
-    bool needsCopyOrPostblit() override;
-    bool needsNested() override;
-    bool hasVoidInitPointers() override;
-    bool hasUnsafeBitpatterns() override;
-    bool hasInvariant() override;
-    unsigned char deduceWild(Type *t, bool isRef) override;
 
     void accept(Visitor *v) override { v->visit(this); }
 };
@@ -706,24 +632,6 @@ public:
 
     const char *kind() override;
     TypeEnum *syntaxCopy() override;
-    unsigned alignsize() override;
-    Type *memType(Loc loc);
-    bool isIntegral() override;
-    bool isFloating() override;
-    bool isReal() override;
-    bool isImaginary() override;
-    bool isComplex() override;
-    bool isScalar() override;
-    bool isUnsigned() override;
-    bool isBoolean() override;
-    bool isString() override;
-    bool needsDestruction() override;
-    bool needsCopyOrPostblit() override;
-    bool needsNested() override;
-    bool hasVoidInitPointers() override;
-    bool hasUnsafeBitpatterns() override;
-    bool hasInvariant() override;
-    Type *nextOf() override;
 
     void accept(Visitor *v) override { v->visit(this); }
 };
@@ -738,9 +646,7 @@ public:
     const char *kind() override;
     TypeClass *syntaxCopy() override;
     ClassDeclaration *isClassHandle() override;
-    unsigned char deduceWild(Type *t, bool isRef) override;
     bool isScopeClass() override;
-    bool isBoolean() override;
 
     void accept(Visitor *v) override { v->visit(this); }
 };
@@ -759,7 +665,6 @@ public:
     static TypeTuple *create(Type *t1, Type *t2);
     const char *kind() override;
     TypeTuple *syntaxCopy() override;
-    bool equals(const RootObject * const o) const override;
     void accept(Visitor *v) override { v->visit(this); }
 };
 
@@ -780,7 +685,6 @@ public:
     const char *kind() override;
 
     TypeNull *syntaxCopy() override;
-    bool isBoolean() override;
 
     void accept(Visitor *v) override { v->visit(this); }
 };
@@ -790,8 +694,6 @@ class TypeNoreturn final : public Type
 public:
     const char *kind() override;
     TypeNoreturn *syntaxCopy() override;
-    bool isBoolean() override;
-    unsigned alignsize() override;
 
     void accept(Visitor *v) override { v->visit(this); }
 };
@@ -844,4 +746,35 @@ namespace dmd
     uinteger_t size(Type *type, Loc loc);
     MATCH implicitConvTo(Type* from, Type* to);
     MATCH constConv(Type* from, Type* to);
+    bool hasUnsafeBitpatterns(Type* type);
+    bool hasInvariant(Type* type);
+    bool hasVoidInitPointers(Type* type);
+    void transitive(TypeNext* type);
+    structalign_t alignment(Type* type);
+    Type* memType(TypeEnum* type);
+    unsigned alignsize(Type* type);
+    Type *makeConst(Type* type);
+    Type* makeMutable(Type* type);
+    Type* makeImmutable(Type* type);
+    Type* makeShared(Type* type);
+    Type* makeSharedConst(Type* type);
+    Type *makeWild(Type* type);
+    Type *makeWildConst(Type* type);
+    Type *makeSharedWild(Type* type);
+    Type *makeSharedWildConst(Type* type);
+    Type *isLazyArray(Parameter* param);
+    unsigned char deduceWild(Type* type, Type* t, bool isRef);
+    bool isIntegral(Type* type);
+    bool isFloating(Type* type);
+    bool isScalar(Type* type);
+    bool isReal(Type* type);
+    bool isImaginary(Type* type);
+    bool isComplex(Type* type);
+    bool isString(Type* type);
+    bool isBoolean(Type* type);
+    bool isUnsigned(Type* type);
+    bool needsNested(Type* type);
+    bool needsDestruction(Type* type);
+    bool needsCopyOrPostblit(Type* type);
+    bool hasDeprecatedAliasThis(Type* type);
 }
index f528d411bfd5632e2b53fe7ce2f245809e7245d7..3221f55107b39291e4152c71c922c7be8acc75f4 100644 (file)
@@ -29,6 +29,7 @@ import dmd.escape;
 import dmd.expression;
 import dmd.expressionsem;
 import dmd.func;
+import dmd.funcsem : isRootTraitsCompilesScope;
 import dmd.globals;
 import dmd.id;
 import dmd.identifier;
@@ -39,7 +40,7 @@ import dmd.rootobject : RootObject, DYNCAST;
 import dmd.semantic2;
 import dmd.semantic3;
 import dmd.tokens;
-import dmd.typesem : unqualify;
+import dmd.typesem : unqualify, toBasetype;
 import dmd.visitor;
 import dmd.visitor.postorder;
 
@@ -101,7 +102,7 @@ public:
             err = true;
             return true;
         }
-        if (f.setGC(e.loc, msg))
+        if (sc.setGC(f, e.loc, msg))
         {
             error(e.loc, "%s causes a GC allocation in `@nogc` %s `%s`", msg, f.kind(), f.toChars());
             err = true;
@@ -176,7 +177,7 @@ public:
     {
         if (e.placement)
             return;     // placement new doesn't use the GC
-        if (e.member && !e.member.isNogc() && f.setGC(e.loc, null))
+        if (e.member && e.member !is f && !e.member.isNogc() && sc.setGC(f, e.loc, null))
         {
             // @nogc-ness is already checked in NewExp::semantic
             return;
@@ -313,6 +314,7 @@ private FuncDeclaration stripHookTraceImpl(FuncDeclaration fd)
  * so mark it as not nogc (not no-how).
  *
  * Params:
+ *     sc = scope that the GC action is in
  *     fd = function
  *     loc = location of GC action
  *     fmt = format string for error message. Must include "%s `%s`" for the function kind and name.
@@ -321,7 +323,7 @@ private FuncDeclaration stripHookTraceImpl(FuncDeclaration fd)
  * Returns:
  *      true if function is marked as @nogc, meaning a user error occurred
  */
-extern (D) bool setGC(FuncDeclaration fd, Loc loc, const(char)* fmt, RootObject[] args...)
+extern (D) bool setGC(Scope* sc, FuncDeclaration fd, Loc loc, const(char)* fmt, RootObject[] args...)
 {
     //printf("setGC() %s\n", toChars());
     if (fd.nogcInprocess && fd.semanticRun < PASS.semantic3 && fd._scope)
@@ -330,6 +332,19 @@ extern (D) bool setGC(FuncDeclaration fd, Loc loc, const(char)* fmt, RootObject[
         fd.semantic3(fd._scope);
     }
 
+    if (sc && isRootTraitsCompilesScope(sc)) // __traits(compiles, x)
+    {
+        if (sc.func.isNogcBypassingInference())
+        {
+            // Message wil be gagged, but still call error() to update global.errors and for
+            // -verrors=spec
+            string action = AttributeViolation(loc, fmt, args).action;
+            .error(loc, "%.*s is not allowed in a `@nogc` function", action.fTuple.expand);
+            return true;
+        }
+        return false;
+    }
+
     if (fd.nogcInprocess)
     {
         fd.nogcInprocess = false;
@@ -348,7 +363,7 @@ extern (D) bool setGC(FuncDeclaration fd, Loc loc, const(char)* fmt, RootObject[
 
         fd.type.toTypeFunction().isNogc = false;
         if (fd.fes)
-            fd.fes.func.setGC(Loc.init, null, null);
+            sc.setGC(fd.fes.func, Loc.init, null, null);
     }
     else if (fd.isNogc())
         return true;
@@ -358,21 +373,22 @@ extern (D) bool setGC(FuncDeclaration fd, Loc loc, const(char)* fmt, RootObject[
 /**************************************
  * The function calls non-`@nogc` function f, mark it as not nogc.
  * Params:
- *     fd = function doin the call
+ *     sc = scope that the GC action is in
+ *     fd = function doing the call
  *     f = function being called
  * Returns:
  *      true if function is marked as @nogc, meaning a user error occurred
  */
-extern (D) bool setGCCall(FuncDeclaration fd, FuncDeclaration f)
+extern (D) bool setGCCall(Scope* sc, FuncDeclaration fd, FuncDeclaration f)
 {
-    return fd.setGC(fd.loc, null, f);
+    return sc.setGC(fd, fd.loc, null, f);
 }
 
- bool isNogc(FuncDeclaration fd)
+bool isNogc(FuncDeclaration fd)
 {
     //printf("isNogc() %s, inprocess: %d\n", toChars(), !!(flags & FUNCFLAG.nogcInprocess));
     if (fd.nogcInprocess)
-        fd.setGC(fd.loc, null);
+        setGC(null, fd, fd.loc, null);
     return fd.type.toTypeFunction().isNogc;
 }
 
index 5b7afd5dd57a9a72cb6cc3264c5efdac89a81d00..604487d405a04353f8ab2d0b0531c5f1330c54b2 100644 (file)
@@ -17,12 +17,11 @@ import dmd.aggregate;
 import dmd.arraytypes;
 import dmd.astenums;
 import dmd.attrib;
-import dmd.attribsem;
+import dmd.attribsem : foreachUda;
 import dmd.cond;
 import dmd.dclass;
 import dmd.declaration;
 import dmd.denum;
-import dmd.dmodule;
 import dmd.dscope;
 import dmd.dstruct;
 import dmd.dsymbol;
index 791762ac18b64d540fe7cfe36c9d95da46a92352..0fd85726a384f13ea375321862d4ac936025146c 100644 (file)
@@ -721,7 +721,7 @@ Expression opOverloadEqual(EqualExp e, Scope* sc, Type[2] aliasThisStop)
             /* The explicit cast is necessary for interfaces
              * https://issues.dlang.org/show_bug.cgi?id=4088
              */
-            Type to = ClassDeclaration.object.getType();
+            Type to = dmd.dsymbolsem.getType(ClassDeclaration.object);
             if (cd1.isInterfaceDeclaration())
                 e1x = new CastExp(e.loc, e.e1, t1.isMutable() ? to : to.constOf());
             if (cd2.isInterfaceDeclaration())
index 88a947f6b02a8506ae46d8b01bb8ed0ffe7d9270..5f89c4b70d289fbbf89bc7e404220286199e54dd 100644 (file)
@@ -459,7 +459,20 @@ Expression optimize(Expression e, int result, bool keepLvalue = false)
         {
             if (!ve.var.isReference() && !ve.var.isImportedSymbol())
             {
-                ret = new SymOffExp(e.loc, ve.var, 0, ve.hasOverloads);
+                bool hasOverloads = ve.hasOverloads;
+                if (auto v = ve.var.isVarDeclaration())
+                {
+                    if (v.needThis())
+                    {
+                        auto t = v.isThis();
+                        assert(t);
+                        .error(e.loc, "taking the address of non-static variable `%s` requires an instance of `%s`", v.toChars(), t.toChars());
+                        ret = ErrorExp.get();
+                        return;
+                    }
+                    hasOverloads = false;
+                }
+                ret = new SymOffExp(e.loc, ve.var, 0, hasOverloads);
                 ret.type = e.type;
                 return;
             }
index ee078f359d774993e23f8d3733b553af9273f94d..419b0ae8a86dd794c7bde4640e1444aaa45088e6 100644 (file)
@@ -1266,10 +1266,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
             }
             else
             {
-                version (IN_GCC)
-                    error("attribute `scope` cannot be applied with `in`, use `-fpreview=in` instead");
-                else
-                    error("attribute `scope` cannot be applied with `in`, use `-preview=in` instead");
+                error("attribute `scope` cannot be applied with `in`, use `-%spreview=in` instead", compileEnv.switchPrefix.ptr);
             }
             return orig;
         }
@@ -1291,10 +1288,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
             }
             else
             {
-                version (IN_GCC)
-                    error("attribute `in` cannot be added after `scope`: remove `scope` and use `-fpreview=in`");
-                else
-                    error("attribute `in` cannot be added after `scope`: remove `scope` and use `-preview=in`");
+                error("attribute `in` cannot be added after `scope`: remove `scope` and use `-%spreview=in`", compileEnv.switchPrefix.ptr);
             }
             return orig;
         }
@@ -2558,7 +2552,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
         AST.Expression constraint = tpl ? parseConstraint() : null;
 
         AST.Type tf = new AST.TypeFunction(parameterList, null, linkage, stc); // ReturnType -> auto
-        tf = tf.addSTC(stc);
+        tf = AST.addSTC(tf, stc);
 
         auto f = new AST.CtorDeclaration(loc, Loc.initial, stc, tf);
         AST.Dsymbol s = parseContracts(f, !!tpl);
@@ -2878,6 +2872,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
         auto parameters = new AST.Parameters();
         VarArg varargs = VarArg.none;
         STC varargsStc;
+        bool hasAutoRefParam = false;
 
         // Attributes allowed for ...
         enum VarArgsStc = STC.const_ | STC.immutable_ | STC.shared_ | STC.scope_ | STC.return_ | STC.returnScope | STC.returnRef;
@@ -3013,6 +3008,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
                 }
                 L2:
                     storageClass = appendStorageClass(storageClass, stc);
+                    hasAutoRefParam |= (storageClass & (STC.auto_ | STC.ref_)) == (STC.auto_ | STC.ref_);
                     continue;
 
                 default:
@@ -3044,7 +3040,9 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
                         }
                         else
                         {
-                            at = parseType(&ai, null, &loc);
+                            if (tpl && !*tpl && hasAutoRefParam)
+                                *tpl = new AST.TemplateParameters();
+                            at = parseType(&ai, &loc);
                         }
                         ae = null;
                         if (token.value == TOK.assign) // = defaultArg
@@ -3217,7 +3215,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
                     {
                     Ltype:
                         // Type identifier
-                        type = parseType(&ident, null);
+                        type = parseType(&ident);
                         if (type == AST.Type.terror)
                         {
                             type = null;
@@ -3583,11 +3581,18 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
 
     /* Parse a type and optional identifier
      * Params:
-     *  pident       = set to Identifier if there is one, null if not
-     *  ptpl         = if !null, then set to TemplateParameterList
-     *  pdeclLoc     = if !null, then set to location of the declarator
+     *  pident   = set to Identifier if there is one, null if not
+     *  pdeclLoc = if !null, then set to location of the declarator
+     *  stc      = Storage Class
      */
-    AST.Type parseType(Identifier* pident = null, AST.TemplateParameters** ptpl = null, Loc* pdeclLoc = null)
+    AST.Type parseType(STC stc)
+    {
+        return parseType(null, null, stc);
+    }
+
+    ///Ditto
+    AST.Type parseType(Identifier* pident = null, Loc* pdeclLoc = null,
+                       STC stc = STC.none)
     {
         /* Take care of the storage class prefixes that
          * serve as type attributes:
@@ -3600,7 +3605,6 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
          *        shared inout type
          *  shared inout const type
          */
-        STC stc = STC.none;
         while (1)
         {
             switch (token.value)
@@ -3647,10 +3651,10 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
         if (pdeclLoc)
             *pdeclLoc = token.loc;
         int alt = 0;
-        t = parseDeclarator(t, alt, pident, ptpl);
+        t = parseDeclarator(t, alt, pident, null);
         checkCstyleTypeSyntax(typeLoc, t, alt, pident ? *pident : null);
 
-        t = t.addSTC(stc);
+        t = AST.addSTC(t, stc);
         return t;
     }
 
@@ -3829,7 +3833,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
             // const(type)
             nextToken();
             check(TOK.leftParenthesis);
-            t = parseType().addSTC(STC.const_);
+            t = parseType(STC.const_);
             check(TOK.rightParenthesis);
             break;
 
@@ -3837,7 +3841,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
             // immutable(type)
             nextToken();
             check(TOK.leftParenthesis);
-            t = parseType().addSTC(STC.immutable_);
+            t = parseType(STC.immutable_);
             check(TOK.rightParenthesis);
             break;
 
@@ -3845,7 +3849,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
             // shared(type)
             nextToken();
             check(TOK.leftParenthesis);
-            t = parseType().addSTC(STC.shared_);
+            t = parseType(STC.shared_);
             check(TOK.rightParenthesis);
             break;
 
@@ -3853,7 +3857,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
             // wild(type)
             nextToken();
             check(TOK.leftParenthesis);
-            t = parseType().addSTC(STC.wild);
+            t = parseType(STC.wild);
             check(TOK.rightParenthesis);
             break;
 
@@ -4091,7 +4095,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
                         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);
+                            tf = cast(AST.TypeFunction)AST.addSTC(tf, stc);
                     }
                     t = save == TOK.delegate_ ? new AST.TypeDelegate(tf) : new AST.TypePointer(tf); // pointer to function
                     continue;
@@ -4265,7 +4269,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
                     STC stc = parsePostfix(storageClass, pudas);
 
                     AST.Type tf = new AST.TypeFunction(parameterList, t, linkage, stc);
-                    tf = tf.addSTC(stc);
+                    tf = AST.addSTC(tf, stc);
                     if (pdisable)
                         *pdisable = stc & STC.disable ? true : false;
 
@@ -5276,7 +5280,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
         }
 
         auto tf = new AST.TypeFunction(parameterList, tret, linkage, stc);
-        tf = cast(AST.TypeFunction)tf.addSTC(stc);
+        tf = cast(AST.TypeFunction)AST.addSTC(tf, stc);
         auto fd = new AST.FuncLiteralDeclaration(loc, Loc.initial, tf, save, null, null, stc & STC.auto_);
 
         if (token.value == TOK.goesTo)
@@ -5328,10 +5332,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
                 error("missing `do { ... }` after `in` or `out`");
             const returnloc = token.loc;
             nextToken();
-            if (f.isCtorDeclaration)
-                f.fbody = new AST.ExpStatement(returnloc, parseExpression());
-            else
-                f.fbody = new AST.ReturnStatement(returnloc, parseExpression());
+            f.fbody = new AST.ReturnStatement(returnloc, parseExpression());
             f.endloc = token.loc;
             check(TOK.semicolon);
             break;
@@ -5511,7 +5512,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
     {
         if (token.value != TOK.else_ && token.value != TOK.catch_ && token.value != TOK.finally_ && lookingForElse.isValid)
         {
-            eSink.warning(elseloc, "else is dangling, add { } after condition at %s", lookingForElse.toChars());
+            eSink.error(elseloc, "else is dangling, add { } after condition at %s", lookingForElse.toChars());
         }
     }
 
@@ -8730,7 +8731,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
             error("expression expected, not `%s`", token.toChars());
         Lerr:
             // Anything for e, as long as it's not NULL
-            e = AST.ErrorExp.get();
+            e = new AST.ErrorExp();
             nextToken();
             break;
         }
@@ -8848,7 +8849,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
                 else
                 {
                     AST.Type t = parseType(); // cast( type )
-                    t = t.addSTC(AST.ModToStc(m)); // cast( const type )
+                    t = AST.addSTC(t, AST.ModToStc(m)); // cast( const type )
                     check(TOK.rightParenthesis);
                     e = parseUnaryExp();
                     e = new AST.CastExp(loc, e, t);
@@ -8863,7 +8864,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
                 STC stc = parseTypeCtor();
 
                 AST.Type t = parseBasicType();
-                t = t.addSTC(stc);
+                t = AST.addSTC(t, stc);
 
                 if (stc == 0 && token.value == TOK.dot)
                 {
@@ -8872,7 +8873,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
                     {
                         error("identifier expected following `%s.`, not `%s`",
                             t.toChars(), token.toChars());
-                        return AST.ErrorExp.get();
+                        return new AST.ErrorExp();
                     }
                     e = new AST.DotIdExp(loc, new AST.TypeExp(loc, t), token.ident);
                     nextToken();
@@ -8986,7 +8987,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
                                     {
                                         error("identifier or new keyword expected following `(...)`.");
                                         nextToken();
-                                        return AST.ErrorExp.get();
+                                        return new AST.ErrorExp();
                                     }
                                     e = new AST.TypeExp(loc, t);
                                     e.parens = true;
@@ -9635,7 +9636,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
         const stc = parseTypeCtor();
         auto t = parseBasicType(true);
         t = parseTypeSuffixes(t);
-        t = t.addSTC(stc);
+        t = AST.addSTC(t, stc);
         if (t.ty == Taarray)
         {
             AST.TypeAArray taa = cast(AST.TypeAArray)t;
index 44597740a2ac629583a7feeefdd57a5636ee3516..71d677078529baf74be18d45f34a6ce184994909 100644 (file)
@@ -14,6 +14,7 @@
 module dmd.pragmasem;
 
 import core.stdc.stdio;
+import core.stdc.string;
 
 import dmd.astenums;
 import dmd.arraytypes;
@@ -39,73 +40,14 @@ import dmd.statement;
  */
 void pragmaDeclSemantic(PragmaDeclaration pd, Scope* sc)
 {
-    import dmd.aggregate;
     import dmd.common.outbuffer;
     import dmd.dmodule;
     import dmd.dsymbolsem;
     import dmd.identifier;
-    import dmd.mangle : isValidMangling;
     import dmd.root.rmem;
-    import dmd.root.utf;
     import dmd.target;
     import dmd.utils;
 
-    StringExp verifyMangleString(ref Expression e)
-    {
-        auto se = semanticString(sc, e, "mangled name");
-        if (!se)
-            return null;
-        e = se;
-        if (!se.len)
-        {
-            .error(pd.loc, "%s `%s` - zero-length string not allowed for mangled name", pd.kind, pd.toPrettyChars);
-            return null;
-        }
-        if (se.sz != 1)
-        {
-            .error(pd.loc, "%s `%s` - mangled name characters can only be of type `char`", pd.kind, pd.toPrettyChars);
-            return null;
-        }
-        version (all)
-        {
-            import dmd.common.charactertables;
-
-            /* 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
-                    {
-                        .error(pd.loc, "%s `%s` char 0x%02x not allowed in mangled name", pd.kind, pd.toPrettyChars, c);
-                        break;
-                    }
-                }
-                if (const msg = utf_decodeChar(slice, i, c))
-                {
-                    .error(pd.loc, "%s `%s` %.*s", pd.kind, pd.toPrettyChars, cast(int)msg.length, msg.ptr);
-                    break;
-                }
-                if (!isAnyIdentifierCharacter(c))
-                {
-                    .error(pd.loc, "%s `%s` char `0x%04x` not allowed in mangled name", pd.kind, pd.toPrettyChars, c);
-                    break;
-                }
-            }
-        }
-        return se;
-    }
     void declarations()
     {
         if (!pd.decl)
@@ -126,58 +68,6 @@ void pragmaDeclSemantic(PragmaDeclaration pd, Scope* sc)
             }
 
             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.pMangleOverride = new MangleOverride;
-                void setString(ref Expression e)
-                {
-                    if (auto se = verifyMangleString(e))
-                    {
-                        const name = (cast(const(char)[])se.peekData()).xarraydup;
-                        ad.pMangleOverride.id = Identifier.idPool(name);
-                        e = se;
-                    }
-                    else
-                        error(e.loc, "must be a string");
-                }
-                if (agg)
-                {
-                    ad.pMangleOverride.agg = agg;
-                    if (pd.args.length == 2)
-                    {
-                        setString((*pd.args)[1]);
-                    }
-                    else
-                        ad.pMangleOverride.id = agg.ident;
-                }
-                else
-                    setString((*pd.args)[0]);
-            }
-            else if (auto td = s.isTemplateDeclaration())
-            {
-                .error(pd.loc, "%s `%s` cannot apply to a template declaration", pd.kind, pd.toPrettyChars);
-                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)
-                    .error(pd.loc, "%s `%s` can only apply to a single declaration", pd.kind, pd.toPrettyChars);
-            }
         }
     }
 
@@ -264,16 +154,11 @@ void pragmaDeclSemantic(PragmaDeclaration pd, Scope* sc)
     }
     else if (pd.ident == Id.mangle)
     {
-        if (!pd.args)
-            pd.args = new Expressions();
-        if (pd.args.length == 0 || pd.args.length > 2)
-        {
-            .error(pd.loc, pd.args.length == 0 ? "%s `%s` - string expected for mangled name"
-                                      : "%s `%s` expected 1 or 2 arguments", pd.kind, pd.toPrettyChars);
-            pd.args.setDim(1);
-            (*pd.args)[0] = ErrorExp.get(); // error recovery
-        }
-        return declarations();
+        Scope* sc2 = pd.newScope(sc);
+        pragmaMangleSemantic(pd.loc, sc2, pd.args, pd.decl);
+        if (sc2 != sc)
+            sc2.pop();
+        return;
     }
     else if (pd.ident == Id.crt_constructor || pd.ident == Id.crt_destructor)
     {
@@ -440,20 +325,9 @@ bool pragmaStmtSemantic(PragmaStatement ps, Scope* sc)
     {
         auto es = ps._body ? ps._body.isExpStatement() : null;
         auto de = es ? es.exp.isDeclarationExp() : null;
-        if (!de)
-        {
-            error(ps.loc, "`pragma(mangle)` must be attached to a declaration");
+        Dsymbols decls = de ? Dsymbols(de.declaration) : Dsymbols();
+        if (!pragmaMangleSemantic(ps.loc, sc, ps.args, decls.length ? &decls : null))
             return false;
-        }
-        const se = ps.args && (*ps.args).length == 1 ? semanticString(sc, (*ps.args)[0], "pragma mangle argument") : null;
-        if (!se)
-        {
-            error(ps.loc, "`pragma(mangle)` takes a single argument that must be a string literal");
-            return false;
-        }
-        const cnt = setMangleOverride(de.declaration, cast(const(char)[])se.peekData());
-        if (cnt != 1)
-            assert(0);
     }
     else if (!global.params.ignoreUnsupportedPragmas)
     {
@@ -516,34 +390,6 @@ package PINLINE evalPragmaInline(Loc loc, Scope* sc, Expressions* args)
     return PINLINE.never;
 }
 
-/**
- * Apply pragma mangle to FuncDeclarations and VarDeclarations
- * under `s`, poking through attribute declarations such as
- * `extern(C)` but not through aggregates or function bodies.
- *
- * Params:
- *    s = symbol to apply
- *    sym = overriding symbol name
- */
-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;
-}
-
 /***********************************************************
  * Evaluate and print a `pragma(msg, args)`
  *
@@ -635,3 +481,230 @@ private bool pragmaStartAddressSemantic(Loc loc, Scope* sc, Expressions* args)
     }
     return true;
 }
+
+/***********************************************************
+ * Evaluate `pragma(mangle)` and store the mangled string in `decls`
+ * This accepts any of the following variants.
+ *
+ *      pragma(mangle, StringExp) AggregateDeclaration
+ *      pragma(mangle, StringExp) VarDeclaration
+ *      pragma(mangle, StringExp) FuncDeclaration
+ *      pragma(mangle, TypeExp) AggregateDeclaration
+ *      pragma(mangle, TypeExp, StringExp) AggregateDeclaration
+ *      pragma(mangle, StringExp, TypeExp) AggregateDeclaration
+ *
+ * Params:
+ *    loc = location for error messages
+ *    sc = scope for argument interpretation
+ *    args = pragma arguments
+ *    decls = declarations to set mangled string to
+ * Returns:
+ *    `true` on success
+ */
+private bool pragmaMangleSemantic(Loc loc, Scope* sc, Expressions* args, Dsymbols* decls)
+{
+    import dmd.root.rmem;
+
+    StringExp verifyMangleString(ref Expression e)
+    {
+        import dmd.mangle : isValidMangling;
+        import dmd.root.utf : utf_decodeChar;
+        auto se = semanticString(sc, e, "pragma mangle argument");
+        if (!se)
+            return null;
+        e = se;
+        if (!se.len)
+        {
+            error(loc, "`pragma(mangle)` zero-length string not allowed for mangled name");
+            return null;
+        }
+        if (se.sz != 1)
+        {
+            error(loc, "`pragma(mangle)` mangled name characters can only be of type `char`");
+            return null;
+        }
+        auto slice = se.toStringz();
+        if (strlen(slice.ptr) != se.len)
+            .error(loc, "pragma `mangle` null character not allowed in mangled name");
+        mem.xfree(cast(void*)slice.ptr);
+        return se;
+    }
+
+    bool applyPragmaMangle(Dsymbols* decls, ref uint count, ref bool ignored)
+    {
+        if (decls is null)
+            return true;
+
+        foreach (s; (*decls)[])
+        {
+            import dmd.aggregate;
+            import dmd.common.outbuffer;
+            import dmd.dsymbolsem : dsymbolSemantic;
+            import dmd.hdrgen : arrayObjectsToBuffer;
+            import dmd.identifier : Identifier;
+
+            s.dsymbolSemantic(sc);
+
+            if (auto ad = s.isAggregateDeclaration())
+            {
+                /* pragma(mangle) AggregateDeclaration;
+                   For aggregates there may be one or two AssignExpressions:
+                   - one of which must evaluate at compile time to a string literal
+                   - one which must evaluate to a symbol
+                   The spec does not specify which order these should be in, so we
+                   allow any of:
+                   pragma(mangle, "name") struct { ... }
+                   pragma(mangle, T)      struct { ... }
+                   pragma(mangle, "name", T) struct { ... }
+                   pragma(mangle, T, "name") struct { ... }
+                 */
+                AggregateDeclaration symbol;
+                StringExp literal;
+
+                foreach (ref Expression e; (*args)[])
+                {
+                    sc = sc.startCTFE();
+                    e = e.expressionSemantic(sc);
+                    e = resolveProperties(sc, e);
+                    sc = sc.endCTFE();
+
+                    bool expectedString()
+                    {
+                        error(e.loc, "`string` expected for pragma mangle argument, not `%s` of type `%s`",
+                              e.toChars(), e.type.toChars());
+                        return false;
+                    }
+
+                    bool expectedType()
+                    {
+                        error(e.loc, "`class` or `struct` type expected for pragma mangle argument, not `%s` of type `%s`",
+                              e.toChars(), e.type.toChars());
+                        return false;
+                    }
+
+                    // Validate arguments to pragma(mangle)
+                    if (e.isTypeExp())
+                    {
+                        /* Check for and reject:
+                           pragma(mangle, TypeExp, TypeExp)
+                           where the first TypeExp already resolved to a symbol. */
+                        if (symbol)
+                            return expectedString();
+
+                        /* Type must be a class or struct symbol. */
+                        if (auto tc = e.type.isTypeClass())
+                            symbol = tc.sym;
+                        else if (auto ts = e.type.isTypeStruct())
+                            symbol = ts.sym;
+                        else
+                            return expectedType();
+                    }
+                    else
+                    {
+                        /* Check for and reject:
+                           pragma(mangle, StringExp, AssignExpression)
+                           where AssignExpression did not resolve to a symbol. */
+                        if (literal)
+                            return expectedType();
+
+                        /* Must evaluate to a compile time string literal. */
+                        auto se = verifyMangleString(e);
+                        if (se is null)
+                            return false;
+                        literal = se;
+                    }
+                }
+
+                ad.pMangleOverride = new MangleOverride;
+
+                if (symbol)
+                {
+                    ad.pMangleOverride.agg = symbol;
+                    /* The identifier of the symbol is used when no string is supplied. */
+                    if (literal is null)
+                    {
+                        ad.pMangleOverride.id = symbol.ident;
+                        count += 1;
+                        continue;
+                    }
+                }
+
+                assert(literal);
+                const name = literal.peekString().xarraydup;
+                ad.pMangleOverride.id = Identifier.idPool(name);
+                count += 1;
+            }
+            else if (auto td = s.isTemplateDeclaration())
+            {
+                /* pragma(mangle) TemplateDeclaration
+                   Give an informative error message to avoid pragma(mangle)
+                   silently ignoring the template symbol. */
+                error(loc, "`pragma(mangle)` cannot apply to a template declaration");
+                OutBuffer buf;
+                buf.arrayObjectsToBuffer(cast(Objects*)args);
+                errorSupplemental(loc, "use `template %s(Args...) { pragma(mangle, %s) ... }`", td.ident.toChars(), buf.peekChars());
+                return false;
+            }
+            else if (auto ad = s.isAttribDeclaration())
+            {
+                /* pragma(mangle) AttribDeclaration
+                   Poke through the attribute to get to the underlying declaration. */
+                if (!applyPragmaMangle(ad.include(null), count, ignored))
+                    return false;
+            }
+            else if (s.isFuncDeclaration() || s.isVarDeclaration())
+            {
+                /* pragma(mangle) Declaration;
+                   For all other symbols, there must be one AssignExpression and it
+                   must evaluate at compile time to a string literal. */
+                if (args.length != 1)
+                {
+                    error(loc, "`pragma(mangle)` takes a single argument that must be a string literal");
+                    return false;
+                }
+                auto se = verifyMangleString((*args)[0]);
+                if (!se)
+                    return false;
+
+                const name = se.peekString().xarraydup;
+                s.isDeclaration().mangleOverride = name;
+                count += 1;
+            }
+            else
+            {
+                /* pragma(mangle) only applies to function and variable
+                   symbols. Other symbols are ignored. */
+                ignored = true;
+            }
+        }
+        return true;
+    }
+
+    if (args is null)
+    {
+        error(loc, "`pragma(mangle)` expects string literal argument for mangled name");
+        return false;
+    }
+    if (args.length > 2)
+    {
+        error(loc, "`pragma(mangle)` expects 1 or 2 arguments");
+        return false;
+    }
+
+    uint count = 0;
+    bool ignored = false;
+    if (!applyPragmaMangle(decls, count, ignored))
+        return false;
+
+    if (count == 0 && !ignored)
+    {
+        error(loc, "`pragma(mangle)` must be attached to a declaration");
+        return false;
+    }
+    if (count > 1)
+    {
+        error(loc, "`pragma(mangle)` can only apply to a single declaration");
+        return false;
+    }
+    return true;
+}
index 5e4c9f787f36d170747183fb0ec156453a658893..80995d410305e4fb7ee236753393a3bc31978254 100644 (file)
@@ -14,6 +14,7 @@ module dmd.printast;
 import core.stdc.stdio;
 
 import dmd.expression;
+import dmd.expressionsem : toInteger;
 import dmd.ctfeexpr;
 import dmd.tokens;
 import dmd.visitor;
index ee35e805d66770cbb5274f71cedb7f45a05f632f..217945e6bed3651157f78b3413745c771bc324b7 100644 (file)
@@ -16,7 +16,7 @@ import core.stdc.stdlib : _compare_fp_t;
 import core.stdc.string;
 
 import dmd.root.rmem;
-import dmd.root.string;
+import dmd.root.string : toDString;
 
 // `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;
@@ -908,7 +908,7 @@ bool equal(Range1, Range2)(Range1 range1, Range2 range2)
 
     else
     {
-        static if (hasLength!Range1 && hasLength!Range2 && is(typeof(r1.length == r2.length)))
+        static if (hasLength!Range1 && hasLength!Range2 && is(typeof(range1.length == range2.length)))
         {
             if (range1.length != range2.length)
                 return false;
index b5adaa8903416cdf0301324ca5fa863422cc4072..5d2c18b75fbc135e6ab89ccc441d3fe4db59445c 100644 (file)
@@ -16,6 +16,8 @@ import core.stdc.string;
 
 import dmd.root.rmem;
 
+nothrow:
+
 struct BitArray
 {
 
index 2046e5930c8d4058b3911a8baa7b874d22786d4c..9e3d0cf8704cfb3929857e63d83c55d3eb261f09 100644 (file)
@@ -14,7 +14,6 @@ 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;
index b25392b6386b40355c854a5272bde41fb641ff11..cb08b09476af66400f2b36edf87f2332f343df10 100644 (file)
@@ -51,8 +51,9 @@ version (CRuntime_Glibc)
     extern (C) char* canonicalize_file_name(const char*) nothrow;
 }
 
-alias Strings = Array!(const(char)*);
+nothrow:
 
+alias Strings = Array!(const(char)*);
 
 // Check whether character is a directory separator
 bool isDirSeparator(char c) pure nothrow @nogc @safe
index 40dc2eb1979aa4baac10383582222f51d6acf6d6..885238461f416994e681585e2dcb18c8d9ab96a1 100644 (file)
@@ -11,6 +11,9 @@
 
 module dmd.root.hash;
 
+nothrow:
+@safe:
+
 // MurmurHash2 was written by Austin Appleby, and is placed in the public
 // domain. The author hereby disclaims copyright to this source code.
 // https://github.com/aappleby/smhasher/
index a8efbca1e6255bd303391e2158bd1a546e98a612..9d241b8cc6bb6dec531e9205f60301f3bd7834f1 100644 (file)
@@ -18,6 +18,8 @@ import core.stdc.stdlib;
 import dmd.root.rmem;
 import dmd.root.array;
 
+nothrow:
+
 /*****
  * Simple region storage allocator.
  */
index 32d22d3bebe54a1c5f54a91b38127ccd6a472987..02906731e6654c3b8764a0654264a7f80d7bd1f6 100644 (file)
@@ -18,6 +18,8 @@ import core.stdc.string;
 
 import core.memory : GC;
 
+nothrow:
+
 extern (C++) struct Mem
 {
     static char* xstrdup(const(char)* s) nothrow
index 369a79be66245c5ff1077b2025e071d07e0cd23c..7ae5128530b61bacb879563c3bee4ac606f38cf1 100644 (file)
@@ -13,6 +13,8 @@ module dmd.root.string;
 import core.stdc.string;
 import dmd.root.rmem;
 
+nothrow:
+
 /// Slices a `\0`-terminated C-string, excluding the terminator
 inout(char)[] toDString (inout(char)* s) pure nothrow @nogc
 {
index c7a2c8c56dbc58b756f4c2d7be8500547e8a20e2..1d9e2e078c9b440fd00c47e7c3c1cb5691c4e3b3 100644 (file)
@@ -14,6 +14,8 @@ module dmd.root.stringtable;
 import core.stdc.string;
 import dmd.root.rmem, dmd.root.hash;
 
+nothrow:
+
 private enum POOL_BITS = 12;
 private enum POOL_SIZE = (1U << POOL_BITS);
 
index 71b36a4375631a65ff49e349a7de8f38e51f55f1..7101374e25e0117de85beaf4fb02f44fec111c01 100644 (file)
@@ -38,11 +38,6 @@ extern (C++) class RootObject
     {
     }
 
-    bool equals(const RootObject o) const
-    {
-        return o is this;
-    }
-
     const(char)* toChars() const
     {
         assert(0);
index 330d2c99b2620d80eacbf0b35d3f7c8d8dc1c7bd..297f852e374be79fe43a5b033b4990c7d8074509 100644 (file)
@@ -39,8 +39,6 @@ class RootObject
 public:
     RootObject() { }
 
-    virtual bool equals(const RootObject * const o) const;
-
     /**
      * Pretty-print an Object. Useful for debugging the old-fashioned way.
      */
index 4a3eeb77d0334139a14e2d70b9059a3a98ea28e5..672b1574d26ccfcd251ec5eaf56adea5e58bbf9f 100644 (file)
@@ -36,8 +36,9 @@ import dmd.mtype;
 import dmd.rootobject;
 import dmd.root.string : fTuple;
 import dmd.target;
+import dmd.targetcompiler;
 import dmd.tokens;
-import dmd.typesem : hasPointers, arrayOf, size;
+import dmd.typesem;
 
 /*************************************************************
  * Check for unsafe access in @safe code:
@@ -174,10 +175,17 @@ bool isSafeCast(Expression e, Type tfrom, Type tto, ref string msg)
     // Implicit conversions are always safe
     if (tfrom.implicitConvTo(tto))
         return true;
-
     if (!tto.hasPointers())
+    {
+        // casting to bool is safe as it's a special op
+        // casting to struct with non-pointer @system field is not @safe
+        if (tto.ty != Tbool && tto.hasUnsafeBitpatterns())
+        {
+            msg = "Target element type has unsafe bit patterns";
+            return false;
+        }
         return true;
-
+    }
     auto tfromb = tfrom.toBasetype();
     auto ttob = tto.toBasetype();
 
@@ -354,7 +362,7 @@ bool isTrusted(FuncDeclaration fd)
  * Call when `fd` was just inferred to be @system OR
  * `fd` was @safe and an tried something unsafe.
  * Params:
- *   fd    = function we're gonna rat on
+ *   fd    = function we are gonna rat on
  *   gag   = suppress error message (used in escape.d)
  *   loc   = location of error
  *   format = printf-style format string
@@ -362,12 +370,22 @@ bool isTrusted(FuncDeclaration fd)
  */
 extern (D) void reportSafeError(FuncDeclaration fd, bool gag, Loc loc,
     const(char)* format, RootObject[] args...)
+{
+    reportSafeError(fd, gag, loc, null, format, args);
+}
+
+/// Overload that also stores the variable whose scope status caused the violation
+extern (D) void reportSafeError(FuncDeclaration fd, bool gag, Loc loc,
+    VarDeclaration scopeVar, const(char)* format, RootObject[] args...)
 {
     if (fd.type.toTypeFunction().trust == TRUST.system) // function was just inferred to be @system
     {
         if (format)
         {
-            fd.safetyViolation = new AttributeViolation(loc, format, args);
+            if (scopeVar)
+                fd.safetyViolation = new AttributeViolation(loc, format, scopeVar, args);
+            else
+                fd.safetyViolation = new AttributeViolation(loc, format, args);
         }
         else if (args.length > 0)
         {
@@ -387,10 +405,7 @@ extern (D) void reportSafeError(FuncDeclaration fd, bool gag, Loc loc,
                 buf.writestring(" is not allowed in a `@safe` function");
             else
             {
-                version (IN_GCC)
-                    buf.writestring(" is not allowed in a function with default safety with `-fpreview=safer`");
-                else
-                    buf.writestring(" is not allowed in a function with default safety with `-preview=safer`");
+                buf.printf(" is not allowed in a function with default safety with `-%spreview=safer`", SwitchPrefix.ptr);
             }
             .error(loc, "%s", buf.extractChars());
         }
@@ -455,6 +470,13 @@ extern (D) bool setUnsafeCall(FuncDeclaration fd, FuncDeclaration f)
  * Returns: whether there is a safe error
  */
 bool setUnsafe(Scope* sc, bool gag, Loc loc, const(char)* format, RootObject[] args...)
+{
+    return sc.setUnsafe(gag, loc, null, format, args);
+}
+
+/// Overload that also stores the variable whose scope status caused the violation
+bool setUnsafe(Scope* sc, bool gag, Loc loc, VarDeclaration scopeVar,
+    const(char)* format, RootObject[] args...)
 {
     if (sc.intypeof)
         return false; // typeof(cast(int*)0) is safe
@@ -499,7 +521,7 @@ bool setUnsafe(Scope* sc, bool gag, Loc loc, const(char)* format, RootObject[] a
     {
         if (format || args.length > 0)
         {
-            reportSafeError(sc.func, gag, loc, format, args);
+            reportSafeError(sc.func, gag, loc, scopeVar, format, args);
         }
         return sc.func.isSafe(); // it is only an error if in an @safe function
     }
@@ -526,6 +548,13 @@ bool setUnsafe(Scope* sc, bool gag, Loc loc, const(char)* format, RootObject[] a
  * Returns: whether an actual safe error (not deprecation) occured
  */
 bool setUnsafePreview(Scope* sc, FeatureState fs, bool gag, Loc loc, const(char)* format, RootObject[] args...)
+{
+    return setUnsafePreview(sc, fs, gag, loc, null, format, args);
+}
+
+/// Overload for scope violations that also stores the variable whose scope status caused the issue
+bool setUnsafePreview(Scope* sc, FeatureState fs, bool gag, Loc loc, VarDeclaration scopeVar,
+    const(char)* format, RootObject[] args...)
 {
     //printf("setUnsafePreview() fs:%d %s\n", fs, fmt);
     assert(format);
@@ -535,7 +564,7 @@ bool setUnsafePreview(Scope* sc, FeatureState fs, bool gag, Loc loc, const(char)
         return false;
 
       case enabled:
-        return sc.setUnsafe(gag, loc, format, args);
+        return sc.setUnsafe(gag, loc, scopeVar, format, args);
 
       case default_:
         if (!sc.func)
@@ -551,7 +580,10 @@ bool setUnsafePreview(Scope* sc, FeatureState fs, bool gag, Loc loc, const(char)
         else if (!sc.func.safetyViolation)
         {
             import dmd.func : AttributeViolation;
-            sc.func.safetyViolation = new AttributeViolation(loc, format, args);
+            if (scopeVar)
+                sc.func.safetyViolation = new AttributeViolation(loc, format, scopeVar, args);
+            else
+                sc.func.safetyViolation = new AttributeViolation(loc, format, args);
         }
         return false;
     }
index 0b1d9b66bd0dab5611f6cf096464215fc63d8440..afd9f6c00ab7d2dd141b46730ab9ba137d31f819 100644 (file)
@@ -126,6 +126,8 @@ struct Scope final
     bool fullinst(bool v);
     bool ctfeBlock() const;
     bool ctfeBlock(bool v);
+    bool knownACompileTimeOnlyContext() const;
+    bool knownACompileTimeOnlyContext(bool v);
 
     UserAttributeDeclaration *userAttribDecl;   // user defined attributes
 
index 6e4c7d10d3c19b1741ca8501edfa9ef46acdf578..cca8dcea38062bf8dd6ce05d5313aeae20f91719 100644 (file)
@@ -329,7 +329,7 @@ private extern(C++) final class Semantic2Visitor : Visitor
         // 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, global.errorSink); // create root scope
+        Scope* sc = scopeCreateGlobal(mod, global.errorSink); // create root scope
         //printf("Module = %p\n", sc.scopesym);
         if (mod.members)
         {
@@ -799,11 +799,135 @@ private void doGNUABITagSemantic(ref Expression e, ref Expression* lastTag)
     // `const` (and nor is `StringExp`, by extension).
     static int predicate(const scope Expression* e1, const scope Expression* e2)
     {
-        return (cast(Expression*)e1).toStringExp().compare((cast(Expression*)e2).toStringExp());
+        Expression e11 = cast(Expression) *e1;
+        Expression e22 = cast(Expression) *e2;
+        return e11.toStringExp().compare(e22.toStringExp());
     }
     ale.elements.sort!predicate;
 }
 
+/****************
+ * Find virtual function matching identifier and type.
+ * Used to build virtual function tables for interface implementations.
+ * Params:
+ *  _this = ClassDeclaration's vtbl to search
+ *  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
+ */
+FuncDeclaration findFunc(ClassDeclaration _this, 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)
+    {
+        bool seenInterfaceVirtual;
+        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;
+
+            /* Functions overriding interface functions for extern(C++) with VC++
+             * are not in the normal vtbl, but in vtblFinal. If the implementation
+             * is again overridden in a child class, both would be found here.
+             * The function in the child class should override the function
+             * in the base class, which is done here, because searchVtbl is first
+             * called for the child class. Checking seenInterfaceVirtual makes
+             * sure, that the compared functions are not in the same vtbl.
+             */
+            if (fd.interfaceVirtual &&
+                fd.interfaceVirtual is fdmatch.interfaceVirtual &&
+                !seenInterfaceVirtual &&
+                fdmatch.type.covariant(fd.type) == Covariant.yes)
+            {
+                seenInterfaceVirtual = true;
+                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(_this.vtbl);
+    for (auto cd = _this; cd; cd = cd.baseClass)
+    {
+        searchVtbl(cd.vtblFinal);
+    }
+
+    if (fdambig)
+        _this.classError("%s `%s` ambiguous virtual function `%s`", fdambig.toChars());
+
+    return fdmatch;
+}
+
+
 /**
  * Try lower a variable's Associative Array initializer to a newaa struct
  * so it can be put in static data.
index 4c4c0ec2b355026d556b5efc09ee245d04f17ea5..7ac2352f3fe4a6f3203247900235e42467924651 100644 (file)
@@ -60,6 +60,7 @@ import dmd.opover;
 import dmd.optimize;
 import dmd.parse;
 import dmd.root.filename;
+import dmd.root.array;
 import dmd.common.outbuffer;
 import dmd.root.rmem;
 import dmd.rootobject;
@@ -71,6 +72,7 @@ import dmd.tokens;
 import dmd.semantic2;
 import dmd.statement;
 import dmd.target;
+import dmd.targetcompiler;
 import dmd.templateparamsem;
 import dmd.typesem;
 import dmd.visitor;
@@ -195,7 +197,7 @@ private extern(C++) final class Semantic3Visitor : Visitor
         // 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, global.errorSink); // create root scope
+        Scope* sc = scopeCreateGlobal(mod, global.errorSink); // create root scope
         //printf("Module = %p\n", sc.scopesym);
         if (mod.members)
         {
@@ -206,7 +208,7 @@ private extern(C++) final class Semantic3Visitor : Visitor
                 //printf("Module %s: %s.semantic3()\n", toChars(), s.toChars());
                 s.semantic3(sc);
 
-                mod.runDeferredSemantic2();
+                runDeferredSemantic2();
             }
         }
         if (mod.userAttribDecl)
@@ -226,20 +228,6 @@ private extern(C++) final class Semantic3Visitor : Visitor
         timeTraceBeginEvent(TimeTraceEventType.sema3);
         scope (exit) timeTraceEndEvent(TimeTraceEventType.sema3, funcdecl);
 
-        /* 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.inCfile && funcdecl.isCMain() && f.next.ty == Tint32)
-                return true;
-
-            return f.next.ty == Tvoid && (funcdecl.isMain() || funcdecl.isCMain());
-        }
-
         VarDeclaration _arguments = null;
 
         if (!funcdecl.parent)
@@ -423,12 +411,7 @@ private extern(C++) final class Semantic3Visitor : Visitor
                     if (!global.params.useTypeInfo || !Type.dtypeinfo || !Type.typeinfotypelist)
                     {
                         if (!global.params.useTypeInfo)
-                        {
-                            version (IN_GCC)
-                                .error(funcdecl.loc, "%s `%s` D-style variadic functions cannot be used with `-fno-rtti`", funcdecl.kind, funcdecl.toPrettyChars);
-                            else
-                                .error(funcdecl.loc, "%s `%s` D-style variadic functions cannot be used with -betterC", funcdecl.kind, funcdecl.toPrettyChars);
-                        }
+                            .error(funcdecl.loc, "%s `%s` D-style variadic functions cannot be used with `-%s`", funcdecl.kind, funcdecl.toPrettyChars, SwitchVariadic.ptr);
                         else if (!Type.typeinfotypelist)
                             .error(funcdecl.loc, "%s `%s` `object.TypeInfo_Tuple` could not be found, but is implicitly used in D-style variadic functions", funcdecl.kind, funcdecl.toPrettyChars);
                         else
@@ -647,7 +630,7 @@ private extern(C++) final class Semantic3Visitor : Visitor
                         Expression exp = (*funcdecl.returns)[i].exp;
                         if (exp.op == EXP.variable && (cast(VarExp)exp).var == funcdecl.vresult)
                         {
-                            if (addReturn0())
+                            if (fds.addReturn0())
                                 exp.type = Type.tint32;
                             else
                                 exp.type = f.next;
@@ -839,7 +822,7 @@ private extern(C++) final class Semantic3Visitor : Visitor
 
                 if (funcdecl.returns)
                 {
-                    bool implicit0 = addReturn0();
+                    bool implicit0 = fds.addReturn0();
                     Type tret = implicit0 ? Type.tint32 : f.next;
                     assert(tret.ty != Tvoid);
                     if (funcdecl.vresult || funcdecl.returnLabel)
@@ -985,7 +968,11 @@ private extern(C++) final class Semantic3Visitor : Visitor
                         if (funcdecl.vresult)
                         {
                             // Create: return vresult = exp;
-                            exp = new BlitExp(rs.loc, funcdecl.vresult, exp);
+                            if (canElideCopy(exp, funcdecl.vresult.type, false))
+                                exp = new ConstructExp(rs.loc, funcdecl.vresult, exp);
+                            else
+                                exp = new BlitExp(rs.loc, funcdecl.vresult, exp);
+
                             exp.type = funcdecl.vresult.type;
 
                             if (rs.caseDim)
@@ -1190,7 +1177,7 @@ private extern(C++) final class Semantic3Visitor : Visitor
                         a.push(s);
                     }
                 }
-                if (addReturn0())
+                if (fds.addReturn0())
                 {
                     // Add a return 0; statement
                     Statement s = new ReturnStatement(Loc.initial, IntegerExp.literal!0);
@@ -1406,7 +1393,7 @@ private extern(C++) final class Semantic3Visitor : Visitor
 
                 return false;
             }
-            if (isCppNonMappableType(f.next.toBasetype()))
+            if (isCppNonMappableType(f.next.toBasetype()) && !funcdecl.skipCodegen)
             {
                 .error(funcdecl.loc, "%s `%s` cannot return type `%s` because its linkage is `extern(C++)`", funcdecl.kind, funcdecl.toErrMsg(), f.next.toChars());
                 if (f.next.isTypeDArray())
@@ -1454,6 +1441,10 @@ private extern(C++) final class Semantic3Visitor : Visitor
         if (ctor.semanticRun >= PASS.semantic3)
             return;
 
+        if (!ctor.fbody)
+            return visit(cast(FuncDeclaration)ctor);
+
+
         /* 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
@@ -1464,7 +1455,7 @@ private extern(C++) final class Semantic3Visitor : Visitor
          * https://issues.dlang.org/show_bug.cgi?id=14246
          */
         AggregateDeclaration ad = ctor.isMemberDecl();
-        if (!ctor.fbody || !ad || !ad.fieldDtor ||
+        if (!ad || !ad.fieldDtor ||
             global.params.dtorFields == FeatureState.disabled || !global.params.useExceptions || ctor.type.toTypeFunction.isNothrow)
             return visit(cast(FuncDeclaration)ctor);
 
@@ -1537,7 +1528,6 @@ private extern(C++) final class Semantic3Visitor : Visitor
         visit(cast(FuncDeclaration)ctor);
     }
 
-
     override void visit(Nspace ns)
     {
         if (ns.semanticRun >= PASS.semantic3)
@@ -1599,34 +1589,10 @@ private extern(C++) final class Semantic3Visitor : Visitor
 
         sc2.pop();
 
-        // Instantiate RTInfo!S to provide a pointer bitmap for the GC
-        // Don't do it in -betterC or on unused deprecated / error types
-        if (!ad.getRTInfo && global.params.useTypeInfo && Type.rtinfo &&
-            (!ad.isDeprecated() || global.params.useDeprecated != DiagnosticReporting.error) &&
-            (ad.type && ad.type.ty != Terror))
-        {
-            // Evaluate: RTinfo!type
-            auto tiargs = new Objects(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();
+        else
+            ad.semanticRTInfo();
         ad.semanticRun = PASS.semantic3done;
     }
 
@@ -1737,6 +1703,21 @@ private struct FuncDeclSem3
             }
         }
     }
+
+    /* Determine if function should add `return 0;`
+     */
+    bool addReturn0()
+    {
+        //printf("addReturn0()\n");
+        auto f = funcdecl.type.isTypeFunction();
+        if (!f) return false;
+
+        // C11 5.1.2.2.3
+        if (sc.inCfile && funcdecl.isCMain() && f.next.ty == Tint32)
+            return true;
+
+        return f.next.ty == Tvoid && (funcdecl.isMain() || funcdecl.isCMain());
+    }
 }
 
 /***************************************
@@ -1772,25 +1753,20 @@ FuncDeclaration search_toString(StructDeclaration sd)
  */
 void semanticTypeInfoMembers(StructDeclaration sd)
 {
-    if (sd.xeq &&
-        sd.xeq._scope &&
-        sd.xeq.semanticRun < PASS.semantic3done)
+    void runSemantic(ref FuncDeclaration fd, ref FuncDeclaration errFd)
     {
-        const errors = global.startGagging();
-        sd.xeq.semantic3(sd.xeq._scope);
-        if (global.endGagging(errors))
-            sd.xeq = sd.xerreq;
+        if (fd && fd._scope && fd.semanticRun < PASS.semantic3done)
+        {
+            const errors = global.startGagging();
+            fd.semantic3(fd._scope);
+            if (global.endGagging(errors))
+                fd = errFd;
+        }
     }
 
-    if (sd.xcmp &&
-        sd.xcmp._scope &&
-        sd.xcmp.semanticRun < PASS.semantic3done)
-    {
-        const errors = global.startGagging();
-        sd.xcmp.semantic3(sd.xcmp._scope);
-        if (global.endGagging(errors))
-            sd.xcmp = sd.xerrcmp;
-    }
+    runSemantic(sd.xeq, sd.xerreq);
+    runSemantic(sd.xcmp, sd.xerrcmp);
+
 
     FuncDeclaration ftostr = search_toString(sd);
     if (ftostr &&
@@ -1820,6 +1796,38 @@ void semanticTypeInfoMembers(StructDeclaration sd)
     {
         sd.dtor.semantic3(sd.dtor._scope);
     }
+    sd.semanticRTInfo();
+}
+
+void semanticRTInfo(AggregateDeclaration ad)
+{
+    // Instantiate RTInfo!S to provide a pointer bitmap for the GC
+    // Don't do it in -betterC or on error types
+    if (ad.getRTInfo || !global.params.useTypeInfo || !Type.rtinfo)
+        return;
+    if (!ad.rtInfoScope || !ad.type || ad.type.ty == Terror)
+        return;
+
+    // Evaluate: RTinfo!type
+    auto tiargs = new Objects(ad.type);
+    auto ti = new TemplateInstance(ad.loc, Type.rtinfo, tiargs);
+
+    auto sc = ad.rtInfoScope;
+    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;
 }
 
 /**
@@ -1845,7 +1853,7 @@ extern (D) bool checkClosure(FuncDeclaration fd)
     if (!fd.needsClosure())
         return false;
 
-    if (fd.setGC(fd.loc, "allocating a closure for `%s()`", fd))
+    if (setGC(null, fd, fd.loc, "allocating a closure for `%s()`", fd))
     {
         .error(fd.loc, "%s `%s` is `@nogc` yet allocates closure for `%s()` with the GC", fd.kind, fd.toPrettyChars(), fd.toChars());
         if (global.gag)     // need not report supplemental errors
@@ -1876,21 +1884,16 @@ extern (D) bool checkClosure(FuncDeclaration fd)
                 auto fx = s.isFuncDeclaration();
                 if (!fx)
                     continue;
-                if (fx.isThis() ||
-                    fx.tookAddressOf ||
-                    checkEscapingSiblings(fx, fd))
+                if (fx.isThis() || fx.tookAddressOf || checkEscapingSiblings(fx, fd))
                 {
-                    foreach (f2; a)
+                    if (!a.contains(f))
                     {
-                        if (f2 == f)
-                            break LcheckAncestorsOfANestedRef;
+                        a.push(f);
+                        .errorSupplemental(f.loc, "%s `%s` closes over variable `%s`",
+                            f.kind, f.toErrMsg(), v.toChars());
+                        if (v.ident != Id.This)
+                            .errorSupplemental(v.loc, "`%s` declared here", v.toChars());
                     }
-                    a.push(f);
-                    .errorSupplemental(f.loc, "%s `%s` closes over variable `%s`",
-                        f.kind, f.toErrMsg(), v.toChars());
-                    if (v.ident != Id.This)
-                        .errorSupplemental(v.loc, "`%s` declared here", v.toChars());
-
                     break LcheckAncestorsOfANestedRef;
                 }
             }
index 9a9079943b552c4b4045e10f2928b8f9a912ea1b..35d4b1bf3f4cc668f50d1e04d142dc13dc972034 100644 (file)
@@ -62,6 +62,7 @@ import dmd.semantic2;
 import dmd.sideeffect;
 import dmd.statement;
 import dmd.target;
+import dmd.targetcompiler;
 import dmd.tokens;
 import dmd.typesem;
 import dmd.visitor;
@@ -71,6 +72,15 @@ version (DMDLIB)
     version = CallbackAPI;
 }
 
+/*****************************************
+ * Returns:
+ *     `true` iff ready to call `dmd.statementsem.makeTupleForeach`.
+ */
+bool ready(StaticForeach _this)
+{
+    return _this.aggrfe && _this.aggrfe.aggr && _this.aggrfe.aggr.type && _this.aggrfe.aggr.type.toBasetype().ty == Ttuple;
+}
+
 /*****************************************
  * CTFE requires FuncDeclaration::labtab for the interpretation.
  * So fixing the label name inside in/out contracts is necessary
@@ -786,18 +796,20 @@ Statement statementSemanticVisit(Statement s, Scope* sc)
 
         Dsymbol sapplyOld = sapply; // 'sapply' will be NULL if and after 'inferApplyArgTypes' errors
 
-        /* Check for inference errors and apply mutability checks inline */
+        /* Check for inference errors and apply modifier checks inline */
         if (!inferApplyArgTypes(fs, sc, sapply))
         {
             bool foundMismatch = false;
             size_t foreachParamCount = 0;
+
             if (sapplyOld)
             {
                 if (FuncDeclaration fd = sapplyOld.isFuncDeclaration())
                 {
                     auto fparameters = fd.getParameterList();
 
-                    if (fparameters.length == 1)
+                    // ignore overloads, can't determine which non-matching is closest
+                    if (!fd.overnext && fparameters.length == 1)
                     {
                         // first param should be the callback function
                         Parameter fparam = fparameters[0];
@@ -809,16 +821,14 @@ Statement statementSemanticVisit(Statement s, Scope* sc)
                             foreachParamCount = tf.parameterList.length;
                             foundMismatch = true;
 
-                            // Mutability check
-                            if (fs.aggr && fs.aggr.type && fd.type && fs.aggr.type.isConst() && !fd.type.isConst())
+                            if (fd.isThis() &&
+                                !MODmethodConv(fs.aggr.type.mod, fd.type.mod))
                             {
-                                // First error: The call site
-                                error(fs.loc, "mutable method `%s.%s` is not callable using a `const` object",
-                                    fd.parent ? fd.parent.toPrettyChars() : "unknown", fd.toChars());
-
-                                // Second error: Suggest how to fix
-                                errorSupplemental(fd.loc, "Consider adding `const` or `inout` here");
-
+                                error(fs.aggr.loc, "%s method `%s` is not callable using a `%s` foreach aggregate",
+                                    !fd.type.mod ? "mutable" : fd.type.modToChars(),
+                                    fd.toPrettyChars(),
+                                    fs.aggr.type.toChars());
+                                errorSupplemental(fd.loc, "Consider adding a method type qualifier here");
                                 return setError();
                             }
                         }
@@ -839,24 +849,6 @@ Statement statementSemanticVisit(Statement s, Scope* sc)
             return setError();
         }
 
-        // If inference succeeds, proceed with post-checks
-        if (sapply && sapply.isFuncDeclaration())
-        {
-            FuncDeclaration fd = sapply.isFuncDeclaration();
-
-            if (fs.aggr && fs.aggr.type && fd.type && fs.aggr.type.isConst() && !fd.type.isConst())
-            {
-                // First error: The call site
-                error(fs.loc, "mutable method `%s.%s` is not callable using a `const` object",
-                    fd.parent ? fd.parent.toPrettyChars() : "unknown", fd.toChars());
-
-                // Second error: Suggest how to fix
-                errorSupplemental(fd.loc, "Consider adding `const` or `inout` here");
-
-                return setError();
-            }
-        }
-
         Type tab = fs.aggr.type.toBasetype();
 
         if (tab.ty == Ttuple) // don't generate new scope for tuple loops
@@ -2553,16 +2545,44 @@ Statement statementSemanticVisit(Statement s, Scope* sc)
 
         if (fd.isCtorDeclaration())
         {
-            if (rs.exp)
+            // Constructors implicitly do:
+            //      return this;
+            auto ctorReturn = new ThisExp(Loc.initial);
+            ctorReturn.type = tret;
+
+            bool isConstructorCall(Expression e)
             {
-                error(rs.loc, "cannot return expression from constructor");
-                errors = true;
+                auto ce = e.isCallExp();
+                if (!ce)
+                    return false;
+
+                auto dve = ce.e1.isDotVarExp();
+                if (!dve)
+                    return false;
+
+                return dve.var.isThis !is null;
             }
 
-            // Constructors implicitly do:
-            //      return this;
-            rs.exp = new ThisExp(Loc.initial);
-            rs.exp.type = tret;
+            if (rs.exp)
+            {
+                rs.exp = rs.exp.expressionSemantic(sc);
+
+                if (rs.exp.type.ty != Tvoid && !isConstructorCall(rs.exp))
+                {
+
+                    error(rs.loc, "can only return void expression, `this` call or `super` call from constructor");
+                    errors = true;
+                    rs.exp = ErrorExp.get();
+                }
+                else
+                {
+                    rs.exp = new CommaExp(rs.loc, rs.exp, ctorReturn).expressionSemantic(sc);
+                }
+            }
+            else
+            {
+                rs.exp = ctorReturn;
+            }
         }
         else if (rs.exp)
         {
@@ -2586,7 +2606,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc)
                 rs.exp = resolveAliasThis(sc, rs.exp);
 
             rs.exp = resolveProperties(sc, rs.exp);
-            if (rs.exp.checkType())
+            if (!rs.exp.hasValidType())
                 rs.exp = ErrorExp.get();
             if (auto f = isFuncAddress(rs.exp))
             {
@@ -2596,6 +2616,15 @@ Statement statementSemanticVisit(Statement s, Scope* sc)
             if (checkNonAssignmentArrayOp(rs.exp))
                 rs.exp = ErrorExp.get();
 
+            if (fd.ident == Id.apply && rs.exp.isConst() && rs.exp.toInteger() != 0)
+            {
+                // @@@DEPRECATED_2.121
+                // uncomment ErrorExp and call `error`
+                deprecation(rs.exp.loc, "cannot return non-zero compile-time value from `opApply`");
+                deprecationSupplemental(rs.exp.loc, "Any non-zero value must be the result of calling its delegate");
+                //rs.exp = ErrorExp.get();
+            }
+
             // Extract side-effect part
             rs.exp = Expression.extractLast(rs.exp, e0);
             if (rs.exp.isCallExp())
@@ -3109,12 +3138,12 @@ Statement statementSemanticVisit(Statement s, Scope* sc)
                 auto args = new Parameters(new Parameter(Loc.initial, STC.none, ClassDeclaration.object.type,
                                                          null, null, null));
 
-                FuncDeclaration fdenter = FuncDeclaration.genCfunc(args, Type.tvoid, Id.monitorenter);
+                FuncDeclaration fdenter = 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);
+                FuncDeclaration fdexit = 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);
@@ -3149,7 +3178,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc)
 
             auto enterArgs = new Parameters(new Parameter(Loc.initial, STC.none, t.pointerTo(), null, null, null));
 
-            FuncDeclaration fdenter = FuncDeclaration.genCfunc(enterArgs, Type.tvoid, Id.criticalenter, STC.nothrow_);
+            FuncDeclaration fdenter = 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);
@@ -3158,7 +3187,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc)
 
             auto exitArgs = new Parameters(new Parameter(Loc.initial, STC.none, t, null, null, null));
 
-            FuncDeclaration fdexit = FuncDeclaration.genCfunc(exitArgs, Type.tvoid, Id.criticalexit, STC.nothrow_);
+            FuncDeclaration fdexit = 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);
@@ -3300,7 +3329,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc)
 
         if (!global.params.useExceptions)
         {
-            error(tcs.loc, "cannot use try-catch statements with %s", global.params.betterC ? "-betterC".ptr : "-nothrow".ptr);
+            error(tcs.loc, "cannot use try-catch statements with `%s`", global.params.betterC ? "-betterC".ptr : "-nothrow".ptr);
             return setError();
         }
 
@@ -3429,11 +3458,14 @@ Statement statementSemanticVisit(Statement s, Scope* sc)
             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)
+        // Only rewrite if it was requested.
+        // This has side effects where Error will not run destructors, unsafe.
+        if (global.params.rewriteNoExceptionToSeq && (blockexit & ~BE.halt) == BE.fallthru)
         {
             result = new CompoundStatement(tfs.loc, tfs._body, tfs.finalbody);
             return;
         }
+
         tfs.bodyFallsThru = (blockexit & BE.fallthru) != 0;
         result = tfs;
     }
@@ -3448,10 +3480,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc)
             // https://issues.dlang.org/show_bug.cgi?id=23159
             if (!global.params.useExceptions)
             {
-                version (IN_GCC)
-                    error(oss.loc, "`%s` cannot be used with `-fno-exceptions`", Token.toChars(oss.tok));
-                else
-                    error(oss.loc, "`%s` cannot be used with -betterC", Token.toChars(oss.tok));
+                error(oss.loc, "`%s` cannot be used with `-%s`", Token.toChars(oss.tok), SwitchScopeGuard);
                 return setError();
             }
 
@@ -3649,7 +3678,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc)
         assert(sc.func);
         if (!(cas.stc & STC.pure_) && sc.func.setImpure(cas.loc, "executing an `asm` statement without `pure` annotation"))
             error(cas.loc, "`asm` statement is assumed to be impure - mark it with `pure` if it is not");
-        if (!(cas.stc & STC.nogc) && sc.func.setGC(cas.loc, "executing an `asm` statement without `@nogc` annotation"))
+        if (!(cas.stc & STC.nogc) && sc.setGC(sc.func, cas.loc, "executing an `asm` statement without `@nogc` annotation"))
             error(cas.loc, "`asm` statement is assumed to use the GC - mark it with `@nogc` if it does not");
         // @@@DEPRECATED_2.114@@@
         // change deprecation() to error(), add `else` and remove `| STC.safe`
@@ -3693,7 +3722,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc)
             // for the current scope.
             if (s.mod !is null)
             {
-                Module.addDeferredSemantic2(s);     // https://issues.dlang.org/show_bug.cgi?id=14666
+                addDeferredSemantic2(s);     // https://issues.dlang.org/show_bug.cgi?id=14666
                 sc.insert(s);
 
                 foreach (aliasdecl; s.aliasdecls)
@@ -3724,10 +3753,8 @@ public bool throwSemantic(Loc loc, ref Expression exp, Scope* sc)
 {
     if (!global.params.useExceptions)
     {
-        version (IN_GCC)
-            loc.error("cannot use `throw` statements with `-fno-exceptions`");
-        else
-            loc.error("cannot use `throw` statements with %s", global.params.betterC ? "-betterC".ptr : "-nothrow".ptr);
+        const(char)* s = SwitchExceptions ? SwitchExceptions : global.params.betterC ? "betterC".ptr : "nothrow".ptr;
+        loc.error("cannot use `throw` statements with `-%s`", s);
         return false;
     }
 
@@ -3874,7 +3901,7 @@ private extern(D) Expression applyArray(ForeachStatement fs, Expression flde,
         dgparams.push(new Parameter(Loc.initial, STC.none, Type.tvoidptr, null, null, null));
     dgty = new TypeDelegate(new TypeFunction(ParameterList(dgparams), Type.tint32, LINK.d));
     params.push(new Parameter(Loc.initial, STC.none, dgty, null, null, null));
-    fdapply = FuncDeclaration.genCfunc(params, Type.tint32, fdname.ptr);
+    fdapply = genCfunc(params, Type.tint32, fdname.ptr);
 
     if (tab.isTypeSArray())
         fs.aggr = fs.aggr.castTo(sc2, tn.arrayOf());
@@ -4865,7 +4892,10 @@ private Statements* flatten(Statement statement, Scope* sc)
             {
                 Statement s = p.parseStatement(ParseStatementFlags.curlyScope);
                 if (!s || global.errors != errors)
+                {
+                    errorSupplemental(s.loc, "while parsing string mixin statement");
                     return errorStatements();
+                }
                 a.push(s);
             }
             return a;
index 52ded559e52a19cb48dce9fac4b32db16441eae8..2957fa6e8f3f16b0363c89b9cd4be15c90030fc6 100644 (file)
@@ -18,7 +18,6 @@ import dmd.dsymbol;
 import dmd.expression;
 import dmd.location;
 import dmd.id;
-import dmd.identifier;
 import dmd.visitor;
 
 /***********************************************************
diff --git a/gcc/d/dmd/targetcompiler.d b/gcc/d/dmd/targetcompiler.d
new file mode 100644 (file)
index 0000000..8631c56
--- /dev/null
@@ -0,0 +1,211 @@
+/**
+ * Encapsulates the vagaries of which of the three D compilers (gdc, ldc or dmd) is being built
+ *
+ * Copyright:   Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved
+ * Authors:     $(LINK2 https://www.digitalmars.com, Walter Bright)
+ * License:     $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source:      $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/mars.d, _targetcompiler.d)
+ * Documentation:  https://dlang.org/phobos/dmd_targetcompiler.html
+ * Coverage:    https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/targetcompiler.d
+ */
+
+module dmd.targetcompiler;
+
+version (IN_GCC) {}        // compiler is being built with gdc
+else version (IN_LLVM) {}  // compiler is being built with ldc
+else version = MARS;       // default means compiler is built with Digital Mars compiler (DMD)
+
+/***************************
+ */
+
+version (IN_GCC)
+{
+    enum TargetCompiler = "GNU D";    // compiler being built
+    enum SwitchPrefix = "f";          // prefix to switch
+    enum SwitchVariadic = "fno-rtti"; // disables D-style variadic functions
+    enum const(char)* SwitchExceptions = "fno-exceptions"; // switch that disables exceptions
+    enum const(char)* SwitchScopeGuard = "fno-exceptions"; // switch that disables exceptions
+}
+else version (IN_LLVM)
+{
+    enum TargetCompiler = "LDC";
+    enum SwitchPrefix = "";
+    enum SwitchVariadic = "betterC";
+    enum const(char)* SwitchExceptions = null;
+    enum const(char)* SwitchScopeGuard = "betterC";
+}
+else version (MARS)
+{
+    enum TargetCompiler = "Digital Mars D";
+    enum SwitchPrefix = "";
+    enum SwitchVariadic = "betterC";
+    enum const(char)* SwitchExceptions = null;
+    enum const(char)* SwitchScopeGuard = "betterC";
+}
+else
+    static assert(0, "unknown compiler being built");
+
+
+/**********************************
+ * Extra Fields for VarDeclaration
+ */
+version (IN_GCC)
+{
+    mixin template VarDeclarationExtra() { }
+}
+else version (IN_LLVM)
+{
+    mixin template VarDeclarationExtra() { }
+}
+else version (MARS)
+{
+    mixin template VarDeclarationExtra()
+    {
+        bool inClosure;         /// is inserted into a GC allocated closure
+        bool inAlignSection;    /// is inserted into an aligned section on stack
+    }
+}
+else
+    static assert(0, "unknown compiler being built");
+
+/**********************************
+ * Extra Fields for FuncDeclaration
+ */
+version (IN_GCC)
+{
+    mixin template FuncDeclarationExtra() { }
+}
+else version (IN_LLVM)
+{
+    mixin template FuncDeclarationExtra() { }
+}
+else version (MARS)
+{
+    mixin template FuncDeclarationExtra()
+    {
+        VarDeclarations* alignSectionVars;  /// local variables with alignment needs larger than stackAlign
+        import dmd.backend.cc : Symbol;
+        Symbol* salignSection;              /// pointer to aligned section, if any
+    }
+}
+else
+    static assert(0, "unknown compiler being built");
+
+/**********************************
+ * Extra Fields for FuncDeclaration
+ */
+version (IN_GCC)
+{
+    mixin template alignSectionVarsExtra() { void doAlign() { } }
+}
+else version (IN_LLVM)
+{
+    mixin template alignSectionVarsExtra() { void doAlign() { } }
+}
+else version (MARS)
+{
+    /* If the alignment of a stack local is greater than the stack alignment,
+     * note it in the enclosing function's alignSectionVars
+     */
+    mixin template alignSectionVarsExtra()
+    {
+        void doAlign()
+        {
+            if (!dsym.alignment.isDefault() && sc.func &&
+                dsym.alignment.get() > target.stackAlign() &&
+                sc.func && !dsym.isDataseg() && !dsym.isParameter() && !dsym.isField())
+            {
+                auto fd = sc.func;
+                if (!fd.alignSectionVars)
+                    fd.alignSectionVars = new VarDeclarations();
+                fd.alignSectionVars.push(dsym);
+            }
+        }
+    }
+}
+else
+    static assert(0, "unknown compiler being built");
+
+
+/* Returns: true if v is in an align section
+ */
+mixin template alignSectionVarsContains()
+{
+    version (IN_GCC)
+    {
+        bool isAlignSectionVar(VarDeclaration v) { return false; }
+    }
+    else version (IN_LLVM)
+    {
+        bool isAlignSectionVar(VarDeclaration v) { return false; }
+    }
+    else version (MARS)
+    {
+        bool isAlignSectionVar(VarDeclaration v)
+        {
+            return fd.alignSectionVars && (*fd.alignSectionVars).contains(v);
+        }
+    }
+    else
+        static assert(0, "unknown compiler being built");
+}
+
+/* Returns: true if ansi color is to be used
+ */
+
+mixin template UseAnsiColors()
+{
+    bool useAnsiColors()
+    {
+        version (IN_GCC)
+        {
+            return false;
+        }
+        else version (IN_LLVM)
+        {
+            import dmd.console : detectTerminal;
+            return detectTerminal();
+        }
+        else version (MARS)
+        {
+            // -color=auto is the default value
+            import dmd.console : detectTerminal, detectColorPreference;
+            return detectTerminal() && detectColorPreference();
+        }
+        else
+            static assert(0, "unknown compiler being built");
+    }
+}
+
+/******************************************
+ * Let user know object.d cannot be found.
+ * Parameters:
+ *      loc = location of error
+ *      id = name of symbol that cannot be found
+ *      configFile = configuration file name
+ *      eSink = where to send the error messages
+ */
+
+mixin template HostObjectNotFound()
+{
+    void hostObjectNotFound(Loc loc, const(char)* id, const(char)[] configFile, ErrorSink eSink)
+    {
+        eSink.error(loc, "`%s` not found. object.d may be incorrectly installed or corrupt.", id);
+        version (IN_GCC)
+        {
+        }
+        else version (IN_LLVM)
+        {
+            eSink.errorSupplemental(loc, "ldc2 might not be correctly installed.");
+            eSink.errorSupplemental(loc, "Please check your ldc2.conf configuration file.");
+            eSink.errorSupplemental(loc, "Installation instructions can be found at http://wiki.dlang.org/LDC.");
+        }
+        else version (MARS)
+        {
+            eSink.errorSupplemental(loc, "dmd might not be correctly installed. Run 'dmd -man' for installation instructions.");
+            eSink.errorSupplemental(loc, "config file: %.*s", configFile.length, configFile.ptr);
+        }
+        else
+            static assert(0, "unknown compiler being built");
+    }
+}
index db810511e62b8ee0f6c086f6d703ac9d8ffd8e9c..c38a630d7c9bd26d77e7d190850386f83d36411d 100644 (file)
@@ -78,12 +78,12 @@ public:
     d_bool isTrivialAlias;        // matches pattern `template Alias(T) { alias Alias = qualifiers(T); }`
     d_bool deprecated_;           // this template declaration is deprecated
     d_bool isCmacro;              // Whether this template is a translation of a C macro
+    d_bool haveComputedOneMember;   // Whether computeOneMeber has been called
     Visibility visibility;
 
     TemplatePrevious *previous;         // threaded list of previous instantiation attempts on stack
 
     TemplateDeclaration *syntaxCopy(Dsymbol *) override;
-    bool overloadInsert(Dsymbol *s) override;
     const char *kind() const override;
 
     Visibility visible() override;
@@ -129,7 +129,6 @@ public:
     virtual TemplateTupleParameter *isTemplateTupleParameter();
 
     virtual TemplateParameter *syntaxCopy() = 0;
-    virtual bool declareParameter(Scope *sc) = 0;
     virtual void print(RootObject *oarg, RootObject *oded) = 0;
     virtual RootObject *specialization() = 0;
     virtual bool hasDefaultArg() = 0;
@@ -150,7 +149,6 @@ public:
 
     TemplateTypeParameter *isTemplateTypeParameter() override final;
     TemplateTypeParameter *syntaxCopy() override;
-    bool declareParameter(Scope *sc) override final;
     void print(RootObject *oarg, RootObject *oded) override final;
     RootObject *specialization() override final;
     bool hasDefaultArg() override final;
@@ -180,7 +178,6 @@ public:
 
     TemplateValueParameter *isTemplateValueParameter() override;
     TemplateValueParameter *syntaxCopy() override;
-    bool declareParameter(Scope *sc) override;
     void print(RootObject *oarg, RootObject *oded) override;
     RootObject *specialization() override;
     bool hasDefaultArg() override;
@@ -199,7 +196,6 @@ public:
 
     TemplateAliasParameter *isTemplateAliasParameter() override;
     TemplateAliasParameter *syntaxCopy() override;
-    bool declareParameter(Scope *sc) override;
     void print(RootObject *oarg, RootObject *oded) override;
     RootObject *specialization() override;
     bool hasDefaultArg() override;
@@ -214,7 +210,6 @@ class TemplateTupleParameter final : public TemplateParameter
 public:
     TemplateTupleParameter *isTemplateTupleParameter() override;
     TemplateTupleParameter *syntaxCopy() override;
-    bool declareParameter(Scope *sc) override;
     void print(RootObject *oarg, RootObject *oded) override;
     RootObject *specialization() override;
     bool hasDefaultArg() override;
@@ -248,7 +243,6 @@ public:
     Dsymbol *aliasdecl;                 // !=NULL if instance is an alias for its sole member
     TemplateInstance *inst;             // refer to existing instance
     ScopeDsymbol *argsym;               // argument symbol table
-    hash_t hash;                        // cached result of toHash()
     Expressions *fargs;                 // for function template, these are the function arguments
     Identifiers *fnames;                // for function template, argument names
 
@@ -300,4 +294,5 @@ namespace dmd
     bool isError(const RootObject *const o);
     void printTemplateStats(bool listInstances, ErrorSink* eSink);
     void printInstantiationTrace(TemplateInstance *ti);
+    bool declareParameter(TemplateParameter *tp, Scope *sc);
 }
index 561181a2cc193249dac9e5b5fd5d001e1bfce6be..4954b1d2be7cb4fb9a9c2317c0008a2f4cabb608 100644 (file)
@@ -20,6 +20,7 @@ import dmd.globals;
 import dmd.location;
 import dmd.expression;
 import dmd.expressionsem;
+import dmd.templatesem;
 import dmd.rootobject;
 import dmd.mtype;
 import dmd.typesem;
index b392476a44bcb01e9c42f872ae10ae5f33d58c04..ac107b049241badcd918883b6eef39bc06c6fc46 100644 (file)
@@ -25,6 +25,7 @@ import dmd.declaration;
 import dmd.dinterpret;
 import dmd.dmodule;
 import dmd.dscope;
+import dmd.dstruct;
 import dmd.dsymbol;
 import dmd.dsymbolsem;
 import dmd.dtemplate;
@@ -51,6 +52,7 @@ import dmd.rootobject;
 import dmd.semantic2;
 import dmd.semantic3;
 import dmd.templateparamsem;
+import dmd.timetrace;
 import dmd.tokens;
 import dmd.typesem;
 import dmd.visitor;
@@ -59,154 +61,2323 @@ alias funcLeastAsSpecialized = dmd.funcsem.leastAsSpecialized;
 
 enum LOG = false;
 
+/***********************************************************
+ * 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.length]);
+}
+
+/***********************************************************
+ * 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.
+ */
+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) @safe
+        {
+            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.placement)
+                e.placement.accept(this);
+            if (e.thisexp)
+                e.thisexp.accept(this);
+            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(e.isUnaExp());
+            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(e.isUnaExp());
+            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(e.isUnaExp());
+            // 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(e.isUnaExp());
+            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(e.isUnaExp());
+            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(e.isBinExp());
+        }
+    }
+
+    scope ReliesOnTemplateParameters v = new ReliesOnTemplateParameters(tparams);
+    e.accept(v);
+    return v.result;
+}
+
+void computeOneMember(TemplateDeclaration td)
+{
+    if (td is null || td.haveComputedOneMember)
+        return;
+
+    if (td && td.members && td.ident)
+    {
+        Dsymbol s;
+        if (oneMembers(td.members, s, td.ident) && s)
+        {
+            td.onemember = s;
+            s.parent = td;
+            td.computeIsTrivialAlias(s);
+        }
+        td.haveComputedOneMember = true;
+    }
+}
+
+bool declareParameter(TemplateParameter _this, Scope* sc)
+{
+    static bool typeDeclareParameter(TemplateTypeParameter _this, Scope* sc)
+    {
+        //printf("TemplateTypeParameter.declareParameter('%s')\n", ident.toChars());
+        auto ti = new TypeIdentifier(_this.loc, _this.ident);
+        Declaration ad = new AliasDeclaration(_this.loc, _this.ident, ti);
+        return sc.insert(ad) !is null;
+    }
+
+    static bool valueDeclareParameter(TemplateValueParameter _this, Scope* sc)
+    {
+        /*
+            Do type semantic earlier.
+
+            This means for certain erroneous value parameters
+            their "type" can be known earlier and thus a better
+            error message given.
+
+            For example:
+            `template test(x* x) {}`
+            now yields "undefined identifier" rather than the opaque
+            "variable `x` is used as a type".
+         */
+        if (_this.valType)
+            _this.valType = _this.valType.typeSemantic(_this.loc, sc);
+        auto v = new VarDeclaration(_this.loc, _this.valType, _this.ident, null);
+        v.storage_class = STC.templateparameter;
+        return sc.insert(v) !is null;
+    }
+
+    static bool aliasDeclareParameter(TemplateAliasParameter _this, Scope* sc)
+    {
+        auto ti = new TypeIdentifier(_this.loc, _this.ident);
+        Declaration ad = new AliasDeclaration(_this.loc, _this.ident, ti);
+        return sc.insert(ad) !is null;
+    }
+
+    static bool tupleDeclareParameter(TemplateTupleParameter _this, Scope* sc)
+    {
+        auto ti = new TypeIdentifier(_this.loc, _this.ident);
+        Declaration ad = new AliasDeclaration(_this.loc, _this.ident, ti);
+        return sc.insert(ad) !is null;
+    }
+
+    if (auto tp = _this.isTemplateTypeParameter())
+        return typeDeclareParameter(tp, sc);
+    else if (auto vp = _this.isTemplateValueParameter())
+        return valueDeclareParameter(vp, sc);
+    else if (auto ap = _this.isTemplateAliasParameter())
+        return aliasDeclareParameter(ap, sc);
+    else if (auto tup = _this.isTemplateTupleParameter())
+        return tupleDeclareParameter(tup, sc);
+
+    assert(0); // unreachable
+}
+
 /************************************
- * Perform semantic analysis on template.
+ * Return hash of Objects.
+ */
+private size_t arrayObjectHash(ref 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))
+        {
+            if (auto fa1 = s1.isFuncAliasDeclaration())
+                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 EXP.int64:
+        return cast(size_t) e.isIntegerExp().getInteger();
+
+    case EXP.float64:
+        return CTFloat.hash(e.isRealExp().value);
+
+    case EXP.complex80:
+        auto ce = e.isComplexExp();
+        return mixHash(CTFloat.hash(ce.toReal), CTFloat.hash(ce.toImaginary));
+
+    case EXP.identifier:
+        return cast(size_t)cast(void*) e.isIdentifierExp().ident;
+
+    case EXP.null_:
+        return cast(size_t)cast(void*) e.isNullExp().type;
+
+    case EXP.string_:
+        return calcHash(e.isStringExp.peekData());
+
+    case EXP.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 EXP.arrayLiteral:
+    {
+        auto ae = e.isArrayLiteralExp();
+        size_t hash;
+        foreach (i; 0 .. ae.elements.length)
+            hash = mixHash(hash, expressionHash(ae[i]));
+        return hash;
+    }
+
+    case EXP.assocArrayLiteral:
+    {
+        auto ae = e.isAssocArrayLiteralExp();
+        size_t hash;
+        foreach (i; 0 .. ae.keys.length)
+            // reduction needs associative op as keys are unsorted (use XOR)
+            hash ^= mixHash(expressionHash((*ae.keys)[i]), expressionHash((*ae.values)[i]));
+        return hash;
+    }
+
+    case EXP.structLiteral:
+    {
+        auto se = e.isStructLiteralExp();
+        size_t hash;
+        foreach (elem; *se.elements)
+            hash = mixHash(hash, elem ? expressionHash(elem) : 0);
+        return hash;
+    }
+
+    case EXP.variable:
+        return cast(size_t)cast(void*) e.isVarExp().var;
+
+    case EXP.function_:
+        return cast(size_t)cast(void*) e.isFuncExp().fd;
+
+    default:
+        // no custom equals for this expression
+        //assert((&e.equals).funcptr is &Expression.equals);
+        // equals based on identity
+        return cast(size_t)cast(void*) e;
+    }
+}
+
+/************************************
+ * This struct is needed for TemplateInstance to be the key in an associative array.
+ * Fixing https://issues.dlang.org/show_bug.cgi?id=15813 would make it unnecessary.
+ */
+struct TemplateInstanceBox
+{
+    TemplateInstance ti;
+    size_t hash; // cached result of toHash()
+
+    this(TemplateInstance ti)
+    {
+        this.ti = ti;
+        hash = cast(size_t)cast(void*)this.ti.enclosing;
+        hash += arrayObjectHash(this.ti.tdtypes);
+        hash += hash == 0;
+    }
+
+    size_t toHash() const @safe pure nothrow
+    {
+        assert(hash);
+        return 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.
+             */
+            static if (__VERSION__ < 2099) // https://issues.dlang.org/show_bug.cgi?id=22717
+                res = (cast()s.ti).equalsx(cast()ti);
+            else
+                res = (cast()ti).equalsx(cast()s.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);
+        }
+    }
+}
+
+/************************************
+ * Perform semantic analysis on template.
+ * Params:
+ *      sc = context
+ *      tempdecl = template declaration
+ */
+void templateDeclarationSemantic(Scope* sc, TemplateDeclaration tempdecl)
+{
+    enum log = false;
+    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.initial)
+        return; // semantic() already run
+
+    if (tempdecl._scope)
+    {
+        sc = tempdecl._scope;
+        tempdecl._scope = null;
+    }
+    if (!sc)
+        return;
+
+    import dmd.timetrace;
+    timeTraceBeginEvent(TimeTraceEventType.sema1TemplateDecl);
+    scope (exit) timeTraceEndEvent(TimeTraceEventType.sema1TemplateDecl, tempdecl);
+    // 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.userAttribDecl = sc.userAttribDecl;
+    tempdecl.cppnamespace = sc.namespace;
+    tempdecl.isstatic = tempdecl.toParent().isModule() || (tempdecl._scope.stc & STC.static_);
+    tempdecl.deprecated_ = !!(sc.stc & STC.deprecated_);
+
+    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 = STC.none;
+
+    if (global.params.ddoc.doOutput)
+    {
+        tempdecl.origParameters = new TemplateParameters(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 (!tp.tpsemantic(paramscope, tempdecl.parameters))
+        {
+            tempdecl.errors = true;
+        }
+        if (i + 1 != tempdecl.parameters.length && tp.isTemplateTupleParameter())
+        {
+            tempdecl.computeOneMember(); // for .kind
+            .error(tempdecl.loc, "%s `%s` template sequence parameter must be the last one", tempdecl.kind, tempdecl.toPrettyChars);
+            tempdecl.errors = true;
+        }
+    }
+
+    /* Calculate TemplateParameter.dependent
+     */
+    auto tparams = TemplateParameters(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;
+    tempdecl.computeOneMember();
+    /* 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;
+}
+
+void templateInstanceSemantic(TemplateInstance tempinst, Scope* sc, ArgumentList argumentList)
+{
+    //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.initial)
+    {
+        static if (LOG)
+        {
+            printf("Recursive template expansion\n");
+        }
+        auto ungag = Ungag(global.gag);
+        if (!tempinst.gagged)
+            global.gag = 0;
+        .error(tempinst.loc, "%s `%s` recursive template expansion", tempinst.kind, tempinst.toPrettyChars);
+        if (tempinst.gagged)
+            tempinst.semanticRun = PASS.initial;
+        else
+            tempinst.inst = tempinst;
+        tempinst.errors = true;
+        return;
+    }
+
+    timeTraceBeginEvent(TimeTraceEventType.sema1TemplateInstance);
+    scope (exit) timeTraceEndEvent(TimeTraceEventType.sema1TemplateInstance, tempinst);
+
+    // 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, argumentList))
+    {
+    Lerror:
+        if (tempinst.gagged)
+        {
+            // https://issues.dlang.org/show_bug.cgi?id=13220
+            // Roll back status for later semantic re-running
+            tempinst.semanticRun = PASS.initial;
+        }
+        else
+            tempinst.inst = tempinst;
+        tempinst.errors = true;
+        return;
+    }
+    TemplateDeclaration tempdecl = tempinst.tempdecl.isTemplateDeclaration();
+    assert(tempdecl);
+
+    if (global.params.v.templates)
+        TemplateStats.incInstance(tempdecl, tempinst, global.params.v.templatesListInstances);
+
+    tempdecl.checkDeprecated(tempinst.loc, sc);
+
+    // If tempdecl is a mixin, disallow it
+    if (tempdecl.ismixin)
+    {
+        .error(tempinst.loc, "%s `%s` mixin templates are not regular templates", tempinst.kind, tempinst.toPrettyChars);
+        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, argumentList);
+    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) scope @safe
+                {
+                    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(StructDeclaration sd)
+                {
+                    // need to visit auto-generated methods as well
+                    if (sd.xeq) visit(sd.xeq);
+                    if (sd.xcmp) visit(sd.xcmp);
+                    if (sd.xhash) visit(sd.xhash);
+                    visit(cast(ScopeDsymbol)sd);
+                }
+
+                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 (!global.params.allInst &&
+                tempinst.minst) // if inst was not speculative...
+            {
+                assert(!tempinst.minst.isRoot()); // ... it was previously appended to a non-root module
+                // Append again to the root module members[], so that the instance will
+                // get codegen chances (depending on `tempinst.inst.needsCodegen()`).
+                tempinst.inst.appendToModuleMember();
+            }
+
+            assert(tempinst.inst.memberOf && tempinst.inst.memberOf.isRoot(), "no codegen chances");
+        }
+
+        // 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());
+    }
+    const errorsave = global.errors;
+
+    tempinst.inst = tempinst;
+    tempinst.parent = tempinst.enclosing ? tempinst.enclosing : tempdecl.parent;
+    //printf("parent = '%s'\n", parent.kind());
+
+    if (global.params.v.templates)
+        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.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() is null)
+            continue;
+        Type t = isType((*tempinst.tiargs)[i]);
+        assert(t);
+        if (STC stc = ModToStc(t.mod))
+        {
+            //printf("t = %s, stc = x%llx\n", t.toChars(), stc);
+            auto s = new Dsymbols(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.initial)
+    {
+        .error(tempinst.loc, "%s `%s` template instantiation `%s` forward references template declaration `%s`",
+           tempinst.kind, tempinst.toPrettyChars, 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 = STC.none;
+
+    if (sc.isKnownToHaveACompileTimeContext)
+        _scope.knownACompileTimeOnlyContext = true;
+
+    // Declare each template parameter as an alias for the argument type
+    Scope* paramscope = _scope.push();
+    paramscope.stc = STC.none;
+    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.length = %d\n", tempinst.members.length);
+    if (tempinst.members.length)
+    {
+        Dsymbol s;
+        if (oneMembers(tempinst.members, s, tempdecl.ident) && s)
+        {
+            //printf("tempdecl.ident = %s, s = `%s %s`\n", tempdecl.ident.toChars(), s.kind(), s.toPrettyChars());
+            //printf("setting aliasdecl\n");
+            tempinst.aliasdecl = s;
+        }
+    }
+
+    /* If function template declaration
+     */
+    if (argumentList.length > 0 && 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.inferenceArguments = argumentList;
+        }
+    }
+
+    // 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.length)
+    {
+        Dsymbol s;
+        if (oneMembers(tempinst.members, s, tempdecl.ident) && s)
+        {
+            if (!tempinst.aliasdecl || tempinst.aliasdecl != s)
+            {
+                //printf("tempdecl.ident = %s, 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.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.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());
+
+        /* https://issues.dlang.org/show_bug.cgi?id=23965
+         * DRuntime hooks are not deprecated, but may be used for deprecated
+         * types. Deprecations are disabled while analysing hooks to avoid
+         * spurious error messages.
+         */
+        auto saveUseDeprecated = global.params.useDeprecated;
+        if (sc.isDeprecated() && isDRuntimeHook(tempinst.name))
+            global.params.useDeprecated = DiagnosticReporting.off;
+
+        tempinst.trySemantic3(sc2);
+
+        global.params.useDeprecated = saveUseDeprecated;
+
+        for (size_t i = 0; i < deferred.length; 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.length == 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
+                .error(tempinst.loc, "%s `%s` recursive expansion", tempinst.kind, tempinst.toPrettyChars);
+                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.length)
+                {
+                    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();
+
+        // stop AliasAssign tuple building
+        if (auto td = tempinst.aliasdecl.isTupleDeclaration())
+            td.building = false;
+    }
+
+Laftersemantic:
+    sc2.pop();
+    _scope.pop();
+
+    // Give additional context info if error occurred during instantiation
+    if (global.errors != errorsave)
+    {
+        if (!tempinst.errors)
+        {
+            if (!tempdecl.literal)
+                .error(tempinst.loc, "%s `%s` error instantiating", tempinst.kind, tempinst.toPrettyChars);
+            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.initial;
+            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);
+        (cast(TemplateInstance[TemplateInstanceBox])tempdecl.instances).remove(ti1);
+
+        auto ti2 = TemplateInstanceBox(tempinst);
+        (*(cast(TemplateInstance[TemplateInstanceBox]*) &tempdecl.instances))[ti2] = tempinst;
+    }
+
+    static if (LOG)
+    {
+        printf("-TemplateInstance.dsymbolSemantic('%s', this=%p)\n", tempinst.toChars(), tempinst);
+    }
+}
+
+/*****************************************
+ * Determines if a TemplateInstance will need a nested
+ * generation of the TemplateDeclaration.
+ * Sets enclosing property if so, and returns != 0;
+ */
+private bool hasNestedArgs(TemplateInstance _this, Objects* args, bool isstatic)
+{
+    int nested = 0;
+    //printf("TemplateInstance.hasNestedArgs('%s')\n", tempdecl.ident.toChars());
+
+    // arguments from parent instances are also accessible
+    if (!_this.enclosing)
+    {
+        if (TemplateInstance ti = _this.tempdecl.toParent().isTemplateInstance())
+            _this.enclosing = ti.enclosing;
+    }
+
+    /* Search for the most deeply nested of `dparent` and `enclosing` assigning
+     * `dparent` to `enclosing` if `dparent` is more nested than `enclosing`.
+     *
+     * Returns:
+     *  `true` if an error should be reported
+     */
+    static bool search(Dsymbol dparent, ref Dsymbol enclosing)
+    {
+        if (!dparent || dparent.isModule)
+            return false;
+        if (!enclosing)
+        {
+            enclosing = dparent;
+            return false;
+        }
+        if (enclosing == dparent)
+            return false;
+
+        /* 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)
+                return false; // enclosing is most nested
+        }
+        for (Dsymbol p = dparent; p; p = p.parent)
+        {
+            if (p == enclosing)
+            {
+                enclosing = dparent;
+                return false; // dparent is most nested
+            }
+        }
+        //https://issues.dlang.org/show_bug.cgi?id=17870
+        auto pc = dparent.isClassDeclaration();
+        auto ec = enclosing.isClassDeclaration();
+        if (pc && ec)
+        {
+            if (pc.isBaseOf(ec, null))
+                return false;
+            else if (ec.isBaseOf(pc, null))
+            {
+                enclosing = dparent;
+                return false;
+            }
+        }
+        return true;
+    }
+    int search2(Dsymbol sa)
+    {
+        Dsymbol dparent = sa.toParent2();
+        if (search(dparent, _this.enclosing))
+        {
+            .error(_this.loc, "%s `%s` `%s` is nested in both `%s` and `%s`",
+                   _this.kind, _this.toPrettyChars(), _this.toChars(),
+                   _this.enclosing.toChars(), dparent.toChars());
+            _this.errors = true;
+        }
+        //printf("\tnested inside %s as it references %s\n", enclosing.toChars(), sa.toChars());
+        return 1;
+    }
+    int dsym(Dsymbol sa)
+    {
+        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)
+            return search2(sa);
+        if (ti && ti.enclosing)
+            return search2(sa);
+        if (d && !d.isDataseg()
+              && !(d.storage_class & STC.manifest)
+              && (!d.isFuncDeclaration() || d.isFuncDeclaration().isNested())
+              && !_this.isTemplateMixin())
+        {
+            return search2(sa);
+        }
+        return 0;
+    }
+    /* A nested instance happens when an argument references a local
+     * symbol that is on the stack.
+     */
+    foreach (o; *args)
+    {
+        if (Dsymbol sa = isDsymbol(o))
+        {
+            nested |= dsym(sa);
+            continue;
+        }
+        else if (Tuple va = isTuple(o))
+        {
+            nested |= cast(int)_this.hasNestedArgs(&va.objects, isstatic);
+            continue;
+        }
+        Expression ea = isExpression(o);
+        if (!ea)
+            continue;
+
+        if (auto ve = ea.isVarExp())
+        {
+            nested |= dsym(ve.var);
+            continue;
+        }
+        if (auto te = ea.isThisExp())
+        {
+            nested |= dsym(te.var);
+            continue;
+        }
+        if (auto fe = ea.isFuncExp())
+        {
+            nested |= dsym(fe.td? fe.td : fe.fd);
+            continue;
+        }
+        // Emulate Expression.toMangleBuffer call that had exist in TemplateInstance.genIdent.
+        if (ea.op != EXP.int64 && ea.op != EXP.float64 && ea.op != EXP.complex80 && ea.op != EXP.null_ && ea.op != EXP.string_ && ea.op != EXP.arrayLiteral && ea.op != EXP.assocArrayLiteral && ea.op != EXP.structLiteral)
+        {
+            if (!ea.type.isTypeError())
+                .error(ea.loc, "%s `%s` expression `%s` is not a valid template value argument", _this.kind, _this.toPrettyChars, ea.toChars());
+            _this.errors = true;
+        }
+    }
+    //printf("-TemplateInstance.hasNestedArgs('%s') = %d\n", tempdecl.ident.toChars(), nested);
+    return nested != 0;
+}
+
+/// 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;
+    }
+}
+
+/*****************************************
+ * Append `ti` to the specific module `ti.members[]`
+ */
+private Dsymbols* appendToModuleMember(TemplateInstance ti)
+{
+    Module mi = ti.minst; // instantiated . inserted module
+
+    //printf("%s.appendToModuleMember() enclosing = %s mi = %s\n",
+    //    toPrettyChars(),
+    //    enclosing ? enclosing.toPrettyChars() : null,
+    //    mi ? mi.toPrettyChars() : null);
+    if (global.params.allInst || !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 (needsCodegen()).
+         */
+        static Dsymbol getStrictEnclosing(TemplateInstance ti)
+        {
+            do
+            {
+                if (ti.enclosing)
+                    return ti.enclosing;
+                ti = ti.tempdecl.isInstantiated();
+            } while (ti);
+            return null;
+        }
+
+        Dsymbol enc = getStrictEnclosing(ti);
+        // insert target is made stable by using the module
+        // where tempdecl is declared.
+        mi = (enc ? enc : ti.tempdecl).getModule();
+        if (!mi.isRoot())
+        {
+            if (mi.importedFrom)
+            {
+                mi = mi.importedFrom;
+                assert(mi.isRoot());
+            }
+            else
+            {
+                // This can happen when using the frontend as a library.
+                // Append it to the non-root module.
+            }
+        }
+    }
+    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.
+         * Unless it is re-appended to a root module later (with changed minst).
+         */
+    }
+    //printf("\t-. mi = %s\n", mi.toPrettyChars());
+
+    if (ti.memberOf) // already appended to some module
+    {
+        assert(mi.isRoot(), "can only re-append to a root module");
+        if (ti.memberOf.isRoot())
+            return null; // no need to move to another root module
+    }
+
+    Dsymbols* a = mi.members;
+    a.push(ti);
+    ti.memberOf = mi;
+    if (mi.semanticRun >= PASS.semantic2done && mi.isRoot())
+        addDeferredSemantic2(ti);
+    if (mi.semanticRun >= PASS.semantic3done && mi.isRoot())
+        addDeferredSemantic3(ti);
+    return a;
+}
+
+private void expandMembers(TemplateInstance ti,Scope* sc2)
+{
+    ti.members.foreachDsymbol( (s) { s.setScope (sc2); } );
+
+    ti.members.foreachDsymbol( (s) { s.importAll(sc2); } );
+
+    if (!ti.aliasdecl)
+    {
+        /* static if's are crucial to evaluating aliasdecl correctly. But
+         * evaluating the if/else bodies may require aliasdecl.
+         * So, evaluate the condition for static if's, but not their if/else bodies.
+         * Then try to set aliasdecl.
+         * Later do the if/else bodies.
+         * https://issues.dlang.org/show_bug.cgi?id=23598
+         * It might be better to do this by attaching a lambda to the StaticIfDeclaration
+         * to do the oneMembers call after the sid.include(sc2) is run as part of dsymbolSemantic().
+         */
+        bool done;
+        void staticIfDg(Dsymbol s)
+        {
+            if (done || ti.aliasdecl)
+                return;
+            //printf("\t staticIfDg on '%s %s' in '%s'\n",  s.kind(), s.toChars(), this.toChars());
+            if (!s.isStaticIfDeclaration())
+            {
+                //s.dsymbolSemantic(sc2);
+                done = true;
+                return;
+            }
+            auto sid = s.isStaticIfDeclaration();
+            sid.include(sc2);
+            if (ti.members.length)
+            {
+                Dsymbol sa;
+                if (oneMembers(ti.members, sa, ti.tempdecl.ident) && sa)
+                    ti.aliasdecl = sa;
+            }
+            done = true;
+        }
+
+        ti.members.foreachDsymbol(&staticIfDg);
+    }
+
+    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());
+        runDeferredSemantic();
+    }
+
+    ti.members.foreachDsymbol(&symbolDg);
+}
+
+private void tryExpandMembers(TemplateInstance ti, 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(ti.loc, "%s `%s` recursive expansion exceeded allowed nesting limit", ti.kind, ti.toPrettyChars);
+        fatal();
+    }
+
+    ti.expandMembers(sc2);
+
+    nest--;
+}
+
+private void trySemantic3(TemplateInstance ti, 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(ti.loc, "%s `%s` recursive expansion exceeded allowed nesting limit", ti.kind, ti.toPrettyChars);
+        fatal();
+    }
+
+    semantic3(ti, sc2);
+
+    --nest;
+}
+
+debug (FindExistingInstance)
+{
+    private __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);
+    }
+}
+
+/******************************************************
+ * 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 = STC.none;
+    paramscope.visibility = Visibility(Visibility.Kind.public_);
+
+    TemplateTypeParameter ttp = (*tempdecl.parameters)[0].isTemplateTypeParameter();
+    Type ta = tempinst.tdtypes[0].isType();
+    tempdecl.computeOneMember();
+    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;
+}
+
+/****************************************************
+ * Given a new instance `tithis` of this TemplateDeclaration,
+ * see if there already exists an instance.
+ *
+ * Params:
+ *   td = template declaration
+ *   tithis = template instance to check
+ *   argumentList = For function templates, needed because different
+ *                  `auto ref` resolutions create different instances,
+ *                  even when template parameters are identical
+ *
+ * Returns: that existing instance, or `null` when it doesn't exist
+ */
+private TemplateInstance findExistingInstance(TemplateDeclaration td, TemplateInstance tithis,
+                                      ArgumentList argumentList)
+{
+    //printf("findExistingInstance() %s\n", tithis.toChars());
+    tithis.fargs = argumentList.arguments;
+    tithis.fnames = argumentList.names;
+    auto tibox = TemplateInstanceBox(tithis);
+    auto p = tibox in cast(TemplateInstance[TemplateInstanceBox]) td.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.
+ */
+private TemplateInstance addInstance(TemplateDeclaration td, TemplateInstance ti)
+{
+    //printf("addInstance() %p %s\n", instances, ti.toChars());
+    auto tibox = TemplateInstanceBox(ti);
+    (*(cast(TemplateInstance[TemplateInstanceBox]*) &td.instances))[tibox] = ti;
+    debug (FindExistingInstance) ++nAdded;
+    return ti;
+}
+
+/*******************************************
+ * Remove TemplateInstance from table of instances.
+ * Input:
+ *      handle returned by addInstance()
+ */
+private void removeInstance(TemplateDeclaration td, TemplateInstance ti)
+{
+    //printf("removeInstance() %s\n", ti.toChars());
+    auto tibox = TemplateInstanceBox(ti);
+    debug (FindExistingInstance) ++nRemoved;
+    (cast(TemplateInstance[TemplateInstanceBox])td.instances).remove(tibox);
+}
+
+/******************************************************
+ * 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 = STC.none;
+    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;
+}
+
+/**********************************************
+ * 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 findTempDecl(TemplateInstance ti, Scope* sc, WithScopeSymbol* pwithsym)
+{
+    if (pwithsym)
+        *pwithsym = null;
+
+    if (ti.havetempdecl)
+        return true;
+
+    //printf("TemplateInstance.findTempDecl() %s\n", toChars());
+    if (!ti.tempdecl)
+    {
+        /* Given:
+         *    foo!( ... )
+         * figure out which TemplateDeclaration foo refers to.
+         */
+        Identifier id = ti.name;
+        Dsymbol scopesym;
+        Dsymbol s = sc.search(ti.loc, id, scopesym);
+        if (!s)
+        {
+            s = sc.search_correct(id);
+            if (s)
+                .error(ti.loc, "%s `%s` template `%s` is not defined, did you mean %s?", ti.kind, ti.toPrettyChars(), id.toChars(), s.toChars());
+            else
+                .error(ti.loc, "%s `%s` template `%s` is not defined", ti.kind, ti.toPrettyChars(), 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 ti2;
+        if (s.parent && (ti2 = s.parent.isTemplateInstance()) !is null)
+        {
+            if (ti2.tempdecl && ti2.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 = ti2.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(ti.loc, sc);
+
+        if (!ti.updateTempDecl(sc, s))
+        {
+            return false;
+        }
+    }
+    assert(ti.tempdecl);
+
+    // Look for forward references
+    auto tovers = ti.tempdecl.isOverloadSet();
+    foreach (size_t oi; 0 .. tovers ? tovers.a.length : 1)
+    {
+        Dsymbol dstart = tovers ? tovers.a[oi] : ti.tempdecl;
+        int r = overloadApply(dstart, (Dsymbol s)
+        {
+            auto td = s.isTemplateDeclaration();
+            if (!td)
+                return 0;
+
+            if (td.semanticRun == PASS.initial)
+            {
+                if (td._scope)
+                {
+                    // Try to fix forward reference. Ungag errors while doing so.
+                    auto ungag = td.ungagSpeculative();
+                    td.dsymbolSemantic(td._scope);
+                }
+                if (td.semanticRun == PASS.initial)
+                {
+                    .error(ti.loc, "%s `%s` `%s` forward references template declaration `%s`",
+                           ti.kind, ti.toPrettyChars(), ti.toChars(), td.toChars());
+                    return 1;
+                }
+            }
+            return 0;
+        });
+        if (r)
+            return false;
+    }
+    return true;
+}
+
+bool findMixinTempDecl(TemplateMixin tm, Scope* sc)
+{
+    // Follow qualifications to find the TemplateDeclaration
+    if (!tm.tempdecl)
+    {
+        Expression e;
+        Type t;
+        Dsymbol s;
+        tm.tqual.resolve(tm.loc, sc, e, t, s);
+        if (!s)
+        {
+            .error(tm.loc, "%s `%s` is not defined", tm.kind, tm.toPrettyChars);
+            return false;
+        }
+        s = s.toAlias();
+        tm.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)
+                    {
+                        tm.tempdecl = os;
+                        break;
+                    }
+                    ds = s2;
+                }
+            }
+        }
+        if (!tm.tempdecl)
+        {
+            .error(tm.loc, "%s `%s` - `%s` is a %s, not a template", tm.kind,
+                   tm.toPrettyChars, s.toChars(), s.kind());
+            return false;
+        }
+    }
+    assert(tm.tempdecl);
+
+    // Look for forward references
+    auto tovers = tm.tempdecl.isOverloadSet();
+    foreach (size_t oi; 0 .. tovers ? tovers.a.length : 1)
+    {
+        Dsymbol dstart = tovers ? tovers.a[oi] : tm.tempdecl;
+        int r = overloadApply(dstart, (Dsymbol s)
+        {
+            auto td = s.isTemplateDeclaration();
+            if (!td)
+                return 0;
+
+            if (td.semanticRun == PASS.initial)
+            {
+                if (td._scope)
+                    td.dsymbolSemantic(td._scope);
+                else
+                {
+                    tm.semanticRun = PASS.initial;
+                    return 1;
+                }
+            }
+            return 0;
+        });
+        if (r)
+            return false;
+    }
+    return true;
+}
+
+/******************************************************
+ * Verifies if the given Identifier is a DRuntime hook. It uses the hooks
+ * defined in `id.d`.
+ *
  * Params:
- *      sc = context
- *      tempdecl = template declaration
+ *  id = Identifier to verify
+ * Returns:
+ *  true if `id` is a DRuntime hook
+ *  false otherwise
  */
-void templateDeclarationSemantic(Scope* sc, TemplateDeclaration tempdecl)
+private bool isDRuntimeHook(Identifier id)
+{
+    return id == Id._d_HookTraceImpl ||
+        id == Id._d_newclassT || id == Id._d_newclassTTrace ||
+        id == Id._d_arraycatnTX || id == Id._d_arraycatnTXTrace ||
+        id == Id._d_newThrowable || id == Id._d_delThrowable ||
+        id == Id._d_arrayassign_l || id == Id._d_arrayassign_r ||
+        id == Id._d_arraysetassign || id == Id._d_arraysetctor ||
+        id == Id._d_arrayctor ||
+        id == Id._d_arraysetlengthT ||
+        id == Id._d_arraysetlengthTTrace ||
+        id == Id._d_arrayappendT || id == Id._d_arrayappendTTrace ||
+        id == Id._d_arrayappendcTX;
+}
+
+/******************************
+ * See if two objects match
+ * Params:
+ *      o1 = first object
+ *      o2 = second object
+ * Returns: true if they match
+ */
+private bool match(RootObject o1, RootObject o2)
 {
     enum log = false;
+
     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());
+        printf("match() o1 = %p %s (%d), o2 = %p %s (%d)\n",
+            o1, o1.toChars(), o1.dyncast(), o2, o2.toChars(), o2.dyncast());
     }
-    if (tempdecl.semanticRun != PASS.initial)
-        return; // semantic() already run
 
-    if (tempdecl._scope)
+    bool yes()
     {
-        sc = tempdecl._scope;
-        tempdecl._scope = null;
+        static if (log)
+            printf("\t. match\n");
+        return true;
     }
-    if (!sc)
-        return;
-
-    // Remember templates defined in module object that we need to know about
-    if (sc._module && sc._module.ident == Id.object)
+    bool no()
     {
-        if (tempdecl.ident == Id.RTInfo)
-            Type.rtinfo = tempdecl;
+        static if (log)
+            printf("\t. nomatch\n");
+        return false;
     }
-
-    /* Remember Scope for later instantiations, but make
-     * a copy since attributes can change.
+    /* 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.
      */
-    if (!tempdecl._scope)
+    /* Manifest constants should be compared by their values,
+     * at least in template arguments.
+     */
+
+    if (auto t1 = isType(o1))
     {
-        tempdecl._scope = sc.copy();
-        tempdecl._scope.setNoFree();
-    }
+        auto t2 = isType(o2);
+        if (!t2)
+            return no();
 
-    tempdecl.semanticRun = PASS.semantic;
+        static if (log)
+        {
+            printf("\tt1 = %s\n", t1.toChars());
+            printf("\tt2 = %s\n", t2.toChars());
+        }
+        if (!t1.equals(t2))
+            return no();
 
-    tempdecl.parent = sc.parent;
-    tempdecl.visibility = sc.visibility;
-    tempdecl.userAttribDecl = sc.userAttribDecl;
-    tempdecl.cppnamespace = sc.namespace;
-    tempdecl.isstatic = tempdecl.toParent().isModule() || (tempdecl._scope.stc & STC.static_);
-    tempdecl.deprecated_ = !!(sc.stc & STC.deprecated_);
+        return yes();
+    }
+    if (auto e1 = getExpression(o1))
+    {
+        auto e2 = getExpression(o2);
+        if (!e2)
+            return no();
 
-    checkGNUABITag(tempdecl, sc.linkage);
+        static if (log)
+        {
+            printf("\te1 = %s '%s' %s\n", e1.type ? e1.type.toChars() : "null", EXPtoString(e1.op).ptr, e1.toChars());
+            printf("\te2 = %s '%s' %s\n", e2.type ? e2.type.toChars() : "null", EXPtoString(e2.op).ptr, e2.toChars());
+        }
 
-    if (!tempdecl.isstatic)
-    {
-        if (auto ad = tempdecl.parent.pastMixin().isAggregateDeclaration())
-            ad.makeNested();
+        // 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))
+            return no();
+
+        return yes();
     }
+    if (auto s1 = isDsymbol(o1))
+    {
+        auto s2 = isDsymbol(o2);
+        if (!s2)
+            return no();
 
-    // Set up scope for parameters
-    auto paramsym = new ScopeDsymbol();
-    paramsym.parent = tempdecl.parent;
-    Scope* paramscope = sc.push(paramsym);
-    paramscope.stc = STC.none;
+        static if (log)
+        {
+            printf("\ts1 = %s \n", s1.kind(), s1.toChars());
+            printf("\ts2 = %s \n", s2.kind(), s2.toChars());
+        }
+        if (!s1.equals(s2))
+            return no();
+        if (s1.parent != s2.parent && !s1.isFuncDeclaration() && !s2.isFuncDeclaration())
+            return no();
 
-    if (global.params.ddoc.doOutput)
+        return yes();
+    }
+    if (auto u1 = isTuple(o1))
     {
-        tempdecl.origParameters = new TemplateParameters(tempdecl.parameters.length);
-        for (size_t i = 0; i < tempdecl.parameters.length; i++)
+        auto u2 = isTuple(o2);
+        if (!u2)
+            return no();
+
+        static if (log)
         {
-            TemplateParameter tp = (*tempdecl.parameters)[i];
-            (*tempdecl.origParameters)[i] = tp.syntaxCopy();
+            printf("\tu1 = %s\n", u1.toChars());
+            printf("\tu2 = %s\n", u2.toChars());
         }
+        if (!arrayObjectMatch(u1.objects, u2.objects))
+            return no();
+
+        return yes();
     }
+    return yes();
+}
 
-    for (size_t i = 0; i < tempdecl.parameters.length; i++)
+/************************************
+ * Match an array of them.
+ */
+private bool arrayObjectMatch(ref Objects oa1, ref Objects oa2)
+{
+    if (&oa1 == &oa2)
+        return true;
+    if (oa1.length != oa2.length)
+        return false;
+    immutable oa1dim = oa1.length;
+    auto oa1d = oa1[].ptr;
+    auto oa2d = oa2[].ptr;
+    foreach (j; 0 .. oa1dim)
     {
-        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.length && tp.isTemplateTupleParameter())
+        RootObject o1 = oa1d[j];
+        RootObject o2 = oa2d[j];
+        if (!match(o1, o2))
         {
-            .error(tempdecl.loc, "%s `%s` template sequence parameter must be the last one", tempdecl.kind, tempdecl.toPrettyChars);
-            tempdecl.errors = true;
+            return false;
         }
     }
+    return true;
+}
 
-    /* Calculate TemplateParameter.dependent
-     */
-    auto tparams = TemplateParameters(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;
+/*************************************
+ * Compare proposed template instantiation with existing template instantiation.
+ * Note that this is not commutative because of the auto ref check.
+ * Params:
+ *  ti1 = proposed template instantiation
+ *  ti2 = existing template instantiation
+ * Returns:
+ *  true for match
+ */
+private bool equalsx(TemplateInstance ti1, TemplateInstance ti2)
+{
+    //printf("this = %p, ti2 = %p\n", this, ti2);
+    assert(ti1.tdtypes.length == ti2.tdtypes.length);
 
-            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;
-                }
-            }
-        }
+    // Nesting must match
+    if (ti1.enclosing != ti2.enclosing)
+    {
+        //printf("test2 enclosing %s ti2.enclosing %s\n",
+        //       enclosing ? enclosing.toChars() : "", ti2.enclosing ? ti.enclosing.toChars() : "");
+        return false;
     }
+    //printf("parent = %s, ti2.parent = %s\n", parent.toPrettyChars(), ti2.parent.toPrettyChars());
 
-    paramscope.pop();
+    if (!arrayObjectMatch(ti1.tdtypes, ti2.tdtypes))
+        return false;
 
-    // Compute again
-    tempdecl.onemember = null;
-    if (tempdecl.members)
+    /* Template functions may have different instantiations based on
+     * "auto ref" parameters.
+     */
+    auto fd = ti2.toAlias().isFuncDeclaration();
+    if (!fd)
+        return true;
+    if (fd.errors)
+        return true;
+
+    auto resolvedArgs = fd.type.isTypeFunction().resolveNamedArgs(
+        ArgumentList(ti1.fargs, ti1.fnames), null);
+
+    // resolvedArgs can be null when there's an error: fail_compilation/fail14669.d
+    // In that case, equalsx returns true to prevent endless template instantiations
+    // However, it can also mean the function was explicitly instantiated
+    // without function arguments: fail_compilation/fail14669
+    // Hence the following check:
+    if (ti1.fargs && !resolvedArgs)
+        return true;
+
+    Expression[] args = resolvedArgs ? (*resolvedArgs)[] : [];
+
+    auto fparameters = fd.getParameterList();
+    size_t nfparams = fparameters.length;   // Num function parameters
+    for (size_t j = 0; j < nfparams; j++)
     {
-        Dsymbol s;
-        if (oneMembers(tempdecl.members, s, tempdecl.ident) && s)
+        Parameter fparam = fparameters[j];
+        if (!(fparam.storageClass & STC.autoref) )      // if "auto ref"
+            continue;
+
+        Expression farg = (j < args.length) ? args[j] : fparam.defaultArg;
+        // resolveNamedArgs strips trailing nulls / default params
+        // when it doesn't anymore, the ternary can be replaced with:
+        // assert(j < resolvedArgs.length);
+        if (!farg)
+            farg = fparam.defaultArg;
+        if (!farg)
+            return false;
+        if (farg.isLvalue())
         {
-            tempdecl.onemember = s;
-            s.parent = tempdecl;
+            if (!(fparam.storageClass & STC.ref_))
+                return false; // auto ref's don't match
+        }
+        else
+        {
+            if (fparam.storageClass & STC.ref_)
+                return false; // auto ref's don't match
         }
     }
-
-    /* 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;
+    return true;
 }
 
+
 /*******************************************
  * Match to a particular TemplateParameter.
  * Input:
@@ -284,7 +2455,7 @@ MATCH matchArg(TemplateParameter tp, Loc instLoc, Scope* sc, Objects* tiargs, si
     return matchArgParameter();
 }
 
-MATCH matchArg(TemplateParameter tp, Scope* sc, RootObject oarg, size_t i, TemplateParameters* parameters, ref Objects dedtypes, Declaration* psparam)
+private MATCH matchArg(TemplateParameter tp, Scope* sc, RootObject oarg, size_t i, TemplateParameters* parameters, ref Objects dedtypes, Declaration* psparam)
 {
     MATCH matchArgNoMatch()
     {
@@ -734,12 +2905,12 @@ bool updateTempDecl(TemplateInstance ti, Scope* sc, Dsymbol s)
     if (!s.parent && global.errors)
         return false;
 
-    if (!s.parent && s.getType())
+    if (!s.parent && dmd.dsymbolsem.getType(s))
     {
-        Dsymbol s2 = s.getType().toDsymbol(sc);
+        Dsymbol s2 = dmd.dsymbolsem.getType(s).toDsymbol(sc);
         if (!s2)
         {
-            .error(ti.loc, "`%s` is not a valid template instance, because `%s` is not a template declaration but a type (`%s == %s`)", ti.toChars(), id.toChars(), id.toChars(), s.getType.kind());
+            .error(ti.loc, "`%s` is not a valid template instance, because `%s` is not a template declaration but a type (`%s == %s`)", ti.toChars(), id.toChars(), id.toChars(), dmd.dsymbolsem.getType(s).kind());
             return false;
         }
         // because s can be the alias created for a TemplateParameter
@@ -914,6 +3085,7 @@ MATCH matchWithInstance(Scope* sc, TemplateDeclaration td, TemplateInstance ti,
             ti.parent = td.parent;
 
         // Similar to doHeaderInstantiation
+        td.computeOneMember();
         FuncDeclaration fd = td.onemember ? td.onemember.isFuncDeclaration() : null;
         if (fd)
         {
@@ -1210,7 +3382,7 @@ bool needsCodegen(TemplateInstance ti)
 /****************************
  * Check to see if constraint is satisfied.
  */
-bool evaluateConstraint(TemplateDeclaration td, TemplateInstance ti, Scope* sc, Scope* paramscope, Objects* dedargs, FuncDeclaration fd)
+private bool evaluateConstraint(TemplateDeclaration td, 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
@@ -1342,7 +3514,6 @@ bool evaluateConstraint(TemplateDeclaration td, TemplateInstance ti, Scope* sc,
 const(char)* getConstraintEvalError(TemplateDeclaration td, 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.v.verbose;
     uint count;
@@ -1565,6 +3736,7 @@ bool findBestMatch(TemplateInstance ti, Scope* sc, ArgumentList argumentList)
     else
     {
         auto tdecl = ti.tempdecl.isTemplateDeclaration();
+        tdecl.computeOneMember();
 
         if (errs != global.errors)
             errorSupplemental(ti.loc, "while looking for match for `%s`", ti.toChars());
@@ -1711,7 +3883,7 @@ private void formatParamsWithTiargs(ref TemplateParameters parameters, ref Objec
  * Returns:
  *      new scope for the parameters of ti
  */
-Scope* createScopeForTemplateParameters(TemplateDeclaration td, TemplateInstance ti, Scope* sc)
+private Scope* createScopeForTemplateParameters(TemplateDeclaration td, TemplateInstance ti, Scope* sc)
 {
     ScopeDsymbol paramsym = new ScopeDsymbol();
     paramsym.parent = td._scope.parent;
@@ -1793,7 +3965,7 @@ L1:
     return MATCH.nomatch;
 }
 
-RootObject defaultArg(TemplateParameter tp, Loc instLoc, Scope* sc)
+private RootObject defaultArg(TemplateParameter tp, Loc instLoc, Scope* sc)
 {
     if (tp.isTemplateTupleParameter())
         return null;
@@ -1880,7 +4052,7 @@ RootObject defaultArg(TemplateParameter tp, Loc instLoc, Scope* sc)
  * Returns:
  *      match pair of initial and inferred template arguments
  */
-extern (D) MATCHpair deduceFunctionTemplateMatch(TemplateDeclaration td, TemplateInstance ti, Scope* sc, ref FuncDeclaration fd, Type tthis, ArgumentList argumentList)
+private MATCHpair deduceFunctionTemplateMatch(TemplateDeclaration td, TemplateInstance ti, Scope* sc, ref FuncDeclaration fd, Type tthis, ArgumentList argumentList)
 {
     version (none)
     {
@@ -2362,7 +4534,7 @@ extern (D) MATCHpair deduceFunctionTemplateMatch(TemplateDeclaration td, Templat
                                     return nomatch();
                                 if (m2 < matchTiargs)
                                     matchTiargs = m2; // pick worst match
-                                if (!(*dedtypes)[i].equals(oded))
+                                if (!rootObjectsEqual((*dedtypes)[i], oded))
                                     .error(td.loc, "%s `%s` specialization not allowed for deduced parameter `%s`",
                                         td.kind, td.toPrettyChars, td.kind, td.toPrettyChars, tparam.ident.toChars());
                             }
@@ -2765,7 +4937,7 @@ Lmatch:
                     return nomatch();
                 if (m2 < matchTiargs)
                     matchTiargs = m2; // pick worst match
-                if (!(*dedtypes)[i].equals(oded))
+                if (!rootObjectsEqual((*dedtypes)[i],oded))
                     .error(td.loc, "%s `%s` specialization not allowed for deduced parameter `%s`", td.kind, td.toPrettyChars, tparam.ident.toChars());
             }
             else
@@ -2812,7 +4984,7 @@ Lmatch:
                     return nomatch();
                 if (m2 < matchTiargs)
                     matchTiargs = m2; // pick worst match
-                if (!(*dedtypes)[i].equals(oded))
+                if (!rootObjectsEqual((*dedtypes)[i], oded))
                     .error(td.loc, "%s `%s` specialization not allowed for deduced parameter `%s`", td.kind, td.toPrettyChars, tparam.ident.toChars());
             }
         }
@@ -2962,10 +5134,27 @@ FuncDeclaration doHeaderInstantiation(TemplateDeclaration td, TemplateInstance t
     return fd;
 }
 
+/****************************************************
+ * Declare parameters of template instance, initialize them with the
+ * template instance arguments.
+ */
+void declareParameters(TemplateInstance ti, Scope* sc)
+{
+    TemplateDeclaration tempdecl = ti.tempdecl.isTemplateDeclaration();
+    assert(tempdecl);
+
+    //printf("TemplateInstance.declareParameters()\n");
+    foreach (i, o; ti.tdtypes) // initializer for tp
+    {
+        TemplateParameter tp = (*tempdecl.parameters)[i];
+        //printf("\ttdtypes[%d] = %p\n", i, o);
+        declareParameter(tempdecl, sc, tp, o);
+    }
+}
 /**************************************************
  * Declare template parameter tp with value o, and install it in the scope sc.
  */
-extern (D) RootObject declareParameter(TemplateDeclaration td, Scope* sc, TemplateParameter tp, RootObject o)
+private RootObject declareParameter(TemplateDeclaration td, Scope* sc, TemplateParameter tp, RootObject o)
 {
     //printf("TemplateDeclaration.declareParameter('%s', o = %p)\n", tp.ident.toChars(), o);
     Type ta = isType(o);
@@ -3062,6 +5251,31 @@ extern (D) RootObject declareParameter(TemplateDeclaration td, Scope* sc, Templa
     return o;
 }
 
+/**********************************
+ * Run semantic on the elements of `ti.tiargs`.
+ * Input:
+ *      ti = template instance whose `tiargs` should have semantic done
+ *      sc = scope
+ * 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 semanticTiargs(TemplateInstance ti, Scope* sc)
+{
+    //printf("+TemplateInstance.semanticTiargs() %s\n", toChars());
+    if (ti.semantictiargsdone)
+        return true;
+    if (TemplateInstance_semanticTiargs(ti.loc, sc, ti.tiargs, 0))
+    {
+        // cache the result iff semantic analysis succeeded entirely
+        ti.semantictiargsdone = 1;
+        return true;
+    }
+    return false;
+}
+
 /**********************************
  * Run semantic of tiargs as arguments of template.
  * Input:
@@ -3588,7 +5802,7 @@ void functionResolve(ref MatchAccumulator m, Dsymbol dstart, Loc loc, Scope* sc,
             return 1;
         }
         //printf("td = %s\n", td.toChars());
-
+        td.computeOneMember();
         auto f = td.onemember ? td.onemember.isFuncDeclaration() : null;
         if (!f)
         {
@@ -3835,6 +6049,7 @@ void functionResolve(ref MatchAccumulator m, Dsymbol dstart, Loc loc, Scope* sc,
     if (td_best && ti_best && m.count == 1)
     {
         // Matches to template function
+        td_best.computeOneMember();
         assert(td_best.onemember && td_best.onemember.isFuncDeclaration());
         /* The best match is td_best with arguments tdargs.
          * Now instantiate the template.
@@ -4263,7 +6478,7 @@ private MATCH deduceTypeHelper(Type t, out Type at, Type tparam)
     }
 }
 
-__gshared Expression emptyArrayElement = null;
+private __gshared Expression emptyArrayElement = null;
 
 /*
  * Returns `true` if `t` is a reference type, or an array of reference types.
@@ -4324,22 +6539,21 @@ private MATCH deduceAliasThis(Type t, Scope* sc, Type tparam,
 private MATCH deduceParentInstance(Scope* sc, Dsymbol sym, TypeInstance tpi,
     ref TemplateParameters parameters, ref Objects dedtypes, uint* wm)
 {
-    if (tpi.idents.length)
-    {
-        RootObject id = tpi.idents[tpi.idents.length - 1];
-        if (id.dyncast() == DYNCAST.identifier && sym.ident.equals(cast(Identifier)id))
-        {
-            Type tparent = sym.parent.getType();
-            if (tparent)
-            {
-                tpi.idents.length--;
-                auto m = deduceType(tparent, sc, tpi, parameters, dedtypes, wm);
-                tpi.idents.length++;
-                return m;
-            }
-        }
-    }
-    return MATCH.nomatch;
+    if (!tpi.idents.length)
+        return MATCH.nomatch;
+
+    RootObject id = tpi.idents[tpi.idents.length - 1];
+    if (id.dyncast() != DYNCAST.identifier || !sym.ident.equals(cast(Identifier)id))
+        return MATCH.nomatch;
+
+    Type tparent = dmd.dsymbolsem.getType(sym.parent);
+    if (!tparent)
+        return MATCH.nomatch;
+
+    tpi.idents.length--;
+    auto m = deduceType(tparent, sc, tpi, parameters, dedtypes, wm);
+    tpi.idents.length++;
+    return m;
 }
 
 private MATCH matchAll(TypeDeduced td, Type tt)
@@ -4464,7 +6678,7 @@ MATCH deduceType(scope RootObject o, scope Scope* sc, scope Type tparam,
                             //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 (Type tx = dmd.dsymbolsem.getType(s2))
                                 {
                                     if (s != tx.toDsymbol(sc))
                                         goto Lnomatch;
@@ -4480,7 +6694,7 @@ MATCH deduceType(scope RootObject o, scope Scope* sc, scope Type tparam,
                     //printf("[e] s = %s\n", s?s.toChars():"(null)");
                     if (tp.isTemplateTypeParameter())
                     {
-                        Type tt = s.getType();
+                        Type tt = dmd.dsymbolsem.getType(s);
                         if (!tt)
                             goto Lnomatch;
                         Type at = cast(Type)dedtypes[i];
@@ -4846,7 +7060,7 @@ MATCH deduceType(scope RootObject o, scope Scope* sc, scope Type tparam,
                 {
                     RootObject id1 = t.idents[i];
                     RootObject id2 = tp.idents[i];
-                    if (!id1.equals(id2))
+                    if (!rootObjectsEqual(id1, id2))
                     {
                         result = MATCH.nomatch;
                         return;
@@ -5534,6 +7748,31 @@ private void deduceBaseClassParameters(ref BaseClass b, Scope* sc, Type tparam,
     }
 }
 
+private bool rootObjectsEqual(RootObject o1, RootObject o2)
+{
+    auto d = o1.dyncast();
+    if (d != o2.dyncast())
+        return false;
+    bool check(T)(RootObject id1, RootObject id2)
+    {
+        return (cast(T)id1).equals(cast(T)id2);
+    }
+    with (DYNCAST) final switch(d)
+    {
+        case expression:   return check!Expression(o1, o2);
+        case dsymbol:      return check!Dsymbol   (o1, o2);
+        case type:         return check!Type      (o1, o2);
+        case identifier: //return check!Identifier(o1, o2); // Identifier.equals checks `o1 is o2`
+        case object:
+        case tuple:
+        case parameter:
+        case statement:
+        case condition:
+        case templateparameter:
+        case initializer:
+            return o1 is o2;
+    }
+}
 /*
  * Handle tuple matching for function parameters.
  * If the last parameter of `tp` is a template tuple parameter,
@@ -5551,53 +7790,53 @@ private bool deduceFunctionTuple(TypeFunction t, TypeFunction tp,
     ref TemplateParameters parameters, ref Objects dedtypes,
     size_t nfargs, ref size_t nfparams)
 {
-    if (nfparams > 0 && nfargs >= nfparams - 1)
+    if (nfparams == 0 || nfargs < nfparams - 1)
+        return nfargs == nfparams;
+
+    Parameter fparam = tp.parameterList[nfparams - 1];
+    assert(fparam && fparam.type);
+    if (fparam.type.ty != Tident)
+        return nfargs == nfparams;
+
+    TypeIdentifier tid = fparam.type.isTypeIdentifier();
+    if (tid.idents.length != 0)
+        return nfargs == nfparams;
+
+    size_t tupi = 0;
+    for (; tupi < parameters.length; ++tupi)
     {
-        Parameter fparam = tp.parameterList[nfparams - 1];
-        assert(fparam && fparam.type);
-        if (fparam.type.ty == Tident)
-        {
-            TypeIdentifier tid = fparam.type.isTypeIdentifier();
-            if (tid.idents.length == 0)
-            {
-                size_t tupi = 0;
-                for (; tupi < parameters.length; ++tupi)
-                {
-                    TemplateParameter tx = parameters[tupi];
-                    TemplateTupleParameter tup = tx.isTemplateTupleParameter();
-                    if (tup && tup.ident.equals(tid.ident))
-                        break;
-                }
-                if (tupi == parameters.length)
-                    return nfargs == nfparams;
+        TemplateParameter tx = parameters[tupi];
+        TemplateTupleParameter tup = tx.isTemplateTupleParameter();
+        if (tup && tup.ident.equals(tid.ident))
+            break;
+    }
+    if (tupi == parameters.length)
+        return nfargs == nfparams;
 
-                size_t tuple_dim = nfargs - (nfparams - 1);
+    size_t tuple_dim = nfargs - (nfparams - 1);
 
-                RootObject o = dedtypes[tupi];
-                if (o)
-                {
-                    Tuple tup = isTuple(o);
-                    if (!tup || tup.objects.length != tuple_dim)
-                        return false;
-                    for (size_t i = 0; i < tuple_dim; ++i)
-                    {
-                        if (!t.parameterList[nfparams - 1 + i].type.equals(tup.objects[i]))
-                            return false;
-                    }
-                }
-                else
-                {
-                    auto tup = new Tuple(tuple_dim);
-                    for (size_t i = 0; i < tuple_dim; ++i)
-                        tup.objects[i] = t.parameterList[nfparams - 1 + i].type;
-                    dedtypes[tupi] = tup;
-                }
-                --nfparams; // ignore tuple parameter for further deduction
-                return true;
-            }
+    RootObject o = dedtypes[tupi];
+    if (o)
+    {
+        Tuple tup = isTuple(o);
+        if (!tup || tup.objects.length != tuple_dim)
+            return false;
+        for (size_t i = 0; i < tuple_dim; ++i)
+        {
+            if (!rootObjectsEqual(t.parameterList[nfparams - 1 + i].type,
+                                  tup.objects[i]))
+                return false;
         }
     }
-    return nfargs == nfparams;
+    else
+    {
+        auto tup = new Tuple(tuple_dim);
+        for (size_t i = 0; i < tuple_dim; ++i)
+            tup.objects[i] = t.parameterList[nfparams - 1 + i].type;
+        dedtypes[tupi] = tup;
+    }
+    --nfparams; // ignore tuple parameter for further deduction
+    return true;
 }
 
 /********************
@@ -5826,7 +8065,7 @@ private Expression getValue(Expression e)
     return e;
 }
 
-Expression getExpression(RootObject o)
+private Expression getExpression(RootObject o)
 {
     auto s = isDsymbol(o);
     return s ? .getValue(s) : .getValue(isExpression(o));
index 742dde069a13c8795b63c4ea5eeb9c0ec9f81dc6..5473a574ee172c88828e5e445db84b23c36947e7 100644 (file)
@@ -71,8 +71,12 @@ enum TimeTraceEventType
     semaGeneral,
     sema1Import,
     sema1Module,
+    sema1TemplateDecl,
+    sema1TemplateInstance,
+    sema1Function,
     sema2,
     sema3,
+    dfa,
     ctfe,
     ctfeCall,
     codegenGlobal,
index d304436766050b9e36a60d53d1f024cb1dbbd9f0..05f413b51c7d1a0e31d11e7fc06783c3e95e996a 100644 (file)
@@ -276,6 +276,7 @@ enum TOK : ubyte
     // C only extended keywords
     _assert,
     _import,
+    _module,
     __cdecl,
     __declspec,
     __stdcall,
@@ -586,6 +587,7 @@ private immutable TOK[] keywords =
     // C only extended keywords
     TOK._assert,
     TOK._import,
+    TOK._module,
     TOK.__cdecl,
     TOK.__declspec,
     TOK.__stdcall,
@@ -621,7 +623,7 @@ static immutable TOK[TOK.max + 1] Ckeywords =
                        union_, unsigned, void_, volatile, while_, asm_, typeof_,
                        _Alignas, _Alignof, _Atomic, _Bool, _Complex, _Generic, _Imaginary, _Noreturn,
                        _Static_assert, _Thread_local,
-                       _import, __cdecl, __declspec, __stdcall, __thread, __pragma, __int128, __attribute__,
+                       _import, _module, __cdecl, __declspec, __stdcall, __thread, __pragma, __int128, __attribute__,
                        _assert ];
 
         foreach (kw; Ckwds)
@@ -900,6 +902,7 @@ extern (C++) struct Token
         // C only extended keywords
         TOK._assert       : "__check",
         TOK._import       : "__import",
+        TOK._module       : "__module",
         TOK.__cdecl        : "__cdecl",
         TOK.__declspec     : "__declspec",
         TOK.__stdcall      : "__stdcall",
index ecbcc02474661a6f52b5179ecc69caeb6f4504bd..baa3317b026c4a2e8906d684323aa02eef4da00c 100644 (file)
@@ -282,6 +282,7 @@ enum class TOK : unsigned char
     // C only extended keywords
     _assert,
     _import,
+    _module,
     cdecl_,
     declspec,
     stdcall,
index 40195aa2fa184bc39099e2a5d92b604401909ab5..8ea04c1c387199f62d78565656dacafafa08999d 100644 (file)
@@ -17,14 +17,12 @@ import core.stdc.stdio;
 
 import dmd.aggregate;
 import dmd.arraytypes;
-import dmd.astcodegen;
 import dmd.astenums;
 import dmd.attrib;
 import dmd.attribsem;
 import dmd.canthrow;
 import dmd.dclass;
 import dmd.declaration;
-import dmd.dimport;
 import dmd.dinterpret;
 import dmd.dmodule;
 import dmd.dscope;
@@ -46,17 +44,14 @@ import dmd.mangle : decoToType;
 import dmd.mtype;
 import dmd.nogc;
 import dmd.optimize;
-import dmd.parse;
 import dmd.root.array;
 import dmd.root.speller;
 import dmd.root.stringtable;
 import dmd.target;
-import dmd.templatesem : TemplateInstance_semanticTiargs;
+import dmd.templatesem : TemplateInstance_semanticTiargs, TemplateInstanceBox;
 import dmd.tokens;
 import dmd.typesem;
-import dmd.visitor;
 import dmd.rootobject;
-import dmd.common.outbuffer;
 import dmd.root.string;
 
 enum LOGSEMANTIC = false;
@@ -170,11 +165,14 @@ ulong getTypePointerBitmap(Loc loc, Type t, ref Array!(ulong) data, ErrorSink eS
             ulong nextsize = t.next.size();
             if (nextsize == SIZE_INVALID)
                 error = true;
-            ulong dim = t.dim.toInteger();
-            for (ulong i = 0; i < dim; i++)
+            if (t.hasPointers)
             {
-                offset = arrayoff + i * nextsize;
-                visit(t.next);
+                ulong dim = t.dim.toInteger();
+                for (ulong i = 0; i < dim; i++)
+                {
+                    offset = arrayoff + i * nextsize;
+                    visit(t.next);
+                }
             }
             offset = arrayoff;
         }
@@ -1182,37 +1180,36 @@ Expression semanticTraits(TraitsExp e, Scope* sc)
                 auto fd = s.isFuncDeclaration();
                 if (!fd)
                 {
-                    if (includeTemplates)
+                    if (!includeTemplates)
+                        return 0;
+                    auto td = s.isTemplateDeclaration();
+                    if (!td)
+                        return 0;
+                    // 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 (auto td = s.isTemplateDeclaration())
+                        if (td.instances is null)
                         {
-                            // 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);
+                            // create an empty AA just to copy it
+                            scope ti = new TemplateInstance(Loc.initial, Id.empty, null);
+                            auto tib = TemplateInstanceBox(ti);
+                            (*(cast(TemplateInstance[TemplateInstanceBox]*) &td.instances))[tib] = null;
+                            (cast(TemplateInstance[TemplateInstanceBox])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())
index caddc5e0c894e0e337794f54a0c0327a1d83a170..37bfc4a9db69805bdc3e252418528cbba0285583 100644 (file)
@@ -17,7 +17,6 @@ import core.stdc.stdio;
 
 import dmd.access;
 import dmd.aggregate;
-import dmd.aliasthis;
 import dmd.arrayop;
 import dmd.arraytypes;
 import dmd.astcodegen;
@@ -26,17 +25,16 @@ 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.templatesem : computeOneMember;
 import dmd.dtemplate;
 import dmd.enumsem;
 import dmd.errors;
-import dmd.errorsink;
 import dmd.expression;
 import dmd.expressionsem;
 import dmd.func;
@@ -50,7 +48,6 @@ import dmd.importc;
 import dmd.init;
 import dmd.initsem;
 import dmd.location;
-import dmd.visitor;
 import dmd.mtype;
 import dmd.mangle;
 import dmd.nogc;
@@ -64,13 +61,1358 @@ import dmd.root.rmem;
 import dmd.common.outbuffer;
 import dmd.rootobject;
 import dmd.root.string;
-import dmd.root.stringtable;
 import dmd.safe;
 import dmd.semantic3;
 import dmd.sideeffect;
 import dmd.target;
 import dmd.tokens;
 
+bool hasDeprecatedAliasThis(Type _this)
+{
+    auto ad = isAggregate(_this);
+    return ad && ad.aliasthis && (ad.aliasthis.isDeprecated || ad.aliasthis.sym.isDeprecated);
+}
+
+/*************************************
+ * Apply STCxxxx bits to existing type.
+ * Use *before* semantic analysis is run.
+ */
+Type addSTC(Type _this, STC stc)
+{
+    Type t = _this;
+    if (t.isImmutable())
+    {
+        return t;
+    }
+    else if (stc & STC.immutable_)
+    {
+        t = t.makeImmutable();
+        return t;
+    }
+
+    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;
+}
+
+/***************************************************
+ * 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.isStaticOrDynamicArray() || t.ty == Taarray ||
+            t.ty == Ttuple || t.ty == Tvector);
+}
+
+/**
+ * If the type is a class or struct, returns the symbol for it,
+ * else null.
+ */
+AggregateDeclaration isAggregate(Type t)
+{
+    t = t.toBasetype();
+    if (auto tc = t.isTypeClass())
+        return tc.sym;
+    if (auto ts = t.isTypeStruct())
+        return ts.sym;
+    return 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(Parameter _this)
+{
+    Type tb = _this.type.toBasetype();
+    if (tb.isStaticOrDynamicArray())
+    {
+        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;
+}
+
+/****************************************
+ * Return the mask that an integral type will
+ * fit into.
+ */
+ulong sizemask(Type _this)
+{
+    ulong m;
+    switch (_this.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;
+}
+
+/*************************************
+ * If _this is a type of static array, return its base element type.
+ */
+Type baseElemOf(Type _this)
+{
+    Type t = _this.toBasetype();
+    TypeSArray tsa;
+    while ((tsa = t.isTypeSArray()) !is null)
+        t = tsa.next.toBasetype();
+    return t;
+}
+
+/*************************************
+ * If this is a type of something, return that something.
+ */
+Type nextOf(Type _this)
+{
+    /*******************************
+     * For TypeFunction, nextOf() can return NULL if the function return
+     * type is meant to be inferred, and semantic() hasn't yet been run
+     * on the function. After semantic(), it must no longer be NULL.
+     */
+    if (auto tn = _this.isTypeNext())
+        return tn.next;
+    else if (auto te = _this.isTypeEnum())
+        return te.memType().nextOf();
+    return null;
+}
+
+/***************************
+ * Look for bugs in constructing types.
+ */
+void check(Type _this)
+{
+    if (_this.mcache)
+    with (_this.mcache)
+    switch (_this.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 = _this.nextOf();
+    if (tn && _this.ty != Tfunction && tn.ty != Tfunction && _this.ty != Tenum)
+    {
+        // Verify transitivity
+        switch (_this.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 & _this.mod) == _this.mod);
+            break;
+
+        default:
+            assert(0);
+        }
+        tn.check();
+    }
+}
+
+/**********************************
+ * For our new type '_this', which is type-constructed from t,
+ * fill in the cto, ito, sto, scto, wto shortcuts.
+ */
+void fixTo(Type _this, 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 = _this.nextOf();
+    if (!tn || _this.ty != Tsarray && tn.mod == t.nextOf().mod)
+    {
+        switch (t.mod)
+        {
+        case 0:
+            mto = t;
+            break;
+
+        case MODFlags.const_:
+            _this.getMcache();
+            _this.mcache.cto = t;
+            break;
+
+        case MODFlags.wild:
+            _this.getMcache();
+            _this.mcache.wto = t;
+            break;
+
+        case MODFlags.wildconst:
+            _this.getMcache();
+            _this.mcache.wcto = t;
+            break;
+
+        case MODFlags.shared_:
+            _this.getMcache();
+            _this.mcache.sto = t;
+            break;
+
+        case MODFlags.shared_ | MODFlags.const_:
+            _this.getMcache();
+            _this.mcache.scto = t;
+            break;
+
+        case MODFlags.shared_ | MODFlags.wild:
+            _this.getMcache();
+            _this.mcache.swto = t;
+            break;
+
+        case MODFlags.shared_ | MODFlags.wildconst:
+            _this.getMcache();
+            _this.mcache.swcto = t;
+            break;
+
+        case MODFlags.immutable_:
+            _this.getMcache();
+            _this.mcache.ito = t;
+            break;
+
+        default:
+            break;
+        }
+    }
+    assert(_this.mod != t.mod);
+
+    if (_this.mod)
+    {
+        _this.getMcache();
+        t.getMcache();
+    }
+    switch (_this.mod)
+    {
+    case 0:
+        break;
+
+    case MODFlags.const_:
+        _this.mcache.cto = mto;
+        t.mcache.cto = _this;
+        break;
+
+    case MODFlags.wild:
+        _this.mcache.wto = mto;
+        t.mcache.wto = _this;
+        break;
+
+    case MODFlags.wildconst:
+        _this.mcache.wcto = mto;
+        t.mcache.wcto = _this;
+        break;
+
+    case MODFlags.shared_:
+        _this.mcache.sto = mto;
+        t.mcache.sto = _this;
+        break;
+
+    case MODFlags.shared_ | MODFlags.const_:
+        _this.mcache.scto = mto;
+        t.mcache.scto = _this;
+        break;
+
+    case MODFlags.shared_ | MODFlags.wild:
+        _this.mcache.swto = mto;
+        t.mcache.swto = _this;
+        break;
+
+    case MODFlags.shared_ | MODFlags.wildconst:
+        _this.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);
+    }
+    _this.check();
+    t.check();
+    //printf("fixTo: %s, %s\n", toChars(), t.toChars());
+}
+
+void transitive(TypeNext _this)
+{
+    // Invoke transitivity of type attributes
+    _this.next = _this.next.addMod(_this.mod);
+}
+
+private inout(TypeNext) isTypeNext(inout Type _this)
+{
+    switch(_this.ty)
+    {
+        case Tpointer, Treference, Tfunction, Tdelegate, Tslice, Tarray, Taarray, Tsarray:
+            return cast(typeof(return)) _this;
+        default: return null;
+    }
+}
+
+/********************************
+ * true if when type is copied, it needs a copy constructor or postblit
+ * applied. Only applies to value types, not ref types.
+ */
+bool needsCopyOrPostblit(Type _this)
+{
+    if (auto tsa = _this.isTypeSArray())
+        return tsa.next.needsCopyOrPostblit();
+    else if (auto ts = _this.isTypeStruct())
+        return ts.sym.hasCopyCtor || ts.sym.postblit;
+    else if (auto te = _this.isTypeEnum())
+        return te.memType().needsCopyOrPostblit();
+    return false;
+}
+
+/********************************
+ * true if when type goes out of scope, it needs a destructor applied.
+ * Only applies to value types, not ref types.
+ */
+bool needsDestruction(Type _this)
+{
+    if (auto tsa = _this.isTypeSArray())
+        return tsa.next.needsDestruction();
+    else if (auto ts = _this.isTypeStruct())
+        return ts.sym.dtor !is null;
+    else if (auto te = _this.isTypeEnum())
+        return te.memType().needsDestruction();
+    return false;
+}
+
+bool needsNested(Type _this)
+{
+    static bool typeStructNeedsNested(TypeStruct _this)
+    {
+        if (_this.inuse) return false; // circular type, error instead of crashing
+
+        _this.inuse = true;
+        scope(exit) _this.inuse = false;
+
+        if (_this.sym.isNested())
+            return true;
+
+        for (size_t i = 0; i < _this.sym.fields.length; i++)
+        {
+            VarDeclaration v = _this.sym.fields[i];
+            if (!v.isDataseg() && v.type.needsNested())
+                return true;
+        }
+        return false;
+    }
+
+    if (auto tsa = _this.isTypeSArray())
+        return tsa.next.needsNested();
+    else if (auto ts = _this.isTypeStruct())
+        return typeStructNeedsNested(ts);
+    else if (auto te = _this.isTypeEnum())
+        return te.memType().needsNested();
+    return false;
+}
+
+bool isScalar(Type _this)
+{
+    if (auto tb = _this.isTypeBasic())
+        return (tb.flags & (TFlags.integral | TFlags.floating)) != 0;
+    else if (auto tv = _this.isTypeVector())
+        return tv.basetype.nextOf().isScalar();
+    else if (auto te = _this.isTypeEnum())
+        return te.memType().isScalar();
+    else if (_this.isTypePointer())
+        return true;
+    return false;
+}
+
+bool isUnsigned(Type _this)
+{
+    if (auto tb = _this.isTypeBasic())
+        return (tb.flags & TFlags.unsigned) != 0;
+    else if (auto tv = _this.isTypeVector())
+        return tv.basetype.nextOf().isUnsigned();
+    else if (auto te = _this.isTypeEnum())
+        return te.memType().isUnsigned();
+    return false;
+}
+
+bool isImaginary(Type _this)
+{
+    if (auto te = _this.isTypeEnum())
+        return te.memType().isImaginary();
+    return _this.isImaginaryNonSemantic();
+}
+
+bool isComplex(Type _this)
+{
+    if (auto tb = _this.isTypeBasic())
+        return (tb.flags & TFlags.complex) != 0;
+    else if (auto te = _this.isTypeEnum())
+        return te.memType().isComplex();
+    return false;
+}
+
+// Exposed as it is used in `expressionsem`
+MOD typeDeduceWild(Type _this, Type t, bool isRef)
+{
+    //printf("Type::deduceWild this = '%s', tprm = '%s'\n", toChars(), tprm.toChars());
+    if (t.isWild())
+    {
+        if (_this.isImmutable())
+            return MODFlags.immutable_;
+        if (_this.isWildConst())
+        {
+            if (t.isWildConst())
+                return MODFlags.wild;
+            return MODFlags.wildconst;
+        }
+        if (_this.isWild())
+            return MODFlags.wild;
+        if (_this.isConst())
+            return MODFlags.const_;
+        if (_this.isMutable())
+            return MODFlags.mutable;
+        assert(0);
+    }
+    return 0;
+}
+
+/***************************************
+ * Compute MOD bits matching `this` argument type to wild parameter type.
+ * Params:
+ *  _this = base parameter type
+ *  t = corresponding parameter type
+ *  isRef = parameter is `ref` or `out`
+ * Returns:
+ *  MOD bits
+ */
+MOD deduceWild(Type _this, Type t, bool isRef)
+{
+    static MOD typeNextDeduceWild(TypeNext _this, Type t, bool isRef)
+    {
+        if (_this.ty == Tfunction)
+            return 0;
+
+        ubyte wm;
+
+        Type tn = t.nextOf();
+        if (!isRef && (_this.ty == Tarray || _this.ty == Tpointer) && tn)
+        {
+            wm = _this.next.deduceWild(tn, true);
+            if (!wm)
+                wm = typeDeduceWild(cast(Type)_this, t, true);
+        }
+        else
+        {
+            wm = typeDeduceWild(cast(Type)_this, t, isRef);
+            if (!wm && tn)
+                wm = _this.next.deduceWild(tn, true);
+        }
+        return wm;
+    }
+
+    static MOD typeStructDeduceWild(TypeStruct _this, Type t, bool isRef)
+    {
+        if (_this.ty == t.ty && _this.sym == (cast(TypeStruct)t).sym)
+            return typeDeduceWild(cast(Type)_this, t, isRef);
+
+        ubyte wm = 0;
+
+        if (t.hasWild() && _this.sym.aliasthis && !(_this.att & AliasThisRec.tracing))
+        {
+            if (auto ato = aliasthisOf(_this))
+            {
+                _this.att = cast(AliasThisRec)(_this.att | AliasThisRec.tracing);
+                wm = ato.deduceWild(t, isRef);
+                _this.att = cast(AliasThisRec)(_this.att & ~AliasThisRec.tracing);
+            }
+        }
+
+        return wm;
+    }
+
+    static MOD typeClassDeduceWild(TypeClass _this, Type t, bool isRef)
+    {
+        ClassDeclaration cd = t.isClassHandle();
+        if (cd && (_this.sym == cd || cd.isBaseOf(_this.sym, null)))
+            return typeDeduceWild(cast(Type)_this, t, isRef);
+
+        ubyte wm = 0;
+
+        if (t.hasWild() && _this.sym.aliasthis && !(_this.att & AliasThisRec.tracing))
+        {
+            if (auto ato = aliasthisOf(_this))
+            {
+                _this.att = cast(AliasThisRec)(_this.att | AliasThisRec.tracing);
+                wm = ato.deduceWild(t, isRef);
+                _this.att = cast(AliasThisRec)(_this.att & ~AliasThisRec.tracing);
+            }
+        }
+
+        return wm;
+    }
+
+    if (auto tn = _this.isTypeNext())
+        return typeNextDeduceWild(tn, t, isRef);
+    else if (auto ts = _this.isTypeStruct())
+        return typeStructDeduceWild(ts, t, isRef);
+    else if (auto tc = _this.isTypeClass())
+        return typeClassDeduceWild(tc, t, isRef);
+    return typeDeduceWild(_this, t, isRef);
+}
+
+bool isString(Type _this)
+{
+    if (auto tsa = _this.isTypeSArray())
+    {
+        TY nty = tsa.next.toBasetype().ty;
+        return nty.isSomeChar();
+    }
+    else if (auto tda = _this.isTypeDArray())
+    {
+        TY nty = tda.next.toBasetype().ty;
+        return nty.isSomeChar();
+    }
+    else if (auto te = _this.isTypeEnum())
+        return te.memType().isString();
+    return false;
+}
+
+/**************************
+ * Returns true if T can be converted to boolean value.
+ */
+bool isBoolean(Type _this)
+{
+    switch(_this.ty)
+    {
+        case Tvector, Tstruct: return false;
+        case Tarray, Taarray, Tdelegate, Tclass, Tnull: return true;
+        case Tenum: return _this.isTypeEnum().memType().isBoolean();
+        // bottom type can be implicitly converted to any other type
+        case Tnoreturn: return true;
+        default: return _this.isScalar();
+    }
+}
+
+bool isReal(Type _this)
+{
+    if (auto tb = _this.isTypeBasic())
+        return (tb.flags & TFlags.real_) != 0;
+    else if (auto te = _this.isTypeEnum())
+        return te.memType().isReal();
+    return false;
+}
+
+// real, imaginary, or complex
+bool isFloating(Type _this)
+{
+    if (auto tb = _this.isTypeBasic())
+        return (tb.flags & TFlags.floating) != 0;
+    else if (auto tv = _this.isTypeVector())
+        return tv.basetype.nextOf().isFloating();
+    else if (auto te = _this.isTypeEnum())
+        return te.memType().isFloating();
+    return false;
+}
+
+bool isIntegral(Type _this)
+{
+    if (auto tb = _this.isTypeBasic())
+        return (tb.flags & TFlags.integral) != 0;
+    else if (auto tv = _this.isTypeVector())
+        return tv.basetype.nextOf().isIntegral();
+    else if (auto te = _this.isTypeEnum())
+        return te.memType().isIntegral();
+    return false;
+}
+
+Type makeSharedWildConst(Type _this)
+{
+    if (_this.mcache && _this.mcache.swcto)
+        return _this.mcache.swcto;
+    Type t = _this.nullAttributes();
+    t.mod = MODFlags.shared_ | MODFlags.wildconst;
+
+    if (auto tn = _this.isTypeNext())
+    {
+        //printf("TypeNext::makeSharedWildConst() %s\n", toChars());
+        TypeNext _t = cast(TypeNext) t;
+        if (tn.ty != Tfunction && tn.next.ty != Tfunction && !tn.next.isImmutable())
+        {
+            _t.next = tn.next.sharedWildConstOf();
+        }
+        //printf("TypeNext::makeSharedWildConst() returns %p, %s\n", t, t.toChars());
+        return _t;
+    }
+    return t;
+}
+
+Type makeSharedWild(Type _this)
+{
+    if (_this.mcache && _this.mcache.swto)
+        return _this.mcache.swto;
+    Type t = _this.nullAttributes();
+    t.mod = MODFlags.shared_ | MODFlags.wild;
+
+    if (auto tn = _this.isTypeNext())
+    {
+        //printf("TypeNext::makeSharedWild() %s\n", toChars());
+        TypeNext _t = cast(TypeNext) t;
+        if (tn.ty != Tfunction && tn.next.ty != Tfunction && !tn.next.isImmutable())
+        {
+            if (tn.next.isConst())
+                _t.next = tn.next.sharedWildConstOf();
+            else
+                _t.next = tn.next.sharedWildOf();
+        }
+        //printf("TypeNext::makeSharedWild() returns %p, %s\n", t, t.toChars());
+        return _t;
+    }
+    return t;
+}
+
+Type makeWildConst(Type _this)
+{
+    if (_this.mcache && _this.mcache.wcto)
+        return _this.mcache.wcto;
+    Type t = _this.nullAttributes();
+    t.mod = MODFlags.wildconst;
+
+    if (auto tn = _this.isTypeNext())
+    {
+        //printf("TypeNext::makeWildConst() %s\n", toChars());
+        TypeNext _t = cast(TypeNext) t;
+        if (tn.ty != Tfunction && tn.next.ty != Tfunction && !tn.next.isImmutable())
+        {
+            if (tn.next.isShared())
+                _t.next = tn.next.sharedWildConstOf();
+            else
+                _t.next = tn.next.wildConstOf();
+        }
+        //printf("TypeNext::makeWildConst() returns %p, %s\n", t, t.toChars());
+        return _t;
+    }
+    return t;
+}
+
+Type makeWild(Type _this)
+{
+    if (_this.mcache && _this.mcache.wto)
+        return _this.mcache.wto;
+    Type t = _this.nullAttributes();
+    t.mod = MODFlags.wild;
+
+    if (auto tn = _this.isTypeNext())
+    {
+        //printf("TypeNext::makeWild() %s\n", toChars());
+        TypeNext _t = cast(TypeNext) t;
+        if (tn.ty != Tfunction && tn.next.ty != Tfunction && !tn.next.isImmutable())
+        {
+            if (tn.next.isShared())
+            {
+                if (tn.next.isConst())
+                    _t.next = tn.next.sharedWildConstOf();
+                else
+                    _t.next = tn.next.sharedWildOf();
+            }
+            else
+            {
+                if (tn.next.isConst())
+                    _t.next = tn.next.wildConstOf();
+                else
+                    _t.next = tn.next.wildOf();
+            }
+        }
+        //printf("TypeNext::makeWild() returns %p, %s\n", t, t.toChars());
+        return _t;
+    }
+    return t;
+}
+
+Type makeSharedConst(Type _this)
+{
+    if (_this.mcache && _this.mcache.scto)
+        return _this.mcache.scto;
+    Type t = _this.nullAttributes();
+    t.mod = MODFlags.shared_ | MODFlags.const_;
+
+    if (auto tn = _this.isTypeNext())
+    {
+        //printf("TypeNext::makeSharedConst() %s\n", toChars());
+        TypeNext _t = cast(TypeNext) t;
+        if (tn.ty != Tfunction && tn.next.ty != Tfunction && !tn.next.isImmutable())
+        {
+            if (tn.next.isWild())
+                _t.next = tn.next.sharedWildConstOf();
+            else
+                _t.next = tn.next.sharedConstOf();
+        }
+        //printf("TypeNext::makeSharedConst() returns %p, %s\n", t, t.toChars());
+        return _t;
+    }
+    return t;
+}
+
+Type makeShared(Type _this)
+{
+    if (_this.mcache && _this.mcache.sto)
+        return _this.mcache.sto;
+    Type t = _this.nullAttributes();
+    t.mod = MODFlags.shared_;
+
+    if (auto tn = _this.isTypeNext())
+    {
+        //printf("TypeNext::makeShared() %s\n", toChars());
+        TypeNext _t = cast(TypeNext) t;
+        if (tn.ty != Tfunction && tn.next.ty != Tfunction && !tn.next.isImmutable())
+        {
+            if (tn.next.isWild())
+            {
+                if (tn.next.isConst())
+                    _t.next = tn.next.sharedWildConstOf();
+                else
+                    _t.next = tn.next.sharedWildOf();
+            }
+            else
+            {
+                if (tn.next.isConst())
+                    _t.next = tn.next.sharedConstOf();
+                else
+                    _t.next = tn.next.sharedOf();
+            }
+        }
+        //printf("TypeNext::makeShared() returns %p, %s\n", t, t.toChars());
+        return _t;
+    }
+    return t;
+}
+
+Type makeImmutable(Type _this)
+{
+    if (_this.mcache && _this.mcache.ito)
+        return _this.mcache.ito;
+    Type t = _this.nullAttributes();
+    t.mod = MODFlags.immutable_;
+
+    if (auto tn = _this.isTypeNext())
+    {
+        //printf("TypeNext::makeImmutable() %s\n", toChars());
+        TypeNext _t = cast(TypeNext) t;
+        if (tn.ty != Tfunction && tn.next.ty != Tfunction && !tn.next.isImmutable())
+        {
+            _t.next = tn.next.immutableOf();
+        }
+        return _t;
+    }
+    return t;
+}
+
+Type makeMutable(Type _this)
+{
+    Type t = _this.nullAttributes();
+    t.mod = _this.mod & MODFlags.shared_;
+
+    if (auto tn = _this.isTypeNext())
+    {
+        //printf("TypeNext::makeMutable() %p, %s\n", this, toChars());
+        TypeNext _t = cast(TypeNext)t;
+        if (tn.ty == Tsarray)
+        {
+            _t.next = tn.next.mutableOf();
+        }
+        //printf("TypeNext::makeMutable() returns %p, %s\n", t, t.toChars());
+        return _t;
+    }
+    return t;
+}
+
+Type makeConst(Type _this)
+{
+    if (_this.mcache && _this.mcache.cto)
+    {
+        assert(_this.mcache.cto.mod == MODFlags.const_);
+        return _this.mcache.cto;
+    }
+
+    static Type defaultMakeConst(Type _this)
+    {
+        //printf("Type::makeConst() %p, %s\n", this, toChars());
+        Type t = _this.nullAttributes();
+        t.mod = MODFlags.const_;
+        //printf("-Type::makeConst() %p, %s\n", t, toChars());
+        return t;
+    }
+
+    static Type typeNextMakeConst(TypeNext _this)
+    {
+        //printf("TypeNext::makeConst() %p, %s\n", this, toChars());
+        TypeNext t = cast(TypeNext)defaultMakeConst(cast(Type)_this);
+        if (_this.ty != Tfunction && _this.next.ty != Tfunction && !_this.next.isImmutable())
+        {
+            if (_this.next.isShared())
+            {
+                if (_this.next.isWild())
+                    t.next = _this.next.sharedWildConstOf();
+                else
+                    t.next = _this.next.sharedConstOf();
+            }
+            else
+            {
+                if (_this.next.isWild())
+                    t.next = _this.next.wildConstOf();
+                else
+                    t.next = _this.next.constOf();
+            }
+        }
+        //printf("TypeNext::makeConst() returns %p, %s\n", t, t.toChars());
+        return t;
+    }
+
+    if (auto tn = _this.isTypeNext())
+        return typeNextMakeConst(tn);
+
+    return defaultMakeConst(_this);
+}
+
+/*******************************
+ * If _this is a shell around another type,
+ * get that other type.
+ */
+Type toBasetype(Type _this)
+{
+    /* This function is used heavily.
+     * De-virtualize it so it can be easily inlined.
+     */
+    if (auto te = _this.isTypeEnum())
+        return te.toBasetype2();
+    return _this;
+}
+
+Type toBasetype2(TypeEnum _this)
+{
+    if (!_this.sym.members && !_this.sym.memtype)
+        return _this;
+    auto tb = _this.sym.getMemtype(Loc.initial).toBasetype();
+    return tb.castMod(_this.mod); // retain modifier bits from '_this'
+}
+
+Type memType(TypeEnum _this)
+{
+    return _this.sym.getMemtype(Loc.initial);
+}
+
+uint alignsize(Type _this)
+{
+    static uint structAlignsize(TypeStruct _this)
+    {
+        import dmd.dsymbolsem : size;
+        _this.sym.size(Loc.initial); // give error for forward references
+        return _this.sym.alignsize;
+    }
+
+    static uint enumAlignsize(TypeEnum _this)
+    {
+        Type t = _this.memType();
+        if (t.ty == Terror)
+            return 4;
+        return t.alignsize();
+    }
+
+    if (auto tb = _this.isTypeBasic())
+        return target.alignsize(tb);
+
+    switch(_this.ty)
+    {
+        case Tvector: return cast(uint)_this.isTypeVector().basetype.size();
+        case Tsarray: return _this.isTypeSArray().next.alignsize();
+        // A DArray consists of two ptr-sized values, so align it on pointer size boundary
+        case Tarray, Tdelegate: return target.ptrsize;
+        case Tstruct: return structAlignsize(_this.isTypeStruct());
+        case Tenum: return enumAlignsize(_this.isTypeEnum());
+        case Tnoreturn: return 0;
+        default: return cast(uint)size(_this, Loc.initial);
+    }
+}
+
+/*************************************
+ * 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(Type _this)
+{
+    if (auto tsa = _this.isTypeSArray())
+    {
+        return tsa.next.hasVoidInitPointers();
+    }
+    else if (auto ts = _this.isTypeStruct())
+    {
+        import dmd.dsymbolsem : size;
+        ts.sym.size(Loc.initial); // give error for forward references
+        ts.sym.determineTypeProperties();
+        return ts.sym.hasVoidInitPointers;
+    }
+    else if (auto te = _this.isTypeEnum())
+    {
+        return te.memType().hasVoidInitPointers();
+    }
+    return false;
+}
+
+void Type_init()
+{
+    Type.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
+    ];
+
+    static Type merge(Type t)
+    {
+        import dmd.mangle.basic : tyToDecoBuffer;
+
+        OutBuffer buf;
+        buf.reserve(3);
+
+        if (t.ty == Tnoreturn)
+            buf.writestring("Nn");
+        else
+            tyToDecoBuffer(buf, t.ty);
+
+        auto sv = t.stringtable.update(buf[]);
+        if (sv.value)
+            return sv.value;
+        t.deco = cast(char*)sv.toDchars();
+        sv.value = t;
+        return t;
+    }
+
+    for (size_t i = 0; basetab[i] != Terror; i++)
+    {
+        Type t = new TypeBasic(basetab[i]);
+        .merge(t);
+        t = merge(t);
+        Type.basic[basetab[i]] = t;
+    }
+    Type.basic[Terror] = new TypeError();
+
+    Type.tnoreturn = new TypeNoreturn();
+    Type.tnoreturn.deco = merge(Type.tnoreturn).deco;
+    Type.basic[Tnoreturn] = Type.tnoreturn;
+
+    Type.tvoid = Type.basic[Tvoid];
+    Type.tint8 = Type.basic[Tint8];
+    Type.tuns8 = Type.basic[Tuns8];
+    Type.tint16 = Type.basic[Tint16];
+    Type.tuns16 = Type.basic[Tuns16];
+    Type.tint32 = Type.basic[Tint32];
+    Type.tuns32 = Type.basic[Tuns32];
+    Type.tint64 = Type.basic[Tint64];
+    Type.tuns64 = Type.basic[Tuns64];
+    Type.tint128 = Type.basic[Tint128];
+    Type.tuns128 = Type.basic[Tuns128];
+    Type.tfloat32 = Type.basic[Tfloat32];
+    Type.tfloat64 = Type.basic[Tfloat64];
+    Type.tfloat80 = Type.basic[Tfloat80];
+
+    Type.timaginary32 = Type.basic[Timaginary32];
+    Type.timaginary64 = Type.basic[Timaginary64];
+    Type.timaginary80 = Type.basic[Timaginary80];
+
+    Type.tcomplex32 = Type.basic[Tcomplex32];
+    Type.tcomplex64 = Type.basic[Tcomplex64];
+    Type.tcomplex80 = Type.basic[Tcomplex80];
+
+    Type.tbool = Type.basic[Tbool];
+    Type.tchar = Type.basic[Tchar];
+    Type.twchar = Type.basic[Twchar];
+    Type.tdchar = Type.basic[Tdchar];
+
+    Type.tshiftcnt = Type.tint32;
+    Type.terror = Type.basic[Terror];
+    Type.tnoreturn = Type.basic[Tnoreturn];
+    Type.tnull = new TypeNull();
+    Type.tnull.deco = merge(Type.tnull).deco;
+
+    Type.tvoidptr = Type.tvoid.pointerTo();
+    Type.tstring = Type.tchar.immutableOf().arrayOf();
+    Type.twstring = Type.twchar.immutableOf().arrayOf();
+    Type.tdstring = Type.tdchar.immutableOf().arrayOf();
+
+    const isLP64 = target.isLP64;
+
+    Type.tsize_t    = Type.basic[isLP64 ? Tuns64 : Tuns32];
+    Type.tptrdiff_t = Type.basic[isLP64 ? Tint64 : Tint32];
+    Type.thash_t = Type.tsize_t;
+
+    static if (__VERSION__ == 2081)
+    {
+        // Related issue: https://issues.dlang.org/show_bug.cgi?id=19134
+        // D 2.081.x regressed initializing class objects at compile time.
+        // As a workaround initialize this global at run-time instead.
+        TypeTuple.empty = new TypeTuple();
+    }
+}
+
+/************************************
+ * Return alignment to use for this type.
+ */
+structalign_t alignment(Type _this)
+{
+    if (auto tsa = _this.isTypeSArray())
+    {
+        return tsa.next.alignment();
+    }
+    else if (auto ts = _this.isTypeStruct())
+    {
+        import dmd.dsymbolsem : size;
+        if (ts.sym.alignment.isUnknown())
+            ts.sym.size(ts.sym.loc);
+        return ts.sym.alignment;
+    }
+    structalign_t s;
+    s.setDefault();
+    return s;
+}
+
+/***************************************
+ * Returns: true if type has any invariants
+ */
+bool hasInvariant(Type _this)
+{
+    if (auto tsa = _this.isTypeSArray())
+    {
+        return tsa.next.hasInvariant();
+    }
+    else if (auto ts = _this.isTypeStruct())
+    {
+        import dmd.dsymbolsem : size;
+        ts.sym.size(Loc.initial); // give error for forward references
+        ts.sym.determineTypeProperties();
+        return ts.sym.hasInvariant() || ts.sym.hasFieldWithInvariant;
+    }
+    else if (auto te = _this.isTypeEnum())
+    {
+        return te.memType().hasInvariant();
+    }
+    return false;
+}
+
+/*************************************
+ * Detect if this is an unsafe type because of the presence of `@system` members
+ * Returns:
+ *  true if so
+ */
+bool hasUnsafeBitpatterns(Type _this)
+{
+    static bool tstructImpl(TypeStruct _this)
+    {
+        import dmd.dsymbolsem : size;
+        _this.sym.size(Loc.initial); // give error for forward references
+        _this.sym.determineTypeProperties();
+        return _this.sym.hasUnsafeBitpatterns;
+    }
+
+    if(auto tb = _this.isTypeBasic())
+        return tb.ty == Tbool;
+
+    switch(_this.ty)
+    {
+        case Tenum: return _this.isTypeEnum().memType().hasUnsafeBitpatterns();
+        case Tstruct: return tstructImpl(_this.isTypeStruct());
+        case Tsarray: return _this.isTypeSArray().next.hasUnsafeBitpatterns();
+        default: return false;
+    }
+}
+
 /*************************************
  * Resolve a tuple index, `s[oindex]`, by figuring out what `s[oindex]` represents.
  * Setting one of pe/pt/ps.
@@ -175,7 +1517,7 @@ private void resolveHelper(TypeQualified mt, Loc loc, Scope* sc, Dsymbol s, Dsym
                 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))
+            else if (const q = search_correct_C(id))
                 error(loc, "undefined identifier `%s`, did you mean `%s`?", p, q);
             else if ((id == Id.This   && sc.getStructClassScope()) ||
                      (id == Id._super && sc.getClassScope()))
@@ -240,7 +1582,7 @@ private void resolveHelper(TypeQualified mt, Loc loc, Scope* sc, Dsymbol s, Dsym
             break;
         }
 
-        Type t = s.getType(); // type symbol, type alias, or type tuple?
+        Type t = dmd.dsymbolsem.getType(s); // type symbol, type alias, or type tuple?
         const errorsave = global.errors;
         SearchOptFlags flags = t is null ? SearchOpt.localsOnly : SearchOpt.ignorePrivateImports;
 
@@ -364,7 +1706,7 @@ private void resolveHelper(TypeQualified mt, Loc loc, Scope* sc, Dsymbol s, Dsym
     Type t;
     while (1)
     {
-        t = s.getType();
+        t = dmd.dsymbolsem.getType(s);
         if (t)
             break;
         ps = s;
@@ -614,62 +1956,6 @@ void purityLevel(TypeFunction typeFunction)
     tf.purity = typeFunction.purity;
 }
 
-/******************************************
- * 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(t.isTypeSArray());
-        case Taarray:   return visitAArray(t.isTypeAArray());
-        case Tident:    return visitIdentifier(t.isTypeIdentifier());
-        case Tinstance: return visitInstance(t.isTypeInstance());
-        case Tmixin:    return visitMixin(t.isTypeMixin());
-        default:        return null;
-    }
-}
-
 /*************************************
  * https://issues.dlang.org/show_bug.cgi?id=14488
  * Check if the inner most base type is complex or imaginary.
@@ -1133,7 +2419,7 @@ private extern(D) bool isCopyConstructorCallable (StructDeclaration argStruct,
 
     bool bpure = !f.isPure && sc.func.setImpure(arg.loc, null);
     bool bsafe = !f.isSafe() && !f.isTrusted() && sc.setUnsafe(false, arg.loc, null);
-    bool bnogc = !f.isNogc && sc.func.setGC(arg.loc, null);
+    bool bnogc = !f.isNogc && sc.setGC(sc.func, arg.loc, null);
     if (bpure | bsafe | bnogc)
     {
         const nullptr = "".ptr;
@@ -1246,11 +2532,10 @@ private extern(D) MATCH argumentMatchParameter (FuncDeclaration fd, TypeFunction
         }
 
         // check if the copy constructor may be called to copy the argument
-        if (arg.isLvalue() && !isRef && argStruct && argStruct == prmStruct && argStruct.hasCopyCtor)
+        if (arg.isLvalue() && !isRef && argStruct && argStruct == prmStruct && argStruct.hasCopyCtor &&
+            !isCopyConstructorCallable(argStruct, arg, tprm, sc, pMessage))
         {
-            if (!isCopyConstructorCallable(argStruct, arg, tprm, sc, pMessage))
-                return MATCH.nomatch;
-            m = MATCH.exact;
+            return MATCH.nomatch;
         }
         else
         {
@@ -1475,7 +2760,17 @@ private extern(D) MATCH matchTypeSafeVarArgs(TypeFunction tf, Parameter p,
     default:
         // We can have things as `foo(int[int] wat...)` but they only match
         // with an associative array proper.
-        if (pMessage && trailingArgs.length) *pMessage = tf.getParamError(trailingArgs[0], p);
+        if (!pMessage) {}
+        else if (!trailingArgs.length)
+        {
+            OutBuffer buf;
+            getMatchError(buf, "expected an argument for parameter `%s`",
+                parameterToChars(p, tf, false));
+            *pMessage = buf.extractChars();
+        }
+        else
+            *pMessage = tf.getParamError(trailingArgs[0], p);
+
         return MATCH.nomatch;
     }
 }
@@ -1729,7 +3024,11 @@ uinteger_t size(Type t, Loc loc)
         case Tinstance:
         case Ttypeof:
         case Treturn:       return visitTypeQualified(cast(TypeQualified)t);
-        case Tstruct:       return t.isTypeStruct().sym.size(loc);
+        case Tstruct:
+        {
+            import dmd.dsymbolsem: size;
+            return t.isTypeStruct().sym.size(loc);
+        }
         case Tenum:         return t.isTypeEnum().sym.getMemtype(loc).size(loc);
         case Tnull:         return t.tvoidptr.size(loc);
         case Tnoreturn:     return 0;
@@ -1927,13 +3226,7 @@ Type typeSemantic(Type type, Loc loc, Scope* sc)
 
     Type visitComplex(TypeBasic t)
     {
-        if (!sc.inCfile)
-            return visitType(t);
-
-        auto tc = getComplexLibraryType(loc, sc, t.ty);
-        if (tc.ty == Terror)
-            return tc;
-        return tc.addMod(t.mod).merge();
+        return visitType(t);
     }
 
     Type visitVector(TypeVector mtype)
@@ -2993,6 +4286,7 @@ Type typeSemantic(Type type, Loc loc, Scope* sc)
         if (s)
         {
             auto td = s.isTemplateDeclaration;
+            td.computeOneMember();
             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)`",
@@ -3071,7 +4365,7 @@ Type typeSemantic(Type type, Loc loc, Scope* sc)
         Type t;
         Dsymbol s;
         mtype.resolve(loc, sc, e, t, s);
-        if (s && (t = s.getType()) !is null)
+        if (s && (t = dmd.dsymbolsem.getType(s)) !is null)
             t = t.addMod(mtype.mod);
         if (!t)
         {
@@ -3104,7 +4398,7 @@ Type typeSemantic(Type type, Loc loc, Scope* sc)
         Type t;
         Dsymbol s;
         mtype.resolve(loc, sc, e, t, s);
-        if (s && (t = s.getType()) !is null)
+        if (s && (t = dmd.dsymbolsem.getType(s)) !is null)
             t = t.addMod(mtype.mod);
         if (!t)
         {
@@ -3482,7 +4776,7 @@ Type trySemantic(Type type, Loc loc, Scope* sc)
         // If `typeSemantic` succeeded, there may have been deprecations that
         // were gagged due the `startGagging` above.  Run again to display
         // those deprecations.  https://issues.dlang.org/show_bug.cgi?id=19107
-        if (global.gaggedWarnings > 0)
+        if (global.gaggedDeprecations > 0)
             typeSemantic(tcopy, loc, sc);
     }
     //printf("-trySemantic(%s) %d\n", toChars(), global.errors);
@@ -3617,7 +4911,10 @@ Expression defaultInitLiteral(Type t, Loc loc)
         {
             printf("TypeStruct::defaultInitLiteral() '%s'\n", toChars());
         }
-        ts.sym.size(loc);
+        {
+            import dmd.dsymbolsem: size;
+            ts.sym.size(loc);
+        }
         if (ts.sym.sizeok != Sizeok.done)
             return ErrorExp.get();
 
@@ -4511,7 +5808,7 @@ void resolve(Type mt, Loc loc, Scope* sc, out Expression pe, out Type pt, out Ds
             !mt.exp.type.isTypeTuple())
         {
             if (!sc.inCfile && // in (extended) C typeof may be used on types as with sizeof
-                mt.exp.checkType())
+                !mt.exp.hasValidType())
                 goto Lerr;
 
             /* Today, 'typeof(func)' returns void if func is a
@@ -4872,7 +6169,10 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag
                 {
                     auto ad = v.isMember();
                     objc.checkOffsetof(e, ad);
-                    ad.size(e.loc);
+                    {
+                        import dmd.dsymbolsem: size;
+                        ad.size(e.loc);
+                    }
                     if (ad.sizeok != Sizeok.done)
                         return ErrorExp.get();
                     uint value;
@@ -5533,7 +6833,7 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag
             }
         }
 
-        if (auto t = s.getType())
+        if (auto t = dmd.dsymbolsem.getType(s))
         {
             return (new TypeExp(e.loc, t)).expressionSemantic(sc);
         }
@@ -5606,6 +6906,7 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag
             if (TupleDeclaration tup = d.isTupleDeclaration())
             {
                 e = new TupleExp(e.loc, tup);
+                fillTupleExpExps(e.isTupleExp(), tup);
                 return e.expressionSemantic(sc);
             }
             if (d.needThis() && sc.intypeof != 1)
@@ -5724,7 +7025,10 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag
              */
             e = e.expressionSemantic(sc); // do this before turning on noAccessCheck
 
-            mt.sym.size(e.loc); // do semantic of type
+            {
+                import dmd.dsymbolsem: size;
+                mt.sym.size(e.loc); // do semantic of type
+            }
 
             Expression e0;
             Expression ev = e.op == EXP.type ? null : e;
@@ -5958,7 +7262,7 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag
             }
         }
 
-        if (auto t = s.getType())
+        if (auto t = dmd.dsymbolsem.getType(s))
         {
             return (new TypeExp(e.loc, t)).expressionSemantic(sc);
         }
@@ -6039,6 +7343,7 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag
             if (TupleDeclaration tup = d.isTupleDeclaration())
             {
                 e = new TupleExp(e.loc, tup);
+                fillTupleExpExps(e.isTupleExp(), tup);
                 e = e.expressionSemantic(sc);
                 return e;
             }
@@ -6049,7 +7354,7 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag
                 && d.isFuncDeclaration().objc.selector)
             {
                 auto classRef = new ObjcClassReferenceExp(e.loc, mt.sym);
-                classRef.type = objc.getRuntimeMetaclass(mt.sym).getType();
+                classRef.type = dmd.dsymbolsem.getType((objc.getRuntimeMetaclass(mt.sym)));
                 return new DotVarExp(e.loc, classRef, d).expressionSemantic(sc);
             }
             else if (d.needThis() && sc.intypeof != 1)
@@ -6680,7 +7985,7 @@ Type getComplexLibraryType(Loc loc, Scope* sc, TY ty)
         return *pt;
     }
     s = s.toAlias();
-    if (auto t = s.getType())
+    if (auto t = dmd.dsymbolsem.getType(s))
     {
         if (auto ts = t.toBasetype().isTypeStruct())
         {
@@ -8051,53 +9356,6 @@ bool isOpaqueType(Type t)
 
 private:
 
-/* 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", EXPtoString(e.op).ptr, e.toChars());
-    foreach (id; t.idents[i .. t.idents.length])
-    {
-        //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;
-}
-
 /**************************
  * This evaluates exp while setting length to be the number
  * of elements in the tuple t.
index 503d00bfd340e29b28b51e7e28ae5330d7c95e91..8bcb9dba74a9a0d48339042d2c40834bffdfbb0b 100644 (file)
@@ -24,6 +24,7 @@ import dmd.location;
 import dmd.mtype;
 import dmd.templatesem;
 import dmd.typesem;
+import dmd.dsymbolsem : addDeferredSemantic3;
 import core.stdc.stdio;
 
 /****************************************************
@@ -52,7 +53,7 @@ bool genTypeInfo(Expression e, Loc loc, Type torig, Scope* sc)
             if (e)
                 .error(loc, "expression `%s` uses the GC and cannot be used with switch `-betterC`", e.toChars());
             else
-                .error(loc, "`TypeInfo` cannot be used with -betterC");
+                .error(loc, "`TypeInfo` cannot be used with `-betterC`");
 
             if (sc && sc.tinst)
                 sc.tinst.printInstantiationTrace(Classification.error, uint.max);
@@ -168,7 +169,7 @@ TypeInfoDeclaration getTypeInfoAssocArrayDeclaration(TypeAArray t, Scope* sc)
     t.vtinfo = ti; // assign it early to avoid recursion in expressionSemantic
     ti._scope = sc;
     sc.setNoFree();
-    Module.addDeferredSemantic3(ti);
+    addDeferredSemantic3(ti);
     return ti;
 }
 
index c9ee5a6696f86d6c7a1b68177c35e61355b84bce..6a5059bba899c420e4a053e794130ffbfab6a3d5 100644 (file)
@@ -44,54 +44,6 @@ along with GCC; see the file COPYING3.  If not see
 #include "d-tree.h"
 
 
-/* Determine if type T is a struct that has a postblit.  */
-
-static bool
-needs_postblit (Type *t)
-{
-  t = t->baseElemOf ();
-
-  if (TypeStruct *ts = t->isTypeStruct ())
-    {
-      if (ts->sym->postblit)
-       return true;
-    }
-
-  return false;
-}
-
-/* Determine if type T is a struct that has a destructor.  */
-
-static bool
-needs_dtor (Type *t)
-{
-  t = t->baseElemOf ();
-
-  if (TypeStruct *ts = t->isTypeStruct ())
-    {
-      if (ts->sym->dtor)
-       return true;
-    }
-
-  return false;
-}
-
-/* Determine if expression E is a suitable lvalue.  */
-
-static bool
-lvalue_p (Expression *e)
-{
-  SliceExp *se = e->isSliceExp ();
-  if (se != NULL && se->e1->isLvalue ())
-    return true;
-
-  CastExp *ce = e->isCastExp ();
-  if (ce != NULL && ce->e1->isLvalue ())
-    return true;
-
-  return (e->op != EXP::slice && e->isLvalue ());
-}
-
 /* Build an expression of code CODE, data type TYPE, and operands ARG0 and
    ARG1.  Perform relevant conversions needed for correct code operations.  */
 
@@ -295,14 +247,14 @@ public:
        this->result_ = d_convert (build_ctype (e->type),
                                   build_boolop (code, t1, t2));
       }
-    else if (tb1->isFloating () && tb1->ty != TY::Tvector)
+    else if (dmd::isFloating (tb1) && tb1->ty != TY::Tvector)
       {
        /* For floating-point values, identity is defined as the bits in the
           operands being identical.  */
        tree t1 = d_save_expr (build_expr (e->e1));
        tree t2 = d_save_expr (build_expr (e->e2));
 
-       if (!tb1->isComplex ())
+       if (!dmd::isComplex (tb1))
          this->result_ = build_float_identity (code, t1, t2);
        else
          {
@@ -593,8 +545,8 @@ public:
       {
       case EXP::add:
       case EXP::min:
-       if ((e->e1->type->isReal () && e->e2->type->isImaginary ())
-           || (e->e1->type->isImaginary () && e->e2->type->isReal ()))
+       if ((dmd::isReal (e->e1->type) && dmd::isImaginary (e->e2->type))
+           || (dmd::isImaginary (e->e1->type) && dmd::isReal (e->e2->type)))
          {
            /* If the result is complex, then we can shortcut binary_op.
               Frontend should have already validated types and sizes.  */
@@ -604,7 +556,7 @@ public:
            if (e->op == EXP::min)
              t2 = build1 (NEGATE_EXPR, TREE_TYPE (t2), t2);
 
-           if (e->e1->type->isReal ())
+           if (dmd::isReal (e->e1->type))
              this->result_ = complex_expr (build_ctype (e->type), t1, t2);
            else
              this->result_ = complex_expr (build_ctype (e->type), t2, t1);
@@ -634,12 +586,12 @@ public:
              }
          }
 
-       code = e->e1->type->isIntegral ()
+       code = dmd::isIntegral (e->e1->type)
          ? TRUNC_DIV_EXPR : RDIV_EXPR;
        break;
 
       case EXP::mod:
-       code = e->e1->type->isFloating ()
+       code = dmd::isFloating (e->e1->type)
          ? FLOAT_MOD_EXPR : TRUNC_MOD_EXPR;
        break;
 
@@ -723,12 +675,12 @@ public:
        break;
 
       case EXP::divAssign:
-       code = e->e1->type->isIntegral ()
+       code = dmd::isIntegral (e->e1->type)
          ? TRUNC_DIV_EXPR : RDIV_EXPR;
        break;
 
       case EXP::modAssign:
-       code = e->e1->type->isFloating ()
+       code = dmd::isFloating (e->e1->type)
          ? FLOAT_MOD_EXPR : TRUNC_MOD_EXPR;
        break;
 
@@ -832,6 +784,16 @@ public:
       }
   }
 
+  /* Lower a construction expression, or forward to assignment expression.  */
+
+  void visit (ConstructExp *e) final override
+  {
+    if (!e->lowering)
+      return ExprVisitor::visit ((AssignExp *) e);
+
+    this->result_ = build_expr (e->lowering);
+  }
+
   /* Build an assignment expression.  The right operand is implicitly
      converted to the type of the left operand, and assigned to it.  */
 
@@ -865,10 +827,6 @@ public:
        Type *stype = se->e1->type->toBasetype ();
        Type *etype = stype->nextOf ()->toBasetype ();
 
-       /* Determine if we need to run postblit or dtor.  */
-       bool postblit = needs_postblit (etype) && lvalue_p (e->e2);
-       bool destructor = needs_dtor (etype);
-
        if (e->memset == MemorySet::blockAssign)
          {
            /* Set a range of elements to one value.  */
@@ -880,13 +838,6 @@ public:
            tree init = stabilize_expr (&t1);
            t1 = d_save_expr (t1);
 
-           if ((postblit || destructor) && e->op != EXP::blit)
-             {
-               /* This case should have been rewritten to `_d_arraysetassign`
-                  in the semantic phase.  */
-               gcc_unreachable ();
-             }
-
            if (integer_zerop (t2))
              {
                tree size = size_mult_expr (d_array_length (t1),
@@ -905,65 +856,46 @@ public:
            /* Perform a memcpy operation.  */
            gcc_assert (e->e2->type->ty != TY::Tpointer);
 
-           if (!postblit && !destructor)
-             {
-               tree t1 = d_save_expr (d_array_convert (e->e1));
-               tree t2 = d_save_expr (d_array_convert (e->e2));
+           tree t1 = d_save_expr (d_array_convert (e->e1));
+           tree t2 = d_save_expr (d_array_convert (e->e2));
 
-               /* References to array data.  */
-               tree t1ptr = d_array_ptr (t1);
-               tree t1len = d_array_length (t1);
-               tree t2ptr = d_array_ptr (t2);
+           /* References to array data.  */
+           tree t1ptr = d_array_ptr (t1);
+           tree t1len = d_array_length (t1);
+           tree t2ptr = d_array_ptr (t2);
 
-               /* Generate: memcpy(to, from, size)  */
-               tree size =
-                 size_mult_expr (t1len, size_int (dmd::size (etype)));
-               tree result = build_memcpy_call (t1ptr, t2ptr, size);
+           /* Generate: memcpy(to, from, size)  */
+           tree size = size_mult_expr (t1len, size_int (dmd::size (etype)));
+           tree result = build_memcpy_call (t1ptr, t2ptr, size);
 
-               /* Insert check that array lengths match and do not overlap.  */
-               if (array_bounds_check ())
-                 {
-                   /* tlencmp = (t1len == t2len)  */
-                   tree t2len = d_array_length (t2);
-                   tree tlencmp = build_boolop (EQ_EXPR, t1len, t2len);
-
-                   /* toverlap = (t1ptr + size <= t2ptr
-                                  || t2ptr + size <= t1ptr)  */
-                   tree t1ptrcmp = build_boolop (LE_EXPR,
-                                                 build_offset (t1ptr, size),
-                                                 t2ptr);
-                   tree t2ptrcmp = build_boolop (LE_EXPR,
-                                                 build_offset (t2ptr, size),
-                                                 t1ptr);
-                   tree toverlap = build_boolop (TRUTH_ORIF_EXPR, t1ptrcmp,
-                                                 t2ptrcmp);
-
-                   /* (tlencmp && toverlap) ? memcpy() : _d_arraybounds()  */
-                   tree tassert = build_array_bounds_call (e->loc);
-                   tree tboundscheck = build_boolop (TRUTH_ANDIF_EXPR,
-                                                     tlencmp, toverlap);
-
-                   result = build_condition (void_type_node, tboundscheck,
-                                             result, tassert);
-                 }
-
-               this->result_ = compound_expr (result, t1);
-             }
-           else if ((postblit || destructor)
-                    && e->op != EXP::blit && e->op != EXP::construct)
-             {
-               /* Assigning to a non-trivially copyable array has already been
-                  handled by the front-end.  */
-               gcc_unreachable ();
-             }
-           else
+           /* Insert check that array lengths match and do not overlap.  */
+           if (array_bounds_check ())
              {
-               /* Generate: _d_arraycopy()  */
-               this->result_ = build_libcall (LIBCALL_ARRAYCOPY, e->type, 3,
-                                              size_int (dmd::size (etype)),
-                                              d_array_convert (e->e2),
-                                              d_array_convert (e->e1));
+               /* tlencmp = (t1len == t2len)  */
+               tree t2len = d_array_length (t2);
+               tree tlencmp = build_boolop (EQ_EXPR, t1len, t2len);
+
+               /* toverlap = (t1ptr + size <= t2ptr
+                  || t2ptr + size <= t1ptr)  */
+               tree t1ptrcmp = build_boolop (LE_EXPR,
+                                             build_offset (t1ptr, size),
+                                             t2ptr);
+               tree t2ptrcmp = build_boolop (LE_EXPR,
+                                             build_offset (t2ptr, size),
+                                             t1ptr);
+               tree toverlap = build_boolop (TRUTH_ORIF_EXPR, t1ptrcmp,
+                                             t2ptrcmp);
+
+               /* (tlencmp && toverlap) ? memcpy() : _d_arraybounds()  */
+               tree tassert = build_array_bounds_call (e->loc);
+               tree tboundscheck = build_boolop (TRUTH_ANDIF_EXPR,
+                                                 tlencmp, toverlap);
+
+               result = build_condition (void_type_node, tboundscheck,
+                                         result, tassert);
              }
+
+           this->result_ = compound_expr (result, t1);
          }
 
        return;
@@ -1040,35 +972,18 @@ public:
            this->result_ = build_memset_call (build_expr (e->e1));
            return;
          }
-
-       Type *etype = tb1->nextOf ();
-       gcc_assert (e->e2->type->toBasetype ()->ty == TY::Tsarray);
-
-       /* Determine if we need to run postblit.  */
-       const bool postblit = needs_postblit (etype);
-       const bool destructor = needs_dtor (etype);
-       const bool lvalue = lvalue_p (e->e2);
-
-       /* Optimize static array assignment with array literal.  Even if the
-          elements in rhs are all rvalues and don't have to call postblits,
-          this assignment should call dtors on old assigned elements.  */
-       if ((!postblit && !destructor)
-           || (e->op == EXP::construct && e->e2->op == EXP::arrayLiteral)
-           || (e->op == EXP::construct && e->e2->op == EXP::call)
-           || (e->op == EXP::construct && !lvalue && postblit)
-           || (e->op == EXP::blit || dmd::size (e->e1->type) == 0))
+       else
          {
+           /* Optimize static array assignment with array literal.  Even if the
+              elements in rhs are all rvalues and don't have to call postblits,
+              this assignment should call dtors on old assigned elements.  */
+           gcc_assert (e->e2->type->toBasetype ()->ty == TY::Tsarray);
            tree t1 = build_expr (e->e1);
            tree t2 = convert_for_assignment (e->e2, e->e1->type);
 
            this->result_ = build_assign (modifycode, t1, t2);
            return;
          }
-
-       /* All other kinds of lvalue or rvalue static array assignment.
-          Array construction has already been handled by the front-end.  */
-       gcc_assert (e->op != EXP::construct);
-       gcc_unreachable ();
       }
 
     /* Simple assignment.  */
@@ -1424,12 +1339,12 @@ public:
       {
        AddExp *ae = e->e1->isAddExp ();
        if (ae->e1->op == EXP::address
-           && ae->e2->isConst () && ae->e2->type->isIntegral ())
+           && ae->e2->isConst () && dmd::isIntegral (ae->e2->type))
          {
            Expression *ex = ae->e1->isAddrExp ()->e1;
            tnext = ex->type->toBasetype ();
            result = build_expr (ex);
-           offset = ae->e2->toUInteger ();
+           offset = dmd::toUInteger (ae->e2);
          }
       }
     else if (e->e1->op == EXP::symbolOffset)
@@ -2244,7 +2159,7 @@ public:
        tree new_call;
 
        /* Cannot new an opaque struct.  */
-       if (sd->size (e->loc) == 0)
+       if (dmd::size (sd, e->loc) == 0)
          {
            this->result_ = d_convert (build_ctype (e->type),
                                       integer_zero_node);
@@ -2516,7 +2431,7 @@ public:
     /* Implicitly convert void[n] to ubyte[n].  */
     if (tb->ty == TY::Tsarray && tb->nextOf ()->toBasetype ()->ty == TY::Tvoid)
       {
-       dinteger_t ndim = tb->isTypeSArray ()->dim->toUInteger ();
+       dinteger_t ndim = dmd::toUInteger (tb->isTypeSArray ()->dim);
        tb = dmd::sarrayOf (Type::tuns8, ndim);
       }
 
index e2e2ba49b7d1995bf5e4fa05b2391507204c7e47..a3b3d5e4b26c579e40b91e9b96651b91db30cfec 100644 (file)
@@ -130,7 +130,7 @@ public:
   void visit (VarDeclaration *d) final override
   {
     /* Not all kinds of manifest constants create a CONST_DECL.  */
-    if (!d->canTakeAddressOf () && !d->type->isScalar ())
+    if (!d->canTakeAddressOf () && !dmd::isScalar (d->type))
       return;
 
     visit ((Declaration *) d);
@@ -148,7 +148,7 @@ public:
 
     if (dsym == d)
       {
-       Type *type = d->getType ();
+       Type *type = dmd::getType (d);
 
        /* Type imports should really be part of their own visit method.  */
        if (type != NULL)
index 56f1c91c86e902c3effa4042c1a5f16fe77ef87b..792496af4f0ccb940f8e4786c6b35a6f151560ee 100644 (file)
@@ -253,12 +253,12 @@ DEF_CTFE_BUILTIN (INTRINSIC_FMA, BUILT_IN_FMAL, "fma", "std.math.operations",
 
 /* core.stdc.stdarg intrinsics.  */
 
-DEF_D_BUILTIN (INTRINSIC_VA_ARG, BUILT_IN_NONE, "va_arg", "core.stdc.stdarg",
-              "FK@7va_listK@1TZv")
-DEF_D_BUILTIN (INTRINSIC_C_VA_ARG, BUILT_IN_NONE, "va_arg", "core.stdc.stdarg",
-              "FK@7va_listZ@1T")
-DEF_D_BUILTIN (INTRINSIC_VASTART, BUILT_IN_NONE, "va_start", "core.stdc.stdarg",
-              "FJ@7va_listK@1TZv")
+DEF_D_BUILTIN (INTRINSIC_VA_ARG, BUILT_IN_NONE, "va_arg",
+              "core.internal.vararg.gnu", "FK@7va_listK@1TZv")
+DEF_D_BUILTIN (INTRINSIC_C_VA_ARG, BUILT_IN_NONE, "va_arg",
+              "core.internal.vararg.gnu", "FK@7va_listZ@1T")
+DEF_D_BUILTIN (INTRINSIC_VASTART, BUILT_IN_NONE, "va_start",
+              "core.internal.vararg.gnu", "FJ@7va_listK@1TZv")
 
 /* gcc.simd intrinsics.  */
 
index 430815c0bbc7f4069cd0fa102c191975dc2a280f..2fbab2f8863d93a33fd1c9b380aca7e88b2e9d57 100644 (file)
@@ -519,6 +519,14 @@ shared-libphobos
 Driver
 Link the standard D library dynamically in the compilation.
 
+std=d2024
+D
+Conform to the 2024 D language edition.
+
+std=d202y
+D
+Conform to the latest 202Y D language edition (experimental).
+
 v
 D
 ; Documented in C
index 462977f817396beed7848c205ea59f79c33045a4..eb4e825ce1c8fb483cc419b506ebe8525bf932e8 100644 (file)
@@ -831,7 +831,7 @@ public:
 
     /* A switch statement on a string gets turned into a library call.
        It is not lowered during codegen.  */
-    if (!condtype->isScalar ())
+    if (!dmd::isScalar (condtype))
       {
        error ("cannot handle switch condition of type %s",
               condtype->toChars ());
@@ -920,7 +920,7 @@ public:
     else
       {
        tree casevalue;
-       if (s->exp->type->isScalar ())
+       if (dmd::isScalar (s->exp->type))
          casevalue = build_expr (s->exp);
        else
          casevalue = build_integer_cst (s->index, build_ctype (Type::tint32));
@@ -1378,7 +1378,7 @@ public:
 
   void visit (GccAsmStatement *s) final override
   {
-    StringExp *insn = s->insn->toStringExp ();
+    StringExp *insn = dmd::toStringExp (s->insn);
     tree outputs = NULL_TREE;
     tree inputs = NULL_TREE;
     tree clobbers = NULL_TREE;
@@ -1393,7 +1393,7 @@ public:
            const char *sname = name ? name->toChars () : NULL;
            tree id = name ? build_string (strlen (sname), sname) : NULL_TREE;
 
-           StringExp *constr = (*s->constraints)[i]->toStringExp ();
+           StringExp *constr = dmd::toStringExp ((*s->constraints)[i]);
            const char *cstring = (const char *)(constr->len
                                                 ? constr->string : "");
            tree str = build_string (constr->len, cstring);
@@ -1419,7 +1419,7 @@ public:
       {
        for (size_t i = 0; i < s->clobbers->length; i++)
          {
-           StringExp *clobber = (*s->clobbers)[i]->toStringExp ();
+           StringExp *clobber = dmd::toStringExp ((*s->clobbers)[i]);
            const char *cstring = (const char *)(clobber->len
                                                 ? clobber->string : "");
 
index b6792dfe5cae9763b789cda7619e4fc0e0772017..340a1d67e1706a6caf9a4e48eb9577e8a43dfeff 100644 (file)
@@ -467,7 +467,7 @@ class TypeInfoVisitor : public Visitor
 
        /* Fill in the vtbl[].  */
        if (!cd->isInterfaceDeclaration ())
-         b->fillVtbl (cd, &b->vtbl, 1);
+         dmd::fillVtbl (b, cd, &b->vtbl, 1);
 
        /* ClassInfo for the interface.  */
        value = build_address (get_classinfo_decl (id));
@@ -517,7 +517,7 @@ class TypeInfoVisitor : public Visitor
       return;
 
     /* Fill bvtbl with the functions we want to put out.  */
-    if (cd != bcd && !bs->fillVtbl (cd, &bvtbl, 0))
+    if (cd != bcd && !dmd::fillVtbl (bs, cd, &bvtbl, 0))
       return;
 
     /* First entry is struct Interface reference.  */
@@ -722,7 +722,7 @@ public:
     this->layout_field (build_typeinfo (d->loc, ti->next));
 
     /* Static array length.  */
-    this->layout_field (size_int (ti->dim->toInteger ()));
+    this->layout_field (size_int (dmd::toInteger (ti->dim)));
   }
 
   /* Layout of TypeInfo_AssociativeArray is:
@@ -1093,6 +1093,9 @@ public:
     if (!sd->members)
       return;
 
+    if (sd->semanticRun () < PASS::semantic3done)
+      dmd::semanticTypeInfoMembers (sd);
+
     /* Mangled name of the struct declaration.  */
     this->layout_string (ti->deco);
 
@@ -1142,7 +1145,7 @@ public:
       this->layout_field (null_pointer_node);
 
     /* uint m_align;  */
-    this->layout_field (build_integer_cst (ti->alignsize (), d_uint_type));
+    this->layout_field (build_integer_cst (dmd::alignsize (ti), d_uint_type));
 
     /* immutable(void)* xgetRTInfo;  */
     if (sd->getRTInfo)
@@ -1237,7 +1240,7 @@ base_vtable_offset (ClassDeclaration *cd, BaseClass *bc)
       for (size_t k = 0; k < cd2->vtblInterfaces->length; k++)
        {
          BaseClass *bs = (*cd2->vtblInterfaces)[k];
-         if (bs->fillVtbl (cd, NULL, 0))
+         if (dmd::fillVtbl (bs, cd, NULL, 0))
            {
              if (bc == bs)
                return csymoffset;
index cab9b6c800ac4b6d7bfa980038d5b1666a042fa6..81d02eb7c6bde584bb186240a381fbdc0fbee0fe 100644 (file)
@@ -895,9 +895,9 @@ public:
 
   void visit (TypeSArray *t) final override
   {
-    if (t->dim->isConst () && t->dim->type->isIntegral ())
+    if (t->dim->isConst () && dmd::isIntegral (t->dim->type))
       {
-       uinteger_t size = t->dim->toUInteger ();
+       uinteger_t size = dmd::toUInteger (t->dim);
        t->ctype = make_array_type (t->next, size);
       }
     else
@@ -912,7 +912,7 @@ public:
 
   void visit (TypeVector *t) final override
   {
-    int nunits = t->basetype->isTypeSArray ()->dim->toUInteger ();
+    int nunits = dmd::toUInteger (t->basetype->isTypeSArray ()->dim);
     tree inner = build_ctype (t->elementType ());
 
     /* Same rationale as void static arrays.  */
@@ -1156,7 +1156,7 @@ public:
                tree ident = get_identifier (member->ident->toChars ());
 
                Expression *evalue = member->value ();
-               tree value = build_integer_cst (evalue->toInteger (),
+               tree value = build_integer_cst (dmd::toInteger (evalue),
                                                basetype);
 
                /* Build an identifier for the enumeration constant.  */
index 88cdaf8c99d08552c2857c6b1c19d269b05d406a..73a10473c9d1494339593ba48a1d638bfd5c7849 100644 (file)
@@ -1,5 +1,6 @@
 // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=90601
 // { dg-do compile }
+// { dg-options "-Wno-deprecated" }
 
 int postincr(int a)
 {
diff --git a/gcc/testsuite/gdc.test/compilable/autoreflambda.d b/gcc/testsuite/gdc.test/compilable/autoreflambda.d
new file mode 100644 (file)
index 0000000..a20b273
--- /dev/null
@@ -0,0 +1 @@
+alias f = (auto ref int x) {};
index 7187c37410ec7a6f588adfed482bb32fbbfed522..74717061546e532b5f5c80b8b844c833e4deee99 100644 (file)
@@ -1,4 +1,4 @@
-/* REQUIRED_ARGS: -preview=bitfields
+/*
  */
 
 /***************************************************/
diff --git a/gcc/testsuite/gdc.test/compilable/dotvar_ref_return.d b/gcc/testsuite/gdc.test/compilable/dotvar_ref_return.d
new file mode 100644 (file)
index 0000000..881368e
--- /dev/null
@@ -0,0 +1,11 @@
+// REQUIRED_ARGS: -inline
+
+struct Foo
+{
+    int x;
+    int foo()
+    {
+        ref get() { return x; }
+        return get();
+    }
+}
diff --git a/gcc/testsuite/gdc.test/compilable/extra-files/ucn_vars.i b/gcc/testsuite/gdc.test/compilable/extra-files/ucn_vars.i
new file mode 100644 (file)
index 0000000..f7c5ce3
--- /dev/null
@@ -0,0 +1,11 @@
+struct Vars {
+    int x²;
+    int \u2160;
+    int \u2182;
+    int \u00C0;
+    int \u00C1;
+    int \U000000C2;
+    int wh\u00ff;
+    int a\u00c4b\u0441\U000003b4e;
+    int b\u3021𗘰\u3023e;
+};
diff --git a/gcc/testsuite/gdc.test/compilable/fix21894.d b/gcc/testsuite/gdc.test/compilable/fix21894.d
new file mode 100644 (file)
index 0000000..2ca139b
--- /dev/null
@@ -0,0 +1,15 @@
+// https://github.com/dlang/dmd/issues/21894
+
+inout(int[]) test1(inout(int[]) a, int b)
+{
+    return a ~ b;
+}
+inout(int[]) test2(inout(int[]) a, inout(int[]) b)
+{
+    return a ~ b;
+}
+void main()
+{
+    test1([1], 2);
+    test2([1], [2]);
+}
diff --git a/gcc/testsuite/gdc.test/compilable/fix21945.d b/gcc/testsuite/gdc.test/compilable/fix21945.d
new file mode 100644 (file)
index 0000000..cbd6dcb
--- /dev/null
@@ -0,0 +1,6 @@
+// REQUIRED_ARGS: -preview=dip1000
+
+void assertEq(scope int[][3] x) @safe
+{
+       bool b = x == x;
+}
index 36baef532318d0d0c08d815aa7c7af95f29e9083..84dcfb3d0d60c8bd95402097197950af60119772 100644 (file)
@@ -49,7 +49,7 @@ void nesting(double d, int i)
 
 class Tree {
     int opApply(int delegate(size_t, Tree) dg) {
-        if (dg(0, this)) return 1;
+        if (auto r = dg(0, this)) return r;
         return 0;
     }
 }
diff --git a/gcc/testsuite/gdc.test/compilable/issue19163.d b/gcc/testsuite/gdc.test/compilable/issue19163.d
new file mode 100644 (file)
index 0000000..26c1c44
--- /dev/null
@@ -0,0 +1,20 @@
+struct NameAttribute
+{
+    int delegate() foo;
+}
+
+static NameAttribute getNamedAttribute(alias S)()
+{
+    return __traits(getAttributes, S)[0];
+}
+
+struct TestStruct
+{
+    @NameAttribute({ return 42; }) int a;
+}
+
+void test()
+{
+    TestStruct m;
+    enum nameAttr = getNamedAttribute!(m.a);
+}
diff --git a/gcc/testsuite/gdc.test/compilable/issue21997.d b/gcc/testsuite/gdc.test/compilable/issue21997.d
new file mode 100644 (file)
index 0000000..238c9d7
--- /dev/null
@@ -0,0 +1,10 @@
+extern(C++) struct S
+{
+    static void link(alias MO)(){}
+    __gshared int i;
+}
+
+void main()
+{
+    S.link!(S.i);
+}
diff --git a/gcc/testsuite/gdc.test/compilable/mangle1.d b/gcc/testsuite/gdc.test/compilable/mangle1.d
new file mode 100644 (file)
index 0000000..180bfb8
--- /dev/null
@@ -0,0 +1,25 @@
+//spaces
+__gshared pragma(mangle, "test 9") ubyte test9_1;
+__gshared extern pragma(mangle, "test 9") ubyte test9_1_e;
+
+//\n chars
+__gshared pragma(mangle, "test\\\n9") ubyte test9_2;
+__gshared extern pragma(mangle, "test\\\n9") ubyte test9_2_e;
+
+//\a chars
+__gshared pragma(mangle, "test\a9") ubyte test9_3;
+__gshared extern pragma(mangle, "test\a9") ubyte test9_3_e;
+
+//\x01 chars
+__gshared pragma(mangle, "test\x019") ubyte test9_4;
+__gshared extern pragma(mangle, "test\x019") ubyte test9_4_e;
+
+//\xff chars
+__gshared pragma(mangle, "test\xff9") ubyte test9_6;
+__gshared extern pragma(mangle, "test\xff9") ubyte test9_6_e;
+
+//unicode
+__gshared pragma(mangle, "😀ÀÁÂÃÄÅßàáâãäaåbæçèéêcëìígîïð7ñ9__òóô4õöÆ3ÇÈÉÊËabcÌÍÎÏÐÑÒÓÔÕÖ😄😅🤣😂_ÿ")
+ubyte test9_7;
+__gshared extern pragma(mangle, "😀ÀÁÂÃÄÅßàáâãäaåbæçèéêcëìígîïð7ñ9__òóô4õöÆ3ÇÈÉÊËabcÌÍÎÏÐÑÒÓÔÕÖ😄😅🤣😂_ÿ")
+ubyte test9_7_e;
diff --git a/gcc/testsuite/gdc.test/compilable/pragmamangle1.d b/gcc/testsuite/gdc.test/compilable/pragmamangle1.d
new file mode 100644 (file)
index 0000000..729afd2
--- /dev/null
@@ -0,0 +1,150 @@
+// Tests for pragma mangle
+module pragmamangle;
+
+version (Posix): // Itanium C++ ABI only
+
+string ctfe(string s) { return "mangle_" ~ "ctfe_" ~ s; }
+immutable string const_function = "mangle_const_function";
+immutable string const_variable = "mangle_const_variable";
+
+// This mangle string doesn't propagate to tests that use type_symbol.
+pragma(mangle, "Q_should_mangle_string_propagate")
+struct type_symbol { }
+
+class template_symbol(T) { }
+
+/* 1. Overrides the default mangling for a symbol.
+ */
+pragma(mangle, "mangle_function") void test_fun1();
+static assert(test_fun1.mangleof == "mangle_function");
+
+pragma(mangle, "mangle_attribute") extern(C) { nothrow { void test_pokeattr1(); } }
+static assert(test_pokeattr1.mangleof == "mangle_attribute");
+
+/* 2a. For variables and functions there must be one AssignExpression and it
+ * must evaluate at compile time to a string literal
+ */
+pragma(mangle, ctfe("function")) void test_fun2a1();
+static assert(test_fun2a1.mangleof == "mangle_ctfe_function");
+
+pragma(mangle, const_function) void test_fun2a2();
+static assert(test_fun2a2.mangleof == "mangle_const_function");
+
+pragma(mangle, ctfe("variable")) int test_var2a1;
+static assert(test_var2a1.mangleof == "mangle_ctfe_variable");
+
+pragma(mangle, const_variable) int test_var2a2;
+static assert(test_var2a2.mangleof == "mangle_const_variable");
+
+/* 2b. For aggregates there may be one or two AssignExpressions, one of which
+ * must evaluate at compile time to a string literal and one which must
+ * evaluate to a symbol.
+ * [UNDOCUMENTED] The pragma(mangle) attribute only gets applied to the
+ * encoded parameters types of extern(C++) functions.
+ */
+pragma(mangle, "mangle_struct") extern(C++) { struct S1 { } }
+extern(C++) void test_struct2b1(S1);
+extern(D) void externD_struct2b1(S1);
+static assert(test_struct2b1.mangleof == "_Z14test_struct2b113mangle_struct");
+static assert(externD_struct2b1.mangleof == "_D12pragmamangle17externD_struct2b1FSQBj2S1Zv");
+
+pragma(mangle, type_symbol, "mangle_struct") struct S2 { }
+extern(C++) void test_struct2b3(S2);
+extern(D) void externD_struct2b3(S2);
+static assert(test_struct2b3.mangleof == "_Z14test_struct2b313mangle_struct");
+static assert(externD_struct2b3.mangleof == "_D12pragmamangle17externD_struct2b3FSQBj2S2Zv");
+
+pragma(mangle, "mangle_struct", type_symbol) struct S3 { }
+extern(C++) void test_struct2b4(S3);
+extern(D) void externD_struct2b4(S3);
+static assert(test_struct2b4.mangleof == "_Z14test_struct2b413mangle_struct");
+static assert(externD_struct2b4.mangleof == "_D12pragmamangle17externD_struct2b4FSQBj2S3Zv");
+
+// Repeat struct tests on classes.
+
+pragma(mangle, "mangle_class") extern(C++) { class C1 { } }
+extern(C++) void test_class2b1(C1);
+extern(D) void externD_class2b1(C1);
+static assert(test_class2b1.mangleof == "_Z13test_class2b1P12mangle_class");
+static assert(externD_class2b1.mangleof == "_D12pragmamangle16externD_class2b1FCQBi2C1Zv");
+
+pragma(mangle, type_symbol, "mangle_class") class C2 { }
+extern(C++) void test_class2b3(C2);
+extern(D) void externD_class2b3(C2);
+static assert(test_class2b3.mangleof == "_Z13test_class2b3P12mangle_class");
+static assert(externD_class2b3.mangleof == "_D12pragmamangle16externD_class2b3FCQBi2C2Zv");
+
+pragma(mangle, "mangle_class", type_symbol) class C3 { }
+extern(C++) void test_class2b4(C3);
+extern(D) void externD_class2b4(C3);
+static assert(test_class2b4.mangleof == "_Z13test_class2b4P12mangle_class");
+static assert(externD_class2b4.mangleof == "_D12pragmamangle16externD_class2b4FCQBi2C3Zv");
+
+/* 2c. If that symbol is a TemplateInstance, the aggregate is treated as a
+ * template that has the signature and arguments of the TemplateInstance.
+ */
+template T1(C)
+{
+    pragma(mangle, C, "mangle_template") struct T1 { }
+}
+extern(C++) void test_template2c3(T1!(template_symbol!int));
+extern(C++) void test_template2c4(T1!(template_symbol!float));
+extern(C++) void test_template2c5(T1!(type_symbol));
+static assert(test_template2c3.mangleof == "_Z16test_template2c315mangle_templateIiE");
+static assert(test_template2c4.mangleof == "_Z16test_template2c415mangle_templateIfE");
+static assert(test_template2c5.mangleof == "_Z16test_template2c515mangle_template");
+
+template T2(C)
+{
+    pragma(mangle, "mangle_template", C) struct T2 { }
+}
+extern(C++) void test_template2c6(T2!(template_symbol!int));
+extern(C++) void test_template2c7(T2!(template_symbol!float));
+extern(C++) void test_template2c8(T2!(type_symbol));
+static assert(test_template2c6.mangleof == "_Z16test_template2c615mangle_templateIiE");
+static assert(test_template2c7.mangleof == "_Z16test_template2c715mangle_templateIfE");
+static assert(test_template2c8.mangleof == "_Z16test_template2c815mangle_template");
+
+/* 2d. The identifier of the symbol is used when no string is supplied.
+ */
+pragma(mangle, type_symbol) struct I1 { }
+extern(C++) void test_struct2d1(I1);
+static assert(test_struct2d1.mangleof == "_Z14test_struct2d111type_symbol");
+
+pragma(mangle, type_symbol) class I2 { }
+extern(C++) void test_class2d1(I2);
+static assert(test_class2d1.mangleof == "_Z13test_class2d1P11type_symbol");
+
+template I3(C)
+{
+    pragma(mangle, C) struct I3 { }
+}
+extern(C++) void test_template2d1(I3!(template_symbol!int));
+extern(C++) void test_template2d2(I3!(template_symbol!float));
+extern(C++) void test_template2d3(I3!(type_symbol));
+static assert(test_template2d1.mangleof == "_Z16test_template2d115template_symbolIiE");
+static assert(test_template2d2.mangleof == "_Z16test_template2d215template_symbolIfE");
+static assert(test_template2d3.mangleof == "_Z16test_template2d311type_symbol");
+
+// ??? No template arguments encoded.
+
+pragma(mangle, template_symbol!float) struct I4 { }
+extern(C++) void test_template2d4(I4);
+static assert(test_template2d4.mangleof == "_Z16test_template2d415template_symbol");
+
+/* 3. It only applies to function and variable symbols. Other symbols are
+ * ignored.
+ */
+pragma(mangle, "mangle_alias")
+alias ignored_alias = int;
+
+pragma(mangle, "mangle_enum")
+enum ignored_enum { a = 1 }
+
+pragma(mangle, "mangle_mixed_ignored")
+{
+    enum test_ignored1 { b }
+    void test_not_ignored();
+    alias test_ignored2 = int delegate(int);
+}
+static assert(test_not_ignored.mangleof == "mangle_mixed_ignored");
diff --git a/gcc/testsuite/gdc.test/compilable/pragmamangle2.d b/gcc/testsuite/gdc.test/compilable/pragmamangle2.d
new file mode 100644 (file)
index 0000000..a3ac71a
--- /dev/null
@@ -0,0 +1,145 @@
+// Tests for pragma mangle
+module pragmamangle;
+
+version (Posix): // Itanium C++ ABI only
+
+string ctfe(string s) { return "mangle_" ~ "ctfe_" ~ s; }
+immutable string const_function = "mangle_const_function";
+immutable string const_variable = "mangle_const_variable";
+
+// This mangle string doesn't propagate to tests that use type_symbol.
+pragma(mangle, "Q_should_mangle_string_propagate")
+struct type_symbol { }
+
+class template_symbol(T) { }
+
+void pragma_statement_test()
+{
+    /* 1. Overrides the default mangling for a symbol.
+     */
+    pragma(mangle, "mangle_function") void test_fun1();
+    static assert(test_fun1.mangleof == "mangle_function");
+
+    pragma(mangle, "mangle_attribute") extern(C) nothrow void test_pokeattr1();
+    static assert(test_pokeattr1.mangleof == "mangle_attribute");
+
+    /* 2a. For variables and functions there must be one AssignExpression and it
+     * must evaluate at compile time to a string literal
+     */
+    pragma(mangle, ctfe("function")) void test_fun2a1();
+    static assert(test_fun2a1.mangleof == "mangle_ctfe_function");
+
+    pragma(mangle, const_function) void test_fun2a2();
+    static assert(test_fun2a2.mangleof == "mangle_const_function");
+
+    pragma(mangle, ctfe("variable")) int test_var2a1;
+    static assert(test_var2a1.mangleof == "mangle_ctfe_variable");
+
+    pragma(mangle, const_variable) int test_var2a2;
+    static assert(test_var2a2.mangleof == "mangle_const_variable");
+
+    /* 2b. For aggregates there may be one or two AssignExpressions, one of which
+     * must evaluate at compile time to a string literal and one which must
+     * evaluate to a symbol.
+     * [UNDOCUMENTED] The pragma(mangle) attribute only gets applied to the
+     * encoded parameters types of extern(C++) functions.
+     */
+    pragma(mangle, "mangle_struct") extern(C++) struct S1 { }
+    extern(C++) void test_struct2b1(S1);
+    extern(D) void externD_struct2b1(S1);
+    static assert(test_struct2b1.mangleof == "_ZN21pragma_statement_test14test_struct2b1ENS_13mangle_structE");
+    static assert(externD_struct2b1.mangleof == "_D12pragmamangle21pragma_statement_testFZ17externD_struct2b1MFSQCjQByFZ2S1Zv");
+
+    pragma(mangle, type_symbol, "mangle_struct") struct S2 { }
+    extern(C++) void test_struct2b3(S2);
+    extern(D) void externD_struct2b3(S2);
+    static assert(test_struct2b3.mangleof == "_ZN21pragma_statement_test14test_struct2b3ENS_13mangle_structE");
+    static assert(externD_struct2b3.mangleof == "_D12pragmamangle21pragma_statement_testFZ17externD_struct2b3MFSQCjQByFZ2S2Zv");
+
+    pragma(mangle, "mangle_struct", type_symbol) struct S3 { }
+    extern(C++) void test_struct2b4(S3);
+    extern(D) void externD_struct2b4(S3);
+    static assert(test_struct2b4.mangleof == "_ZN21pragma_statement_test14test_struct2b4ENS_13mangle_structE");
+    static assert(externD_struct2b4.mangleof == "_D12pragmamangle21pragma_statement_testFZ17externD_struct2b4MFSQCjQByFZ2S3Zv");
+
+    // Repeat struct tests on classes.
+
+    pragma(mangle, "mangle_class") extern(C++) class C1 { }
+    extern(C++) void test_class2b1(C1);
+    extern(D) void externD_class2b1(C1);
+    static assert(test_class2b1.mangleof == "_ZN21pragma_statement_test13test_class2b1EPNS_12mangle_classE");
+    static assert(externD_class2b1.mangleof == "_D12pragmamangle21pragma_statement_testFZ16externD_class2b1MFCQCiQBxFZ2C1Zv");
+
+    pragma(mangle, type_symbol, "mangle_class") class C2 { }
+    extern(C++) void test_class2b3(C2);
+    extern(D) void externD_class2b3(C2);
+    static assert(test_class2b3.mangleof == "_ZN21pragma_statement_test13test_class2b3EPNS_12mangle_classE");
+    static assert(externD_class2b3.mangleof == "_D12pragmamangle21pragma_statement_testFZ16externD_class2b3MFCQCiQBxFZ2C2Zv");
+
+    pragma(mangle, "mangle_class", type_symbol) class C3 { }
+    extern(C++) void test_class2b4(C3);
+    extern(D) void externD_class2b4(C3);
+    static assert(test_class2b4.mangleof == "_ZN21pragma_statement_test13test_class2b4EPNS_12mangle_classE");
+    static assert(externD_class2b4.mangleof == "_D12pragmamangle21pragma_statement_testFZ16externD_class2b4MFCQCiQBxFZ2C3Zv");
+
+    /* 2c. If that symbol is a TemplateInstance, the aggregate is treated as a
+     * template that has the signature and arguments of the TemplateInstance.
+     */
+    template T1(C)
+    {
+        pragma(mangle, C, "mangle_template") struct T1 { }
+    }
+    extern(C++) void test_template2c3(T1!(template_symbol!int));
+    extern(C++) void test_template2c4(T1!(template_symbol!float));
+    extern(C++) void test_template2c5(T1!(type_symbol));
+    static assert(test_template2c3.mangleof == "_ZN21pragma_statement_test16test_template2c3ENS_15mangle_templateIiEE");
+    static assert(test_template2c4.mangleof == "_ZN21pragma_statement_test16test_template2c4ENS_15mangle_templateIfEE");
+    static assert(test_template2c5.mangleof == "_ZN21pragma_statement_test16test_template2c5ENS_15mangle_templateE");
+
+    template T2(C)
+    {
+        pragma(mangle, "mangle_template", C) struct T2 { }
+    }
+    extern(C++) void test_template2c6(T2!(template_symbol!int));
+    extern(C++) void test_template2c7(T2!(template_symbol!float));
+    extern(C++) void test_template2c8(T2!(type_symbol));
+    static assert(test_template2c6.mangleof == "_ZN21pragma_statement_test16test_template2c6ENS_15mangle_templateIiEE");
+    static assert(test_template2c7.mangleof == "_ZN21pragma_statement_test16test_template2c7ENS_15mangle_templateIfEE");
+    static assert(test_template2c8.mangleof == "_ZN21pragma_statement_test16test_template2c8ENS_15mangle_templateE");
+
+    /* 2d. The identifier of the symbol is used when no string is supplied.
+     */
+    pragma(mangle, type_symbol) struct I1 { }
+    extern(C++) void test_struct2d1(I1);
+    static assert(test_struct2d1.mangleof == "_ZN21pragma_statement_test14test_struct2d1ENS_11type_symbolE");
+
+    pragma(mangle, type_symbol) class I2 { }
+    extern(C++) void test_class2d1(I2);
+    static assert(test_class2d1.mangleof == "_ZN21pragma_statement_test13test_class2d1EPNS_11type_symbolE");
+
+    template I3(C)
+    {
+        pragma(mangle, C) struct I3 { }
+    }
+    extern(C++) void test_template2d1(I3!(template_symbol!int));
+    extern(C++) void test_template2d2(I3!(template_symbol!float));
+    extern(C++) void test_template2d3(I3!(type_symbol));
+    static assert(test_template2d1.mangleof == "_ZN21pragma_statement_test16test_template2d1ENS_15template_symbolIiEE");
+    static assert(test_template2d2.mangleof == "_ZN21pragma_statement_test16test_template2d2ENS_15template_symbolIfEE");
+    static assert(test_template2d3.mangleof == "_ZN21pragma_statement_test16test_template2d3ENS_11type_symbolE");
+
+    // ??? No template arguments encoded.
+
+    pragma(mangle, template_symbol!float) struct I4 { }
+    extern(C++) void test_template2d4(I4);
+    static assert(test_template2d4.mangleof == "_ZN21pragma_statement_test16test_template2d4ENS_15template_symbolE");
+
+    /* 3. It only applies to function and variable symbols. Other symbols are
+     * ignored.
+     */
+    pragma(mangle, "mangle_alias")
+    alias ignored_alias = int;
+
+    pragma(mangle, "mangle_enum")
+    enum ignored_enum { a = 1 }
+}
diff --git a/gcc/testsuite/gdc.test/compilable/profilegc_typename.d b/gcc/testsuite/gdc.test/compilable/profilegc_typename.d
new file mode 100644 (file)
index 0000000..0defd4c
--- /dev/null
@@ -0,0 +1,10 @@
+// REQUIRED_ARGS: -profile=gc
+struct T(string s) {}
+alias TypeWithQuotes = T!q"EOS
+`"'}])>
+EOS";
+
+void foo() {
+    TypeWithQuotes[] arr;
+    arr ~= TypeWithQuotes();
+}
index 76a4c8a55db6f1bb905d28bcf78834e79f1ec24d..c27152c2ec34b5f29fac52af03496ae90ee30a74 100644 (file)
@@ -22,6 +22,8 @@ class A {
 class B : A{
     // short syntax also overrides the same as long syntax
     override bool isNull() => this !is null;
+
+    this(float y) => super(y);
 }
 
 static assert((new A).x == 34);
@@ -46,6 +48,11 @@ struct T
     void inc() {}
     this(this) => inc();
 
+    // https://github.com/dlang/dmd/issues/21576
+    this(int) => inc();
+
+    this(byte) { return inc(); }
+
     void free() {}
     ~this() => free();
 }
index 67ec60a582b77c654a6e80f4ccf2a4340d827222..a622ae7fd62a298b3297bf792ec545761dc296a0 100644 (file)
@@ -22,6 +22,7 @@ class Task
 {
     void removeTerminationHook(void delegate() hook)
     {
-        moveToEnd([], hook);
+        void delegate()[] array = [() {}];
+        moveToEnd(array, hook);
     }
 }
diff --git a/gcc/testsuite/gdc.test/compilable/test21495.d b/gcc/testsuite/gdc.test/compilable/test21495.d
new file mode 100644 (file)
index 0000000..954bfa9
--- /dev/null
@@ -0,0 +1,15 @@
+// https://github.com/dlang/dmd/issues/21495
+extern (C++)
+{
+    enum string[string] meta = [
+        "hellos": "worlds"
+    ];
+
+    struct metas
+    {
+        static foreach (key, value; meta)
+        {
+            mixin("enum string " ~ key ~ " = `" ~ value ~ "`;");
+        }
+    }
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test21504a.d b/gcc/testsuite/gdc.test/compilable/test21504a.d
new file mode 100644 (file)
index 0000000..8303fd7
--- /dev/null
@@ -0,0 +1,68 @@
+// https://github.com/dlang/dmd/issues/21504
+
+// Test that:
+// 1. __traits(compiles) tests fail when violating an explicitly set attribute.
+// 2. __traits(compiles) tests pass when attribute is inferred (see functions
+//   isPureBypassingInference, isSafeBypassingInference, isNogcBypassingInference).
+// 3. __traits(compiles) tests do not affect the result of attribute inference,
+//    as the they are only evaluated at compile-time, not run-time.
+
+__gshared int testPure;
+
+auto testPure1() pure
+{
+    return __traits(compiles, testPure = 1);
+}
+auto testPure2()
+{
+    return __traits(compiles, testPure = 1);
+}
+
+static assert(!testPure1() && testPure2());
+static assert(__traits(getFunctionAttributes, testPure1) == __traits(getFunctionAttributes, testPure2));
+
+////////////////////////////
+
+__gshared Exception testNothrow;
+
+auto testNothrow1() nothrow
+{
+    return __traits(compiles, throw testNothrow);
+}
+auto testNothrow2()
+{
+    return __traits(compiles, throw testNothrow);
+}
+
+static assert(!testNothrow1() && testNothrow2());
+static assert(__traits(getFunctionAttributes, testNothrow1) == __traits(getFunctionAttributes, testNothrow2));
+
+////////////////////////////
+
+__gshared int* testSafe;
+
+auto testSafe1() @safe
+{
+    return __traits(compiles, testSafe + 1);
+}
+auto testSafe2()
+{
+    return __traits(compiles, testSafe + 1);
+}
+
+static assert(!testSafe1() && testSafe2());
+static assert(__traits(getFunctionAttributes, testSafe1) == __traits(getFunctionAttributes, testSafe2));
+
+////////////////////////////
+
+auto testNogc1() @nogc
+{
+    return __traits(compiles, [1, 2]);
+}
+auto testNogc2()
+{
+    return __traits(compiles, [1, 2]);
+}
+
+static assert(!testNogc1() && testNogc2());
+static assert(__traits(getFunctionAttributes, testNogc1) == __traits(getFunctionAttributes, testNogc2));
diff --git a/gcc/testsuite/gdc.test/compilable/test21504b.d b/gcc/testsuite/gdc.test/compilable/test21504b.d
new file mode 100644 (file)
index 0000000..152448f
--- /dev/null
@@ -0,0 +1,17 @@
+// https://github.com/dlang/dmd/issues/21504
+
+// Test case where `@safe` triggers attribute inference of all nested
+// declarations, and the NOGCVisitor for `new` expressions encounters a self
+// referencing GC action.
+
+void test21504() @nogc @safe
+{
+    static struct Nest
+    {
+        this(int i)
+        {
+            static assert(__traits(compiles, new Nest(i)));
+        }
+    }
+    auto s = Nest(1);
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test21835.d b/gcc/testsuite/gdc.test/compilable/test21835.d
new file mode 100644 (file)
index 0000000..2a4412f
--- /dev/null
@@ -0,0 +1,11 @@
+// https://github.com/dlang/dmd/issues/21832
+// resize array of implicitly non-copyable items
+
+void bugGH21832()
+{
+    static struct S { @disable this(this); }
+    static struct Item { S s; }
+
+    Item[] arr;
+    arr.length++;
+}
index 9f779fd07dae7438a0da9674f36ad498b62c3e65..363a8e5d426b6181c2d6272d0fc5e213771271cf 100644 (file)
@@ -25,7 +25,8 @@ char* g;
 
 void test23862()
 {
-    scope char** _errors;
+    char* source;
+    scope char** _errors = &source;
     g = front_p(_errors);   // should pass
     g = front_r(_errors);   // should pass
 }
index df491321d4821bc8824aab0a0b6e5d64d6529389..19b103082502475ff0a83b5bd40f092583028a38 100644 (file)
@@ -69,7 +69,6 @@ void OpAssignCases(alias X)()
 
     X!(integral, boolean, all)();
     X!(integral, integral, all)();
-    X!(integral, floating, arith)();
 
     X!(floating, boolean, arith)();
     X!(floating, integral, arith)();
diff --git a/gcc/testsuite/gdc.test/compilable/ucn.d b/gcc/testsuite/gdc.test/compilable/ucn.d
new file mode 100644 (file)
index 0000000..5108990
--- /dev/null
@@ -0,0 +1,13 @@
+/+
+REQUIRED_ARGS: -Icompilable/extra-files
+EXTRA_FILES: extra-files/ucn_vars.i
++/
+
+import ucn_vars;
+
+alias M = __traits(allMembers, Vars);
+enum expected = ["x²", "Ⅰ", "ↂ", "À", "Á", "Â", "whÿ", "aÄbсδe", "b〡𗘰〣e"];
+
+static foreach(i; 0 .. M.length) {
+    static assert(M[i] == expected[i]);
+}
index 592ee315ce343a727d80cf8c9ce93c50c08f1a8b..14ae3c64f687c62d9a8d84d400f4136cb024bab0 100644 (file)
@@ -66,7 +66,7 @@ const struct Foo13899
 {
     int opApply(immutable int delegate(const ref int) pure nothrow dg) pure nothrow
     {
-        return 1;
+        return 0;
     }
 }
 
@@ -108,6 +108,7 @@ void testCkeckedInt()
 
     bool overflow;
     assert(mulu(cast(long)3, cast(uint)4, overflow) == 12);
+    assert(mulu(cast(ulong)3, cast(uint)4, overflow) == 12);
 
     assert(testCheckedUnsigned!uint(3,4) == 12);
     assert(testCheckedUnsigned!ulong(3,4) == 12);
index 0dda8bdd3a4c71057f4f5f183b6eab14e9a19543..bbbb6c96d3c92dfc3a778861bf628d819892c5b9 100644 (file)
@@ -1,25 +1,38 @@
-// REQUIRED_ARGS: -w -o-
+// REQUIRED_ARGS: -de
 
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/b3841.d-mixin-32(32): Warning: `char += float` is performing truncating conversion
-fail_compilation/b3841.d-mixin-32(32): Warning: `int += float` is performing truncating conversion
-fail_compilation/b3841.d-mixin-32(32): Warning: `long += double` is performing truncating conversion
-fail_compilation/b3841.d-mixin-32(32): Warning: `char -= float` is performing truncating conversion
-fail_compilation/b3841.d-mixin-32(32): Warning: `int -= float` is performing truncating conversion
-fail_compilation/b3841.d-mixin-32(32): Warning: `long -= double` is performing truncating conversion
-fail_compilation/b3841.d-mixin-32(32): Warning: `char *= float` is performing truncating conversion
-fail_compilation/b3841.d-mixin-32(32): Warning: `int *= float` is performing truncating conversion
-fail_compilation/b3841.d-mixin-32(32): Warning: `long *= double` is performing truncating conversion
-fail_compilation/b3841.d-mixin-32(32): Warning: `char /= float` is performing truncating conversion
-fail_compilation/b3841.d-mixin-32(32): Warning: `int /= float` is performing truncating conversion
-fail_compilation/b3841.d-mixin-32(32): Warning: `long /= double` is performing truncating conversion
-fail_compilation/b3841.d-mixin-32(32): Warning: `char %= float` is performing truncating conversion
-fail_compilation/b3841.d-mixin-32(32): Warning: `int %= float` is performing truncating conversion
-fail_compilation/b3841.d-mixin-32(32): Warning: `long %= double` is performing truncating conversion
-Error: warnings are treated as errors
-       Use -wi if you wish to treat warnings only as informational.
+fail_compilation/b3841.d-mixin-45(45): Deprecation: `char += float` is performing truncating conversion
+fail_compilation/b3841.d(69): Error: template instance `b3841.f!("+=", char, float)` error instantiating
+fail_compilation/b3841.d-mixin-45(45): Deprecation: `int += float` is performing truncating conversion
+fail_compilation/b3841.d(70): Error: template instance `b3841.f!("+=", int, float)` error instantiating
+fail_compilation/b3841.d-mixin-45(45): Deprecation: `long += double` is performing truncating conversion
+fail_compilation/b3841.d(71): Error: template instance `b3841.f!("+=", long, double)` error instantiating
+fail_compilation/b3841.d-mixin-45(45): Deprecation: `char -= float` is performing truncating conversion
+fail_compilation/b3841.d(69): Error: template instance `b3841.f!("-=", char, float)` error instantiating
+fail_compilation/b3841.d-mixin-45(45): Deprecation: `int -= float` is performing truncating conversion
+fail_compilation/b3841.d(70): Error: template instance `b3841.f!("-=", int, float)` error instantiating
+fail_compilation/b3841.d-mixin-45(45): Deprecation: `long -= double` is performing truncating conversion
+fail_compilation/b3841.d(71): Error: template instance `b3841.f!("-=", long, double)` error instantiating
+fail_compilation/b3841.d-mixin-45(45): Deprecation: `char *= float` is performing truncating conversion
+fail_compilation/b3841.d(69): Error: template instance `b3841.f!("*=", char, float)` error instantiating
+fail_compilation/b3841.d-mixin-45(45): Deprecation: `int *= float` is performing truncating conversion
+fail_compilation/b3841.d(70): Error: template instance `b3841.f!("*=", int, float)` error instantiating
+fail_compilation/b3841.d-mixin-45(45): Deprecation: `long *= double` is performing truncating conversion
+fail_compilation/b3841.d(71): Error: template instance `b3841.f!("*=", long, double)` error instantiating
+fail_compilation/b3841.d-mixin-45(45): Deprecation: `char /= float` is performing truncating conversion
+fail_compilation/b3841.d(69): Error: template instance `b3841.f!("/=", char, float)` error instantiating
+fail_compilation/b3841.d-mixin-45(45): Deprecation: `int /= float` is performing truncating conversion
+fail_compilation/b3841.d(70): Error: template instance `b3841.f!("/=", int, float)` error instantiating
+fail_compilation/b3841.d-mixin-45(45): Deprecation: `long /= double` is performing truncating conversion
+fail_compilation/b3841.d(71): Error: template instance `b3841.f!("/=", long, double)` error instantiating
+fail_compilation/b3841.d-mixin-45(45): Deprecation: `char %= float` is performing truncating conversion
+fail_compilation/b3841.d(69): Error: template instance `b3841.f!("%=", char, float)` error instantiating
+fail_compilation/b3841.d-mixin-45(45): Deprecation: `int %= float` is performing truncating conversion
+fail_compilation/b3841.d(70): Error: template instance `b3841.f!("%=", int, float)` error instantiating
+fail_compilation/b3841.d-mixin-45(45): Deprecation: `long %= double` is performing truncating conversion
+fail_compilation/b3841.d(71): Error: template instance `b3841.f!("%=", long, double)` error instantiating
 ---
 */
 
index 6f4fb036b7f076d5629844a9b16ad9ecd0b45ff7..59ace3f6bc985f513b41032f4cfcee6bdf877c1a 100644 (file)
@@ -1,9 +1,9 @@
 /* REQUIRED_ARGS: -betterC
  * TEST_OUTPUT:
 ---
-fail_compilation/betterc.d(12): Error: cannot use `throw` statements with -betterC
-fail_compilation/betterc.d(17): Error: cannot use try-catch statements with -betterC
-fail_compilation/betterc.d(29): Error: `TypeInfo` cannot be used with -betterC
+fail_compilation/betterc.d(12): Error: cannot use `throw` statements with `-betterC`
+fail_compilation/betterc.d(17): Error: cannot use try-catch statements with `-betterC`
+fail_compilation/betterc.d(29): Error: `TypeInfo` cannot be used with `-betterC`
 ---
 */
 
index 9515039a721ce476e95856b4bc3086968fd9831e..e14001b1b9a3ee53f01a889cb63e7fdc487d0620 100644 (file)
@@ -1,4 +1,4 @@
-/* REQUIRED_ARGS: -preview=bitfields
+/*
  * TEST_OUTPUT:
 ---
 fail_compilation/biterrors.d(103): Error: initializer not allowed for bitfield declaration
index 85554931d133004eafed753724c39916eef11c54..7c7de33946190a2fb97d3298abf3e2ac27e04d26 100644 (file)
@@ -1,4 +1,4 @@
-/* REQUIRED_ARGS: -preview=bitfields
+/*
  * TEST_OUTPUT:
 ---
 fail_compilation/biterrors2.d(100): Error: variable `biterrors2.a` - bitfield must be member of struct, union, or class
index e956fc2f6eba547439c1e89db82b5b617a6b911b..6fbf16f923883c47d350ee0520a74998ae696a44 100644 (file)
@@ -1,4 +1,4 @@
-/* REQUIRED_ARGS: -preview=bitfields
+/*
  * TEST_OUTPUT:
 ---
 fail_compilation/biterrors3.d(103): Error: storage class not allowed for bitfield declaration
index 53b5cdc232ff68ae73eee793387f6c3a00b66275..5457ad53c5cb3ce2279080905266bb5d47d2a11d 100644 (file)
@@ -1,4 +1,4 @@
-/* REQUIRED_ARGS: -preview=bitfields
+/*
  * TEST_OUTPUT:
 ---
 fail_compilation/biterrors4.d(109): Error: cannot take address of bitfield `s.a`
index e17516b8f89b7e5a7d22f3d6b6e29562191323eb..3e661799b8cfc8ee4cac297135edcefe98bf8a00 100644 (file)
@@ -1,4 +1,4 @@
-/* REQUIRED_ARGS: -preview=bitfields
+/*
  * TEST_OUTPUT:
 ---
 fail_compilation/biterrors5.d(25): Error: bitfield symbol expected not struct `biterrors5.S`
index d58c3ea56ff034adc90bea966c9ed808f1b5184b..c91621c7220792f39ac56bf3d6acee84ead6bdc4 100644 (file)
@@ -1,4 +1,4 @@
-/* REQUIRED_ARGS: -preview=bitfields
+/*
  */
 
 struct S
index ac167f3f8e9556295d6b82821f21258a839f10bd..b70cf26b24cbb335d301d2fb909c1742b5c50673 100644 (file)
@@ -1,12 +1,15 @@
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/bug15613.d(18): Error: function `f` is not callable using argument types `(typeof(null))`
-fail_compilation/bug15613.d(18):        cannot pass argument `null` of type `typeof(null)` to parameter `int...`
-fail_compilation/bug15613.d(13):        `bug15613.f(int...)` declared here
-fail_compilation/bug15613.d(19): Error: function `g` is not callable using argument types `(int)`
-fail_compilation/bug15613.d(19):        cannot pass argument `8` of type `int` to parameter `Object`
-fail_compilation/bug15613.d(14):        `bug15613.g(Object, ...)` declared here
+fail_compilation/bug15613.d(21): Error: function `f` is not callable using argument types `(typeof(null))`
+fail_compilation/bug15613.d(21):        cannot pass argument `null` of type `typeof(null)` to parameter `int...`
+fail_compilation/bug15613.d(16):        `bug15613.f(int...)` declared here
+fail_compilation/bug15613.d(22): Error: function `f` is not callable using argument types `()`
+fail_compilation/bug15613.d(22):        expected an argument for parameter `int...`
+fail_compilation/bug15613.d(16):        `bug15613.f(int...)` declared here
+fail_compilation/bug15613.d(23): Error: function `g` is not callable using argument types `(int)`
+fail_compilation/bug15613.d(23):        cannot pass argument `8` of type `int` to parameter `Object`
+fail_compilation/bug15613.d(17):        `bug15613.g(Object, ...)` declared here
 ---
 */
 
@@ -16,9 +19,11 @@ void g(Object, ...);
 void main()
 {
     f(null);
+    f();
     g(8);
 }
 
+#line 22
 /*
 TEST_OUTPUT:
 ---
index 606b628270b86f1be72d7b8225fa531e8fb87325..cc168e57adbdf81f0e0a12f8ecc7dc747699d905 100644 (file)
@@ -3,8 +3,8 @@
 TEST_OUTPUT:
 ---
 fail_compilation/bug8150b.d(15): Error: `object.Exception` is thrown but not caught
-fail_compilation/bug8150b.d(13): Error: constructor `bug8150b.Foo.__ctor!().this` may throw but is marked as `nothrow`
-fail_compilation/bug8150b.d(20): Error: template instance `bug8150b.Foo.__ctor!()` error instantiating
+fail_compilation/bug8150b.d(13): Error: constructor `bug8150b.Foo.this!().this` may throw but is marked as `nothrow`
+fail_compilation/bug8150b.d(20): Error: template instance `bug8150b.Foo.this!()` error instantiating
 ---
 */
 
diff --git a/gcc/testsuite/gdc.test/fail_compilation/cast_system.d b/gcc/testsuite/gdc.test/fail_compilation/cast_system.d
new file mode 100644 (file)
index 0000000..c8f5fd6
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+REQUIRED_ARGS: -preview=systemVariables
+TEST_OUTPUT:
+---
+fail_compilation/cast_system.d(31): Error: cast from `int[1]` to `UniqueInt` is not allowed in a `@safe` function
+fail_compilation/cast_system.d(31):        Target element type has unsafe bit patterns
+---
+*/
+
+struct UniqueInt
+{
+    @system int n;
+
+    this(int n) @safe
+    {
+        this.n = n;
+    }
+
+    @disable this(ref inout UniqueInt) inout;
+
+    ~this() @trusted
+    {
+        this.n = 0;
+    }
+}
+
+void main() @safe
+{
+    auto s = cast(UniqueInt) 7; // OK, calls ctor
+    int[1] a;
+    s = cast(UniqueInt) a; // doesn't call ctor
+}
+
+void ok() @safe
+{
+    auto b = cast(bool) 7; // OK
+}
index 7738770775d45e498f365bf91e4b8b0a832af554..9ee5342ba0b4b60c86252368c6b84942de53152b 100644 (file)
@@ -3,9 +3,11 @@ https://issues.dlang.org/show_bug.cgi?id=21538
 
 TEST_OUTPUT:
 ---
-fail_compilation/covariant_override.d(23): Error: function `@safe void covariant_override.CI.f(void delegate() @safe dg)` does not override any function, did you mean to override `@safe void covariant_override.I.f(void delegate() @system dg)`?
-fail_compilation/covariant_override.d(34): Error: function `@safe void covariant_override.CA.f(void delegate() @safe dg)` does not override any function, did you mean to override `@safe void covariant_override.A.f(void delegate() @system dg)`?
-fail_compilation/covariant_override.d(20): Error: class `covariant_override.CI` interface function `void f(void delegate() @system dg) @safe` is not implemented
+fail_compilation/covariant_override.d(25): Error: function `@safe void covariant_override.CI.f(void delegate() @safe dg)` does not override any function
+fail_compilation/covariant_override.d(19):        did you mean to override `@safe void covariant_override.I.f(void delegate() @system dg)`?
+fail_compilation/covariant_override.d(36): Error: function `@safe void covariant_override.CA.f(void delegate() @safe dg)` does not override any function
+fail_compilation/covariant_override.d(30):        did you mean to override `@safe void covariant_override.A.f(void delegate() @system dg)`?
+fail_compilation/covariant_override.d(22): Error: class `covariant_override.CI` interface function `void f(void delegate() @system dg) @safe` is not implemented
 ---
 ++/
 
index 0dd1a0bf96e78819087ac0db07862b87aa08123a..5d135b7bf4cbd96816f075b43ad4ed6ac84ef098 100644 (file)
@@ -1,4 +1,4 @@
-/* REQUIRED_ARGS: -preview=bitfields
+/*
  * TEST_OUTPUT:
 ---
 fail_compilation/dbitfields.d(118): Error: reinterpretation through overlapped field `e` is not allowed in CTFE
index 4f996b6dae2e9c8c001c7b5dcd8617885aff74ce..f40da8a7818d10d97c4a25db2a942cb77e913683 100644 (file)
@@ -3,10 +3,10 @@ REQUIRED_ARGS: -preview=dip1000
 TEST_OUTPUT:
 ---
 fail_compilation/diag23295.d(21): Error: assigning scope variable `x` to non-scope parameter `y` calling `foo` is not allowed in a `@safe` function
-fail_compilation/diag23295.d(32):        which is assigned to non-scope parameter `z`
-fail_compilation/diag23295.d(34):        which is not `scope` because of `f = & z`
+fail_compilation/diag23295.d(32):        `y` is assigned to non-scope parameter `z`
+fail_compilation/diag23295.d(34):        `z` is not `scope` because of `f = & z`
 fail_compilation/diag23295.d(24): Error: assigning scope variable `ex` to non-scope parameter `e` calling `thro` is not allowed in a `@safe` function
-fail_compilation/diag23295.d(39):        which is not `scope` because of `throw e`
+fail_compilation/diag23295.d(39):        `e` is not `scope` because of `throw e`
 ---
 */
 
index 889d1fada2b14c6247d111ee7eb8a23e4965558c..64279815c0096da9c682b788761ae3abcdcbd60d 100644 (file)
@@ -1,10 +1,13 @@
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/diag9191.d(16): Error: function `void diag9191.C1.aaa()` does not override any function, did you mean to override `void diag9191.B1.aa()`?
-fail_compilation/diag9191.d(22): Error: function `diag9191.C2.aaa` does not override any function
-fail_compilation/diag9191.d(33): Error: function `void diag9191.C3.foo()` does not override any function, did you mean to override `void diag9191.B2._foo()`?
-fail_compilation/diag9191.d(38): Error: function `void diag9191.C4.toStringa()` does not override any function, did you mean to override `string object.Object.toString()`?
+fail_compilation/diag9191.d(19): Error: function `void diag9191.C1.aaa()` does not override any function
+fail_compilation/diag9191.d(15):        did you mean to override `void diag9191.B1.aa()`?
+fail_compilation/diag9191.d(25): Error: function `diag9191.C2.aaa` does not override any function
+fail_compilation/diag9191.d(36): Error: function `void diag9191.C3.foo()` does not override any function
+fail_compilation/diag9191.d(31):        did you mean to override `void diag9191.B2._foo()`?
+fail_compilation/diag9191.d(41): Error: function `void diag9191.C4.toStringa()` does not override any function
+$p:druntime/import/object.d$($n$):        did you mean to override `string object.Object.toString()`?
 ---
 */
 
diff --git a/gcc/testsuite/gdc.test/fail_compilation/edition_switch.d b/gcc/testsuite/gdc.test/fail_compilation/edition_switch.d
new file mode 100644 (file)
index 0000000..48695f6
--- /dev/null
@@ -0,0 +1,10 @@
+/*
+REQUIRED_ARGS: -edition=2024
+TEST_OUTPUT:
+---
+fail_compilation/edition_switch.d(10): Error: usage of identifer `body` as a keyword is obsolete. Use `do` instead.
+---
+*/
+
+void test()
+in { } body { }
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail12901.d b/gcc/testsuite/gdc.test/fail_compilation/fail12901.d
deleted file mode 100644 (file)
index 31c90c1..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-/*
-TEST_OUTPUT:
----
-fail_compilation/fail12901.d(11): Error: constructor `fail12901.S.this` `in` and `out` contracts can only appear without a body when they are virtual interface functions or abstract
----
-*/
-
-struct S
-{
-    int a;
-    this(int n)
-    in { a = n; }
-    // no body
-}
index 4eb3663afc6d8f814e8e9c1855c0d316b100f260..ea14c442910a15bd5086c99ffbe99ce554ca84b9 100644 (file)
@@ -1,8 +1,9 @@
 /* TEST_OUTPUT:
 ---
-fail_compilation/fail18093.d(19): Error: function `void fail18093.GenericTransitiveVisitor!(ASTCodegen).GenericTransitiveVisitor.ParseVisitMethods!(ASTCodegen).visit()` does not override any function, did you mean to override `extern (C++) void fail18093.ParseTimeVisitor!(ASTCodegen).ParseTimeVisitor.visit()`?
-fail_compilation/fail18093.d(24): Error: mixin `fail18093.GenericTransitiveVisitor!(ASTCodegen).GenericTransitiveVisitor.ParseVisitMethods!(ASTCodegen)` error instantiating
-fail_compilation/fail18093.d(27): Error: template instance `fail18093.GenericTransitiveVisitor!(ASTCodegen)` error instantiating
+fail_compilation/fail18093.d(20): Error: function `void fail18093.GenericTransitiveVisitor!(ASTCodegen).GenericTransitiveVisitor.ParseVisitMethods!(ASTCodegen).visit()` does not override any function
+fail_compilation/fail18093.d(16):        did you mean to override `extern (C++) void fail18093.ParseTimeVisitor!(ASTCodegen).ParseTimeVisitor.visit()`?
+fail_compilation/fail18093.d(25): Error: mixin `fail18093.GenericTransitiveVisitor!(ASTCodegen).GenericTransitiveVisitor.ParseVisitMethods!(ASTCodegen)` error instantiating
+fail_compilation/fail18093.d(28): Error: template instance `fail18093.GenericTransitiveVisitor!(ASTCodegen)` error instantiating
 ---
  * https://issues.dlang.org/show_bug.cgi?id=18093
  */
index 672db305223a02bcac5c35d3350a113995565afb..94c32a256006d4a997b42c6f8ab732730578643d 100644 (file)
@@ -2,7 +2,7 @@
 REQUIRED_ARGS: -betterC
 TEST_OUTPUT:
 ---
-fail_compilation/fail19911a.d(9): Error: function `fail19911a.fun` D-style variadic functions cannot be used with -betterC
+fail_compilation/fail19911a.d(9): Error: function `fail19911a.fun` D-style variadic functions cannot be used with `-betterC`
 ---
 */
 
index e196b253065b47b590b7cb0fa3649054af163cda..e1df9d846d8cc2a7eca83ac5e02be44dd5378f9c 100644 (file)
@@ -2,7 +2,7 @@
 TEST_OUTPUT:
 ---
 fail_compilation/fail222.d(11): Error: template `fail222.getMixin(TArg..., int i = 0)()` template sequence parameter must be the last one
-fail_compilation/fail222.d(18): Error: template instance `getMixin!()` does not match template declaration `getMixin(TArg..., int i = 0)()`
+fail_compilation/fail222.d(18): Error: template instance `getMixin!()` does not match template declaration `getMixin(TArg..., int i = 0)`
 fail_compilation/fail222.d(21): Error: template instance `fail222.Thing!()` error instantiating
 fail_compilation/fail222.d(23): Error: template `fail222.fooBar(A..., B...)()` template sequence parameter must be the last one
 ---
index 405ab557d758e2592a7f453a40f80cf1a0672282..44242a83386456000b87e2f02091839b224b8cb9 100644 (file)
@@ -2,9 +2,10 @@
 REQUIRED_ARGS: -de
 TEST_OUTPUT:
 ---
-fail_compilation/fail22351.d(18): Deprecation: overriding `extern(C++)` function `fail22351.C22351.func(int*)` with `const` qualified function `fail22351.Fail22351.func(const(int*))` is deprecated
-fail_compilation/fail22351.d(18):        Either remove `override`, or adjust the `const` qualifiers of the overriding function parameters
-fail_compilation/fail22351.d(19): Error: function `extern (C++) void fail22351.Fail22351.func(const(int*)**)` does not override any function, did you mean to override `extern (C++) void fail22351.C22351.func(int*)`?
+fail_compilation/fail22351.d(19): Deprecation: overriding `extern(C++)` function `fail22351.C22351.func(int*)` with `const` qualified function `fail22351.Fail22351.func(const(int*))` is deprecated
+fail_compilation/fail22351.d(19):        Either remove `override`, or adjust the `const` qualifiers of the overriding function parameters
+fail_compilation/fail22351.d(20): Error: function `extern (C++) void fail22351.Fail22351.func(const(int*)**)` does not override any function
+fail_compilation/fail22351.d(13):        did you mean to override `extern (C++) void fail22351.C22351.func(int*)`?
 ---
 */
 extern(C++) class C22351
index c39676a285815fe78477e44f2867eca75370e5f5..a196d710f18930ec6d902bc28fef4e5fa6eacffd 100644 (file)
@@ -3,7 +3,7 @@ REQUIRED_ARGS: -preview=dip1000
 TEST_OUTPUT:
 ---
 fail_compilation/fail24208.d(19): Error: assigning reference to local variable `n` to non-scope parameter `p` calling `escape` is not allowed in a `@safe` function
-fail_compilation/fail24208.d(15):        which is not `scope` because of `escaped = p`
+fail_compilation/fail24208.d(15):        `p` is not `scope` because of `escaped = p`
 ---
 +/
 void test() @safe
index f48ddbb69f16558997913268864b030fb78f1eb6..69e2e11bcf9bfc3da20ea675a75aec536f789793 100644 (file)
@@ -1,8 +1,9 @@
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/fail246.d-mixin-11(11): Error: identifier expected, not `End of File`
-fail_compilation/fail246.d-mixin-11(11): Error: `;` expected after `mixin`
+fail_compilation/fail246.d-mixin-12(12): Error: identifier expected, not `End of File`
+fail_compilation/fail246.d-mixin-12(12): Error: `;` expected after `mixin`
+fail_compilation/fail246.d-mixin-12(12):        while parsing string mixin statement
 ---
 */
 
index 7c92c7c18261709034601313515b217df45d865e..7a7355931bbe5ddf272739e90f2c8f0ca70199be 100644 (file)
@@ -1,7 +1,8 @@
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/fail262.d(23): Error: function `const void fail262.B.f()` does not override any function, did you mean to override `shared const void fail262.A.f()`?
+fail_compilation/fail262.d(24): Error: function `const void fail262.B.f()` does not override any function
+fail_compilation/fail262.d(16):        did you mean to override `shared const void fail262.A.f()`?
 ---
 */
 
index 558510984934e9b947e689faa9f019d7e2b5f276..e4b7275873c47c558d0f443b6f211fae6c080a55 100644 (file)
@@ -1,7 +1,8 @@
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/fail265.d-mixin-10(10): Error: found `End of File` instead of statement
+fail_compilation/fail265.d-mixin-11(11): Error: found `End of File` instead of statement
+fail_compilation/fail265.d-mixin-11(11):        while parsing string mixin statement
 ---
 */
 
index 322bcb3289bbf79a57a375175487051265d4bbfe..e0a95e2e3f6f9dc5c931a51b232dfe2d41d6814a 100644 (file)
@@ -1,11 +1,8 @@
-// REQUIRED_ARGS: -w
 // https://issues.dlang.org/show_bug.cgi?id=4375: Dangling else
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/fail4375a.d(16): Warning: else is dangling, add { } after condition at fail_compilation/fail4375a.d(13)
-Error: warnings are treated as errors
-       Use -wi if you wish to treat warnings only as informational.
+fail_compilation/fail4375a.d(13): Error: else is dangling, add { } after condition at fail_compilation/fail4375a.d(10)
 ---
 */
 
index 6e6b9b09262225bf4df973bbf428c94307320e59..1baa1556b6f9d31d2705814d3cbcb52b26c91171 100644 (file)
@@ -1,11 +1,8 @@
-// REQUIRED_ARGS: -w
 // https://issues.dlang.org/show_bug.cgi?id=4375: Dangling else
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/fail4375b.d(18): Warning: else is dangling, add { } after condition at fail_compilation/fail4375b.d(14)
-Error: warnings are treated as errors
-       Use -wi if you wish to treat warnings only as informational.
+fail_compilation/fail4375b.d(15): Error: else is dangling, add { } after condition at fail_compilation/fail4375b.d(11)
 ---
 */
 
index ef7a79272c9e2e319d387b576df1e7d51a0c185f..e3ff8219cb1f072a0925481cb29a8878f49f2e01 100644 (file)
@@ -1,11 +1,8 @@
-// REQUIRED_ARGS: -w
 // https://issues.dlang.org/show_bug.cgi?id=4375: Dangling else
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/fail4375c.d(17): Warning: else is dangling, add { } after condition at fail_compilation/fail4375c.d(13)
-Error: warnings are treated as errors
-       Use -wi if you wish to treat warnings only as informational.
+fail_compilation/fail4375c.d(14): Error: else is dangling, add { } after condition at fail_compilation/fail4375c.d(10)
 ---
 */
 
index 7b2740ac5207033558d2b22fe7d9e50c397f5500..355e566224d3c144363ee4cc5eb971b10719d86f 100644 (file)
@@ -1,11 +1,8 @@
-// REQUIRED_ARGS: -w
 // https://issues.dlang.org/show_bug.cgi?id=4375: Dangling else
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/fail4375d.d(17): Warning: else is dangling, add { } after condition at fail_compilation/fail4375d.d(13)
-Error: warnings are treated as errors
-       Use -wi if you wish to treat warnings only as informational.
+fail_compilation/fail4375d.d(14): Error: else is dangling, add { } after condition at fail_compilation/fail4375d.d(10)
 ---
 */
 
index 1b7f7d29cfe5fd3c0b2f1de340a0c004885e5041..4df6bf9ee8747c215a24f2cc3221fb1f0a76be23 100644 (file)
@@ -1,11 +1,8 @@
-// REQUIRED_ARGS: -w
 // https://issues.dlang.org/show_bug.cgi?id=4375: Dangling else
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/fail4375e.d(16): Warning: else is dangling, add { } after condition at fail_compilation/fail4375e.d(13)
-Error: warnings are treated as errors
-       Use -wi if you wish to treat warnings only as informational.
+fail_compilation/fail4375e.d(13): Error: else is dangling, add { } after condition at fail_compilation/fail4375e.d(10)
 ---
 */
 
index c603bfa2262fe2f160e50826eb3770917db1cf75..7103b20f73a26e30fa34d2d32e4952d88ad55bfc 100644 (file)
@@ -1,11 +1,8 @@
-// REQUIRED_ARGS: -w
 // https://issues.dlang.org/show_bug.cgi?id=4375: Dangling else
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/fail4375f.d(16): Warning: else is dangling, add { } after condition at fail_compilation/fail4375f.d(13)
-Error: warnings are treated as errors
-       Use -wi if you wish to treat warnings only as informational.
+fail_compilation/fail4375f.d(13): Error: else is dangling, add { } after condition at fail_compilation/fail4375f.d(10)
 ---
 */
 
index a3d7c85f7a5f8cb5e200d5d3f8cc0a2447de8066..bc602bebaddd6fac877191d9924958108c2757d7 100644 (file)
@@ -1,11 +1,8 @@
-// REQUIRED_ARGS: -w
 // https://issues.dlang.org/show_bug.cgi?id=4375: Dangling else
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/fail4375g.d(16): Warning: else is dangling, add { } after condition at fail_compilation/fail4375g.d(13)
-Error: warnings are treated as errors
-       Use -wi if you wish to treat warnings only as informational.
+fail_compilation/fail4375g.d(13): Error: else is dangling, add { } after condition at fail_compilation/fail4375g.d(10)
 ---
 */
 
index 04833b1bc7385d7ef4388bb6fa201f68d701db35..6bc1557d14d09cbdd4a828614ebe479b70c7e615 100644 (file)
@@ -1,11 +1,8 @@
-// REQUIRED_ARGS: -w
 // https://issues.dlang.org/show_bug.cgi?id=4375: Dangling else
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/fail4375h.d(18): Warning: else is dangling, add { } after condition at fail_compilation/fail4375h.d(15)
-Error: warnings are treated as errors
-       Use -wi if you wish to treat warnings only as informational.
+fail_compilation/fail4375h.d(15): Error: else is dangling, add { } after condition at fail_compilation/fail4375h.d(12)
 ---
 */
 
index 2fe2bb718b1c8a1b4b427bae511e9623c246c2bd..668d108b0dd604ee7e17918bd9e4f785fb1f4f08 100644 (file)
@@ -1,11 +1,8 @@
-// REQUIRED_ARGS: -w
 // https://issues.dlang.org/show_bug.cgi?id=4375: Dangling else
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/fail4375i.d(18): Warning: else is dangling, add { } after condition at fail_compilation/fail4375i.d(13)
-Error: warnings are treated as errors
-       Use -wi if you wish to treat warnings only as informational.
+fail_compilation/fail4375i.d(15): Error: else is dangling, add { } after condition at fail_compilation/fail4375i.d(10)
 ---
 */
 
index 89ad096dc7bcbe31830b2b1a7213ccc109bc223c..d9d5c8d6a8b34a4393f3e5fa92a0923f0353651c 100644 (file)
@@ -1,11 +1,8 @@
-// REQUIRED_ARGS: -w
 // https://issues.dlang.org/show_bug.cgi?id=4375: Dangling else
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/fail4375j.d(18): Warning: else is dangling, add { } after condition at fail_compilation/fail4375j.d(13)
-Error: warnings are treated as errors
-       Use -wi if you wish to treat warnings only as informational.
+fail_compilation/fail4375j.d(15): Error: else is dangling, add { } after condition at fail_compilation/fail4375j.d(10)
 ---
 */
 
index 05ceb06c70c858900d2b2e860043a1b3c0b1ab94..436e6ee5f85324554b52bce79c3d81d075bdda25 100644 (file)
@@ -1,11 +1,9 @@
-// REQUIRED_ARGS: -w
 // https://issues.dlang.org/show_bug.cgi?id=4375: Dangling else
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/fail4375k.d-mixin-13(17): Warning: else is dangling, add { } after condition at fail_compilation/fail4375k.d-mixin-13(14)
-Error: warnings are treated as errors
-       Use -wi if you wish to treat warnings only as informational.
+fail_compilation/fail4375k.d-mixin-11(15): Error: else is dangling, add { } after condition at fail_compilation/fail4375k.d-mixin-11(12)
+fail_compilation/fail4375k.d-mixin-11(12):        while parsing string mixin statement
 ---
 */
 
index 731ead64cc27183b2b599c8784d263fa713c3cc5..dffce4537b5496529c251582d0351edee6b56e23 100644 (file)
@@ -1,11 +1,8 @@
-// REQUIRED_ARGS: -w
 // https://issues.dlang.org/show_bug.cgi?id=4375: Dangling else
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/fail4375l.d(17): Warning: else is dangling, add { } after condition at fail_compilation/fail4375l.d(13)
-Error: warnings are treated as errors
-       Use -wi if you wish to treat warnings only as informational.
+fail_compilation/fail4375l.d(14): Error: else is dangling, add { } after condition at fail_compilation/fail4375l.d(10)
 ---
 */
 
index b5d1c1683161d28111acf18edd19c06ebfe560bb..d79fd851bb7dcfa720a2edcd2f49d178f3132d8c 100644 (file)
@@ -1,11 +1,8 @@
-// REQUIRED_ARGS: -w
 // https://issues.dlang.org/show_bug.cgi?id=4375: Dangling else
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/fail4375m.d(17): Warning: else is dangling, add { } after condition at fail_compilation/fail4375m.d(14)
-Error: warnings are treated as errors
-       Use -wi if you wish to treat warnings only as informational.
+fail_compilation/fail4375m.d(14): Error: else is dangling, add { } after condition at fail_compilation/fail4375m.d(11)
 ---
 */
 
index 3112de7d10f72cc28b4ca44b94358589f5eee885..872b99df2f3cf8e1214e9f03a575b19146d774ab 100644 (file)
@@ -1,11 +1,8 @@
-// REQUIRED_ARGS: -w
 // https://issues.dlang.org/show_bug.cgi?id=4375: Dangling else
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/fail4375o.d(17): Warning: else is dangling, add { } after condition at fail_compilation/fail4375o.d(13)
-Error: warnings are treated as errors
-       Use -wi if you wish to treat warnings only as informational.
+fail_compilation/fail4375o.d(14): Error: else is dangling, add { } after condition at fail_compilation/fail4375o.d(10)
 ---
 */
 
index 62c61a4cb7a42de78cd7c2c73e7c301051cca132..be8a03a00233708f16b2a119db2e77e958659be5 100644 (file)
@@ -1,10 +1,8 @@
-// REQUIRED_ARGS: -w
 // https://issues.dlang.org/show_bug.cgi?id=4375: Dangling else
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/fail4375p.d(19): Warning: else is dangling, add { } after condition at fail_compilation/fail4375p.d(12)
-fail_compilation/fail4375p.d(16): Error: undefined identifier `x`
+fail_compilation/fail4375p.d(17): Error: else is dangling, add { } after condition at fail_compilation/fail4375p.d(10)
 ---
 */
 
index f57e746b6ed33e7a9215c4a196bf61f32d57c7cd..d68fc2672be643ddffc374dcedcf6a87de2df558 100644 (file)
@@ -1,10 +1,8 @@
-// REQUIRED_ARGS: -w
 // https://issues.dlang.org/show_bug.cgi?id=4375: Dangling else
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/fail4375q.d(17): Warning: else is dangling, add { } after condition at fail_compilation/fail4375q.d(13)
-fail_compilation/fail4375q.d(14): Error: `with` expression types must be enums or aggregates or pointers to them, not `int`
+fail_compilation/fail4375q.d(15): Error: else is dangling, add { } after condition at fail_compilation/fail4375q.d(11)
 ---
 */
 
index 865bd6dae8e35f04ef246b33c11a0de41645fc74..df800e245740f438cb897a486c9a1cb9f2564f02 100644 (file)
@@ -1,11 +1,8 @@
-// REQUIRED_ARGS: -w
 // https://issues.dlang.org/show_bug.cgi?id=4375: Dangling else
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/fail4375r.d(19): Warning: else is dangling, add { } after condition at fail_compilation/fail4375r.d(13)
-Error: warnings are treated as errors
-       Use -wi if you wish to treat warnings only as informational.
+fail_compilation/fail4375r.d(16): Error: else is dangling, add { } after condition at fail_compilation/fail4375r.d(10)
 ---
 */
 
index 972f27643af174c66a9fb8650b0428d18ae06e5f..83a39073730bd59155a03edd33ef61fd88685729 100644 (file)
@@ -1,11 +1,8 @@
-// REQUIRED_ARGS: -w
 // https://issues.dlang.org/show_bug.cgi?id=4375: Dangling else
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/fail4375s.d(19): Warning: else is dangling, add { } after condition at fail_compilation/fail4375s.d(13)
-Error: warnings are treated as errors
-       Use -wi if you wish to treat warnings only as informational.
+fail_compilation/fail4375s.d(16): Error: else is dangling, add { } after condition at fail_compilation/fail4375s.d(10)
 ---
 */
 
index 07a79e57200f140838679e42f8e7fe5b62279562..71ff51cacf19525e6a5e6509834c69b27caa3dff 100644 (file)
@@ -1,11 +1,9 @@
-// REQUIRED_ARGS: -w -unittest
+// REQUIRED_ARGS: -unittest
 // https://issues.dlang.org/show_bug.cgi?id=4375: Dangling else
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/fail4375t.d(16): Warning: else is dangling, add { } after condition at fail_compilation/fail4375t.d(13)
-Error: warnings are treated as errors
-       Use -wi if you wish to treat warnings only as informational.
+fail_compilation/fail4375t.d(14): Error: else is dangling, add { } after condition at fail_compilation/fail4375t.d(11)
 ---
 */
 
index a3b22e950743fc80a0990fc8e4aa55873b9920db..3b5bdbb0b2130ca4074131c3bc1be87f7c8540fc 100644 (file)
@@ -1,11 +1,8 @@
-// REQUIRED_ARGS: -w
 // https://issues.dlang.org/show_bug.cgi?id=4375: Dangling else
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/fail4375u.d(15): Warning: else is dangling, add { } after condition at fail_compilation/fail4375u.d(13)
-Error: warnings are treated as errors
-       Use -wi if you wish to treat warnings only as informational.
+fail_compilation/fail4375u.d(12): Error: else is dangling, add { } after condition at fail_compilation/fail4375u.d(10)
 ---
 */
 
index 2be98a61fbf3b8ddbaf5d234954a76bcc7066ed2..3df451550ac1fbf405f51fc73a4d4b9570436c4b 100644 (file)
@@ -1,11 +1,8 @@
-// REQUIRED_ARGS: -w
 // https://issues.dlang.org/show_bug.cgi?id=4375: Dangling else
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/fail4375v.d(15): Warning: else is dangling, add { } after condition at fail_compilation/fail4375v.d(13)
-Error: warnings are treated as errors
-       Use -wi if you wish to treat warnings only as informational.
+fail_compilation/fail4375v.d(12): Error: else is dangling, add { } after condition at fail_compilation/fail4375v.d(10)
 ---
 */
 
index eb0779f9c0e3a0d91dbb0bbf7dbe70dd89241053..36d9ba7a42ce0125bf54092c78d8e21b6bc38170 100644 (file)
@@ -1,11 +1,8 @@
-// REQUIRED_ARGS: -w
 // https://issues.dlang.org/show_bug.cgi?id=4375: Dangling else
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/fail4375w.d(15): Warning: else is dangling, add { } after condition at fail_compilation/fail4375w.d(13)
-Error: warnings are treated as errors
-       Use -wi if you wish to treat warnings only as informational.
+fail_compilation/fail4375w.d(12): Error: else is dangling, add { } after condition at fail_compilation/fail4375w.d(10)
 ---
 */
 
index 6a5f9dbefd69dbf910fe08ad82fd8d55a348963a..611fb5e76a8510414563783815714f930d7ea663 100644 (file)
@@ -1,11 +1,8 @@
-// REQUIRED_ARGS: -w
 // https://issues.dlang.org/show_bug.cgi?id=4375: Dangling else
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/fail4375x.d(16): Warning: else is dangling, add { } after condition at fail_compilation/fail4375x.d(13)
-Error: warnings are treated as errors
-       Use -wi if you wish to treat warnings only as informational.
+fail_compilation/fail4375x.d(13): Error: else is dangling, add { } after condition at fail_compilation/fail4375x.d(10)
 ---
 */
 
index 28dc991469eec3083f6342152ea3a275410fac8e..12756e1329ccbb25ca2b10903f4274ee82af17eb 100644 (file)
@@ -1,11 +1,8 @@
-// REQUIRED_ARGS: -w
 // https://issues.dlang.org/show_bug.cgi?id=4375: Dangling else
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/fail4375y.d(18): Warning: else is dangling, add { } after condition at fail_compilation/fail4375y.d(13)
-Error: warnings are treated as errors
-       Use -wi if you wish to treat warnings only as informational.
+fail_compilation/fail4375y.d(15): Error: else is dangling, add { } after condition at fail_compilation/fail4375y.d(10)
 ---
 */
 
index 1cb0292cc8c7e38526ad9e7ad39e64557860b187..4a99565a47eb19b75393114122dcbe76d532f5b8 100644 (file)
@@ -1,11 +1,11 @@
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/fail4544.d(15): Error: character constant has multiple characters
+fail_compilation/fail4544.d(15): Error: character constant has multiple characters - did you mean "asd"?
 fail_compilation/fail4544.d(16): Error: `0x` isn't a valid integer literal, use `0x0` instead
 fail_compilation/fail4544.d(16): Error: variable name expected after type `int`, not `0`
 fail_compilation/fail4544.d(17): Error: unterminated character constant
-fail_compilation/fail4544.d(18): Error: character constant has multiple characters
+fail_compilation/fail4544.d(18): Error: character constant has multiple characters - did you mean "asdasdsa"?
 ---
 */
 
index c6aa178bdaa4fee2a28bd6c79f28c428011f715f..ebdac471c3ee72b4320d5286e5c493439798cadb 100644 (file)
@@ -2,10 +2,10 @@
 TEST_OUTPUT:
 ---
 fail_compilation/fail50.d(12): Error: taking the address of non-static variable `a` requires an instance of `Marko`
-fail_compilation/fail50.d(12): Error: variable `a` cannot be read at compile time
 ---
 */
 
+
 struct Marko
 {
     int a;
index 0fd1ff2f4eef851cdde05c83be1cd98cf1ac61ed..60499f9df84094f86759728649de8ee0f1021cc4 100644 (file)
@@ -1,9 +1,10 @@
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/fail6497.d(13): Error: taking the address of stack-allocated local variable `n` is not allowed in a `@safe` function
-fail_compilation/fail6497.d(13): Error: taking the address of stack-allocated local variable `n` is not allowed in a `@safe` function
-fail_compilation/fail6497.d(19): Error: taking the address of local variable `i` is not allowed in a `@safe` function
+fail_compilation/fail6497.d(14): Error: taking the address of stack-allocated local variable `n` is not allowed in a `@safe` function
+fail_compilation/fail6497.d(14): Error: taking the address of stack-allocated local variable `n` is not allowed in a `@safe` function
+fail_compilation/fail6497.d(20): Error: taking the address of local variable `i` is not allowed in a `@safe` function
+fail_compilation/fail6497.d(28): Error: taking the address of local variable `i` is not allowed in a `@safe` function
 ---
 */
 
@@ -18,3 +19,12 @@ void f() @safe
     ref i = *new int;
     auto b = &i;
 }
+
+const(int)* ptr;
+
+int g() @safe
+out (i)
+{
+    ptr = &i;
+}
+do { return 0; }
index 4f5b0763d4810d1ac8ac111c8882197db39308ad..175553fdc6c3676d9b26e3fa854adcf9b9bc0c75 100644 (file)
@@ -1,7 +1,8 @@
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/fail8631.d(14): Error: function `shared const int fail8631.D.foo()` does not override any function, did you mean to override `immutable int fail8631.B.foo()`?
+fail_compilation/fail8631.d(15): Error: function `shared const int fail8631.D.foo()` does not override any function
+fail_compilation/fail8631.d(10):        did you mean to override `immutable int fail8631.B.foo()`?
 ---
 */
 
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail_contracts5.d b/gcc/testsuite/gdc.test/fail_compilation/fail_contracts5.d
new file mode 100644 (file)
index 0000000..110ce07
--- /dev/null
@@ -0,0 +1,34 @@
+
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail_contracts5.d(13): Error: constructor `fail_contracts5.S.this` `in` and `out` contracts can only appear without a body when they are virtual interface functions or abstract
+---
+*/
+
+// bugzilla 12901
+struct S
+{
+    int a;
+    this(int n)
+    in { a = n; }
+    // no body
+}
+
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail_contracts5.d(33): Error: function `fail_contracts5.C2.foo` `in` and `out` contracts can only appear without a body when they are virtual interface functions or abstract
+fail_compilation/fail_contracts5.d(32): Error: constructor `fail_contracts5.C2.this` `in` and `out` contracts can only appear without a body when they are virtual interface functions or abstract
+---
+*/
+// https://github.com/dlang/dmd/issues/21403
+abstract class C1 {}
+
+class C2 : C1
+{
+    bool member;
+
+    this() out (member) {}
+    final void foo() out (member) {}
+}
index ebd36a7cc1d2f6a711a33cac5af979d64aa5c685..8c31433253d440507d393d12370934c288497460 100644 (file)
@@ -2,23 +2,25 @@
 REQUIRED_ARGS: -verrors=context
 TEST_OUTPUT:
 ---
-fail_compilation/fail_pretty_errors.d(29): Error: undefined identifier `a`
+fail_compilation/fail_pretty_errors.d(31): Error: undefined identifier `a`
     a = 1;
     ^
-fail_compilation/fail_pretty_errors.d-mixin-34(34): Error: undefined identifier `b`
+fail_compilation/fail_pretty_errors.d-mixin-36(36): Error: undefined identifier `b`
 b = 1;
 ^
-fail_compilation/fail_pretty_errors.d(39): Error: cannot implicitly convert expression `5` of type `int` to `string`
+fail_compilation/fail_pretty_errors.d(41): Error: cannot implicitly convert expression `5` of type `int` to `string`
     string x = 5;
                ^
-fail_compilation/fail_pretty_errors.d(44): Error: mixin `fail_pretty_errors.testMixin2.mixinTemplate!()` error instantiating
+fail_compilation/fail_pretty_errors.d(46): Error: mixin `fail_pretty_errors.testMixin2.mixinTemplate!()` error instantiating
     mixin mixinTemplate;
     ^
-fail_compilation/fail_pretty_errors.d(50): Error: invalid array operation `"" + ""` (possible missing [])
+fail_compilation/fail_pretty_errors.d(52): Error: invalid array operation `"" + ""` (possible missing [])
     auto x = ""+"";
                ^
-fail_compilation/fail_pretty_errors.d(50):        did you mean to concatenate (`"" ~ ""`) instead ?
-fail_compilation/fail_pretty_errors.d(53): Error: cannot implicitly convert expression `1111` of type `int` to `byte`
+fail_compilation/fail_pretty_errors.d(52):        did you mean to concatenate (`"" ~ ""`) instead ?
+    auto x = ""+"";
+               ^
+fail_compilation/fail_pretty_errors.d(55): Error: cannot implicitly convert expression `1111` of type `int` to `byte`
         byte ɑ =    1111;
                     ^
 ---
diff --git a/gcc/testsuite/gdc.test/fail_compilation/failexpression1.d b/gcc/testsuite/gdc.test/fail_compilation/failexpression1.d
new file mode 100644 (file)
index 0000000..d14132a
--- /dev/null
@@ -0,0 +1,154 @@
+/*
+REQUIRED_ARGS: -de
+TEST_OUTPUT:
+---
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `byte += float` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `byte -= float` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `byte *= float` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `byte /= float` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `byte %= float` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `byte += double` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `byte -= double` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `byte *= double` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `byte /= double` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `byte %= double` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `byte += real` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `byte -= real` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `byte *= real` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `byte /= real` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `byte %= real` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `ubyte += float` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `ubyte -= float` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `ubyte *= float` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `ubyte /= float` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `ubyte %= float` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `ubyte += double` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `ubyte -= double` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `ubyte *= double` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `ubyte /= double` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `ubyte %= double` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `ubyte += real` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `ubyte -= real` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `ubyte *= real` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `ubyte /= real` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `ubyte %= real` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `short += float` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `short -= float` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `short *= float` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `short /= float` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `short %= float` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `short += double` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `short -= double` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `short *= double` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `short /= double` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `short %= double` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `short += real` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `short -= real` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `short *= real` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `short /= real` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `short %= real` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `ushort += float` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `ushort -= float` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `ushort *= float` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `ushort /= float` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `ushort %= float` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `ushort += double` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `ushort -= double` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `ushort *= double` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `ushort /= double` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `ushort %= double` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `ushort += real` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `ushort -= real` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `ushort *= real` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `ushort /= real` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `ushort %= real` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `int += float` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `int -= float` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `int *= float` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `int /= float` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `int %= float` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `int += double` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `int -= double` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `int *= double` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `int /= double` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `int %= double` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `int += real` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `int -= real` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `int *= real` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `int /= real` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `int %= real` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `uint += float` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `uint -= float` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `uint *= float` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `uint /= float` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `uint %= float` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `uint += double` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `uint -= double` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `uint *= double` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `uint /= double` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `uint %= double` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `uint += real` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `uint -= real` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `uint *= real` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `uint /= real` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `uint %= real` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `long += float` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `long -= float` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `long *= float` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `long /= float` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `long %= float` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `long += double` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `long -= double` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `long *= double` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `long /= double` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `long %= double` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `long += real` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `long -= real` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `long *= real` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `long /= real` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `long %= real` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `ulong += float` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `ulong -= float` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `ulong *= float` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `ulong /= float` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `ulong %= float` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `ulong += double` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `ulong -= double` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `ulong *= double` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `ulong /= double` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `ulong %= double` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `ulong += real` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `ulong -= real` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `ulong *= real` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `ulong /= real` is performing truncating conversion
+fail_compilation/failexpression1.d-mixin-138(138): Deprecation: `ulong %= real` is performing truncating conversion
+fail_compilation/failexpression1.d(148): Error: template instance `failexpression1.X!(integral, floating, arith)` error instantiating
+fail_compilation/failexpression1.d(153):        instantiated from here: `OpAssignCases!(TestOpAssign)`
+---
+*/
+template TT(T...) { alias T TT; }
+
+void TestOpAssign(Tx, Ux, ops)()
+{
+    foreach(T; Tx.x)
+    foreach(U; Ux.x)
+    foreach(op; ops.x)
+    {
+        T a = cast(T)1;
+        mixin("a " ~ op ~ " cast(U)1;");
+    }
+}
+
+struct integral  { alias TT!(byte, ubyte, short, ushort, int, uint, long, ulong) x; }
+struct floating  { alias TT!(float, double, real) x; }
+struct arith     { alias TT!("+=", "-=", "*=", "/=", "%=") x; }
+
+void OpAssignCases(alias X)()
+{
+    X!(integral, floating, arith)();
+}
+
+void main()
+{
+    OpAssignCases!TestOpAssign();
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/failexpression2.d b/gcc/testsuite/gdc.test/fail_compilation/failexpression2.d
new file mode 100644 (file)
index 0000000..037c20e
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+REQUIRED_ARGS: -de
+TEST_OUTPUT:
+---
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `byte += float` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `byte -= float` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `byte *= float` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `byte /= float` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `byte %= float` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `byte += double` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `byte -= double` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `byte *= double` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `byte /= double` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `byte %= double` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `byte += real` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `byte -= real` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `byte *= real` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `byte /= real` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `byte %= real` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `ubyte += float` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `ubyte -= float` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `ubyte *= float` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `ubyte /= float` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `ubyte %= float` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `ubyte += double` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `ubyte -= double` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `ubyte *= double` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `ubyte /= double` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `ubyte %= double` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `ubyte += real` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `ubyte -= real` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `ubyte *= real` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `ubyte /= real` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `ubyte %= real` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `short += float` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `short -= float` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `short *= float` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `short /= float` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `short %= float` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `short += double` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `short -= double` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `short *= double` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `short /= double` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `short %= double` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `short += real` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `short -= real` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `short *= real` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `short /= real` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `short %= real` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `ushort += float` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `ushort -= float` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `ushort *= float` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `ushort /= float` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `ushort %= float` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `ushort += double` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `ushort -= double` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `ushort *= double` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `ushort /= double` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `ushort %= double` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `ushort += real` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `ushort -= real` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `ushort *= real` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `ushort /= real` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `ushort %= real` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `int += float` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `int -= float` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `int *= float` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `int /= float` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `int %= float` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `int += double` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `int -= double` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `int *= double` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `int /= double` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `int %= double` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `int += real` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `int -= real` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `int *= real` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `int /= real` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `int %= real` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `uint += float` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `uint -= float` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `uint *= float` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `uint /= float` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `uint %= float` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `uint += double` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `uint -= double` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `uint *= double` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `uint /= double` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `uint %= double` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `uint += real` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `uint -= real` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `uint *= real` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `uint /= real` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `uint %= real` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `long += float` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `long -= float` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `long *= float` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `long /= float` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `long %= float` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `long += double` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `long -= double` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `long *= double` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `long /= double` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `long %= double` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `long += real` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `long -= real` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `long *= real` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `long /= real` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `long %= real` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `ulong += float` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `ulong -= float` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `ulong *= float` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `ulong /= float` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `ulong %= float` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `ulong += double` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `ulong -= double` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `ulong *= double` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `ulong /= double` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `ulong %= double` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `ulong += real` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `ulong -= real` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `ulong *= real` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `ulong /= real` is performing truncating conversion
+fail_compilation/failexpression2.d-mixin-140(140): Deprecation: `ulong %= real` is performing truncating conversion
+fail_compilation/failexpression2.d(150): Error: template instance `failexpression2.X!(integral, floating, arith)` error instantiating
+fail_compilation/failexpression2.d(155):        instantiated from here: `OpAssignCases!(TestOpAssignAssign)`
+---
+*/
+template TT(T...) { alias T TT; }
+
+void TestOpAssignAssign(Tx, Ux, ops)()
+{
+    foreach(T; Tx.x)
+    foreach(U; Ux.x)
+    foreach(op; ops.x)
+    {
+        T a = cast(T)1;
+        U b = cast(U)1;
+        T r;
+        mixin("r = a " ~ op ~ " cast(U)1;");
+    }
+}
+
+struct integral  { alias TT!(byte, ubyte, short, ushort, int, uint, long, ulong) x; }
+struct floating  { alias TT!(float, double, real) x; }
+struct arith     { alias TT!("+=", "-=", "*=", "/=", "%=") x; }
+
+void OpAssignCases(alias X)()
+{
+    X!(integral, floating, arith)();
+}
+
+void main()
+{
+    OpAssignCases!TestOpAssignAssign(); // was once disabled due to bug 7436
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/failexpression3.d b/gcc/testsuite/gdc.test/fail_compilation/failexpression3.d
new file mode 100644 (file)
index 0000000..3a27e96
--- /dev/null
@@ -0,0 +1,155 @@
+/*
+REQUIRED_ARGS: -de
+TEST_OUTPUT:
+---
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `byte += float` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `byte -= float` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `byte *= float` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `byte /= float` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `byte %= float` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `byte += double` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `byte -= double` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `byte *= double` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `byte /= double` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `byte %= double` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `byte += real` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `byte -= real` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `byte *= real` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `byte /= real` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `byte %= real` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `ubyte += float` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `ubyte -= float` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `ubyte *= float` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `ubyte /= float` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `ubyte %= float` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `ubyte += double` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `ubyte -= double` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `ubyte *= double` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `ubyte /= double` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `ubyte %= double` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `ubyte += real` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `ubyte -= real` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `ubyte *= real` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `ubyte /= real` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `ubyte %= real` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `short += float` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `short -= float` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `short *= float` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `short /= float` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `short %= float` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `short += double` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `short -= double` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `short *= double` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `short /= double` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `short %= double` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `short += real` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `short -= real` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `short *= real` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `short /= real` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `short %= real` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `ushort += float` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `ushort -= float` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `ushort *= float` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `ushort /= float` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `ushort %= float` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `ushort += double` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `ushort -= double` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `ushort *= double` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `ushort /= double` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `ushort %= double` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `ushort += real` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `ushort -= real` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `ushort *= real` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `ushort /= real` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `ushort %= real` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `int += float` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `int -= float` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `int *= float` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `int /= float` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `int %= float` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `int += double` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `int -= double` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `int *= double` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `int /= double` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `int %= double` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `int += real` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `int -= real` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `int *= real` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `int /= real` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `int %= real` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `uint += float` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `uint -= float` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `uint *= float` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `uint /= float` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `uint %= float` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `uint += double` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `uint -= double` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `uint *= double` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `uint /= double` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `uint %= double` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `uint += real` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `uint -= real` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `uint *= real` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `uint /= real` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `uint %= real` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `long += float` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `long -= float` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `long *= float` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `long /= float` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `long %= float` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `long += double` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `long -= double` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `long *= double` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `long /= double` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `long %= double` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `long += real` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `long -= real` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `long *= real` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `long /= real` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `long %= real` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `ulong += float` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `ulong -= float` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `ulong *= float` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `ulong /= float` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `ulong %= float` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `ulong += double` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `ulong -= double` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `ulong *= double` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `ulong /= double` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `ulong %= double` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `ulong += real` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `ulong -= real` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `ulong *= real` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `ulong /= real` is performing truncating conversion
+fail_compilation/failexpression3.d-mixin-139(139): Deprecation: `ulong %= real` is performing truncating conversion
+fail_compilation/failexpression3.d(149): Error: template instance `failexpression3.X!(integral, floating, arith)` error instantiating
+fail_compilation/failexpression3.d(154):        instantiated from here: `OpAssignCases!(TestOpAssignAuto)`
+---
+*/
+template TT(T...) { alias T TT; }
+
+void TestOpAssignAuto(Tx, Ux, ops)()
+{
+    foreach(T; Tx.x)
+    foreach(U; Ux.x)
+    foreach(op; ops.x)
+    {
+        T a = cast(T)1;
+        U b = cast(U)1;
+        mixin("auto r = a " ~ op ~ " cast(U)1;");
+    }
+}
+
+struct integral  { alias TT!(byte, ubyte, short, ushort, int, uint, long, ulong) x; }
+struct floating  { alias TT!(float, double, real) x; }
+struct arith     { alias TT!("+=", "-=", "*=", "/=", "%=") x; }
+
+void OpAssignCases(alias X)()
+{
+    X!(integral, floating, arith)();
+}
+
+void main()
+{
+    OpAssignCases!TestOpAssignAuto(); // https://issues.dlang.org/show_bug.cgi?id=5181
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/failexpression4.d b/gcc/testsuite/gdc.test/fail_compilation/failexpression4.d
new file mode 100644 (file)
index 0000000..0f8cc22
--- /dev/null
@@ -0,0 +1,155 @@
+/*
+REQUIRED_ARGS: -de
+TEST_OUTPUT:
+---
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(float)cast(int)a + 1.0F` of type `float` to `byte`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(float)cast(int)a - 1.0F` of type `float` to `byte`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(float)cast(int)a * 1.0F` of type `float` to `byte`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(float)cast(int)a / 1.0F` of type `float` to `byte`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(float)cast(int)a % 1.0F` of type `float` to `byte`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(double)cast(int)a + 1.0` of type `double` to `byte`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(double)cast(int)a - 1.0` of type `double` to `byte`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(double)cast(int)a * 1.0` of type `double` to `byte`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(double)cast(int)a / 1.0` of type `double` to `byte`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(double)cast(int)a % 1.0` of type `double` to `byte`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(real)cast(int)a + 1.0L` of type `real` to `byte`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(real)cast(int)a - 1.0L` of type `real` to `byte`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(real)cast(int)a * 1.0L` of type `real` to `byte`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(real)cast(int)a / 1.0L` of type `real` to `byte`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(real)cast(int)a % 1.0L` of type `real` to `byte`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(float)cast(int)a + 1.0F` of type `float` to `ubyte`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(float)cast(int)a - 1.0F` of type `float` to `ubyte`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(float)cast(int)a * 1.0F` of type `float` to `ubyte`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(float)cast(int)a / 1.0F` of type `float` to `ubyte`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(float)cast(int)a % 1.0F` of type `float` to `ubyte`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(double)cast(int)a + 1.0` of type `double` to `ubyte`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(double)cast(int)a - 1.0` of type `double` to `ubyte`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(double)cast(int)a * 1.0` of type `double` to `ubyte`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(double)cast(int)a / 1.0` of type `double` to `ubyte`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(double)cast(int)a % 1.0` of type `double` to `ubyte`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(real)cast(int)a + 1.0L` of type `real` to `ubyte`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(real)cast(int)a - 1.0L` of type `real` to `ubyte`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(real)cast(int)a * 1.0L` of type `real` to `ubyte`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(real)cast(int)a / 1.0L` of type `real` to `ubyte`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(real)cast(int)a % 1.0L` of type `real` to `ubyte`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(float)cast(int)a + 1.0F` of type `float` to `short`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(float)cast(int)a - 1.0F` of type `float` to `short`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(float)cast(int)a * 1.0F` of type `float` to `short`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(float)cast(int)a / 1.0F` of type `float` to `short`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(float)cast(int)a % 1.0F` of type `float` to `short`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(double)cast(int)a + 1.0` of type `double` to `short`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(double)cast(int)a - 1.0` of type `double` to `short`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(double)cast(int)a * 1.0` of type `double` to `short`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(double)cast(int)a / 1.0` of type `double` to `short`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(double)cast(int)a % 1.0` of type `double` to `short`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(real)cast(int)a + 1.0L` of type `real` to `short`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(real)cast(int)a - 1.0L` of type `real` to `short`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(real)cast(int)a * 1.0L` of type `real` to `short`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(real)cast(int)a / 1.0L` of type `real` to `short`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(real)cast(int)a % 1.0L` of type `real` to `short`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(float)cast(int)a + 1.0F` of type `float` to `ushort`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(float)cast(int)a - 1.0F` of type `float` to `ushort`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(float)cast(int)a * 1.0F` of type `float` to `ushort`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(float)cast(int)a / 1.0F` of type `float` to `ushort`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(float)cast(int)a % 1.0F` of type `float` to `ushort`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(double)cast(int)a + 1.0` of type `double` to `ushort`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(double)cast(int)a - 1.0` of type `double` to `ushort`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(double)cast(int)a * 1.0` of type `double` to `ushort`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(double)cast(int)a / 1.0` of type `double` to `ushort`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(double)cast(int)a % 1.0` of type `double` to `ushort`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(real)cast(int)a + 1.0L` of type `real` to `ushort`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(real)cast(int)a - 1.0L` of type `real` to `ushort`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(real)cast(int)a * 1.0L` of type `real` to `ushort`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(real)cast(int)a / 1.0L` of type `real` to `ushort`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(real)cast(int)a % 1.0L` of type `real` to `ushort`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(float)a + 1.0F` of type `float` to `int`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(float)a - 1.0F` of type `float` to `int`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(float)a * 1.0F` of type `float` to `int`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(float)a / 1.0F` of type `float` to `int`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(float)a % 1.0F` of type `float` to `int`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(double)a + 1.0` of type `double` to `int`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(double)a - 1.0` of type `double` to `int`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(double)a * 1.0` of type `double` to `int`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(double)a / 1.0` of type `double` to `int`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(double)a % 1.0` of type `double` to `int`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(real)a + 1.0L` of type `real` to `int`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(real)a - 1.0L` of type `real` to `int`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(real)a * 1.0L` of type `real` to `int`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(real)a / 1.0L` of type `real` to `int`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(real)a % 1.0L` of type `real` to `int`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(float)a + 1.0F` of type `float` to `uint`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(float)a - 1.0F` of type `float` to `uint`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(float)a * 1.0F` of type `float` to `uint`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(float)a / 1.0F` of type `float` to `uint`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(float)a % 1.0F` of type `float` to `uint`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(double)a + 1.0` of type `double` to `uint`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(double)a - 1.0` of type `double` to `uint`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(double)a * 1.0` of type `double` to `uint`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(double)a / 1.0` of type `double` to `uint`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(double)a % 1.0` of type `double` to `uint`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(real)a + 1.0L` of type `real` to `uint`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(real)a - 1.0L` of type `real` to `uint`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(real)a * 1.0L` of type `real` to `uint`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(real)a / 1.0L` of type `real` to `uint`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(real)a % 1.0L` of type `real` to `uint`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(float)a + 1.0F` of type `float` to `long`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(float)a - 1.0F` of type `float` to `long`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(float)a * 1.0F` of type `float` to `long`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(float)a / 1.0F` of type `float` to `long`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(float)a % 1.0F` of type `float` to `long`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(double)a + 1.0` of type `double` to `long`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(double)a - 1.0` of type `double` to `long`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(double)a * 1.0` of type `double` to `long`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(double)a / 1.0` of type `double` to `long`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(double)a % 1.0` of type `double` to `long`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(real)a + 1.0L` of type `real` to `long`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(real)a - 1.0L` of type `real` to `long`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(real)a * 1.0L` of type `real` to `long`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(real)a / 1.0L` of type `real` to `long`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(real)a % 1.0L` of type `real` to `long`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(float)a + 1.0F` of type `float` to `ulong`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(float)a - 1.0F` of type `float` to `ulong`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(float)a * 1.0F` of type `float` to `ulong`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(float)a / 1.0F` of type `float` to `ulong`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(float)a % 1.0F` of type `float` to `ulong`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(double)a + 1.0` of type `double` to `ulong`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(double)a - 1.0` of type `double` to `ulong`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(double)a * 1.0` of type `double` to `ulong`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(double)a / 1.0` of type `double` to `ulong`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(double)a % 1.0` of type `double` to `ulong`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(real)a + 1.0L` of type `real` to `ulong`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(real)a - 1.0L` of type `real` to `ulong`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(real)a * 1.0L` of type `real` to `ulong`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(real)a / 1.0L` of type `real` to `ulong`
+fail_compilation/failexpression4.d-mixin-139(139): Error: cannot implicitly convert expression `cast(real)a % 1.0L` of type `real` to `ulong`
+fail_compilation/failexpression4.d(149): Error: template instance `failexpression4.X!(integral, floating, arith)` error instantiating
+fail_compilation/failexpression4.d(154):        instantiated from here: `OpReAssignCases!(TestOpAndAssign)`
+---
+*/
+template TT(T...) { alias T TT; }
+
+void TestOpAndAssign(Tx, Ux, ops)()
+{
+    foreach(T; Tx.x)
+    foreach(U; Ux.x)
+    foreach(op; ops.x)
+    {
+        T a = cast(T)1;
+        U b = cast(U)1;
+        mixin("a = a " ~ op[0..$-1] ~ " cast(U)1;");
+    }
+}
+
+struct integral  { alias TT!(byte, ubyte, short, ushort, int, uint, long, ulong) x; }
+struct floating  { alias TT!(float, double, real) x; }
+struct arith     { alias TT!("+=", "-=", "*=", "/=", "%=") x; }
+
+void OpReAssignCases(alias X)()
+{
+    X!(integral, floating, arith)();
+}
+
+void main()
+{
+    OpReAssignCases!TestOpAndAssign();
+}
index 3e3989bc290a902312950f7601d2d1e124bd05c4..553432da08a226937d742df7e30c963f202eefee 100644 (file)
@@ -1,10 +1,10 @@
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/ice13788.d(11): Error: pragma `mangle` - string expected for mangled name
-fail_compilation/ice13788.d(12): Error: `string` expected for mangled name, not `(1)` of type `int`
-fail_compilation/ice13788.d(13): Error: pragma `mangle` - zero-length string not allowed for mangled name
-fail_compilation/ice13788.d(14): Error: pragma `mangle` - mangled name characters can only be of type `char`
+fail_compilation/ice13788.d(11): Error: `pragma(mangle)` expects string literal argument for mangled name
+fail_compilation/ice13788.d(12): Error: `string` expected for pragma mangle argument, not `(1)` of type `int`
+fail_compilation/ice13788.d(13): Error: `pragma(mangle)` zero-length string not allowed for mangled name
+fail_compilation/ice13788.d(14): Error: `pragma(mangle)` mangled name characters can only be of type `char`
 ---
 */
 
index 0d87d63bb01308926f72fcf0f614c06866434c7d..9794de1d13d353a9417ae83f4b398a5a4d14bc52 100644 (file)
@@ -2,8 +2,8 @@
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/ice21095.d(14): Error: constructor `ice21095.Mutex.__ctor!().this` `in` and `out` contracts can only appear without a body when they are virtual interface functions or abstract
-fail_compilation/ice21095.d(12): Error: template instance `ice21095.Mutex.__ctor!()` error instantiating
+fail_compilation/ice21095.d(14): Error: constructor `ice21095.Mutex.this!().this` `in` and `out` contracts can only appear without a body when they are virtual interface functions or abstract
+fail_compilation/ice21095.d(12): Error: template instance `ice21095.Mutex.this!()` error instantiating
 ---
 */
 class Mutex
index 33aa6e4d54b4b464f900dffcdf43ba252736a3b3..7933187232b5f812c32b8604b02bdcb5772c6c8f 100644 (file)
@@ -1,12 +1,14 @@
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/ice8795.d-mixin-14(14): Error: found `End of File` when expecting `(`
-fail_compilation/ice8795.d-mixin-14(14): Error: expression expected, not `End of File`
-fail_compilation/ice8795.d-mixin-14(14): Error: missing closing `)` after `switch (__error`
-fail_compilation/ice8795.d-mixin-14(14): Error: found `End of File` instead of statement
-fail_compilation/ice8795.d-mixin-15(15): Error: { } expected following `interface` declaration
-fail_compilation/ice8795.d-mixin-15(15): Error: anonymous interfaces not allowed
+fail_compilation/ice8795.d-mixin-16(16): Error: found `End of File` when expecting `(`
+fail_compilation/ice8795.d-mixin-16(16): Error: expression expected, not `End of File`
+fail_compilation/ice8795.d-mixin-16(16): Error: missing closing `)` after `switch (__error`
+fail_compilation/ice8795.d-mixin-16(16): Error: found `End of File` instead of statement
+fail_compilation/ice8795.d-mixin-16(16):        while parsing string mixin statement
+fail_compilation/ice8795.d-mixin-17(17): Error: { } expected following `interface` declaration
+fail_compilation/ice8795.d-mixin-17(17): Error: anonymous interfaces not allowed
+fail_compilation/ice8795.d-mixin-17(17):        while parsing string mixin statement
 ---
 */
 void main()
index c2f44a344ade5f1cff45ce2fbd4c8e3dd418a8ce..8e2aebb75d49552a5f0a08f7d5c551f6eecbf069 100644 (file)
@@ -1,8 +1,8 @@
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/ice9273a.d(19): Error: constructor `ice9273a.C.__ctor!().this` no match for implicit `super()` call in constructor
-fail_compilation/ice9273a.d(23): Error: template instance `ice9273a.C.__ctor!()` error instantiating
+fail_compilation/ice9273a.d(19): Error: constructor `ice9273a.C.this!().this` no match for implicit `super()` call in constructor
+fail_compilation/ice9273a.d(23): Error: template instance `ice9273a.C.this!()` error instantiating
 ---
 */
 
index 1e87f0a141d9f0fddad0514b04b386de6d7b73c7..ac67c51d71b17befbcc37897ffd4752f7a77b8ef 100644 (file)
@@ -3,7 +3,7 @@ TEST_OUTPUT:
 ---
 fail_compilation/ice9284.d(14): Error: template `__ctor` is not callable using argument types `!()(int)`
 fail_compilation/ice9284.d(12):        Candidate is: `this()(string)`
-fail_compilation/ice9284.d(20): Error: template instance `ice9284.C.__ctor!()` error instantiating
+fail_compilation/ice9284.d(20): Error: template instance `ice9284.C.this!()` error instantiating
 ---
 */
 
index 7679d673c9e9eb93517f0c453e71dfb9d58c483d..b2f12dfd8ff83da7b92d22b69122bac5e7a19602 100644 (file)
@@ -3,8 +3,8 @@
 REQUIRED_ARGS: -de
 TEST_OUTPUT:
 ---
-fail_compilation/issue21203.d(12): Error: pragma `mangle` cannot apply to a template declaration
-fail_compilation/issue21203.d(12):        use `template Class(Args...){ pragma(mangle, "other_name") class Class {} }`
+fail_compilation/issue21203.d(12): Error: `pragma(mangle)` cannot apply to a template declaration
+fail_compilation/issue21203.d(12):        use `template F(Args...) { pragma(mangle, "gdkfjgh") ... }`
 ---
 */
 
index 80e8311b3a77b0f9ba0b79215ceaa81c4c212ffb..90da35da2a2fa7bd7365f4372ba913e87cdeaac3 100644 (file)
@@ -1,10 +1,9 @@
 /* TEST_OUTPUT:
 ---
-fail_compilation/issue22682.d(14): Error: `pragma(mangle)` must be attached to a declaration
-fail_compilation/issue22682.d(15): Error: `pragma(mangle)` takes a single argument that must be a string literal
-fail_compilation/issue22682.d(16): Error: `string` expected for pragma mangle argument, not `(0)` of type `int`
-fail_compilation/issue22682.d(16): Error: `pragma(mangle)` takes a single argument that must be a string literal
-fail_compilation/issue22682.d(17): Error: `pragma(mangle)` must be attached to a declaration
+fail_compilation/issue22682.d(13): Error: `pragma(mangle)` expects string literal argument for mangled name
+fail_compilation/issue22682.d(14): Error: `pragma(mangle)` expects string literal argument for mangled name
+fail_compilation/issue22682.d(15): Error: `string` expected for pragma mangle argument, not `(0)` of type `int`
+fail_compilation/issue22682.d(16): Error: `pragma(mangle)` expects string literal argument for mangled name
 ---
  */
 module issue22682;
index adf12f5b6bf9fcca0682d7b95283f4b83600adfc..052acda848402b69b81f210098cba6ecc6179ab2 100644 (file)
@@ -1,16 +1,15 @@
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/lexer23465.d(22): Error: character 0x1f37a is not allowed as a continue character in an identifier
-fail_compilation/lexer23465.d(22): Error: character 0x1f37a is not a valid token
-fail_compilation/lexer23465.d(23): Error: character '\' is not a valid token
-fail_compilation/lexer23465.d(24): Error: octal digit expected, not `9`
-fail_compilation/lexer23465.d(24): Error: octal literals larger than 7 are no longer supported
-fail_compilation/lexer23465.d(25): Error: integer overflow
-fail_compilation/lexer23465.d(26): Error: unterminated /+ +/ comment
-fail_compilation/lexer23465.d(27): Error: found `End of File` instead of array initializer
-fail_compilation/lexer23465.d(27): Error: semicolon needed to end declaration of `arr`, instead of `End of File`
-fail_compilation/lexer23465.d(20):        `arr` declared here
+fail_compilation/lexer23465.d(21): Error: character 0x1f37a is not allowed as a continue character in an identifier
+fail_compilation/lexer23465.d(22): Error: character '\' is not a valid token
+fail_compilation/lexer23465.d(23): Error: octal digit expected, not `9`
+fail_compilation/lexer23465.d(23): Error: octal literals larger than 7 are no longer supported
+fail_compilation/lexer23465.d(24): Error: integer overflow
+fail_compilation/lexer23465.d(25): Error: unterminated /+ +/ comment
+fail_compilation/lexer23465.d(26): Error: found `End of File` instead of array initializer
+fail_compilation/lexer23465.d(26): Error: semicolon needed to end declaration of `arr`, instead of `End of File`
+fail_compilation/lexer23465.d(19):        `arr` declared here
 ---
 */
 
index a97a55b121a5b6067b8f977146d3dd67c4d0bcf3..8676616664791bd6e89b48026fbd71708b2e4687 100644 (file)
@@ -1,7 +1,7 @@
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/mangle1.d(8): Error: pragma `mangle` can only apply to a single declaration
+fail_compilation/mangle1.d(8): Error: `pragma(mangle)` can only apply to a single declaration
 ---
 */
 
index 69e148f7a16acc9e07b39fcfaa9092432d161bd5..bcb7f2799aa92cbd89990b14c884f72b5572f911 100644 (file)
@@ -1,41 +1,10 @@
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/mangle2.d(20): Error: pragma `mangle` char 0x20 not allowed in mangled name
-fail_compilation/mangle2.d(21): Error: pragma `mangle` char 0x20 not allowed in mangled name
-fail_compilation/mangle2.d(24): Error: pragma `mangle` char 0x0a not allowed in mangled name
-fail_compilation/mangle2.d(25): Error: pragma `mangle` char 0x0a not allowed in mangled name
-fail_compilation/mangle2.d(28): Error: pragma `mangle` char 0x07 not allowed in mangled name
-fail_compilation/mangle2.d(29): Error: pragma `mangle` char 0x07 not allowed in mangled name
-fail_compilation/mangle2.d(32): Error: pragma `mangle` char 0x01 not allowed in mangled name
-fail_compilation/mangle2.d(33): Error: pragma `mangle` char 0x01 not allowed in mangled name
-fail_compilation/mangle2.d(36): Error: pragma `mangle` char 0x00 not allowed in mangled name
-fail_compilation/mangle2.d(37): Error: pragma `mangle` char 0x00 not allowed in mangled name
-fail_compilation/mangle2.d(40): Error: pragma `mangle` Outside Unicode code space
-fail_compilation/mangle2.d(41): Error: pragma `mangle` Outside Unicode code space
+fail_compilation/mangle2.d(9): Error: pragma `mangle` null character not allowed in mangled name
+fail_compilation/mangle2.d(10): Error: pragma `mangle` null character not allowed in mangled name
 ---
 */
-
-//spaces
-__gshared pragma(mangle, "test 9") ubyte test9_1;
-__gshared extern pragma(mangle, "test 9") ubyte test9_1_e;
-
-//\n chars
-__gshared pragma(mangle, "test\n9") ubyte test9_2;
-__gshared extern pragma(mangle, "test\n9") ubyte test9_2_e;
-
-//\a chars
-__gshared pragma(mangle, "test\a9") ubyte test9_3;
-__gshared extern pragma(mangle, "test\a9") ubyte test9_3_e;
-
-//\x01 chars
-__gshared pragma(mangle, "test\x019") ubyte test9_4;
-__gshared extern pragma(mangle, "test\x019") ubyte test9_4_e;
-
 //\0 chars
 __gshared pragma(mangle, "test\09") ubyte test9_5;
 __gshared extern pragma(mangle, "test\09") ubyte test9_5_e;
-
-//\xff chars
-__gshared pragma(mangle, "test\xff9") ubyte test9_6;
-__gshared extern pragma(mangle, "test\xff9") ubyte test9_6_e;
diff --git a/gcc/testsuite/gdc.test/fail_compilation/opApply_return.d b/gcc/testsuite/gdc.test/fail_compilation/opApply_return.d
new file mode 100644 (file)
index 0000000..acfa4e4
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+REQUIRED_ARGS: -de
+TEST_OUTPUT:
+---
+fail_compilation/opApply_return.d(18): Deprecation: cannot return non-zero compile-time value from `opApply`
+fail_compilation/opApply_return.d(18):        Any non-zero value must be the result of calling its delegate
+fail_compilation/opApply_return.d(19): Deprecation: cannot return non-zero compile-time value from `opApply`
+fail_compilation/opApply_return.d(19):        Any non-zero value must be the result of calling its delegate
+fail_compilation/opApply_return.d(20): Deprecation: cannot return non-zero compile-time value from `opApply`
+fail_compilation/opApply_return.d(20):        Any non-zero value must be the result of calling its delegate
+---
+*/
+
+class Tree {
+    Tree lhs;
+    Tree rhs;
+    int opApply(int delegate(Tree) dg) {
+        if (lhs && lhs.opApply(dg)) return 1;
+        if (dg(this)) return 1;
+        if (rhs && rhs.opApply(dg)) return 1;
+        return 0;
+    }
+}
index 033d078e19105d9eb19bad7dd0e171432eadb994..2cb15eb530ee33c577f018c1d45bca5828bfa12f 100644 (file)
@@ -1,21 +1,24 @@
 /* TEST_OUTPUT:
 ---
-fail_compilation/placenew.d(23): Error: PlacementExpression `3` is an rvalue, but must be an lvalue
-fail_compilation/placenew.d(28): Error: undefined identifier `x`
-fail_compilation/placenew.d(36): Error: `new ( i )` PlacementExpression cannot be evaluated at compile time
-fail_compilation/placenew.d(39):        called from here: `xxx()`
-fail_compilation/placenew.d(39):        while evaluating: `static assert(xxx() == 1)`
-fail_compilation/placenew.d(48): Error: new placement size 24 must be >= object size 40
-fail_compilation/placenew.d(54): Error: placement new cannot be used with associative arrays
-fail_compilation/placenew.d(67): Error: new placement size 4 must be >= class object size $?:32=16|64=24$
-fail_compilation/placenew.d(77): Error: `@safe` function `test7` cannot use placement `new` is not allowed in a `@safe` function
+fail_compilation/placenew.d(26): Error: PlacementExpression `3` is an rvalue, but must be an lvalue
+fail_compilation/placenew.d(31): Error: undefined identifier `x`
+fail_compilation/placenew.d(39): Error: `new ( i )` PlacementExpression cannot be evaluated at compile time
+fail_compilation/placenew.d(42):        called from here: `xxx()`
+fail_compilation/placenew.d(42):        while evaluating: `static assert(xxx() == 1)`
+fail_compilation/placenew.d(51): Error: new placement size 24 must be >= object size 40
+fail_compilation/placenew.d(57): Error: placement new cannot be used with associative arrays
+fail_compilation/placenew.d(70): Error: new placement size 4 must be >= class object size $?:32=16|64=24$
+fail_compilation/placenew.d(80): Error: placement `new` is not allowed in a `@safe` function
+fail_compilation/placenew.d(89): Error: PlacementExpression cannot be a dynamic array
+fail_compilation/placenew.d(92): Error: placement new cannot be used with dynamic arrays
+fail_compilation/placenew.d(95): Error: PlacementExpression `s` of type `const(int)` must be unshared and mutable
 ---
 */
 
 void test0()
 {
     int i;
-    int* pi = new (i) int;
+    int* pi = new (i) int; // OK
 }
 
 void test1()
@@ -78,3 +81,16 @@ void test7()
 }
 
 /*************************************************/
+
+void test8()
+{
+    void[] a;
+    a.reserve(int.sizeof);
+    int* ps = new(a) int;
+
+    ubyte[a.sizeof] sa;
+    int[] ia = new(sa) int[4];
+
+    const int s;
+    ps = new(s) int;
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/pragmamangle1.d b/gcc/testsuite/gdc.test/fail_compilation/pragmamangle1.d
new file mode 100644 (file)
index 0000000..a697706
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/pragmamangle1.d(35): Error: `pragma(mangle)` expects string literal argument for mangled name
+fail_compilation/pragmamangle1.d(37): Error: `pragma(mangle)` expects string literal argument for mangled name
+fail_compilation/pragmamangle1.d(44): Error: `pragma(mangle)` must be attached to a declaration
+fail_compilation/pragmamangle1.d(46): Error: `pragma(mangle)` must be attached to a declaration
+fail_compilation/pragmamangle1.d(53): Error: `pragma(mangle)` expects string literal argument for mangled name
+fail_compilation/pragmamangle1.d(56): Error: `pragma(mangle)` zero-length string not allowed for mangled name
+fail_compilation/pragmamangle1.d(59): Error: `pragma(mangle)` expects 1 or 2 arguments
+fail_compilation/pragmamangle1.d(62): Error: `pragma(mangle)` cannot apply to a template declaration
+fail_compilation/pragmamangle1.d(62):        use `template cannot_apply(Args...) { pragma(mangle, "template") ... }`
+fail_compilation/pragmamangle1.d(65): Error: `pragma(mangle)` takes a single argument that must be a string literal
+fail_compilation/pragmamangle1.d(68): Error: `pragma(mangle)` takes a single argument that must be a string literal
+fail_compilation/pragmamangle1.d(71): Error: `string` expected for pragma mangle argument, not `(T)` of type `T`
+fail_compilation/pragmamangle1.d(74): Error: `string` expected for pragma mangle argument, not `(T[T])` of type `T[T]`
+fail_compilation/pragmamangle1.d(77): Error: `pragma(mangle)` can only apply to a single declaration
+fail_compilation/pragmamangle1.d(85): Error: `class` or `struct` type expected for pragma mangle argument, not `"mangle"` of type `string`
+fail_compilation/pragmamangle1.d(88): Error: `class` or `struct` type expected for pragma mangle argument, not `F()` of type `T`
+fail_compilation/pragmamangle1.d(91): Error: `class` or `struct` type expected for pragma mangle argument, not `F()` of type `T`
+fail_compilation/pragmamangle1.d(94): Error: `string` expected for pragma mangle argument, not `(F())` of type `T`
+fail_compilation/pragmamangle1.d(97): Error: `string` expected for pragma mangle argument, not `(F())` of type `T`
+fail_compilation/pragmamangle1.d(100): Error: `string` expected for pragma mangle argument, not `(V)` of type `T`
+fail_compilation/pragmamangle1.d(103): Error: `string` expected for pragma mangle argument, not `(V)` of type `T`
+fail_compilation/pragmamangle1.d(106): Error: `string` expected for pragma mangle argument, not `T` of type `T`
+fail_compilation/pragmamangle1.d(109): Error: `class` or `struct` type expected for pragma mangle argument, not `int` of type `int`
+fail_compilation/pragmamangle1.d(112): Error: `class` or `struct` type expected for pragma mangle argument, not `T[T]` of type `T[T]`
+---
+*/
+struct T { }
+T F() { return T(); }
+T V;
+alias A = T[T];
+
+pragma(mangle);
+
+pragma(mangle)
+{
+    nothrow: pure: @nogc: @property
+    {
+    }
+}
+
+pragma(mangle, "no_declaration");
+
+pragma(mangle, "no_declaration")
+{
+    nothrow: pure: @nogc: @property
+    {
+    }
+}
+
+pragma(mangle)
+void no_string_literal();
+
+pragma(mangle, "")
+void empty_string_mangle();
+
+pragma(mangle, "too", "many", "arguments")
+void expects_less_arguments();
+
+pragma(mangle, "template")
+template cannot_apply(T) { }
+
+pragma(mangle, "func", "mangle")
+void func_too_many_arguments();
+
+pragma(mangle, "var", "mangle")
+int var_too_many_arguments;
+
+pragma(mangle, T)
+void func_arg1_not_a_string();
+
+pragma(mangle, A)
+int var_arg1_not_a_string;
+
+pragma(mangle, "too_many_declarations")
+{
+    nothrow pure:
+    void func1();
+    @nogc:
+    extern(C++) void func2();
+}
+
+pragma(mangle, "struct", "mangle")
+struct arg2_must_be_symbol { }
+
+pragma(mangle, "struct", F)
+struct struct_wrong_symbol { }
+
+pragma(mangle, "class", F)
+class class_wrong_symbol { }
+
+pragma(mangle, F(), T)
+struct arg1_not_ctfe_string { }
+
+pragma(mangle, T, F())
+struct arg2_not_ctfe_string { }
+
+pragma(mangle, V, T)
+struct arg1_not_string { }
+
+pragma(mangle, T, V)
+struct arg2_not_string { }
+
+pragma(mangle, T, T)
+struct arg2_expect_string { }
+
+pragma(mangle, int)
+struct arg1_not_class_or_struct { }
+
+pragma(mangle, "struct", A)
+struct arg2_not_class_or_struct { }
diff --git a/gcc/testsuite/gdc.test/fail_compilation/pragmamangle2.d b/gcc/testsuite/gdc.test/fail_compilation/pragmamangle2.d
new file mode 100644 (file)
index 0000000..a523515
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/pragmamangle2.d(37): Error: `pragma(mangle)` expects string literal argument for mangled name
+fail_compilation/pragmamangle2.d(39): Error: `pragma(mangle)` expects string literal argument for mangled name
+fail_compilation/pragmamangle2.d(46): Error: `pragma(mangle)` must be attached to a declaration
+fail_compilation/pragmamangle2.d(48): Error: `pragma(mangle)` must be attached to a declaration
+fail_compilation/pragmamangle2.d(55): Error: `pragma(mangle)` expects string literal argument for mangled name
+fail_compilation/pragmamangle2.d(58): Error: `pragma(mangle)` zero-length string not allowed for mangled name
+fail_compilation/pragmamangle2.d(61): Error: `pragma(mangle)` expects 1 or 2 arguments
+fail_compilation/pragmamangle2.d(64): Error: `pragma(mangle)` cannot apply to a template declaration
+fail_compilation/pragmamangle2.d(64):        use `template cannot_apply(Args...) { pragma(mangle, "template") ... }`
+fail_compilation/pragmamangle2.d(67): Error: `pragma(mangle)` takes a single argument that must be a string literal
+fail_compilation/pragmamangle2.d(70): Error: `pragma(mangle)` takes a single argument that must be a string literal
+fail_compilation/pragmamangle2.d(73): Error: `string` expected for pragma mangle argument, not `(T)` of type `T`
+fail_compilation/pragmamangle2.d(76): Error: `string` expected for pragma mangle argument, not `(T[T])` of type `T[T]`
+fail_compilation/pragmamangle2.d(79): Error: `pragma(mangle)` must be attached to a declaration
+fail_compilation/pragmamangle2.d(85): Error: `class` or `struct` type expected for pragma mangle argument, not `"mangle"` of type `string`
+fail_compilation/pragmamangle2.d(88): Error: `class` or `struct` type expected for pragma mangle argument, not `F()` of type `T`
+fail_compilation/pragmamangle2.d(91): Error: `class` or `struct` type expected for pragma mangle argument, not `F()` of type `T`
+fail_compilation/pragmamangle2.d(94): Error: `string` expected for pragma mangle argument, not `(F())` of type `T`
+fail_compilation/pragmamangle2.d(97): Error: `string` expected for pragma mangle argument, not `(F())` of type `T`
+fail_compilation/pragmamangle2.d(100): Error: `string` expected for pragma mangle argument, not `(V)` of type `T`
+fail_compilation/pragmamangle2.d(103): Error: `string` expected for pragma mangle argument, not `(V)` of type `T`
+fail_compilation/pragmamangle2.d(106): Error: `string` expected for pragma mangle argument, not `T` of type `T`
+fail_compilation/pragmamangle2.d(109): Error: `class` or `struct` type expected for pragma mangle argument, not `int` of type `int`
+fail_compilation/pragmamangle2.d(112): Error: `class` or `struct` type expected for pragma mangle argument, not `T[T]` of type `T[T]`
+---
+*/
+void pragma_statement_test()
+{
+    struct T { }
+    T F() { return T(); }
+    T V;
+    alias A = T[T];
+
+    pragma(mangle);
+
+    pragma(mangle)
+    {
+        synchronized
+        {
+        }
+    }
+
+    pragma(mangle, "no_declaration");
+
+    pragma(mangle, "no_declaration")
+    {
+        synchronized
+        {
+        }
+    }
+
+    pragma(mangle)
+    void no_string_literal();
+
+    pragma(mangle, "")
+    void empty_string_mangle();
+
+    pragma(mangle, "too", "many", "arguments")
+    void expects_less_arguments();
+
+    pragma(mangle, "template")
+    template cannot_apply(T) { }
+
+    pragma(mangle, "func", "mangle")
+    void func_too_many_arguments();
+
+    pragma(mangle, "var", "mangle")
+    static int var_too_many_arguments;
+
+    pragma(mangle, T)
+    void func_arg1_not_a_string();
+
+    pragma(mangle, A)
+    static int var_arg1_not_a_string;
+
+    pragma(mangle, "too_many_declarations")
+    {
+        nothrow pure void func1();
+        @nogc extern(C++) void func2();
+    }
+
+    pragma(mangle, "struct", "mangle")
+    struct arg2_must_be_symbol { }
+
+    pragma(mangle, "struct", F)
+    struct struct_wrong_symbol { }
+
+    pragma(mangle, "class", F)
+    class class_wrong_symbol { }
+
+    pragma(mangle, F(), T)
+    struct arg1_not_ctfe_string { }
+
+    pragma(mangle, T, F())
+    struct arg2_not_ctfe_string { }
+
+    pragma(mangle, V, T)
+    struct arg1_not_string { }
+
+    pragma(mangle, T, V)
+    struct arg2_not_string { }
+
+    pragma(mangle, T, T)
+    struct arg2_expect_string { }
+
+    pragma(mangle, int)
+    struct arg1_not_class_or_struct { }
+
+    pragma(mangle, "struct", A)
+    struct arg2_not_class_or_struct { }
+}
index 2432d1b30ea0ccf01e77b88fcabbe3310bcf1f55..b64b80ce90fd00acb1c34ec72197b0d6b6dce213 100644 (file)
@@ -124,11 +124,11 @@ fail_compilation/reserved_version.d(225): Error: version identifier `D_ProfileGC
 fail_compilation/reserved_version.d(226): Error: version identifier `D_Invariants` is reserved and cannot be set
 fail_compilation/reserved_version.d(227): Error: version identifier `D_Optimized` is reserved and cannot be set
 fail_compilation/reserved_version.d(228): Error: version identifier `VisionOS` is reserved and cannot be set
+fail_compilation/reserved_version.d(229): Error: version identifier `D_Profile` is reserved and cannot be set
 ---
 */
 
 // Some extra empty lines to help fixup the manual line numbering after adding new version identifiers
-
 #line 105
 version = MSP430;
 version = D_P16;
@@ -254,6 +254,7 @@ version = D_ProfileGC;
 version = D_Invariants;
 version = D_Optimized;
 version = VisionOS;
+version = D_Profile;
 
 // This should work though
 debug = DigitalMars;
index f124a3bf80f838e89028c2da7d9d5d66c783793d..1d82b22f944232f6c1235c334d19acd216e92688 100644 (file)
 // REQUIRED_ARGS: -version=none
 // REQUIRED_ARGS: -version=D_PreConditions
 // REQUIRED_ARGS: -version=D_PostConditions
+// REQUIRED_ARGS: -version=D_Profile
 // REQUIRED_ARGS: -version=D_ProfileGC
 // REQUIRED_ARGS: -version=D_Invariants
 // REQUIRED_ARGS: -version=D_Optimized
@@ -336,6 +337,7 @@ Error: version identifier `all` is reserved and cannot be set
 Error: version identifier `none` is reserved and cannot be set
 Error: version identifier `D_PreConditions` is reserved and cannot be set
 Error: version identifier `D_PostConditions` is reserved and cannot be set
+Error: version identifier `D_Profile` is reserved and cannot be set
 Error: version identifier `D_ProfileGC` is reserved and cannot be set
 Error: version identifier `D_Invariants` is reserved and cannot be set
 Error: version identifier `D_Optimized` is reserved and cannot be set
index ca64653efaec8e8a13da80fd26ad7f8252dcf98d..8062cb1d070dff78f3038e8ae833b26b9507a2ca 100644 (file)
@@ -1,8 +1,10 @@
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/retref2.d(18): Error: function `ref int retref2.D.foo(return ref int)` does not override any function, did you mean to override `ref int retref2.C.foo(ref int)`?
-fail_compilation/retref2.d(19): Error: function `ref int retref2.D.bar() scope return` does not override any function, did you mean to override `ref int retref2.C.bar()`?
+fail_compilation/retref2.d(20): Error: function `ref int retref2.D.foo(return ref int)` does not override any function
+fail_compilation/retref2.d(14):        did you mean to override `ref int retref2.C.foo(ref int)`?
+fail_compilation/retref2.d(21): Error: function `ref int retref2.D.bar() scope return` does not override any function
+fail_compilation/retref2.d(15):        did you mean to override `ref int retref2.C.bar()`?
 ---
 */
 
index 39fa8ab57afd492ce2c85a4fa49fc81cf99c5532..489a7c1a97e362e737510426ad4a9746f150253e 100644 (file)
@@ -181,7 +181,8 @@ void foo11() @safe
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/retscope.d(197): Error: returning scope variable `e` is not allowed in a `@safe` function
+fail_compilation/retscope.d(198): Error: returning scope variable `e` is not allowed in a `@safe` function
+fail_compilation/retscope.d(197):        `e` inferred `scope` because of `e.DG = d`
 ---
 */
 
@@ -201,7 +202,7 @@ void* escapeDg1(scope void* d) @safe
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/retscope.d(212): Error: assigning scope variable `p` to non-scope `e.e` is not allowed in a `@safe` function
+fail_compilation/retscope.d(213): Error: assigning scope variable `p` to non-scope `e.e` is not allowed in a `@safe` function
 ---
 */
 struct Escaper3 { void* e; }
@@ -218,7 +219,7 @@ void* escape3 (scope void* p) @safe {
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/retscope.d(229): Error: scope parameter `ptr` may not be returned
+fail_compilation/retscope.d(230): Error: scope parameter `ptr` may not be returned
 ---
 */
 
@@ -234,10 +235,10 @@ void* funretscope(scope dg_t ptr) @safe
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/retscope.d(248): Error: cannot implicitly convert expression `() => & x` of type `void* delegate() pure nothrow @nogc @safe` to `void* delegate() scope @safe`
-fail_compilation/retscope.d(248): Error: cannot implicitly convert expression `() => & x` of type `void* delegate() pure nothrow @nogc @safe` to `void* delegate() scope @safe`
 fail_compilation/retscope.d(249): Error: cannot implicitly convert expression `() => & x` of type `void* delegate() pure nothrow @nogc @safe` to `void* delegate() scope @safe`
 fail_compilation/retscope.d(249): Error: cannot implicitly convert expression `() => & x` of type `void* delegate() pure nothrow @nogc @safe` to `void* delegate() scope @safe`
+fail_compilation/retscope.d(250): Error: cannot implicitly convert expression `() => & x` of type `void* delegate() pure nothrow @nogc @safe` to `void* delegate() scope @safe`
+fail_compilation/retscope.d(250): Error: cannot implicitly convert expression `() => & x` of type `void* delegate() pure nothrow @nogc @safe` to `void* delegate() scope @safe`
 ---
 */
 
@@ -254,7 +255,7 @@ void escape4() @safe
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/retscope.d(266): Error: taking address of `scope` variable `p` with pointers is not allowed in a `@safe` function
+fail_compilation/retscope.d(267): Error: taking address of `scope` variable `p` with pointers is not allowed in a `@safe` function
 ---
 */
 
@@ -271,7 +272,7 @@ void escape5() @safe
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/retscope.d(286): Error: escaping a reference to local variable `b` by returning `foo6(& b)`  is not allowed in a `@safe` function
+fail_compilation/retscope.d(287): Error: escaping a reference to local variable `b` by returning `foo6(& b)`  is not allowed in a `@safe` function
 ---
 */
 
@@ -302,9 +303,9 @@ struct S7
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/retscope.d(315): Error: scope parameter `p` may not be returned
-fail_compilation/retscope.d(316): Error: returning `p[]` escapes a reference to parameter `p`
-fail_compilation/retscope.d(319): Error: scope parameter `p` may not be returned
+fail_compilation/retscope.d(316): Error: scope parameter `p` may not be returned
+fail_compilation/retscope.d(317): Error: returning `p[]` escapes a reference to parameter `p`
+fail_compilation/retscope.d(320): Error: scope parameter `p` may not be returned
 ---
 */
 
@@ -323,7 +324,7 @@ ref int[3] asStatic2(      scope int[] p) @safe { return p[0 .. 3]; }
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/retscope.d(335): Error: assigning reference to local variable `i` to non-scope `f` is not allowed in a `@safe` function
+fail_compilation/retscope.d(336): Error: assigning reference to local variable `i` to non-scope `f` is not allowed in a `@safe` function
 ---
 */
 
@@ -347,7 +348,7 @@ int* bar10( scope int** ptr ) @safe
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/retscope.d(358): Error: taking address of `scope` variable `aa` with pointers is not allowed in a `@safe` function
+fail_compilation/retscope.d(359): Error: taking address of `scope` variable `aa` with pointers is not allowed in a `@safe` function
 ---
 */
 
@@ -420,6 +421,7 @@ class Foo13
 TEST_OUTPUT:
 ---
 fail_compilation/retscope.d(1205): Error: scope variable `f14` calling non-scope member function `Foo14.foo()` is not allowed in a `@safe` function
+fail_compilation/retscope.d(1204):        `f14` inferred `scope` because of `f14.v = & i`
 ---
 */
 
@@ -680,6 +682,7 @@ TEST_OUTPUT:
 ---
 fail_compilation/retscope.d(1907): Error: assigning scope variable `x` to `ref` variable `this` with longer lifetime is not allowed in a `@safe` function
 fail_compilation/retscope.d(1913): Error: returning scope variable `x` is not allowed in a `@safe` function
+fail_compilation/retscope.d(1912):        `x` inferred `scope` because of `x = __param_0`
 ---
 */
 #line 1900
index 18c7fe64cc9a1c6dcbe7152e827e5f84e83c10d4..ff8b875ba24191dd531bc55106ab64574e189f3f 100644 (file)
@@ -230,6 +230,7 @@ void test17428() @safe
 TEST_OUTPUT:
 ---
 fail_compilation/retscope2.d(1107): Error: returning scope variable `dg` is not allowed in a `@safe` function
+fail_compilation/retscope2.d(1106):        `dg` inferred `scope` because of `dg = &s.foo`
 ---
 */
 
index e3b734f03d40ca7ed97d32aa17d6a85d93248d09..72231be291ec8b8cd4a70c0c8c5cb7ec67a51772 100644 (file)
@@ -78,11 +78,11 @@ void foo() @safe
 fail_compilation/retscope6.d(8016): Error: assigning address of variable `i` to `p` with longer lifetime is not allowed in a `@safe` function
 fail_compilation/retscope6.d(8031): Error: assigning reference to local variable `i` to non-scope parameter `p` calling `betty` is not allowed in a `@safe` function
 fail_compilation/retscope6.d(8031): Error: assigning reference to local variable `j` to non-scope parameter `q` calling `betty` is not allowed in a `@safe` function
-fail_compilation/retscope6.d(8023):        which is not `scope` because of `p = q`
+fail_compilation/retscope6.d(8023):        `q` is not `scope` because of `p = q`
 fail_compilation/retscope6.d(8048): Error: assigning reference to local variable `i` to non-scope parameter `p` calling `archie` is not allowed in a `@safe` function
-fail_compilation/retscope6.d(8039):        which is not `scope` because of `r = p`
+fail_compilation/retscope6.d(8039):        `p` is not `scope` because of `r = p`
 fail_compilation/retscope6.d(8048): Error: assigning reference to local variable `j` to non-scope parameter `q` calling `archie` is not allowed in a `@safe` function
-fail_compilation/retscope6.d(8038):        which is not `scope` because of `p = q`
+fail_compilation/retscope6.d(8038):        `q` is not `scope` because of `p = q`
 ---
 */
 
@@ -261,7 +261,7 @@ void escape_throw_20150() @safe
 /* TEST_OUTPUT:
 ---
 fail_compilation/retscope6.d(14019): Error: assigning scope variable `scopePtr` to non-scope parameter `x` calling `noInfer23021` is not allowed in a `@safe` function
-fail_compilation/retscope6.d(14009):        which is not `scope` because of `*escapeHole = cast(const(int)*)x`
+fail_compilation/retscope6.d(14009):        `x` is not `scope` because of `*escapeHole = cast(const(int)*)x`
 fail_compilation/retscope6.d(14022): Error: returning scope variable `scopePtr` is not allowed in a `@safe` function
 ---
 */
@@ -300,7 +300,7 @@ ref int escape23021() @safe
 /* TEST_OUTPUT:
 ---
 fail_compilation/retscope6.d(14050): Error: assigning scope variable `z` to non-scope parameter `y` calling `f23294` is not allowed in a `@safe` function
-fail_compilation/retscope6.d(14044):        which is not `scope` because of `x = y`
+fail_compilation/retscope6.d(14044):        `y` is not `scope` because of `x = y`
 ---
 */
 
diff --git a/gcc/testsuite/gdc.test/fail_compilation/safeprintf.d b/gcc/testsuite/gdc.test/fail_compilation/safeprintf.d
new file mode 100644 (file)
index 0000000..60bff2a
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+DISABLED: win32 win64 freebsd32 openbsd32 linux32 osx32
+TEST_OUTPUT:
+---
+fail_compilation/safeprintf.d(20): Error: `@safe` function `safeprintf.func` cannot call `@system` function `safeprintf.printf`
+fail_compilation/safeprintf.d(15):        `safeprintf.printf` is declared here
+fail_compilation/safeprintf.d(21): Error: `@safe` function `safeprintf.func` cannot call `@system` function `safeprintf.printf`
+fail_compilation/safeprintf.d(15):        `safeprintf.printf` is declared here
+fail_compilation/safeprintf.d(22): Error: `@safe` function `safeprintf.func` cannot call `@system` function `safeprintf.printf`
+fail_compilation/safeprintf.d(15):        `safeprintf.printf` is declared here
+fail_compilation/safeprintf.d(22): Deprecation: format specifier `"%Z"` is invalid
+---
+*/
+
+extern (C) @system pragma(printf) int printf(const(char)* format, ...);
+
+@safe void func(int i, char* s, dchar* d)
+{
+    printf("i: %d\n", i);
+    printf("s: %s\n", s);
+    printf("s: %S\n", d);
+    printf("s: %Z\n", s);
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/scope_infer_diagnostic.d b/gcc/testsuite/gdc.test/fail_compilation/scope_infer_diagnostic.d
new file mode 100644 (file)
index 0000000..50b3d32
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+REQUIRED_ARGS: -preview=dip1000
+TEST_OUTPUT:
+---
+fail_compilation/scope_infer_diagnostic.d(34): Error: `@safe` function `scope_infer_diagnostic.outer` cannot call `@system` function `scope_infer_diagnostic.inner!(void delegate(const(char)[]) pure nothrow @nogc @safe).inner`
+fail_compilation/scope_infer_diagnostic.d(28):        and assigning scope variable `w` to non-scope parameter `w` calling `callee` makes it fail to infer `@safe`
+fail_compilation/scope_infer_diagnostic.d(21):        `w` is not `scope` because of `globalPtr = & w`
+fail_compilation/scope_infer_diagnostic.d(25):        `scope_infer_diagnostic.inner!(void delegate(const(char)[]) pure nothrow @nogc @safe).inner` is declared here
+---
+*/
+
+// Test that scope violation error messages show WHY the callee's parameter
+// is not scope when going through the @safe inference chain.
+// This is similar to how @nogc violations show the full inference chain.
+
+void* globalPtr;
+
+void callee(Writer)(Writer w) @trusted
+{
+    // This escapes w, preventing it from being inferred as scope
+    globalPtr = cast(void*)&w;
+    w("x");
+}
+
+void inner(Writer)(scope Writer w)
+{
+    // Scope violation: passing scope w to non-scope parameter
+    callee(w);
+}
+
+void outer() @safe
+{
+    int x;
+    inner((const(char)[] s) { x++; });
+}
index 43478e782f47f1c6050f79e7003b57edd52112f3..e91af394ec7e68d8cc1f26e0f97d98edf6bdf97d 100644 (file)
@@ -5,8 +5,8 @@ TEST_OUTPUT:
 fail_compilation/systemvariables_void_init.d(48): Error: `void` initializing a type with unsafe bit patterns is not allowed in a `@safe` function
 fail_compilation/systemvariables_void_init.d(49): Error: `void` initializing a type with unsafe bit patterns is not allowed in a `@safe` function
 fail_compilation/systemvariables_void_init.d(50): Error: `void` initializing a type with unsafe bit patterns is not allowed in a `@safe` function
-fail_compilation/systemvariables_void_init.d(51): Error: void intializing a bool (which must always be 0 or 1) is not allowed in a `@safe` function
-fail_compilation/systemvariables_void_init.d(52): Error: void intializing a bool (which must always be 0 or 1) is not allowed in a `@safe` function
+fail_compilation/systemvariables_void_init.d(51): Error: `void` initializing a `bool` (which must always be 0 or 1) is not allowed in a `@safe` function
+fail_compilation/systemvariables_void_init.d(52): Error: `void` initializing a `bool` (which must always be 0 or 1) is not allowed in a `@safe` function
 fail_compilation/systemvariables_void_init.d(53): Error: `void` initializing a type with unsafe bit patterns is not allowed in a `@safe` function
 fail_compilation/systemvariables_void_init.d(54): Error: `void` initializing a type with unsafe bit patterns is not allowed in a `@safe` function
 ---
index 36543f92ebe97032395b563cdf6112eb4d462b23..59c49870b0117c81ed916e247a75149152b2d174 100644 (file)
@@ -1,8 +1,10 @@
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/test13867.d(12): Error: function `void test13867.X.blah()` does not override any function, did you mean to override `extern (C++) void test13867.Base.blah()`?
-fail_compilation/test13867.d(19): Error: function `void test13867.Z.blah()` does not override any function, did you mean to override `extern (C++) void test13867.Base.blah()`?
+fail_compilation/test13867.d(14): Error: function `void test13867.X.blah()` does not override any function
+fail_compilation/test13867.d(11):        did you mean to override `extern (C++) void test13867.Base.blah()`?
+fail_compilation/test13867.d(21): Error: function `void test13867.Z.blah()` does not override any function
+fail_compilation/test13867.d(11):        did you mean to override `extern (C++) void test13867.Base.blah()`?
 ---
 */
 extern (C++) class Base {
index e3fedf34bd9213f29ba58c01699c760ef105cec3..eaa3352276b71cc62f19ca4c522ce590b6c12e7c 100644 (file)
@@ -1,4 +1,4 @@
-/* REQUIRED_ARGS: -preview=bitfields
+/*
  * TEST_OUTPUT:
 ---
 fail_compilation/test16188.d(101): Error: no property `name` for `Where()` of type `test16188.Where`
index a68a0b32d2ba72aa667a830b2cb5102678bb0826..0ad4a271338c5ae2505a6ef7e0a85c69b786c549 100644 (file)
@@ -4,7 +4,7 @@ TEST_OUTPUT:
 fail_compilation/test17284.d(17): Error: accessing overlapped field `U.c` with pointers is not allowed in a `@safe` function
 pure nothrow @safe void(U t)
 ---
-REQUIRED_ARGS: -preview=bitfields
+
 */
 
 // https://issues.dlang.org/show_bug.cgi?id=17284
index e5577ddb54f5ef48cc9f7ccfba6e5877762def25..68d2f490547c56555f888af7fbeb422e8d69574f 100644 (file)
@@ -2,7 +2,8 @@
 REQUIRED_ARGS: -preview=dip1000
 TEST_OUTPUT:
 ---
-fail_compilation/test17422.d(23): Error: returning scope variable `p` is not allowed in a `@safe` function
+fail_compilation/test17422.d(24): Error: returning scope variable `p` is not allowed in a `@safe` function
+fail_compilation/test17422.d(23):        `p` inferred `scope` because of `p = rc.get()`
 ---
 */
 struct RC
index 785bbe7fc41bb2eeb6fcf89dbe3d17401a6af850..0d889310297dc770654e405501d85464105e2c6b 100644 (file)
@@ -1,8 +1,9 @@
 /* REQUIRED_ARGS: -preview=dip1000
 TEST_OUTPUT:
 ---
-fail_compilation/test17423.d(27): Error: assigning reference to local `this` to non-scope parameter `dlg` calling `opApply` is not allowed in a `@safe` function
-fail_compilation/test17423.d(16):        which is not `scope` because of `this.myDlg = dlg`
+fail_compilation/test17423.d(28): Error: assigning reference to local `this` to non-scope parameter `dlg` calling `opApply` is not allowed in a `@safe` function
+fail_compilation/test17423.d(17):        `dlg` is not `scope` because of `this.myDlg = dlg`
+fail_compilation/test17423.d(28):        `this` inferred `scope` because of `int(int _) => 0`
 ---
 */
 
index f06b1bfc601a1f568a8ff27e709bf66dc073f6be..d6786d5a32df1c020748fa30fd2111ab6be9d7f2 100644 (file)
@@ -1,13 +1,14 @@
 /* REQUIRED_ARGS: -preview=dip1000
    TEST_OUTPUT:
 ---
-fail_compilation/test18282.d(25): Error: returning scope variable `aa` is not allowed in a `@safe` function
-fail_compilation/test18282.d(34): Error: escaping a reference to local variable `i` by copying `& i` into allocated memory is not allowed in a `@safe` function
+fail_compilation/test18282.d(26): Error: returning scope variable `aa` is not allowed in a `@safe` function
 fail_compilation/test18282.d(35): Error: escaping a reference to local variable `i` by copying `& i` into allocated memory is not allowed in a `@safe` function
-fail_compilation/test18282.d(36): Error: returning scope variable `staa` is not allowed in a `@safe` function
-fail_compilation/test18282.d(44): Error: escaping a reference to local variable `i` by copying `S2000(& i)` into allocated memory is not allowed in a `@safe` function
-fail_compilation/test18282.d(53): Error: escaping a reference to local variable `i` by copying `& i` into allocated memory is not allowed in a `@safe` function
-fail_compilation/test18282.d(53): Error: escaping a reference to local variable `c` by copying `& c` into allocated memory is not allowed in a `@safe` function
+fail_compilation/test18282.d(36): Error: escaping a reference to local variable `i` by copying `& i` into allocated memory is not allowed in a `@safe` function
+fail_compilation/test18282.d(37): Error: returning scope variable `staa` is not allowed in a `@safe` function
+fail_compilation/test18282.d(34):        `staa` inferred `scope` because of `staa = [& i]`
+fail_compilation/test18282.d(45): Error: escaping a reference to local variable `i` by copying `S2000(& i)` into allocated memory is not allowed in a `@safe` function
+fail_compilation/test18282.d(54): Error: escaping a reference to local variable `i` by copying `& i` into allocated memory is not allowed in a `@safe` function
+fail_compilation/test18282.d(54): Error: escaping a reference to local variable `c` by copying `& c` into allocated memory is not allowed in a `@safe` function
 ---
  */
 
index f415dd92079cec5eaae4275948b41d6594180f59..9acea999669ded4714f697a912999b6c5d476e86 100644 (file)
@@ -1,14 +1,21 @@
 /* REQUIRED_ARGS: -preview=dip1000
  * TEST_OUTPUT:
 ---
-fail_compilation/test19097.d(44): Error: returning scope variable `s` is not allowed in a `@safe` function
-fail_compilation/test19097.d(48): Error: returning scope variable `s1` is not allowed in a `@safe` function
-fail_compilation/test19097.d(77): Error: assigning scope variable `z` to `ref` variable `refPtr` with longer lifetime is not allowed in a `@safe` function
-fail_compilation/test19097.d(108): Error: returning scope variable `s4` is not allowed in a `@safe` function
-fail_compilation/test19097.d(126): Error: returning scope variable `s5c` is not allowed in a `@safe` function
-fail_compilation/test19097.d(130): Error: returning scope variable `s5m` is not allowed in a `@safe` function
-fail_compilation/test19097.d(147): Error: returning scope variable `s6c` is not allowed in a `@safe` function
-fail_compilation/test19097.d(151): Error: returning scope variable `s6m` is not allowed in a `@safe` function
+fail_compilation/test19097.d(51): Error: returning scope variable `s` is not allowed in a `@safe` function
+fail_compilation/test19097.d(50):        `s` inferred `scope` because of `s = & i`
+fail_compilation/test19097.d(55): Error: returning scope variable `s1` is not allowed in a `@safe` function
+fail_compilation/test19097.d(54):        `s1` inferred `scope` because of `s1 = & i`
+fail_compilation/test19097.d(84): Error: assigning scope variable `z` to `ref` variable `refPtr` with longer lifetime is not allowed in a `@safe` function
+fail_compilation/test19097.d(115): Error: returning scope variable `s4` is not allowed in a `@safe` function
+fail_compilation/test19097.d(114):        `s4` inferred `scope` because of `s4 = & x`
+fail_compilation/test19097.d(133): Error: returning scope variable `s5c` is not allowed in a `@safe` function
+fail_compilation/test19097.d(132):        `s5c` inferred `scope` because of `s5c = i`
+fail_compilation/test19097.d(137): Error: returning scope variable `s5m` is not allowed in a `@safe` function
+fail_compilation/test19097.d(136):        `s5m` inferred `scope` because of `s5m = i`
+fail_compilation/test19097.d(154): Error: returning scope variable `s6c` is not allowed in a `@safe` function
+fail_compilation/test19097.d(153):        `s6c` inferred `scope` because of `s6c = i`
+fail_compilation/test19097.d(158): Error: returning scope variable `s6m` is not allowed in a `@safe` function
+fail_compilation/test19097.d(157):        `s6m` inferred `scope` because of `s6m = i`
 ---
  */
 
index 5484786d64ca892afeb76c1652172350f624162d..a37e596f9b6ad978d609a2ab57081bd2d73f0970 100644 (file)
@@ -2,16 +2,21 @@
 REQUIRED_ARGS: -preview=dip1000
 TEST_OUTPUT:
 ---
-fail_compilation/test20245.d(21): Error: assigning reference to local variable `x` to non-scope parameter `ptr` calling `escape` is not allowed in a `@safe` function
-fail_compilation/test20245.d(22): Error: escaping a reference to parameter `x` by copying `&x` into allocated memory is not allowed in a `@safe` function
-fail_compilation/test20245.d(23): Error: returning scope variable `a` is not allowed in a `@safe` function
-fail_compilation/test20245.d(27): Error: taking address of `scope` variable `x` with pointers is not allowed in a `@safe` function
-fail_compilation/test20245.d(33): Error: assigning reference to local variable `x` to non-scope parameter `ptr` calling `escape` is not allowed in a `@safe` function
-fail_compilation/test20245.d(34): Error: escaping a reference to parameter `x` by copying `&x` into allocated memory is not allowed in a `@safe` function
-fail_compilation/test20245.d(50): Error: assigning reference to local variable `price` to non-scope `this.minPrice` is not allowed in a `@safe` function
-fail_compilation/test20245.d(69): Error: reference to local variable `this.content[]` calling non-scope member function `Exception.this()` is not allowed in a `@safe` function
-fail_compilation/test20245.d(89): Error: assigning reference to local variable `this` to non-scope parameter `content` calling `listUp` is not allowed in a `@safe` function
-fail_compilation/test20245.d(82):        which is not `scope` because of `charPtr = content`
+fail_compilation/test20245.d(26): Error: assigning reference to local variable `x` to non-scope parameter `ptr` calling `escape` is not allowed in a `@safe` function
+fail_compilation/test20245.d(25):        `x` inferred `scope` because of `a = &x`
+fail_compilation/test20245.d(27): Error: escaping a reference to parameter `x` by copying `&x` into allocated memory is not allowed in a `@safe` function
+fail_compilation/test20245.d(28): Error: returning scope variable `a` is not allowed in a `@safe` function
+fail_compilation/test20245.d(25):        `a` inferred `scope` because of `a = &x`
+fail_compilation/test20245.d(32): Error: taking address of `scope` variable `x` with pointers is not allowed in a `@safe` function
+fail_compilation/test20245.d(38): Error: assigning reference to local variable `x` to non-scope parameter `ptr` calling `escape` is not allowed in a `@safe` function
+fail_compilation/test20245.d(37):        `x` inferred `scope` because of `a = &x`
+fail_compilation/test20245.d(39): Error: escaping a reference to parameter `x` by copying `&x` into allocated memory is not allowed in a `@safe` function
+fail_compilation/test20245.d(55): Error: assigning reference to local variable `price` to non-scope `this.minPrice` is not allowed in a `@safe` function
+fail_compilation/test20245.d(74): Error: reference to local variable `this.content[]` calling non-scope member function `Exception.this()` is not allowed in a `@safe` function
+fail_compilation/test20245.d(74):        `this` inferred `scope` because of `this.content[]`
+fail_compilation/test20245.d(94): Error: assigning reference to local variable `this` to non-scope parameter `content` calling `listUp` is not allowed in a `@safe` function
+fail_compilation/test20245.d(87):        `content` is not `scope` because of `charPtr = content`
+fail_compilation/test20245.d(94):        `this` inferred `scope` because of `&this.content`
 ---
 */
 
index 1cd5350959c5708d7886b45fc71a99b3c9905fcc..23da6a99efb00c569aeb57b54c4c5a39944bd715 100644 (file)
@@ -1,10 +1,9 @@
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/test20489.d(19): Error: function `pure nothrow @nogc @safe int test20489.D.f(int delegate(int) pure nothrow @nogc @safe body)` does not override any function, did you mean to override `pure nothrow @nogc @safe int test20489.B.f(scope int delegate(int) pure nothrow @nogc @safe)`?
-fail_compilation/test20489.d(19):        Did you intend to override:
-fail_compilation/test20489.d(19):        `pure nothrow @nogc @safe int test20489.B.f(scope int delegate(int) pure nothrow @nogc @safe)`
-fail_compilation/test20489.d(19):        Parameter 1 is missing `scope`
+fail_compilation/test20489.d(18): Error: function `pure nothrow @nogc @safe int test20489.D.f(int delegate(int) pure nothrow @nogc @safe body)` does not override any function
+fail_compilation/test20489.d(14):        did you mean to override `pure nothrow @nogc @safe int test20489.B.f(scope int delegate(int) pure nothrow @nogc @safe)`?
+fail_compilation/test20489.d(18):        parameter 1 is missing `scope`
 ---
 */
 
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test20863a.d b/gcc/testsuite/gdc.test/fail_compilation/test20863a.d
new file mode 100644 (file)
index 0000000..545b436
--- /dev/null
@@ -0,0 +1,23 @@
+// must not assert/crash with empty declarations
+/* TEST_OUTPUT:
+---
+fail_compilation/test20863a.d(21): Error: no property `Entry` for type `object.TypeInfo_AssociativeArray`
+fail_compilation/test20863a.d(17):        class `TypeInfo_AssociativeArray` defined here
+fail_compilation/test20863a.d(21): Error: no property `aaGetHash` for type `object.TypeInfo_AssociativeArray`
+fail_compilation/test20863a.d(17):        class `TypeInfo_AssociativeArray` defined here
+fail_compilation/test20863a.d(21): Error: no property `aaOpEqual` for type `object.TypeInfo_AssociativeArray`
+fail_compilation/test20863a.d(17):        class `TypeInfo_AssociativeArray` defined here
+---
+*/
+
+module object;
+
+class Object { }
+class TypeInfo { }
+class TypeInfo_AssociativeArray { }
+
+extern(C) int main()
+{
+    int[int] aa;
+    return 0;
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test20863b.d b/gcc/testsuite/gdc.test/fail_compilation/test20863b.d
new file mode 100644 (file)
index 0000000..27a1dbe
--- /dev/null
@@ -0,0 +1,25 @@
+// must not assert/crash with mismatched declarations
+/* TEST_OUTPUT:
+---
+fail_compilation/test20863b.d(23): Error: `Entry` isn't a template
+fail_compilation/test20863b.d(23): Error: `aaGetHash` isn't a template function
+fail_compilation/test20863b.d(23): Error: `aaOpEqual` isn't a template function
+---
+*/
+
+module object;
+
+class Object { }
+class TypeInfo { }
+class TypeInfo_AssociativeArray
+{
+    int Entry;
+    struct aaOpEqual(K, V) { }
+    struct aaGetHash(K, V) { }
+}
+
+extern(C) int main()
+{
+    int[int] aa;
+    return 0;
+}
index 02c0b350171d1566c388f826f3faa6a83c53d812..8e27d9850569b725a73416134bdb453f72e8ba42 100644 (file)
@@ -3,7 +3,8 @@
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/test21246.d(16): Error: function `void test21246.C.set(Clock clock)` does not override any function, did you mean to override `void imports.test21246.B.set(imports.test21246.Clock clock)`?
+fail_compilation/test21246.d(17): Error: function `void test21246.C.set(Clock clock)` does not override any function
+fail_compilation/imports/test21246.d(7):        did you mean to override `void imports.test21246.B.set(imports.test21246.Clock clock)`?
 ---
 */
 module test21246;
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test21634.d b/gcc/testsuite/gdc.test/fail_compilation/test21634.d
new file mode 100644 (file)
index 0000000..2cf2a7b
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+REQUIRED_ARGS: -verrors=context
+TEST_OUTPUT:
+---
+fail_compilation/test21634.d(22): Error: function literal `(int x) { }` is not callable using argument types `(string)`
+    (int x) {} ("%s");
+               ^
+fail_compilation/test21634.d(22):        cannot pass argument `"%s"` of type `string` to parameter `int x`
+    (int x) {} ("%s");
+               ^
+fail_compilation/test21634.d(24): Error: declaration `test21634.main.foo` is already defined
+    int foo;
+    ^
+fail_compilation/test21634.d(23):        `variable` `foo` is defined here
+    float foo;
+          ^
+---
+*/
+void f(int x) {}
+void main()
+{
+    (int x) {} ("%s");
+    float foo;
+    int foo;
+}
index e47569aacb3c24b47a13aa57c5ce43880836c1cc..14a9ac11ad858dc53f9192865f10dd0f264ce7af 100644 (file)
@@ -3,7 +3,7 @@ REQUIRED_ARGS: -preview=dip1000
 TEST_OUTPUT:
 ---
 fail_compilation/test23073.d(28): Error: assigning scope variable `c` to non-scope parameter `c` calling `assignNext` is not allowed in a `@safe` function
-fail_compilation/test23073.d(22):        which is not `scope` because of `c.next = c`
+fail_compilation/test23073.d(22):        `c` is not `scope` because of `c.next = c`
 ---
 */
 
index cdafc613b59d763a3030cc2304a732f0b6c0a23d..84d1c41e850759531ec85fd2a2d3751f8b3cfbe0 100644 (file)
@@ -4,8 +4,8 @@
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/test23159.d(14): Error: `scope(failure)` cannot be used with -betterC
-fail_compilation/test23159.d(18): Error: `scope(success)` cannot be used with -betterC
+fail_compilation/test23159.d(14): Error: `scope(failure)` cannot be used with `-betterC`
+fail_compilation/test23159.d(18): Error: `scope(success)` cannot be used with `-betterC`
 ---
 */
 
index dac70fd9fb77e3e980de18633f497c4cb433d3ba..2388febce1cc46d135876d3f942c1bc163d73fe2 100644 (file)
@@ -3,7 +3,7 @@ REQUIRED_ARGS: -preview=dip1000
 TEST_OUTPUT:
 ---
 fail_compilation/test23982.d(35): Error: assigning scope variable `a` to non-scope parameter `a` calling `foo2` is not allowed in a `@safe` function
-fail_compilation/test23982.d(26):        which is not `scope` because of `b = a`
+fail_compilation/test23982.d(26):        `a` is not `scope` because of `b = a`
 ---
 */
 // https://issues.dlang.org/show_bug.cgi?id=23982
index 51de15ea21aeb91f659338a2e2113c235c0883d3..66577dce9a733e9cc80326e6bbec4c5acf56e6af 100644 (file)
@@ -1,8 +1,8 @@
 /* REQUIRED_ARGS: -nothrow
  * TEST_OUTPUT:
 ---
-fail_compilation/test24084.d(110): Error: cannot use `throw` statements with -nothrow
-fail_compilation/test24084.d(112): Error: cannot use try-catch statements with -nothrow
+fail_compilation/test24084.d(110): Error: cannot use `throw` statements with `-nothrow`
+fail_compilation/test24084.d(112): Error: cannot use try-catch statements with `-nothrow`
 ---
  */
 
index 76174aee62dda9639cf9caa0c66c0bb296571554..89987046f1f16eed19b428f1ce19de873e772c3e 100644 (file)
@@ -1,10 +1,24 @@
 // https://issues.dlang.org/show_bug.cgi?id=24353
 
-/**
+/*
+REQUIRED_ARGS: -verrors=context
 TEST_OUTPUT:
 ---
-fail_compilation/test24353.d(23): Error: mutable method `test24353.S.opApply` is not callable using a `const` object
-fail_compilation/test24353.d(14):        Consider adding `const` or `inout` here
+fail_compilation/test24353.d(37): Error: mutable method `test24353.S.opApply` is not callable using a `const(S)` foreach aggregate
+    foreach (e; s) {} // mod error
+                ^
+fail_compilation/test24353.d(28):        Consider adding a method type qualifier here
+    int opApply(int delegate(int) dg)
+        ^
+fail_compilation/test24353.d(40): Error:  shared const method `test24353.S2.opApply` is not callable using a `const(S2)` foreach aggregate
+    foreach (i, e; s2) {} // mod error
+                   ^
+fail_compilation/test24353.d(47):        Consider adding a method type qualifier here
+    int opApply(int delegate(int, int) dg) const shared;
+        ^
+fail_compilation/test24353.d(42): Error: cannot uniquely infer `foreach` argument types
+    foreach (i, e; const S3()) {} // cannot infer
+    ^
 ---
 */
 
@@ -20,5 +34,21 @@ struct S
 void example()
 {
     const S s;
-    foreach (e; s) {} // Error expected here
+    foreach (e; s) {} // mod error
+
+    const S2 s2;
+    foreach (i, e; s2) {} // mod error
+
+    foreach (i, e; const S3()) {} // cannot infer
+}
+
+struct S2
+{
+    int opApply(int delegate(int, int) dg) const shared;
+}
+
+struct S3
+{
+    int opApply(int delegate(int) dg);
+    int opApply(int delegate(int, int) dg);
 }
index a0ade760fe523861e1e6f07234e2cf6e0487eb06..1f63f1fe792545df034dec31edc9ccfc3472a87f 100644 (file)
@@ -4,7 +4,7 @@
 TEST_OUTPUT:
 ---
 fail_compilation/testrvaluecpctor.d(16): Error: cannot define both an rvalue constructor and a copy constructor for `struct Foo`
-fail_compilation/testrvaluecpctor.d(24):        Template instance `testrvaluecpctor.Foo!int.Foo.__ctor!(immutable(Foo!int), immutable(Foo!int))` creates an rvalue constructor for `struct Foo`
+fail_compilation/testrvaluecpctor.d(24):        Template instance `testrvaluecpctor.Foo!int.Foo.this!(immutable(Foo!int), immutable(Foo!int))` creates an rvalue constructor for `struct Foo`
 fail_compilation/testrvaluecpctor.d(24): Error: none of the overloads of `this` can construct an immutable object with argument types `(immutable(Foo!int))`. Expected `immutable(immutable(Foo!int))`
 fail_compilation/testrvaluecpctor.d(18):        Candidates are: `testrvaluecpctor.Foo!int.Foo.this(ref scope Foo!int rhs)`
 fail_compilation/testrvaluecpctor.d(16):                        `this(Rhs, this This)(scope Rhs rhs)`
@@ -30,7 +30,7 @@ void fail22593()
 TEST_OUTPUT:
 ---
 fail_compilation/testrvaluecpctor.d(40): Error: cannot define both an rvalue constructor and a copy constructor for `struct Test`
-fail_compilation/testrvaluecpctor.d(46):        Template instance `testrvaluecpctor.Test.__ctor!()` creates an rvalue constructor for `struct Test`
+fail_compilation/testrvaluecpctor.d(46):        Template instance `testrvaluecpctor.Test.this!()` creates an rvalue constructor for `struct Test`
 ---
 */
 
index 73fe52eb4c2843696f4d7ba9cfad1b5dc941ff4f..05a30beb1849d1669675979752f797f59feafa85 100644 (file)
@@ -1,4 +1,4 @@
-/* REQUIRED_ARGS: -preview=bitfields
+/*
  */
 
 struct T
index 1382f2d35ce0cfa059578b546afc581d23a366a4..b589dddc9e04af93b904d0b9e334c86670538795 100644 (file)
@@ -952,6 +952,49 @@ void test14730x()
 }
 }
 
+/************************************/
+// https://github.com/dlang/dmd/issues/20917
+
+auto test20917a(int a) @nogc
+{
+    int foo() @nogc { return a + 3; }
+    enum yes = __traits(compiles, { return a; });
+    assert(foo() == a + 3);
+    return 1;
+}
+
+void test20917b() @nogc
+{
+    int i;
+    static if (is(typeof(() @safe @nogc
+    {
+        i++;
+    })))
+    {
+        () @safe @nogc {
+            i++;
+        }();
+    }
+    assert(i == 1);
+}
+
+void test20917c() @nogc
+{
+    int i;
+    static if (is(typeof(() @safe @nogc
+    {
+        return (){
+            i++;
+        };
+    })))
+    {
+        () @safe @nogc {
+            i++;
+        }();
+    }
+    assert(i == 1);
+}
+
 /************************************/
 
 int main()
@@ -986,6 +1029,9 @@ int main()
     test12406();
     test14730();
     //test14730x();
+    test20917a(1);
+    test20917b();
+    test20917c();
 
     printf("Success\n");
     return 0;
index aa154c7bbb2178aea02bc48f88c00a7b1514eedd..c8ebdc8f87a42aafe636707f74449737926370e1 100644 (file)
@@ -1,4 +1,4 @@
-/* REQUIRED_ARGS: -preview=bitfields
+/*
  */
 
 struct S
diff --git a/gcc/testsuite/gdc.test/runnable/dbitfieldsdm.d b/gcc/testsuite/gdc.test/runnable/dbitfieldsdm.d
new file mode 100644 (file)
index 0000000..e18ad9a
--- /dev/null
@@ -0,0 +1,196 @@
+/* test bitfields for Digital Mars
+ * Note that this test is for win32 only
+ *
+ *
+ * DISABLED: win32mscoff win64 linux freebsd osx
+ * RUN_OUTPUT:
+---
+                DM |   MS |  P32 |  P64
+T0  =  1 1 ||  1 1 |  1 1 |  1 1 |  1 1
+T1  =  2 2 ||  2 2 |  2 2 |  2 2 |  2 2
+T2  =  4 4 ||  4 4 |  4 4 |  4 4 |  4 4
+T3  = 16 8 || 16 8 | 16 8 |  8 4 |  8 8
+T4  = 16 8 || 16 8 | 16 8 | 12 4 | 16 8
+T5  = 16 8 || 16 8 | 16 8 |  8 4 |  8 8
+S1  =  8 8 ||  8 8 |  8 8 |  4 4 |  8 8
+S2  =  4 4 ||  4 4 |  4 4 |  4 4 |  4 4
+S3  =  8 4 ||  8 4 |  8 4 |  4 4 |  4 4
+S4  =  8 4 ||  8 4 |  8 4 |  4 4 |  4 4
+S5  =  8 4 ||  8 4 |  8 4 |  4 4 |  4 4
+S6  =  2 2 ||  2 2 |  2 2 |  2 2 |  2 2
+S7  = 16 8 || 16 8 | 16 8 |  4 4 |  8 8
+S8  =  4 2 ||  4 2 |  4 2 |  2 2 |  2 2
+S8A =  4 2 ||  4 2 |  4 2 |  2 2 |  2 2
+S8B =  6 2 ||  6 2 |  6 2 |  2 2 |  2 2
+S8C =  8 4 ||  8 4 |  8 4 |  4 4 |  4 4
+S9  =  4 2 ||  4 2 |  4 2 |  4 2 |  4 2
+S10 =  1 1 ||  0 0 |  * * |  0 1 |  0 1
+S11 =  1 1 ||  0 0 |  4 1 |  0 1 |  0 1
+S12 =  4 4 ||  4 4 |  4 4 |  4 4 |  4 4
+S13 =  8 4 ||  8 4 |  8 4 |  8 4 |  8 4
+S14 =  8 4 ||  8 4 |  8 4 |  8 4 |  8 4
+S15 =  8 4 ||  8 4 |  8 4 |  4 4 |  4 4
+S16 =  4 4 ||  0 0 |  4 4 |  4 1 |  4 1
+S17 =  4 4 ||  4 4 |  4 4 |  4 4 |  4 4
+S18 =  2 1 ||  2 1 |  2 1 |  5 1 |  9 1
+A0  = 16 8 || 16 8 | 16 8 | 12 4 | 16 8
+A1  = 12 4 || 12 4 | 12 4 | 12 4 | 12 4
+A2  = 12 4 || 12 4 | 12 4 | 12 4 | 12 4
+A3  = 16 4 || 16 4 | 16 4 | 16 4 | 16 4
+A4  = 12 4 || 12 4 | 12 4 |  8 4 |  8 4
+A5  =  2 1 ||  2 1 |  2 1 |  2 1 |  2 1
+A6  =  4 2 ||  4 2 |  4 2 |  2 2 |  2 2
+A7  = 16 4 || 16 4 | 16 4 | 12 4 | 16 8
+A8  = 12 4 || 12 4 | 12 4 |  8 4 |  8 8
+A9  = 32 8 || 32 8 | 32 8 | 16 4 | 16 8
+A10 =  4 2 ||  4 2 |  4 2 |  2 2 |  2 2
+A11 = 16 4 || 16 4 | 16 4 | 12 4 | 12 4
+S9 = x30200
+S14 = x300000201
+S15 = x201
+S18 = 1 should be 4
+A0 = x1
+---
+ */
+
+import core.stdc.stdio;
+
+int is64bit() { return size_t.sizeof == 8; }  // otherwise assume 32 bit
+
+/*************************************************************/
+
+struct T0  { ubyte x:1; };                       //
+struct T1  { short x:1; };                       //
+struct T2  { int x:1; };                         //
+struct T3  { ubyte a,b,c,d; long x:1; };         //
+struct T4  { ubyte a,b,c,d,e,f,g,h; long x:1; }; //
+struct T5  { ubyte a,b,c,d,e,f,g; long x:1; };   //
+struct S1  { long f:1; };                        //
+struct S2  { int x:1; int y:1; };                //
+struct S3  { short c; int x:1; uint y:1; };      //
+struct S4  { int x:1; short y:1; };              //
+struct S5  { short x:1; int y:1; };              //
+struct S6  { short x:1; short y:1; };            //
+struct S7  { short x:1; int y:1; long z:1; };    //
+struct S8  { ubyte a; ubyte b:1; short c:2; };   //
+struct S8A { ubyte b:1; short c:2; };            //
+struct S8B { ubyte a; short b:1; ubyte c:2; };   //
+struct S8C { ubyte a; int b:1; };                //
+struct S9  { ubyte a; ubyte b:2; short c:9; };   //
+struct S10 { };                                  // sizeof differs from C treatment
+struct S11 { int :0; };                          // sizeof differs from C treatment
+struct S12 { int :0; int x; };                   //
+struct S13 { uint x:12; uint x1:1; uint x2:1; uint x3:1; uint x4:1; int w; }; //
+struct S14 { ubyte a; ubyte b:4; int c:30; };    //
+struct S15 { ubyte a; ubyte b:2; int c:9; };     //
+struct S16 { int :32; };                         // sizeof differs from C treatment
+struct S17 { int a:32; };                        //
+struct S18 { ubyte a; long :0; ubyte b; };       //
+struct A0  { int a; long b:34, c:4; };           //
+struct A1  { int a; uint b:11; int c; };         //
+struct A2  { int a; uint b:11, c:5, d:16;        //
+             int e; };
+struct A3  { int a; uint b:11, c:5, :0, d:16;    //
+             int e; };
+struct A4  { int a:8; short b:7;                 //
+             uint c:29; };
+struct A5  { ubyte a:7, b:2; };                  //
+struct A6  { ubyte a:7; short b:2; };            //
+struct A7  { short a:8; int b:16; int c;         //
+             ubyte d:7; };
+struct A8  { short a:8; int b:16; int :0;        //
+             ubyte c:7; };
+struct A9  { ushort a:8; int b:16;               //
+             uint c:29; long d:9;
+             uint e:2, f:31; };
+struct A10 { ushort a:8; ubyte b; };             //
+struct A11 { ubyte a; int b:5, c:11, :0, d:8;    //
+             struct { int ee:8; } };
+
+int main()
+{
+    /* MS produces identical results for 32 and 64 bit compiles,
+     * DM is 32 bit only
+     */
+    printf("                DM |   MS |  P32 |  P64\n");
+    printf("T0  = %2d %d ||  1 1 |  1 1 |  1 1 |  1 1\n", cast(int)T0.sizeof, cast(int)T0.alignof);
+    printf("T1  = %2d %d ||  2 2 |  2 2 |  2 2 |  2 2\n", cast(int)T1.sizeof, cast(int)T1.alignof);
+    printf("T2  = %2d %d ||  4 4 |  4 4 |  4 4 |  4 4\n", cast(int)T2.sizeof, cast(int)T2.alignof);
+    printf("T3  = %2d %d || 16 8 | 16 8 |  8 4 |  8 8\n", cast(int)T3.sizeof, cast(int)T3.alignof);
+    printf("T4  = %2d %d || 16 8 | 16 8 | 12 4 | 16 8\n", cast(int)T4.sizeof, cast(int)T4.alignof);
+    printf("T5  = %2d %d || 16 8 | 16 8 |  8 4 |  8 8\n", cast(int)T5.sizeof, cast(int)T5.alignof);
+    printf("S1  = %2d %d ||  8 8 |  8 8 |  4 4 |  8 8\n", cast(int)S1.sizeof, cast(int)S1.alignof);
+    printf("S2  = %2d %d ||  4 4 |  4 4 |  4 4 |  4 4\n", cast(int)S2.sizeof, cast(int)S2.alignof);
+    printf("S3  = %2d %d ||  8 4 |  8 4 |  4 4 |  4 4\n", cast(int)S3.sizeof, cast(int)S3.alignof);
+    printf("S4  = %2d %d ||  8 4 |  8 4 |  4 4 |  4 4\n", cast(int)S4.sizeof, cast(int)S4.alignof);
+    printf("S5  = %2d %d ||  8 4 |  8 4 |  4 4 |  4 4\n", cast(int)S5.sizeof, cast(int)S5.alignof);
+    printf("S6  = %2d %d ||  2 2 |  2 2 |  2 2 |  2 2\n", cast(int)S6.sizeof, cast(int)S6.alignof);
+    printf("S7  = %2d %d || 16 8 | 16 8 |  4 4 |  8 8\n", cast(int)S7.sizeof, cast(int)S7.alignof);
+    printf("S8  = %2d %d ||  4 2 |  4 2 |  2 2 |  2 2\n", cast(int)S8.sizeof, cast(int)S8.alignof);
+    printf("S8A = %2d %d ||  4 2 |  4 2 |  2 2 |  2 2\n", cast(int)S8A.sizeof, cast(int)S8A.alignof);
+    printf("S8B = %2d %d ||  6 2 |  6 2 |  2 2 |  2 2\n", cast(int)S8B.sizeof, cast(int)S8B.alignof);
+    printf("S8C = %2d %d ||  8 4 |  8 4 |  4 4 |  4 4\n", cast(int)S8C.sizeof, cast(int)S8C.alignof);
+    printf("S9  = %2d %d ||  4 2 |  4 2 |  4 2 |  4 2\n", cast(int)S9.sizeof,  cast(int)S9.alignof);
+    printf("S10 = %2d %d ||  0 0 |  * * |  0 1 |  0 1\n", cast(int)S10.sizeof, cast(int)S10.alignof); // MS doesn't compile
+    printf("S11 = %2d %d ||  0 0 |  4 1 |  0 1 |  0 1\n", cast(int)S11.sizeof, cast(int)S11.alignof);
+    printf("S12 = %2d %d ||  4 4 |  4 4 |  4 4 |  4 4\n", cast(int)S12.sizeof, cast(int)S12.alignof);
+    printf("S13 = %2d %d ||  8 4 |  8 4 |  8 4 |  8 4\n", cast(int)S13.sizeof, cast(int)S13.alignof);
+    printf("S14 = %2d %d ||  8 4 |  8 4 |  8 4 |  8 4\n", cast(int)S14.sizeof, cast(int)S14.alignof);
+    printf("S15 = %2d %d ||  8 4 |  8 4 |  4 4 |  4 4\n", cast(int)S15.sizeof, cast(int)S15.alignof);
+    printf("S16 = %2d %d ||  0 0 |  4 4 |  4 1 |  4 1\n", cast(int)S16.sizeof, cast(int)S16.alignof);
+    printf("S17 = %2d %d ||  4 4 |  4 4 |  4 4 |  4 4\n", cast(int)S17.sizeof, cast(int)S17.alignof);
+    printf("S18 = %2d %d ||  2 1 |  2 1 |  5 1 |  9 1\n", cast(int)S18.sizeof, cast(int)S18.alignof);
+    printf("A0  = %2d %d || 16 8 | 16 8 | 12 4 | 16 8\n", cast(int)A0.sizeof,  cast(int)A0.alignof);
+    printf("A1  = %2d %d || 12 4 | 12 4 | 12 4 | 12 4\n", cast(int)A1.sizeof,  cast(int)A1.alignof);
+    printf("A2  = %2d %d || 12 4 | 12 4 | 12 4 | 12 4\n", cast(int)A2.sizeof,  cast(int)A2.alignof);
+    printf("A3  = %2d %d || 16 4 | 16 4 | 16 4 | 16 4\n", cast(int)A3.sizeof,  cast(int)A3.alignof);
+    printf("A4  = %2d %d || 12 4 | 12 4 |  8 4 |  8 4\n", cast(int)A4.sizeof,  cast(int)A4.alignof);
+    printf("A5  = %2d %d ||  2 1 |  2 1 |  2 1 |  2 1\n", cast(int)A5.sizeof,  cast(int)A5.alignof);
+    printf("A6  = %2d %d ||  4 2 |  4 2 |  2 2 |  2 2\n", cast(int)A6.sizeof,  cast(int)A6.alignof);
+    printf("A7  = %2d %d || 16 4 | 16 4 | 12 4 | 16 8\n", cast(int)A7.sizeof,  cast(int)A7.alignof);
+    printf("A8  = %2d %d || 12 4 | 12 4 |  8 4 |  8 8\n", cast(int)A8.sizeof,  cast(int)A8.alignof);
+    printf("A9  = %2d %d || 32 8 | 32 8 | 16 4 | 16 8\n", cast(int)A9.sizeof,  cast(int)A9.alignof);
+    printf("A10 = %2d %d ||  4 2 |  4 2 |  2 2 |  2 2\n", cast(int)A10.sizeof, cast(int)A10.alignof);
+    printf("A11 = %2d %d || 16 4 | 16 4 | 12 4 | 12 4\n", cast(int)A11.sizeof, cast(int)A11.alignof);
+
+    {
+        S9 s;
+        uint x;
+        *cast(uint *)&s = 0;
+        s.b = 2; s.c = 3;
+        x = *cast(uint *)&s;
+        printf("S9 = x%x\n", x);
+    }
+    {
+        S14 s = { 1, 2, 3 };
+        ulong v;
+        *cast(long *)&s = 0;
+        s.a = 1;
+        s.b = 2;
+        s.c = 3;
+        v = *cast(ulong *)&s;
+        printf("S14 = x%llx\n", v);
+    }
+    {
+        S15 s = { 1,2,3 };
+        uint x;
+        *cast(uint *)&s = 0;
+        s.a = 1; s.b = 2; s.c = 3;
+        x = *cast(uint *)&s;
+        printf("S15 = x%x\n", x);
+    }
+    {
+        S18 s;
+        printf("S18 = %d should be %d\n", cast(int)(&s.b - &s.a), is64bit() ? 8 : 4);
+    }
+    {
+        A0 s;
+        long x;
+        *cast(long *)&s = 0;
+        s.a = 1; s.b = 15;
+        x = *cast(long *)&s;
+        printf("A0 = x%llx\n", x);
+    }
+
+    return 0;
+}
diff --git a/gcc/testsuite/gdc.test/runnable/imports/pragmainline_a.d b/gcc/testsuite/gdc.test/runnable/imports/pragmainline_a.d
new file mode 100644 (file)
index 0000000..f8d2480
--- /dev/null
@@ -0,0 +1,39 @@
+module imports.pragmainline_a;
+
+pragma(inline, true)
+int foo()
+{
+    return 1;
+}
+
+pragma(inline, true)
+auto bar()
+{
+    return &foo;
+}
+
+mixin template LengthField(alias sym)
+{
+    pragma(inline, true)
+    size_t length() const
+    {
+        return sym.length;
+    }
+}
+
+struct Data
+{
+    string data;
+    mixin LengthField!data;
+
+    pragma(inline, true)
+    int opApply(scope int delegate(const Data) dg) {
+        return dg(this);
+    }
+}
+
+pragma(inline, true)
+int value()
+{
+    return 10;
+}
diff --git a/gcc/testsuite/gdc.test/runnable/imports/test10442a.d b/gcc/testsuite/gdc.test/runnable/imports/test10442a.d
new file mode 100644 (file)
index 0000000..3793c6d
--- /dev/null
@@ -0,0 +1,7 @@
+module imports.test10442a;
+
+struct S
+{
+    int x;
+    void* p;
+}
diff --git a/gcc/testsuite/gdc.test/runnable/imports/你好.d b/gcc/testsuite/gdc.test/runnable/imports/你好.d
new file mode 100644 (file)
index 0000000..e772fa1
--- /dev/null
@@ -0,0 +1,6 @@
+module imports.你好;
+
+int foo()
+{
+       return 0;
+}
diff --git a/gcc/testsuite/gdc.test/runnable/inline4.d b/gcc/testsuite/gdc.test/runnable/inline4.d
new file mode 100644 (file)
index 0000000..40c4553
--- /dev/null
@@ -0,0 +1,397 @@
+struct S
+{
+    int a;
+    int b;
+    int c;
+
+    bool test(int a_, int b_, int c_) inout
+    {
+        return a_ == a && b_ == b && c_ == c;
+    }
+}
+
+__gshared S globalS = S(2, 3, 4);
+__gshared S globalS2 = S();
+immutable S immutableS = S(9, 8, 7);
+
+S getVal()
+{
+    // Returns by value
+    return globalS;
+}
+
+ref S getRef()
+{
+    // Returns by reference
+    return globalS;
+}
+
+ref S getRefRvalue() __rvalue
+{
+    // Takes ownership of globalS
+    return globalS;
+}
+
+S getImmutable()
+{
+    return immutableS;
+}
+
+__gshared int globalDtorCount;
+
+struct D
+{
+    int d;
+
+    this(D rhs)
+    {
+        d = rhs.d;
+        rhs.d = 0xdeadbeef;
+    }
+
+    this(ref D rhs)
+    {
+        d = 12345;
+    }
+
+    ~this()
+    {
+        if (d == 123456)
+            globalDtorCount++;
+        d = 0xdeadbeef;
+    }
+}
+
+__gshared D globalD = D(123456);
+__gshared D globalD2 = D(10000);
+
+void resetD()
+{
+    globalDtorCount = 0;
+    globalD.d = 123456;
+    globalD2.d = 10000;
+}
+
+D getD()
+{
+    // Returns by value
+    return globalD;
+}
+
+D getRvalueD()
+{
+    // Returns by moving globalD, but should not have reference semantics
+    // Tests if the inliner incorrectly applies reference semantics
+    return __rvalue(globalD);
+}
+
+ref D getRefD()
+{
+    // Returns by reference
+    return globalD;
+}
+
+ref D getRefRvalueD() __rvalue
+{
+    // Takes ownership of globalS
+    return globalD;
+}
+
+/************************************/
+// Test inlining of return by value
+
+S funcVal1()
+{
+    return getVal();
+}
+
+S funcVal2()
+{
+    return getRef();
+}
+
+S funcVal3()
+{
+    return getRefRvalue();
+}
+
+S funcVal4()
+{
+    return globalS.a ? immutableS : globalS;
+}
+
+S funcVal5()
+{
+    return globalS.a ? globalS : S();
+}
+
+D funcVal6()
+{
+    return getD();
+}
+
+D funcVal7()
+{
+    return getRvalueD();
+}
+
+D funcVal8()
+{
+    return getRefD();
+}
+
+D funcVal9()
+{
+    return getRefRvalueD();
+}
+
+D funcVal10()
+{
+    return __rvalue(globalS.a ? globalD : D());
+}
+
+void consumeD(D d) {}
+
+void testValueReturn()
+{
+    // Returning by value should not change the source object
+    getVal().a++;
+    assert(globalS.test(2, 3, 4));
+    getImmutable().a++;
+    assert(immutableS.test(9, 8, 7));
+    funcVal1().a++;
+    assert(globalS.test(2, 3, 4));
+    funcVal2().c++;
+    assert(globalS.test(2, 3, 4));
+    funcVal3().c++;
+    assert(globalS.test(2, 3, 4));
+    funcVal4().a++;
+    assert(immutableS.test(9, 8, 7));
+    funcVal5().a++;
+    assert(globalS.test(2, 3, 4));
+
+    consumeD(getD());
+    assert(globalD.d == 123456);
+    assert(globalDtorCount == 0);
+
+    consumeD(__rvalue(getD()));
+    assert(globalD.d == 123456);
+    assert(globalDtorCount == 0);
+
+    consumeD(getRvalueD());
+    assert(globalD.d == 0xdeadbeef);
+    assert(globalDtorCount == 1);
+    resetD();
+
+    consumeD(__rvalue(getRvalueD()));
+    assert(globalD.d == 0xdeadbeef);
+    assert(globalDtorCount == 1);
+    resetD();
+
+    consumeD(funcVal6());
+    assert(globalD.d == 123456);
+    assert(globalDtorCount == 0);
+
+    consumeD(__rvalue(funcVal6()));
+    assert(globalD.d == 123456);
+    assert(globalDtorCount == 0);
+
+    consumeD(funcVal7());
+    assert(globalD.d == 0xdeadbeef);
+    assert(globalDtorCount == 1);
+    resetD();
+
+    consumeD(__rvalue(funcVal7()));
+    assert(globalD.d == 0xdeadbeef);
+    assert(globalDtorCount == 1);
+    resetD();
+
+    consumeD(funcVal8());
+    assert(globalD.d == 123456);
+    assert(globalDtorCount == 0);
+
+    consumeD(__rvalue(funcVal8()));
+    assert(globalD.d == 123456);
+    assert(globalDtorCount == 0);
+
+    consumeD(funcVal9());
+    assert(globalD.d == 0xdeadbeef);
+    assert(globalDtorCount == 1);
+    resetD();
+
+    consumeD(__rvalue(funcVal9()));
+    assert(globalD.d == 0xdeadbeef);
+    assert(globalDtorCount == 1);
+    resetD();
+
+    consumeD(funcVal10());
+    assert(globalD.d == 123456);
+    assert(globalDtorCount == 0);
+
+    consumeD(__rvalue(funcVal10()));
+    assert(globalD.d == 123456);
+    assert(globalDtorCount == 0);
+}
+
+
+/************************************/
+// Test inlining of return by reference
+
+ref S funcRef1()
+{
+    return getRef();
+}
+
+ref S funcRef2()
+{
+
+    return globalS.a ? globalS : globalS2;
+}
+
+ref D funcRef3()
+{
+    return getRefD();
+}
+
+ref D funcRef4()
+{
+    return __rvalue(globalS.a ? globalD : globalD2);
+}
+
+void consumeRefD(ref D d)
+{
+    assert(d.d == 123456);
+    consumeD(__rvalue(d));
+    assert(d.d == 0xdeadbeef);
+}
+
+void testRefReturn()
+{
+    // Returning by ref should mutate the source object
+    funcRef1().a++;
+    assert(globalS.test(3, 3, 4));
+    funcRef2().c++;
+    assert(globalS.test(3, 3, 5));
+
+    // Passing ref to value parameter should trigger a copy
+    resetD();
+    consumeD(funcRef3());
+    assert(globalD.d == 123456);
+    assert(globalDtorCount == 0);
+
+    // ... but not if there is __rvalue
+    consumeD(__rvalue(funcRef3()));
+    assert(globalD.d == 0xdeadbeef);
+    assert(globalDtorCount == 1);
+
+    // ditto
+    resetD();
+    consumeD(funcRef4());
+    assert(globalD.d == 123456);
+    assert(globalDtorCount == 0);
+    consumeD(__rvalue(funcRef4()));
+    assert(globalD.d == 0xdeadbeef);
+    assert(globalDtorCount == 1);
+
+    resetD();
+    consumeRefD(funcRef3());
+
+    resetD();
+    consumeRefD(funcRef4());
+}
+
+/************************************/
+// Test inlining of return by rvalue reference
+
+ref S funcRvalueRef1() __rvalue
+{
+    return getRef();
+}
+
+ref S funcRvalueRef2() __rvalue
+{
+    return globalS.a ? globalS : globalS2;
+}
+
+ref D funcRvalueRef3() __rvalue
+{
+    return getRefD();
+}
+
+ref D funcRvalueRef4() __rvalue
+{
+    return globalS.a ? globalD : globalD2;
+}
+
+void consumeRvalueRefD(D d)
+{
+    assert(d.d == 123456);
+    consumeD(__rvalue(d));
+    assert(d.d == 0xdeadbeef);
+}
+
+void testRvalueRefReturn()
+{
+    // rvalue ref is ref
+    funcRvalueRef1().a++;
+    assert(globalS.test(4, 3, 5));
+    funcRvalueRef2().c++;
+    assert(globalS.test(4, 3, 6));
+
+    resetD();
+    consumeRvalueRefD(funcRvalueRef3());
+
+    resetD();
+    consumeRvalueRefD(funcRvalueRef4());
+}
+
+/************************************/
+// https://github.com/dlang/dmd/issues/22157
+class Test22157
+{
+    override string toString()
+    {
+        auto e = { return cast() super; } ();
+        return null;
+    }
+}
+
+/************************************/
+// https://issues.dlang.org/show_bug.cgi?id=22089
+
+struct S22089
+{
+    @disable this(ref return scope typeof(this) rhs);
+    @disable this(this);
+    void* cons;
+    this(int i)
+    {
+        cons = cast(void*)&this;
+    }
+    void start()
+    {
+        void* s = cast(void*)&this;
+        assert(cons == s);
+    }
+}
+
+auto fun22089()
+{
+    return S22089(42);
+}
+
+void test22089()
+{
+    auto op = fun22089();
+    op.start();
+}
+
+/************************************/
+
+void main()
+{
+    testValueReturn();
+    testRefReturn();
+    testRvalueRefReturn();
+    test22089();
+}
diff --git a/gcc/testsuite/gdc.test/runnable/issue22069.d b/gcc/testsuite/gdc.test/runnable/issue22069.d
new file mode 100644 (file)
index 0000000..41a94a9
--- /dev/null
@@ -0,0 +1,27 @@
+// REQUIRED_ARGS: -O
+// https://github.com/dlang/dmd/issues/22069
+
+int recurse(int* i)
+{
+  L1:
+    int j = 0;
+    int* p = &j;
+
+    if (*i)
+    {
+        return recurse(p);
+    }
+    else
+    {
+        if (p is i)
+            assert(0);
+        else
+            return j;
+    }
+}
+
+void main()
+{
+    int i = 1;
+    recurse(&i);
+}
index 04731ac90857b2ecb537e1960062f1cbb5c30118..7455df41cafb3e66cd6c16d0d66f4dc373b408ce 100644 (file)
@@ -9,6 +9,31 @@ template tuple(A...) { alias tuple = A; }
 
 ///////////////////////
 
+void testIntegralPromotions()
+{
+    uint uconv1(int q, ubyte  p) { return p; } assert(uconv1(0,0xFF)        == 0xFF);
+    uint uconv2(int q, ushort p) { return p; } assert(uconv2(0,0xFFFF)      == 0xFFFF);
+    uint uconv3(int q, uint   p) { return p; } assert(uconv3(0,0xFFFF_FFFF) == 0xFFFF_FFFF);
+    //uint uconv4(int q, ulong  p) { return p; }
+
+    ulong uconv5(int q, ubyte  p) { return p; } assert(uconv5(0,0xFF)                  == 0xFF);
+    ulong uconv6(int q, ushort p) { return p; } assert(uconv6(0,0xFFFF)                == 0xFFFF);
+    ulong uconv7(int q, uint   p) { return p; } assert(uconv7(0,0xFFFF_FFFF)           == 0xFFFF_FFFF);
+    ulong uconv8(int q, ulong  p) { return p; } assert(uconv8(0,0xFFFF_FFFF_FFFF_FFFF) == 0xFFFF_FFFF_FFFF_FFFF);
+
+    uint sconv1(int q, byte  p) { return p; } assert(sconv1(0,cast(byte)0xFF)    == 0xFFFF_FFFF);
+    uint sconv2(int q, short p) { return p; } assert(sconv2(0,cast(short)0xFFFF) == 0xFFFF_FFFF);
+    uint sconv3(int q, int   p) { return p; } assert(sconv3(0,0xFFFF_FFFF)       == 0xFFFF_FFFF);
+    //uint sconv4(int q, long  p) { return p; }
+
+    ulong sconv5(int q, byte  p) { return p; } assert(sconv5(0,cast(byte)0xFF)        == 0xFFFF_FFFF_FFFF_FFFF);
+    ulong sconv6(int q, short p) { return p; } assert(sconv6(0,cast(short)0xFFFF)     == 0xFFFF_FFFF_FFFF_FFFF);
+    ulong sconv7(int q, int   p) { return p; } assert(sconv7(0,0xFFFF_FFFF)           == 0xFFFF_FFFF_FFFF_FFFF);
+    ulong sconv8(int q, long  p) { return p; } assert(sconv8(0,0xFFFF_FFFF_FFFF_FFFF) == 0xFFFF_FFFF_FFFF_FFFF);
+}
+
+///////////////////////
+
 // https://github.com/dlang/dmd/pull/11441
 
 long sdiv1(long l)
@@ -2566,6 +2591,8 @@ void test10()
 
 int main()
 {
+    testIntegralPromotions();
+
     // All the various integer divide tests
     testsdiv2();
     testulldiv();
index 4c99c59e8a96e3c95c04340e76d9ba9e45f19cba..89617de58093715092ef814b09bfd7efd7505302 100644 (file)
@@ -2526,6 +2526,27 @@ void test14846()
     foo14846({ S* p = new S(1); });
     foo14846({ S[] a = [S()]; });
     foo14846({ S[] a = [S(1)]; });
+
+    void named1()
+    {
+        S s;
+    }
+
+    foo14846(&named1);
+
+    void named2()
+    {
+        S s = S(1);
+    }
+
+    foo14846(&named2);
+
+    void named3()
+    {
+        S[3] s;
+    }
+
+    foo14846(&named3);
 }
 
 /*******************************************/
diff --git a/gcc/testsuite/gdc.test/runnable/opcolon.d b/gcc/testsuite/gdc.test/runnable/opcolon.d
new file mode 100644 (file)
index 0000000..554d542
--- /dev/null
@@ -0,0 +1,24 @@
+// REQUIRED_ARGS: -betterC
+
+/* This test triggers an address miscalculation bug in DMD 2.111
+ * with -inline.
+ * May not crash if the data segment has a different layout,
+ * e.g. when pasted into another file.
+ */
+
+struct S {
+   int i;
+}
+
+__gshared S gs = S(1);
+ref S get()
+{
+    return gs;
+}
+
+extern (C)
+int main()
+{
+    (get().i ? get() : get()).i++;
+    return 0;
+}
index 71468262553879dbb90888333766f5263960467e..980014016917e8b3444b8fb7f8426d4aac75e654 100644 (file)
@@ -169,6 +169,31 @@ struct S12124
     // speculative opCall instantiation for diagnostic message should not cause false errors
 }
 
+/**************************************/
+// https://github.com/dlang/dmd/issues/20927
+
+struct NoStatic0 { auto opCall() => 2; }
+struct NoStatic1 { int x; NoStatic1 opCall() => NoStatic1(3); }
+
+struct Yes0 { static opCall() => 2; }
+struct Yes1 { static opCall()() => 2; }
+struct Yes2
+{
+    auto call()(int x) => this.init;
+    template call() { static call() => 2; }
+    alias opCall = call;
+}
+
+void test20927()
+{
+    assert(NoStatic0() == NoStatic0.init);
+    assert(NoStatic1()() == NoStatic1(3));
+
+    assert(Yes0() == 2);
+    assert(Yes1() == 2);
+    assert(Yes2() == 2);
+}
+
 /**************************************/
 
 void main()
@@ -181,4 +206,5 @@ void main()
     test3c();
     test4();
     test12070();
+    test20927();
 }
index c413ade029a6327b209bdd63de75e52264430976..40fb4729e37ecc3cc5965fb226a57780e658ba49 100644 (file)
@@ -1262,6 +1262,23 @@ void test21522()
 
 /***************************************************/
 
+// https://github.com/dlang/dmd/issues/21920
+
+struct S21920
+{
+    void funB2(char a) {}
+    void funB(int b) {}
+    alias funB = funB2;
+    void funB()(float t) {}
+}
+
+void test21920()
+{
+    static assert(__traits(getOverloads, S21920, "funB", true).length == 3);
+}
+
+/***************************************************/
+
 int main()
 {
     test1528a();
@@ -1298,6 +1315,7 @@ int main()
     test14965();
     test21481();
     test21522();
+    test21920();
 
     printf("Success\n");
     return 0;
diff --git a/gcc/testsuite/gdc.test/runnable/pragmainline.d b/gcc/testsuite/gdc.test/runnable/pragmainline.d
new file mode 100644 (file)
index 0000000..f1b132f
--- /dev/null
@@ -0,0 +1,54 @@
+// REQUIRED_ARGS: -wi
+// EXTRA_FILES: imports/pragmainline_a.d
+/* TEST_OUTPUT:
+---
+---
+*/
+
+
+import imports.pragmainline_a;
+
+auto anonclass()
+{
+    return new class {
+        pragma(inline, true)
+        final size_t foo()
+        {
+            return value();
+        }
+    };
+}
+
+auto testAlwaysInline()
+{
+    size_t var;
+
+    foreach (d; Data("string"))
+    {
+        var = d.length();
+    }
+
+    assert(var == 6);
+
+    var = anonclass().foo();
+
+    assert(var == 10);
+
+    auto nested = (size_t i) {
+        return i - value();
+    };
+
+    var = nested(var);
+
+    assert(var == 0);
+}
+
+void main()
+{
+    immutable baz = () => 1;
+    assert(foo() == bar()());
+    assert(foo() == baz());
+    assert(bar()() == baz());
+
+    testAlwaysInline();
+}
diff --git a/gcc/testsuite/gdc.test/runnable/real_to_float.d b/gcc/testsuite/gdc.test/runnable/real_to_float.d
new file mode 100644 (file)
index 0000000..8b5c970
--- /dev/null
@@ -0,0 +1,28 @@
+/* The test related to https://github.com/dlang/dmd/issues/22322
+ * The issue title:
+ * "converting real to float uses double rounding for 64-bit code
+ * causing unexpected results"
+ */
+pragma(inline, false)
+void test(real r)
+{
+    assert(r == 0x1.000002fffffffcp-1);
+    double d = r;
+    assert(d == 0x1.000003p-1);
+    float f = r;
+    assert(f == 0x1.000002p-1);
+    float fd = d;
+    assert(fd == 0x1.000004p-1);
+    real rd = d;
+    assert(rd == 0x1.000003p-1);
+    float frd = rd;
+    assert(frd == 0x1.000004p-1);
+}
+
+void main()
+{
+    static if (real.sizeof > 8)
+    {
+        test(0x1.000002fffffffcp-1);
+    }
+}
index 2592f2a9e95e04643ece253d9e9fdab8573e94b1..e50ccd0839571aff96ffc78843f72e1fcb4d1f1e 100644 (file)
@@ -1,4 +1,4 @@
-/* PERMUTE_ARGS: -preview=rvaluerefparam
+/* REQUIRED_ARGS: -preview=rvaluerefparam
 /* testing __rvalue */
 
 import core.stdc.stdio;
@@ -242,6 +242,63 @@ void test10()
     assert(s.p.s == "hello");
 }
 
+/********************************/
+// https://github.com/dlang/dmd/issues/22111
+
+__gshared int copyCount11;
+__gshared int moveCount11;
+__gshared int dtorCount11;
+
+struct S11
+{
+    int i;
+    this(S11 rhs) { moveCount11++; }
+    this(ref S rhs) { copyCount11++; }
+    ~this() { dtorCount11++; }
+}
+
+__gshared S11 s11obj;
+
+ref S11 refS11() { return s11obj; }
+ref S11 moveS11() __rvalue { return s11obj; }
+S11 copyRefS11() { return __rvalue(refS11()); }
+S11 copyMoveS11() { return moveS11(); }
+
+void test11()
+{
+    copyRefS11();
+    assert(copyCount11 == 0 && moveCount11 == 1 && dtorCount11 == 2);
+    moveCount11 = dtorCount11 = 0;
+    copyMoveS11();
+    assert(copyCount11 == 0 && moveCount11 == 1 && dtorCount11 == 2);
+}
+
+/********************************/
+
+struct S12{
+    S12* ptr;
+    this(int) { ptr = &this; }
+    this(ref inout S12) { ptr = &this; }
+    this(S12) { ptr = &this; }
+}
+
+struct V12
+{
+    S12 s;
+    this(int) { s = S12(1); }
+}
+
+S12 foo12()
+{
+    return __rvalue(V12(1).s);
+}
+
+void test12()
+{
+    S12 s = foo12();
+    assert(&s == s.ptr);
+}
+
 /********************************/
 
 int main()
@@ -255,6 +312,9 @@ int main()
     test7();
     test8();
     test9();
+    test10();
+    test11();
+    test12();
 
     return 0;
 }
index 9311a2796432235483d0411c898d38820247b915..cf09b76296a938b95df3a828b8d8ccce20ce944c 100644 (file)
@@ -209,16 +209,49 @@ void testClassLiteral()
 // https://github.com/dlang/dmd/issues/21690
 void testMultiDim()
 {
-    // int[int][int] aa1 = [ 1: [2: 3] ]; // Error: invalid value `[2:3]` in initializer (see #17804)
+    int[int][int] aa1 = [ 1: [2: 3] ]; // Error: invalid value `[2:3]` in initializer (see #17804)
     int[int][int] aa2 = ([ 1: [2: 3] ]); // workaround
     auto aa3 = [ 1: [2: 3] ]; // works, too
     static auto aa4 = [ 1: [2: 3] ]; // Error: internal compiler error: failed to detect static initialization of associative array
+    assert(aa1 == aa2);
     assert(aa2 == aa3);
     assert(aa3 == aa4);
 }
 
 /////////////////////////////////////////////
 
+// https://github.com/dlang/dmd/issues/17804
+void testMultiDimInit()
+{
+    static struct D
+    {
+        string[string][string] aa;
+        this(string[string][string] _aa) { aa = _aa; }
+    }
+    static struct S
+    {
+        //Error: not an associative array initializer
+        D a = ["fdsa": ["fdsafd": "fdsfa"]];
+
+        // OK
+        D b = (["fdsa": ["fdsafd": "fdsfa"]]);
+    }
+    immutable string[7] a = [
+        3 : null,
+        2 : "D",
+        1 : "C",
+    ];
+    static int[char][char] arr = ['A' : ['B': 0]]; // error
+    assert(arr.length == 1);
+    assert(arr['A'] == ['B':0]);
+
+    S s;
+    assert(s.a.aa["fdsa"]["fdsafd"] == "fdsfa");
+    assert(s.b.aa["fdsa"]["fdsafd"] == "fdsfa");
+}
+
+/////////////////////////////////////////////
+
 void main()
 {
     testSimple();
@@ -232,4 +265,5 @@ void main()
     testStaticArray();
     testClassLiteral();
     testMultiDim();
+    testMultiDimInit();
 }
diff --git a/gcc/testsuite/gdc.test/runnable/structlit_rvalue.d b/gcc/testsuite/gdc.test/runnable/structlit_rvalue.d
new file mode 100644 (file)
index 0000000..c8f63c7
--- /dev/null
@@ -0,0 +1,15 @@
+void refCounted(ProcessPipes val)
+{
+    val = ProcessPipes.init;
+}
+
+struct ProcessPipes
+{
+    char[33] arr;
+    ~this() @safe {}
+}
+
+void main()
+{
+    refCounted(ProcessPipes.init);
+}
index 7a55b2dbfee059d02fee95b652ffe316b59123ee..42d64495604d91cea3e38a88f64cfb7f517d9c3e 100644 (file)
@@ -3405,7 +3405,7 @@ template map_front12186(fun...)
 
 void test12186()
 {
-    immutable int[][] mat;
+    immutable int[][] mat = new int[][](1);
 
     mat.map_front12186!((in r) => 0);              // OK
     mat.map_front12186!((const r) => 0);           // OK
diff --git a/gcc/testsuite/gdc.test/runnable/test10442.d b/gcc/testsuite/gdc.test/runnable/test10442.d
new file mode 100644 (file)
index 0000000..26691ba
--- /dev/null
@@ -0,0 +1,16 @@
+// EXTRA_FILES: imports/test10442a.d
+module test10442;
+import imports.test10442a;
+
+struct T
+{
+    int x;
+    void* p;
+}
+
+void main()
+{
+    // assumes enum RTInfo(T) merges identical bitmaps
+    assert(typeid(T).rtInfo !is null); // ok
+    assert(typeid(S).rtInfo is typeid(T).rtInfo); // fails
+}
diff --git a/gcc/testsuite/gdc.test/runnable/test20275.d b/gcc/testsuite/gdc.test/runnable/test20275.d
new file mode 100644 (file)
index 0000000..cbdf1f6
--- /dev/null
@@ -0,0 +1,8 @@
+// EXTRA_SOURCES: imports/你好.d
+
+import imports.你好;
+
+int main()
+{
+       return foo();
+}
diff --git a/gcc/testsuite/gdc.test/runnable/test20301.d b/gcc/testsuite/gdc.test/runnable/test20301.d
new file mode 100644 (file)
index 0000000..6fa36d8
--- /dev/null
@@ -0,0 +1,12 @@
+// https://github.com/dlang/dmd/issues/20301
+void main()
+{
+}
+
+string genline(string i)
+{
+    return "struct S" ~ i ~ " { S" ~ i ~ " *p; int x, y, z; } S" ~ i ~ "* foo" ~ i ~ "() { return null; }";
+}
+
+static foreach(i; 0..12288)
+    mixin(genline(i.stringof));
diff --git a/gcc/testsuite/gdc.test/runnable/test21478a.d b/gcc/testsuite/gdc.test/runnable/test21478a.d
new file mode 100644 (file)
index 0000000..1906e4e
--- /dev/null
@@ -0,0 +1,23 @@
+// https://github.com/dlang/dmd/issues/21478
+
+// Test struct that implements "rule of five"
+
+struct S21478
+{
+    // 1. destructor
+    ~this() { }
+    // 2. copy constructor
+    this(ref return scope S21478) { assert(0); }
+    // 3. copy assign
+    void opAssign(const ref S21478) { }
+    // 4. move constructor
+    this(return scope S21478) { assert(0); }
+    // 5. move assign
+    void opAssign(const S21478) { assert(0); }
+}
+
+void main()
+{
+    S21478 sa, sb;
+    sb = sa;    // Should call 3, not 2 + 5.
+}
diff --git a/gcc/testsuite/gdc.test/runnable/test21478b.d b/gcc/testsuite/gdc.test/runnable/test21478b.d
new file mode 100644 (file)
index 0000000..05b2e1f
--- /dev/null
@@ -0,0 +1,36 @@
+// https://github.com/dlang/dmd/issues/21478
+
+// Test struct that implements copy constructor follows rvalue expression spec:
+//      If both ref and non-ref parameter overloads are present,
+//      an rvalue is preferably matched to the non-ref parameters,
+//      and an lvalue is preferably matched to the ref parameter.
+//      An RvalueExpression will preferably match with the non-ref parameter.
+
+struct S21478
+{
+    this(ref return scope S21478) { assert(0); }
+}
+
+struct P21478
+{
+    int plain_old_data;
+}
+
+int overload(const S21478) { return 1; }
+int overload(const ref S21478) { return 2; }
+
+int overload(const P21478) { return 1; }
+int overload(const ref P21478) { return 2; }
+
+void main()
+{
+    S21478 s;
+    assert(overload(s) == 2);
+    assert(overload(S21478()) == 1);
+    assert(overload(__rvalue(s)) == 1);
+
+    P21478 p;
+    assert(overload(p) == 2);
+    assert(overload(P21478()) == 1);
+    assert(overload(__rvalue(p)) == 1);
+}
diff --git a/gcc/testsuite/gdc.test/runnable/test21757.d b/gcc/testsuite/gdc.test/runnable/test21757.d
new file mode 100644 (file)
index 0000000..ad910db
--- /dev/null
@@ -0,0 +1,10 @@
+// https://github.com/dlang/dmd/issues/21757
+
+struct S
+{
+    void*[300_000] arr; // generates stupidly large symbols for RTImfoImpl!() that crash MS link.exe
+}
+
+void main()
+{
+}
diff --git a/gcc/testsuite/gdc.test/runnable/test22079.d b/gcc/testsuite/gdc.test/runnable/test22079.d
new file mode 100644 (file)
index 0000000..a2e6767
--- /dev/null
@@ -0,0 +1,18 @@
+struct S {
+    ~this() {}
+}
+
+__gshared S s = S();
+
+ref S refS() {
+    return s;
+}
+
+void takeS(S s2) {
+    assert(&s !is &s2);
+}
+
+void main()
+{
+    takeS(refS());
+}
index f744712f2f90407aeec3735f3ab6b9bcd7feaeb4..06c77467c2e9c69a71985ac1310d0f0eecc67d1c 100644 (file)
@@ -1,3 +1,5 @@
+// PERMUTE_ARGS: -fPIC -inline -release -g -O
+
 module test;
 
 import core.stdc.stdio;
@@ -1272,6 +1274,126 @@ void test18576()
 
 /*******************************************/
 
+struct S67
+{
+    S67* ptr;
+
+    this(int)
+    {
+        pragma(inline, false);
+        ptr = &this;
+    }
+
+    @disable this(this);
+}
+
+pragma(inline, false)
+S67 make67()
+{
+    return S67(1);
+}
+
+__gshared int i67;
+
+S67 f67()
+out (s; s.ptr == &s)
+{
+    i67++;
+    return make67();
+}
+
+void test67()
+{
+    S67 s = f67();
+    assert(s.ptr == &s);
+}
+
+/*******************************************/
+// https://github.com/dlang/dmd/issues/22160
+
+struct Vector22160(T)
+{
+    T[] _payload;
+
+    ~this() const nothrow {}
+
+    @property size_t length() const
+    {
+        return _payload.length;
+    }
+
+    @property T* ptr() inout {
+        return cast(T*) _payload.ptr;
+    }
+}
+
+struct DEREncoder
+{
+    Vector22160!ubyte getContentsUnlocked()
+    {
+        return Vector22160!ubyte();
+    }
+
+    Vector22160!ubyte m_contents;
+}
+
+Vector22160!ubyte putInSequence()(const auto ref Vector22160!ubyte contents)
+{
+    return DEREncoder().getContentsUnlocked();
+}
+
+void foo22160(T)(Vector22160!T*, T* ptr, size_t)
+{
+    assert(ptr is null);
+}
+
+void bar22160(Vector22160!ubyte val)
+{
+    ulong[16] padding = 0x1234567890123456UL;
+    foo22160(&val, val.ptr, val.length);
+}
+
+void test22160()
+{
+    bar22160(putInSequence(Vector22160!ubyte()));
+}
+
+/*******************************************/
+// https://github.com/dlang/dmd/issues/22292
+
+struct Vector22292(T)
+{
+    T[] payload = [1];
+
+    ~this() nothrow
+    {
+        payload = null;
+    }
+
+    void check(ref Vector22292 val)
+    {
+        assert(val.payload.length == 1);
+    }
+}
+
+struct DEREncoder2
+{
+    ref Vector22292!int getContentsUnlocked()
+    {
+        return m_contents;
+    }
+
+    Vector22292!int m_contents;
+}
+
+void test22292()
+{
+    Vector22292!int o;
+    o.check(DEREncoder2().getContentsUnlocked());
+}
+
+/*******************************************/
+
 void main()
 {
     printf("Start\n");
@@ -1336,6 +1458,9 @@ void main()
     test64();
     test65();
     test18576();
+    test67();
+    test22160();
+    test22292();
 
     printf("Success\n");
 }
index 1ccd9c347f454c0506703089a2caf0f24ccc1896..29a6bd9a7c630bc72947ce08886e6f995955cab9 100644 (file)
@@ -6043,7 +6043,7 @@ void test7436()
 {
     ubyte a = 10;
     float f = 6;
-    ubyte b = a += f;
+    ubyte b = a += cast(ubyte)f;
     assert(b == 16);
 }
 
diff --git a/gcc/testsuite/gdc.test/runnable/test_delegate_init_in_struct.d b/gcc/testsuite/gdc.test/runnable/test_delegate_init_in_struct.d
new file mode 100644 (file)
index 0000000..9e74d35
--- /dev/null
@@ -0,0 +1,15 @@
+struct S
+{
+    string[] delegate() dg;
+}
+
+S s = {
+    dg: () => ["hello"] // SEGFAULT without explicit `delegate`
+};
+
+void main()
+{
+    auto result = s.dg();
+    assert(result.length == 1);
+    assert(result[0] == "hello");
+}
diff --git a/gcc/testsuite/gdc.test/runnable/test_real_array_param.d b/gcc/testsuite/gdc.test/runnable/test_real_array_param.d
new file mode 100644 (file)
index 0000000..327cccc
--- /dev/null
@@ -0,0 +1,16 @@
+void fn(int* x, real[1] arr)
+{
+    auto y = *x;  // should not segfault
+    assert(y == 42, "x parameter corrupted");
+    assert(arr[0] == 1.0, "arr (real[1]) corrupted");
+}
+
+void main()
+{
+    real[1] arr = [1.0];
+    int x = 42;
+    fn(&x, arr);
+
+    assert(x == 42, "x value corrupted");
+    assert(arr[0] == 1.0, "arr (real[1]) value corrupted");
+}
index 74da97d55e709027af06b5c3cdfc94349149c456..9247bbf84e737a6b3b7a449696bcd58d14653015 100644 (file)
@@ -327,6 +327,14 @@ void test21207()
     foo();
 }
 
+/************************************************/
+void foo() @safe
+{
+    immutable key = 1;
+    int[int] aa;
+    aa[key] = 123;
+}
+
 /************************************************/
 
 void testEvaluationOrder()
index 2aadc35afc0a6cfb5fd21dad6bd374de15fa2648..1d7cc616cfdb911deb915fdfb1b34cd6a5d00bfd 100644 (file)
@@ -370,6 +370,20 @@ void test12403()
     assert(m.get(0, 1) == 1);
 }
 
+/***************************************************/
+// regressions after converting AA to template
+void test21066()
+{
+    const(int) getValue() { return 0; }
+    int[] arr;
+    arr = [getValue()]; // works
+    int[][string] aa;
+    aa["a"] = [getValue()]; // fails: cannot implicitly convert expression `__aaval` of type `const(int)[]` to `int[]`
+
+    string[int[]] aa2;
+    aa2[[getValue()]] = "a"; // no problem because key type is automatically const(int)[]
+}
+
 /***************************************************/
 
 void main()
@@ -396,4 +410,5 @@ void main()
     testTypeinfo();
     test12220();
     test12403();
+    test21066();
 }
index c2b8a513ee14353dad64f65e6aa12c6532f8be55..9e64f04f50fe7377ffb11b564dc0bd7ba3ff4981 100644 (file)
@@ -1127,7 +1127,7 @@ void test13044()
 void test12500()
 {
     size_t foo;
-    ++foo *= 1.5;   // Rewrite to: (foo += 1) *= 1.5;
+    ++foo *= cast(size_t)1.5;   // Rewrite to: (foo += 1) *= 1.5;
 }
 
 /***************************************************/
index 0385d98c8ffe4ecd18e1ef7a11c348fd8ba2913e..5522f1a715112369a0f9c37c53fb271bb7ae6fdf 100644 (file)
@@ -108,6 +108,19 @@ void testExternCppClass()
     assert(init == (cast(void*) ac)[0 .. init.length]);
 }
 
+void testFuncParam()
+{
+    // https://github.com/dlang/dmd/issues/22135
+    struct S {}
+
+    void foo(T)(T _) {}
+
+    void bar()
+    {
+        foo(__traits(initSymbol, S));
+    }
+}
+
 void main()
 {
     testZero();
@@ -116,4 +129,5 @@ void main()
     testAlignedStruct();
     testAlignedClass();
     testExternCppClass();
+    testFuncParam();
 }
index d4e4c2ae95ea42400dd147fac77a9bac87171abb..133ae05457338dcb9a174a02f06ca05040db5a87 100644 (file)
@@ -697,6 +697,12 @@ static if(is(typeof(foo20831) Params20831 == __parameters))
 
 /************************************************/
 
+// https://github.com/dlang/dmd/issues/19788
+@10 void f(@20 int x)
+{
+    static assert([__traits(getAttributes, x)] == [20]);
+}
+
 /************************************************/
 // https://issues.dlang.org/show_bug.cgi?id=15804
 
index 3f0c1c6580273a5e1156e564871df3e390aa81a1..072c4434c68039b5b6d1a254eca1bf49bdb87acd 100644 (file)
@@ -107,6 +107,9 @@ proc gdc-convert-args { args } {
            lappend out "-Wdeprecated"
            lappend out "-Wno-error"
 
+       } elseif [regexp -- {^-edition=(\w+)} $arg pattern value] {
+           lappend out "-std=d[string tolower $value]"
+
        } elseif [regexp -- {^-extern-std=([\w+]+)} $arg pattern value] {
            lappend out "-fextern-std=$value"
 
index a9235246e938eb70da44bd52b8e67e746c5e2913..423f245b7534efb0220caac5ad66da8c6ab82687 100644 (file)
@@ -1,4 +1,4 @@
-e1f6680f50d147846316c2fa3363461a2aa7ac1d
+24a41073c2dbf456d4f7a6fe6a7965d6ce6fc5cb
 
 The first line of this file holds the git revision number of the last
 merge done from the dlang/dmd repository.
index 9493962e4689ed0cb77d3103877e640a43105153..d0c173c694ab39c2d3ac2c7814a5448d9ab94d28 100644 (file)
-/* This D file is implicitly imported by all ImportC source files.
- * It provides definitions for C compiler builtin functions and declarations.
- * The purpose is to make it unnecessary to hardwire them into the compiler.
- * As the leading double underscore suggests, this is for internal use only.
- *
- * Copyright: Copyright D Language Foundation 2022-2025
- * License:   $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
- * Authors:   Walter Bright
- * Source: $(DRUNTIMESRC __importc_builtins.di)
- */
+/* GDC -- D front-end for GCC
+   Copyright (C) 2025 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.
 
-module __builtins;
-
-/* gcc relies on internal __builtin_xxxx functions and templates to
- * accomplish <stdarg.h>. D does the same thing with templates in core.stdc.stdarg.
- * Here, we redirect the gcc builtin declarations to the equivalent
- * ones in core.stdc.stdarg, thereby avoiding having to hardwire them
- * into the D compiler.
- */
-
-alias va_list = imported!"core.stdc.stdarg".va_list;
-
-version (Posix)
-{
-    version (X86_64)
-        alias __va_list_tag = imported!"core.stdc.stdarg".__va_list_tag;
-}
-
-alias __builtin_va_start = imported!"core.stdc.stdarg".va_start;
-
-alias __builtin_va_end = imported!"core.stdc.stdarg".va_end;
-
-alias __builtin_va_copy = imported!"core.stdc.stdarg".va_copy;
-
-/* dmd's ImportC rewrites __builtin_va_arg into an instantiation of va_arg
- */
-alias va_arg = imported!"core.stdc.stdarg".va_arg;
-
-version (CRuntime_Microsoft)
-{
-    //https://docs.microsoft.com/en-us/cpp/cpp/int8-int16-int32-int64?view=msvc-170
-    alias __int8 = byte;
-    alias __int16 = short;
-    alias __int32 = int;
-    alias __int64 = long;
-}
-
-/*********** floating point *************/
-
-/* https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html
- */
+   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.
 
-version (DigitalMars)
-{
-    immutable float __nan = float.nan;
+   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/>.
+*/
 
-    float __builtin_nanf()(char*)  { return float.nan; }
+/* This module is implicitly imported by all ImportC source files.  */
 
-    double __builtin_inf()()  { return double.infinity; }
-    float  __builtin_inff()() { return float.infinity; }
-    real   __builtin_infl()() { return real.infinity; }
-
-    alias __builtin_huge_val  = __builtin_inf;
-    alias __builtin_huge_valf = __builtin_inff;
-    alias __builtin_huge_vall = __builtin_infl;
-
-    alias __builtin_fabs  = imported!"core.stdc.math".fabs;
-    alias __builtin_fabsf = imported!"core.stdc.math".fabsf;
-    alias __builtin_fabsl = imported!"core.stdc.math".fabsl;
-
-    ushort __builtin_bswap16()(ushort value)
-    {
-        return cast(ushort) (((value >> 8) & 0xFF) | ((value << 8) & 0xFF00U));
-    }
-
-    uint __builtin_bswap32()(uint value)
-    {
-        import core.bitop;
-        return core.bitop.bswap(value);
-    }
-
-    ulong  __builtin_bswap64()(ulong value)
-    {
-        import core.bitop;
-        return core.bitop.bswap(value);
-    }
-
-    uint  __builtin__popcount()(ulong value)
-    {
-        import core.bitop;
-        return core.bitop._popcnt(value);
-    }
-
-    // Lazily imported on first use
-    private alias c_long = imported!"core.stdc.config".c_long;
-
-    // Stub these out to no-ops
-    int    __builtin_constant_p(T)(T exp) { return 0; } // should be something like __traits(compiles, enum X = expr)
-    c_long __builtin_expect()(c_long exp, c_long c) { return exp; }
-    void*  __builtin_assume_aligned()(const void* p, size_t align_, ...) { return cast(void*)p; }
-
-    // https://releases.llvm.org/13.0.0/tools/clang/docs/LanguageExtensions.html#builtin-assume
-    void __builtin_assume(T)(lazy T arg) { }
+module __builtins;
 
-    /* Header on macOS for arm64 references this.
-     * Don't need to implement it, it just needs to compile
-     */
-    align (16) struct __uint128_t
-    {
-        ulong a, b;
-    }
-}
+public import gcc.builtins;
index 5b305c759a2c722ad521045ae809bf01130673b5..107ef4b9abc86ec33ccc2b1c451d2c4348d0c400 100644 (file)
@@ -815,15 +815,13 @@ ulong mulu()(ulong x, uint y, ref bool overflow)
 {
     version (D_InlineAsm_X86_64)
     {
-        if (!__ctfe)
-            return mulu(x, ulong(y), overflow);
+        return __ctfe ? mulu_generic(x, y, overflow)
+            : mulu(x, ulong(y), overflow);
+    }
+    else
+    {
+        return mulu_generic(x, y, overflow);
     }
-
-    ulong r = x * y;
-    if (x >> 32 &&
-            r / x != y)
-        overflow = true;
-    return r;
 }
 
 /// ditto
@@ -856,6 +854,16 @@ ulong mulu()(ulong x, ulong y, ref bool overflow)
     return r;
 }
 
+private ulong mulu_generic()(ulong x, uint y, ref bool overflow)
+{
+    pragma(inline, true)
+    ulong r = x * y;
+    if (x >> 32 &&
+        r / x != y)
+        overflow = true;
+    return r;
+}
+
 @betterC
 unittest
 {
index c7b302c4520fa54d50d5aa1a0c224b8ca5161592..e1b69583e0f1931d8358619f9e3c1088eed81985 100644 (file)
@@ -528,7 +528,11 @@ unittest
 //       behavior should occur within the handler itself.  This delegate
 //       is __gshared for now based on the assumption that it will only
 //       set by the main thread during program initialization.
-private __gshared AssertHandler _assertHandler = null;
+private __gshared
+{
+    AssertHandler _assertHandler = null;
+    FilterThreadThrowableHandler _filterThreadThrowableHandler = null;
+}
 
 
 /**
@@ -548,6 +552,22 @@ alias AssertHandler = void function(string file, size_t line, string msg) nothro
     _assertHandler = handler;
 }
 
+/**
+Gets/sets the Throwable filter function for threads. null means no handler is called.
+*/
+alias FilterThreadThrowableHandler = void function(ref Throwable) @system nothrow;
+
+/// ditto
+@property FilterThreadThrowableHandler filterThreadThrowableHandler() @trusted nothrow @nogc
+{
+    return _filterThreadThrowableHandler;
+}
+
+/// ditto
+@property void filterThreadThrowableHandler(FilterThreadThrowableHandler handler) @trusted nothrow @nogc
+{
+    _filterThreadThrowableHandler = handler;
+}
 
 ///////////////////////////////////////////////////////////////////////////////
 // Overridable Callbacks
index 6cab62ef995bfc514bd1bf28856a3f2db37de18b..37f0832bf50a650777266cdce8498b1d3921502c 100644 (file)
@@ -292,12 +292,10 @@ interface GC
      * (from the registry hook) should assume this function may not be called
      * on termination if the GC is never initialized.
      *
-     * Most times, this is called from the thread that is the given thread
-     * reference, but it's possible the `thread` parameter is not the same as
-     * the current thread.
-     *
-     * There is no guarantee the thread is still registered as
-     * `ThreadBase.getThis()`.
+     * This function is only called from a thread that was started from the D
+     * runtime, and is terminating. The GC can assume the thread is still in
+     * the list of running threads and can be paused for a GC cycle. This is
+     * not called for the main thread which initialized the GC.
      */
     void cleanupThread(ThreadBase thread) nothrow @nogc;
 }
index b1247891e69cc69f1a7da15ecabb1fecc70af4fa..8bd718ff4ba6b91572f915d977d49a0a399f3c2c 100644 (file)
@@ -113,6 +113,20 @@ Cent neg(Cent c)
     return c;
 }
 
+/*****************************
+ * Absolute value
+ * Note: This is a signed operation.
+ * Params:
+ *      c = Cent to get absolute value of
+ * Returns:
+ *      absolute value of c
+ */
+pure
+Cent abs(Cent c)
+{
+    return (cast(I)c.hi < 0) ? neg(c) : c;
+}
+
 /*****************************
  * Increment
  * Params:
@@ -237,6 +251,9 @@ in (n < Ubits * 2)
 
 /*****************************
  * Arithmetic shift right n bits
+ *
+ * Note: This is a signed shift (preserves the sign bit).
+ *
  * Params:
  *      c = Cent to shift
  *      n = number of bits to shift
@@ -417,6 +434,7 @@ Cent sub(Cent c1, Cent c2)
 
 /****************************
  * Multiply c1 * c2.
+ * Note: The algorithm is identical for both signed and unsigned multiplication.
  * Params:
  *      c1 = operand 1
  *      c2 = operand 2
@@ -721,6 +739,7 @@ U udivmod(Cent c1, U c2, out U modulus)
 
 /****************************
  * Signed divide c1 / c2.
+ * Note: Performs signed division. Use udiv() for unsigned division.
  * Params:
  *      c1 = dividend
  *      c2 = divisor
@@ -768,6 +787,38 @@ Cent divmod(Cent c1, Cent c2, out Cent modulus)
         return udivmod(c1, c2, modulus);
 }
 
+/*****************************
+ * Unsigned remainder c1 % c2.
+ * Params:
+ *      c1 = dividend
+ *      c2 = divisor
+ * Returns:
+ *      remainder c1 % c2
+ */
+pure
+Cent urem(Cent c1, Cent c2)
+{
+    Cent modulus;
+    udivmod(c1, c2, modulus);
+    return modulus;
+}
+
+/*****************************
+ * Signed remainder c1 % c2.
+ * Params:
+ *      c1 = dividend
+ *      c2 = divisor
+ * Returns:
+ *      remainder c1 % c2
+ */
+pure
+Cent rem(Cent c1, Cent c2)
+{
+    Cent modulus;
+    divmod(c1, c2, modulus);
+    return modulus;
+}
+
 /****************************
  * If c1 > c2 unsigned
  * Params:
@@ -1052,4 +1103,16 @@ unittest
     assert(ror(C7_9, 1) == ror1(C7_9));
     assert(rol(C7_9, 0) == C7_9);
     assert(ror(C7_9, 0) == C7_9);
+
+    // Test abs()
+    assert(abs(Cm10) == C10);
+    assert(abs(C10) == C10);
+    assert(abs(C0) == C0);
+
+    // Test rem/urem
+    assert(rem(C10, C3) == C1);   // 10 % 3 = 1
+    assert(urem(C10, C3) == C1);
+    assert(rem(Cm10, C3) == Cm1); // -10 % 3 = -1
+
+    assert(urem(Cm10, C3) == C0);
 }
index e5c57e67da47c40c1c0d78df7e2adeda36b86851..9eb014aad300816dc89f8b8ea1d517d29dd9f512 100644 (file)
@@ -122,7 +122,7 @@ version (D_ProfileGC)
         version (D_TypeInfo)
         {
             import core.internal.array.utils: TraceHook, gcStatsPure, accumulatePure;
-            mixin(TraceHook!(Tarr.stringof, "_d_arrayappendcTX"));
+            mixin(TraceHook!("Tarr", "_d_arrayappendcTX"));
 
             return _d_arrayappendcTX(px, n);
         }
@@ -207,7 +207,7 @@ version (D_ProfileGC)
         version (D_TypeInfo)
         {
             import core.internal.array.utils: TraceHook, gcStatsPure, accumulatePure;
-            mixin(TraceHook!(Tarr.stringof, "_d_arrayappendT"));
+            mixin(TraceHook!("Tarr", "_d_arrayappendT"));
 
             return _d_arrayappendT(x, y);
         }
index 42e6d44ec32da93b928955ffc0f55f34b8eb577e..833918664d958eded60cd4e7e6dd7334fbaed037 100644 (file)
@@ -254,8 +254,8 @@ private size_t _d_arraysetlengthT_(Tarr : T[], T)(return ref scope Tarr arr, siz
     }
 
     enum sizeelem = T.sizeof;
-    enum hasPostblit = __traits(hasMember, T, "__postblit");
-    enum hasEnabledPostblit = hasPostblit && !__traits(isDisabled, T.__postblit);
+    enum hasPostblit = __traits(hasMember, T, "__xpostblit");
+    enum hasEnabledPostblit = hasPostblit && !__traits(isDisabled, T.__xpostblit);
 
     bool overflow = false;
     const newsize = mulu(sizeelem, newlength, overflow);
index fab2a5a9b7c1f0001009eee52f2c81772e43fe8a..fe85d24a70df9e4b6f43969a8807300156bb8d97 100644 (file)
@@ -17,7 +17,7 @@ module core.internal.array.concatenation;
  * Returns:
  *      A newly allocated array that contains all the elements from `froms`.
  */
-Tret _d_arraycatnTX(Tret, Tarr...)(auto ref Tarr froms) @trusted
+Tret _d_arraycatnTX(Tret: Tret_El[], Tret_El, Tarr...)(auto ref Tarr froms) @trusted
 {
     import core.exception : onOutOfMemoryError;
     import core.internal.array.utils : __arrayAlloc;
@@ -44,7 +44,7 @@ Tret _d_arraycatnTX(Tret, Tarr...)(auto ref Tarr froms) @trusted
         return res; // Return an empty array if no elements are present
 
     // Allocate memory for mutable arrays using __arrayAlloc
-    res = cast(Tret) __arrayAlloc!(UnqT)(elemSize * totalLen);
+    res = (cast(Tret_El*) &__arrayAlloc!(UnqT)(elemSize * totalLen)[0])[0 .. totalLen];
 
     if (res.ptr is null)
         onOutOfMemoryError(); // Abort if allocation fails
@@ -174,7 +174,7 @@ version (D_ProfileGC)
         version (D_TypeInfo)
         {
             import core.internal.array.utils: TraceHook, gcStatsPure, accumulatePure;
-            mixin(TraceHook!(Tarr.stringof, "_d_arraycatnTX"));
+            mixin(TraceHook!("Tarr", "_d_arraycatnTX"));
 
             import core.lifetime: forward;
             return _d_arraycatnTX!Tret(forward!froms);
index b4012d2f1fa14abfec507b2c6477d3f70374dd77..4797244993fe8b6679b988597f1f7397199ed29f 100644 (file)
@@ -463,20 +463,36 @@ unittest
 version (D_ProfileGC)
 {
     /**
-    * TraceGC wrapper around $(REF _d_newitemT, core,lifetime).
+    * TraceGC wrapper around $(REF _d_newarrayT, core,internal.array.construction).
     */
     T[] _d_newarrayTTrace(T)(size_t length, bool isShared, string file = __FILE__, int line = __LINE__, string funcname = __FUNCTION__) @trusted
     {
         version (D_TypeInfo)
         {
             import core.internal.array.utils : TraceHook, gcStatsPure, accumulatePure;
-            mixin(TraceHook!(T.stringof, "_d_newarrayT"));
+            mixin(TraceHook!("T", "_d_newarrayT"));
 
             return _d_newarrayT!T(length, isShared);
         }
         else
             assert(0, "Cannot create new array if compiling without support for runtime type information!");
     }
+
+    /**
+    * TraceGC wrapper around $(REF _d_newarrayU, core,internal.array.construction).
+    */
+    T[] _d_newarrayUTrace(T)(size_t length, bool isShared, string file = __FILE__, int line = __LINE__, string funcname = __FUNCTION__) @trusted
+    {
+        version (D_TypeInfo)
+        {
+            import core.internal.array.utils : TraceHook, gcStatsPure, accumulatePure;
+            mixin(TraceHook!("T", "_d_newarrayU"));
+
+            return _d_newarrayUPureNothrow!T(length, isShared);
+        }
+        else
+            assert(0, "Cannot create new array if compiling without support for runtime type information!");
+    }
 }
 
 /**
@@ -602,7 +618,7 @@ version (D_ProfileGC)
         version (D_TypeInfo)
         {
             import core.internal.array.utils : TraceHook, gcStatsPure, accumulatePure;
-            mixin(TraceHook!(T.stringof, "_d_newarraymTX"));
+            mixin(TraceHook!("T", "_d_newarraymTX"));
 
             return _d_newarraymTX!(Tarr, T)(dims, isShared);
         }
index e0a811bf07225d9a6575b0eec21c4e7af345c66c..7a7dc5ceb26b6b67d3ba0582cd8fac8c06e2086b 100644 (file)
@@ -27,6 +27,21 @@ bool __equals(T1, T2)(scope T1[] lhs, scope T2[] rhs) @trusted
     return (cast(PureType)&isEqual!(T1,T2))(lhs, rhs, lhs.length);
 }
 
+pragma(inline, true)
+bool __equals(T1, T2, size_t N)(scope ref T1[N] lhs, scope T2[] rhs) @trusted {
+    return __equals(lhs[], rhs);
+}
+
+pragma(inline, true)
+bool __equals(T1, T2, size_t N)(scope T1[] lhs, scope ref T2[N] rhs) @trusted {
+    return __equals(lhs, rhs[]);
+}
+
+pragma(inline, true)
+bool __equals(T1, T2, size_t N, size_t M)(scope ref T1[N] lhs, scope ref T2[M] rhs) @trusted {
+    return __equals(lhs[], rhs[]);
+}
+
 /******************************
  * Helper function for __equals().
  * Outlined to enable __equals() to be inlined, as dmd cannot inline loops.
index 3d85e6277c90ccebd4864e1f694382152f1542d9..9eae8f092b2d47d11ed2854031c868e8e9078a64 100644 (file)
@@ -41,16 +41,16 @@ version (D_ProfileGC)
     /**
      * TraceGC wrapper generator around the runtime hook `Hook`.
      * Params:
-     *   Type = The type of hook to report to accumulate
+     *   TypeIdent = The symbol of the type of hook to report to accumulate
      *   Hook = The name hook to wrap
      */
-    template TraceHook(string Type, string Hook)
+    template TraceHook(string TypeIdent, string Hook)
     {
         const char[] TraceHook = q{
             import core.internal.array.utils : gcStatsPure, accumulatePure;
 
             pragma(inline, false);
-            string name = } ~ "`" ~ Type ~ "`;" ~ q{
+            string name = } ~ TypeIdent ~ q{.stringof;
 
             // FIXME: use rt.tracegc.accumulator when it is accessable in the future.
             ulong currentlyAllocated = gcStatsPure().allocatedInCurrentThread;
@@ -91,7 +91,7 @@ version (D_ProfileGC)
     {
         version (D_TypeInfo)
         {
-            mixin(TraceHook!(T.stringof, __traits(identifier, Hook)));
+            mixin(TraceHook!("T", __traits(identifier, Hook)));
             return Hook(parameters);
         }
         else
index c4de76ea62660c5809ef73b742fe75cf618a697c..f141b20995de13f7c3b938d2faa936f6a3fd62e5 100644 (file)
@@ -132,55 +132,44 @@ private void* _d_interface_cast(To)(void* p) @trusted
 */
 void* _d_cast(To, From)(From o) @trusted
 {
-    static if (is(From == class) && is(To == interface))
+    static if (is(From == To))
+    {
+        return *cast(void**) &o;
+    }
+    else static if (is(From == class) && is(To == interface))
     {
         return _d_dynamic_cast!To(o);
     }
-
-    static if (is(From == class) && is(To == class))
+    else static if (is(From == class) && is(To == class))
     {
-        static if (is(From FromSupers == super) && is(To ToSupers == super))
-        {
-            /* Check for:
-            *  class A { }
-            *  final class B : A { }
-            *  ... cast(B) A ...
-            */
-            // Multiple inheritance is not allowed, so we can safely assume
-            // that the second super can only be an interface.
-            static if (__traits(isFinalClass, To) && is(ToSupers[0] == From) &&
-                       ToSupers.length == 1 && FromSupers.length <= 1)
-            {
-                return _d_paint_cast!To(o);
-            }
-        }
 
-        static if (is (To : From))
+        /* Check for:
+        *  class A { }
+        *  final class B : A { }
+        *  ... cast(B) A ...
+        */
+        /* Multiple inheritance is not allowed, so we can safely assume
+         * that the second super can only be an interface.
+         */
+        static if (is(From FromSupers == super) && is(To ToSupers == super) &&
+            __traits(isFinalClass, To) && is(ToSupers[0] == From) &&
+            ToSupers.length == 1 && FromSupers.length <= 1)
         {
-            static if (is (To == From))
-            {
-                return cast(void*)o;
-            }
-            else
-            {
-                return _d_class_cast!To(o);
-            }
+            return _d_paint_cast!To(o);
         }
-
-        return null;
-    }
-
-    static if (is(From == interface))
-    {
-        static if (is(From == To))
+        else static if (is (To : From))
         {
-            return cast(void*)o;
+            return _d_class_cast!To(o);
         }
         else
         {
-            return _d_interface_cast!To(cast(void*)o);
+            return null;
         }
     }
+    else static if (is(From == interface))
+    {
+        return _d_interface_cast!To(cast(void*)o);
+    }
     else
     {
         return null;
@@ -309,3 +298,13 @@ private bool _d_isbaseof2(To)(scope ClassInfo oc, scope ref size_t offset)
     assert(_d_cast!I1(ci) !is null); // I3(c) to I1
     assert(_d_interface_cast!I1(cast(void*)ci) !is null);
 }
+
+// https://github.com/dlang/dmd/issues/21646
+@system pure unittest {
+    static class C {
+        @disable void opCast(T)();
+    }
+
+    const(C) const_c = new C();
+    C mutable_c = cast() const_c;
+}
index b583341d06b8cad2018a153972404c14133ef694..f1a7e5b28a0543a3ec4fe74a809ec1991acc58dd 100644 (file)
@@ -215,6 +215,13 @@ unittest
     {
         // Overflow ary.length.
         auto ary = Array!size_t(cast(size_t*)0xdeadbeef, -1);
+        scope(failure)
+        {
+            // prevent destructor from cleaning up invalid memory.
+            ary._length = 0;
+            ary._ptr = null;
+        }
+
         ary.insertBack(0);
     }
     catch (OutOfMemoryError)
@@ -224,6 +231,12 @@ unittest
     {
         // Overflow requested memory size for common.xrealloc().
         auto ary = Array!size_t(cast(size_t*)0xdeadbeef, -2);
+        scope(failure)
+        {
+            // prevent destructor from cleaning up invalid memory.
+            ary._length = 0;
+            ary._ptr = null;
+        }
         ary.insertBack(0);
     }
     catch (OutOfMemoryError)
index 120fbdfcfe62bf6d4c884470b1e51bee41dde630..be628ed77793d53e6a09215c16bf6d9e81e55d1a 100644 (file)
@@ -651,56 +651,49 @@ const(ubyte)[] toUbyte(T)(return scope const T[] arr) if (T.sizeof == 1)
     return cast(const(ubyte)[])arr;
 }
 
-@trusted pure nothrow @nogc
-const(ubyte)[] toUbyte(T)(return scope const T[] arr) if (T.sizeof > 1)
+private const(ubyte)[] toUbyte_array_ctfe(T)(return scope const T[] arr)
 {
-    if (__ctfe)
+    pragma(inline, false);
+    ubyte[] ret = ctfe_alloc(T.sizeof * arr.length);
+    static if (is(T EType == enum)) // Odd style is to avoid template instantiation in most cases.
+        alias E = OriginalType!EType;
+    else
+        alias E = T;
+    static if (is(E == struct) || is(E == union) || __traits(isStaticArray, E) || !is(typeof(arr[0] is null)))
     {
-        ubyte[] ret = ctfe_alloc(T.sizeof * arr.length);
-        static if (is(T EType == enum)) // Odd style is to avoid template instantiation in most cases.
-            alias E = OriginalType!EType;
-        else
-            alias E = T;
-        static if (is(E == struct) || is(E == union) || __traits(isStaticArray, E) || !is(typeof(arr[0] is null)))
+        size_t offset = 0;
+        foreach (ref cur; arr)
         {
-            size_t offset = 0;
-            foreach (ref cur; arr)
-            {
-                ret[offset .. offset + T.sizeof] = toUbyte(cur)[0 .. T.sizeof];
-                offset += T.sizeof;
-            }
+            ret[offset .. offset + T.sizeof] = toUbyte(cur)[0 .. T.sizeof];
+            offset += T.sizeof;
         }
-        else
-        {
-            foreach (cur; arr)
-                assert(cur is null, "Unable to compute byte representation of non-null pointer at compile time");
-        }
-        return ret;
     }
     else
     {
-        return (cast(const(ubyte)*)(arr.ptr))[0 .. T.sizeof*arr.length];
+        foreach (cur; arr)
+            assert(cur is null, "Unable to compute byte representation of non-null pointer at compile time");
     }
+    return ret;
 }
 
 @trusted pure nothrow @nogc
-const(ubyte)[] toUbyte(T)(const ref scope T val) if (__traits(isIntegral, T) && !is(T == enum) && !is(T == __vector))
+const(ubyte)[] toUbyte(T)(return scope const T[] arr) if (T.sizeof > 1)
+{
+    pragma(inline, true);
+    return __ctfe ? toUbyte_array_ctfe(arr)
+                  : (cast(const(ubyte)*)(arr.ptr))[0 .. T.sizeof*arr.length];
+}
+
+private const(ubyte)[] toUbyte_integral_ctfe(T)(const return ref scope T val)
 {
+    pragma(inline, false);
     static if (T.sizeof == 1)
     {
-        pragma(inline, true);
-        if (__ctfe)
-        {
-            ubyte[] result = ctfe_alloc(1);
-            result[0] = cast(ubyte) val;
-            return result;
-        }
-        else
-        {
-            return (cast(const(ubyte)*)&val)[0 .. T.sizeof];
-        }
+        ubyte[] result = ctfe_alloc(1);
+        result[0] = cast(ubyte) val;
+        return result;
     }
-    else if (__ctfe)
+    else
     {
         import core.internal.traits : Unqual;
         ubyte[] tmp = ctfe_alloc(T.sizeof);
@@ -715,19 +708,20 @@ const(ubyte)[] toUbyte(T)(const ref scope T val) if (__traits(isIntegral, T) &&
         }
         return tmp;
     }
-    else
-    {
-        return (cast(const(ubyte)*)&val)[0 .. T.sizeof];
-    }
 }
 
 @trusted pure nothrow @nogc
-const(ubyte)[] toUbyte(T)(const ref scope T val) if (is(T == __vector))
+const(ubyte)[] toUbyte(T)(const ref scope T val) if (__traits(isIntegral, T) && !is(T == enum) && !is(T == __vector))
 {
     pragma(inline, true);
-    if (!__ctfe)
-        return (cast(const ubyte*) &val)[0 .. T.sizeof];
-    else static if (is(typeof(val[0]) : void))
+    return __ctfe ? toUbyte_integral_ctfe(val)
+                  : (cast(const ubyte*) &val)[0 .. T.sizeof];
+}
+
+private const(ubyte)[] toUbyte_vector_ctfe(T)(const return ref scope T val)
+{
+    pragma(inline, false);
+    static if (is(typeof(val[0]) : void))
         assert(0, "Unable to compute byte representation of " ~ T.stringof ~ " at compile time.");
     else
     {
@@ -744,19 +738,27 @@ const(ubyte)[] toUbyte(T)(const ref scope T val) if (is(T == __vector))
     }
 }
 
+@trusted pure nothrow @nogc
+const(ubyte)[] toUbyte(T)(const ref scope T val) if (is(T == __vector))
+{
+    pragma(inline, true);
+    return __ctfe ? toUbyte_vector_ctfe(val)
+                  : (cast(const ubyte*) &val)[0 .. T.sizeof];
+}
+
+private const(ubyte)[] toUbyte_enum_ctfe(T)(const return ref scope T val)
+{
+    pragma(inline, false);
+    static if (is(T V == enum)){}
+    return toUbyte(*cast(const V*) &val);
+}
+
 @trusted pure nothrow @nogc
 const(ubyte)[] toUbyte(T)(const ref return scope T val) if (is(T == enum))
 {
     pragma(inline, true);
-    if (__ctfe)
-    {
-        static if (is(T V == enum)){}
-        return toUbyte(*cast(const V*) &val);
-    }
-    else
-    {
-        return (cast(const(ubyte)*)&val)[0 .. T.sizeof];
-    }
+    return __ctfe ? toUbyte_enum_ctfe(val)
+                  : (cast(const(ubyte)*)&val)[0 .. T.sizeof];
 }
 
 nothrow pure @safe unittest
@@ -768,54 +770,50 @@ nothrow pure @safe unittest
     enum ctfe_works = (() { Month x = Month.jan; return toUbyte(x).length > 0; })();
 }
 
+private const(ubyte)[] toUbyte_delegate_ctfe(T)(const return ref scope T val)
+{
+    pragma(inline, false);
+    if (val !is null) assert(0, "Unable to compute byte representation of non-null pointer at compile time");
+    return ctfe_alloc(T.sizeof);
+}
+
 @trusted pure nothrow @nogc
 const(ubyte)[] toUbyte(T)(const ref T val) if (is(T == delegate) || is(T : V*, V) && __traits(getAliasThis, T).length == 0)
 {
     pragma(inline, true);
-    if (__ctfe)
-    {
-        if (val !is null) assert(0, "Unable to compute byte representation of non-null pointer at compile time");
-        return ctfe_alloc(T.sizeof);
-    }
-    else
+    return __ctfe ? toUbyte_delegate_ctfe(val)
+                  : (cast(const(ubyte)*)&val)[0 .. T.sizeof];
+}
+
+private const(ubyte)[] toUbyte_aggregate_ctfe(T)(const return ref scope T val)
+{
+    pragma(inline, false);
+    ubyte[] bytes = ctfe_alloc(T.sizeof);
+    foreach (key, ref cur; val.tupleof)
     {
-        return (cast(const(ubyte)*)&val)[0 .. T.sizeof];
+        static if (is(typeof(cur) EType == enum)) // Odd style is to avoid template instantiation in most cases.
+            alias CurType = OriginalType!EType;
+        else
+            alias CurType = typeof(cur);
+        static if (is(CurType == struct) || is(CurType == union) || __traits(isStaticArray, CurType) || !is(typeof(cur is null)))
+        {
+            bytes[val.tupleof[key].offsetof .. val.tupleof[key].offsetof + CurType.sizeof] = toUbyte(cur)[];
+        }
+        else
+        {
+            assert(cur is null, "Unable to compute byte representation of non-null reference field at compile time");
+            //skip, because val bytes are zeros
+        }
     }
+    return bytes;
 }
 
 @trusted pure nothrow @nogc
 const(ubyte)[] toUbyte(T)(const return ref scope T val) if (is(T == struct) || is(T == union))
 {
     pragma(inline, true);
-    if (__ctfe)
-    {
-        ubyte[] bytes = ctfe_alloc(T.sizeof);
-        foreach (key, ref cur; val.tupleof)
-        {
-            static if (is(typeof(cur) EType == enum)) // Odd style is to avoid template instantiation in most cases.
-                alias CurType = OriginalType!EType;
-            else
-                alias CurType = typeof(cur);
-            static if (is(CurType == struct) || is(CurType == union) || __traits(isStaticArray, CurType) || !is(typeof(cur is null)))
-            {
-                bytes[val.tupleof[key].offsetof .. val.tupleof[key].offsetof + CurType.sizeof] = toUbyte(cur)[];
-            }
-            else
-            {
-                assert(cur is null, "Unable to compute byte representation of non-null reference field at compile time");
-                //skip, because val bytes are zeros
-            }
-        }
-        return bytes;
-    }
-    else
-    {
-        // We're escaping a reference to `val` here because we cannot express
-        // ref return + scope, it's currently seen as ref + return scope
-        // https://issues.dlang.org/show_bug.cgi?id=22541
-        // Once fixed, the @system lambda should be removed
-        return (() @system => (cast(const(ubyte)*)&val)[0 .. T.sizeof])();
-    }
+    return __ctfe ? toUbyte_aggregate_ctfe(val)
+                  : (cast(const(ubyte)*)&val)[0 .. T.sizeof];
 }
 
 // Strips off all `enum`s from type `T`.
index 7aeb2e2d24ad1d87e89bea074ba69d4c16faeffe..f38e5cbe55ad78ba6c6a0ef9015652d82294f335 100644 (file)
@@ -7,7 +7,7 @@
  */
 module core.internal.gc.bits;
 
-import core.internal.gc.os : os_mem_map, os_mem_unmap, HaveFork;
+import core.internal.gc.os;
 
 import core.bitop;
 import core.exception : onOutOfMemoryError;
@@ -37,12 +37,12 @@ struct GCBits
     {
         if (data)
         {
-            static if (!HaveFork)
+            if (!AllocSupportsShared || !share)
                 free(data);
-            else if (share)
-                os_mem_unmap(data, nwords * data[0].sizeof);
+            else static if (AllocSupportsShared)
+                os_mem_unmap_shared(data, nwords * data[0].sizeof);
             else
-                free(data);
+                assert(false); // unreachable
             data = null;
         }
     }
@@ -50,12 +50,12 @@ struct GCBits
     void alloc(size_t nbits, bool share = false) nothrow
     {
         this.nbits = nbits;
-        static if (!HaveFork)
+        if (!AllocSupportsShared || !share)
             data = cast(typeof(data[0])*)calloc(nwords, data[0].sizeof);
-        else if (share)
-            data = cast(typeof(data[0])*)os_mem_map(nwords * data[0].sizeof, true); // Allocate as MAP_SHARED
+        else static if (AllocSupportsShared)
+            data = cast(typeof(data[0])*)os_mem_map_shared(nwords * data[0].sizeof); // Allocate as MAP_SHARED
         else
-            data = cast(typeof(data[0])*)calloc(nwords, data[0].sizeof);
+            assert(false); // unreachable
         if (!data)
             onOutOfMemoryError();
     }
index 8b81de30ab1a956dd022059ff147fd3dea65b65d..af7ea7901f2ae75bf7473eed801b2e434fa46fa5 100644 (file)
@@ -97,10 +97,6 @@ private
         // to avoid inlining - see https://issues.dlang.org/show_bug.cgi?id=13725.
         noreturn onInvalidMemoryOperationError(void* pretend_sideffect = null, string file = __FILE__, size_t line = __LINE__) @trusted pure nothrow @nogc;
         noreturn onOutOfMemoryError(void* pretend_sideffect = null, string file = __FILE__, size_t line = __LINE__) @trusted pure nothrow @nogc;
-
-        version (COLLECT_FORK)
-            version (OSX)
-                pid_t __fork() nothrow;
     }
 
     enum
@@ -1517,7 +1513,7 @@ class ConservativeGC : GC
         auto existingUsed = slice.length + offset;
 
         size_t typeInfoSize = (info.attr & BlkAttr.STRUCTFINAL) ? size_t.sizeof : 0;
-        if (__setArrayAllocLengthImpl(info, offset + newUsed, atomic, existingUsed, typeInfoSize))
+        if (__setArrayAllocLengthImpl(info, newUsed, atomic, existingUsed, typeInfoSize))
         {
             // could expand without extending
             if (!bic && !atomic)
@@ -1804,7 +1800,7 @@ struct Gcx
         }
         debug(INVARIANT) initialized = true;
         version (COLLECT_FORK)
-            shouldFork = config.fork;
+            shouldFork = AllocSupportsShared && config.fork;
 
     }
 
@@ -3191,11 +3187,7 @@ struct Gcx
         {
             fflush(null); // avoid duplicated FILE* output
         }
-        version (OSX)
-        {
-            auto pid = __fork(); // avoids calling handlers (from libc source code)
-        }
-        else version (linux)
+        version (linux)
         {
             // clone() fits better as we don't want to do anything but scanning in the child process.
             // no fork-handlers are called, so we can avoid deadlocks due to malloc locks. Probably related:
@@ -3213,11 +3205,13 @@ struct Gcx
             auto stack = stackbuf.ptr + (isStackGrowingDown ? stackbuf.length : 0);
             auto pid = clone(&wrap_delegate, stack, flags, &dg);
         }
+        else static if (__traits(compiles, _Fork))
+        {
+            auto pid = _Fork();
+        }
         else
         {
-            fork_needs_lock = false;
-            auto pid = fork();
-            fork_needs_lock = true;
+            auto pid = -1;  // fork based GC not supported
         }
         switch (pid)
         {
@@ -3493,25 +3487,34 @@ Lmark:
         // before a fork.
         // This must not happen if fork is called from the GC with the lock already held
 
-        __gshared bool fork_needs_lock = true; // racing condition with cocurrent calls of fork?
-
+        __gshared int fork_cancel_state = 0;
 
         extern(C) static void _d_gcx_atfork_prepare()
         {
-            if (instance && fork_needs_lock)
+            static if (__traits(compiles, os_unblock_gc_signals))
+                os_unblock_gc_signals();
+
+            if (instance)
+            {
                 ConservativeGC.lockNR();
+                fork_cancel_state = thread_cancelDisable();
+            }
         }
 
         extern(C) static void _d_gcx_atfork_parent()
         {
-            if (instance && fork_needs_lock)
+            if (instance)
+            {
+                thread_cancelRestore(fork_cancel_state);
                 ConservativeGC.gcLock.unlock();
+            }
         }
 
         extern(C) static void _d_gcx_atfork_child()
         {
-            if (instance && fork_needs_lock)
+            if (instance)
             {
+                thread_cancelRestore(fork_cancel_state);
                 ConservativeGC.gcLock.unlock();
 
                 // make sure the threads and event handles are reinitialized in a fork
@@ -5280,6 +5283,7 @@ unittest
 // https://issues.dlang.org/show_bug.cgi?id=19281
 debug (SENTINEL) {} else // cannot allow >= 4 GB with SENTINEL
 debug (MEMSTOMP) {} else // might take too long to actually touch the memory
+version (OnlyLowMemUnittests) {} else
 version (D_LP64) unittest
 {
     static if (__traits(compiles, os_physical_mem))
@@ -5393,8 +5397,34 @@ unittest
     }
 }
 
+// https://github.com/dlang/dmd/issues/22004
+@safe unittest
+{
+outer:
+    foreach (i; 1 .. 100)
+    {
+        foreach (j; 0 .. i)
+        {
+            int[] orig;
+            orig.length = i;
+            if (orig.capacity == orig.length)
+                // skip legitimate realloc cases
+                continue outer;
+
+            int[] slice = orig[j .. $];
+            assert(slice.capacity != 0);
+
+            auto ptr = &slice[0];
+            slice.length += 1;
+            // should not have realloc'd
+            assert(&slice[0] is ptr);
+        }
+    }
+}
+
 // https://github.com/dlang/dmd/issues/21615
 debug(SENTINEL) {} else // no additional capacity with SENTINEL
+version (OnlyLowMemUnittests) {} else
 @safe unittest
 {
     size_t numReallocations = 0;
index d5431d4ed97af8697ae9fe9f2f72ee9f09d65ea8..e9a253369e4b8c1a4710eaf6985e49d3fb6c76b5 100644 (file)
@@ -33,10 +33,21 @@ else version (Posix)
     else version (WatchOS)
         version = Darwin;
 
-    public import core.sys.posix.unistd : fork, pid_t;
-    import core.stdc.errno : ECHILD, EINTR, errno;
-    import core.sys.posix.sys.mman : MAP_ANON, MAP_FAILED, MAP_PRIVATE, MAP_SHARED, mmap, munmap, PROT_READ, PROT_WRITE;
-    import core.sys.posix.sys.wait : waitpid, WNOHANG;
+    public import core.sys.posix.unistd : pid_t;
+
+    static import core.sys.posix.unistd;
+    static if (__traits(compiles, core.sys.posix.unistd._Fork))
+        public import core.sys.posix.unistd : _Fork;
+
+    static import core.sys.posix.sys.mman;
+    static if (__traits(compiles, core.sys.posix.sys.mman.mmap))
+        import core.sys.posix.sys.mman : MAP_ANON, MAP_FAILED, MAP_PRIVATE, MAP_SHARED, mmap, munmap, PROT_READ, PROT_WRITE;
+
+    static import core.sys.posix.stdlib;
+    static if (__traits(compiles, core.sys.posix.stdlib.valloc))
+        import core.sys.posix.stdlib : valloc;
+
+    import core.stdc.stdlib : free, malloc;
 
 
     /// Possible results for the wait_pid() function.
@@ -57,6 +68,8 @@ else version (Posix)
     ChildStatus wait_pid(pid_t pid, bool block = true) nothrow @nogc
     {
         import core.exception : onForkError;
+        import core.stdc.errno : ECHILD, EINTR, errno;
+        import core.sys.posix.sys.wait : waitpid, WNOHANG;
 
         int status = void;
         pid_t waited_pid = void;
@@ -76,6 +89,13 @@ else version (Posix)
         return ChildStatus.done;
     }
 
+    version (DragonFlyBSD)
+        version = GCSignalsUnblock;
+    version (FreeBSD)
+        version = GCSignalsUnblock;
+    version (Solaris)
+        version = GCSignalsUnblock;
+
     //version = GC_Use_Alloc_MMap;
 }
 else
@@ -97,33 +117,55 @@ else static if (is(typeof(malloc)))
 else static assert(false, "No supported allocation methods available.");
 +/
 
-static if (is(typeof(VirtualAlloc))) // version (GC_Use_Alloc_Win32)
+version (CoreDdoc)
 {
-    /**
-    * Indicates if an implementation supports fork().
-    *
-    * The value shown here is just demostrative, the real value is defined based
-    * on the OS it's being compiled in.
-    * enum HaveFork = true;
-    */
-    enum HaveFork = false;
-
     /**
      * Map memory.
      */
     void *os_mem_map(size_t nbytes) nothrow @nogc
     {
-        return VirtualAlloc(null, nbytes, MEM_RESERVE | MEM_COMMIT,
-                PAGE_READWRITE);
+        return null;
     }
 
+    /**
+     * Unmap memory allocated with os_mem_map()
+     * Returns:
+     *      0       success
+     *      !=0     failure
+     */
+    int os_mem_unmap(void *base, size_t nbytes) nothrow @nogc
+    {
+        return 0;
+    }
+
+    /**
+     * Map memory that will be shared by child processes.
+     * Note: only available if the OS supports this feature. Use `AllocSupportsShared` to test.
+     */
+    void *os_mem_map_shared(size_t nbytes) nothrow @nogc
+    {
+        return null;
+    }
 
     /**
-     * Unmap memory allocated with os_mem_map().
+     * Unmap memory allocated with os_mem_map_shared()
      * Returns:
      *      0       success
      *      !=0     failure
      */
+    int os_mem_unmap_shared(void *base, size_t nbytes) nothrow @nogc
+    {
+        return 0;
+    }
+}
+else static if (is(typeof(VirtualAlloc))) // version (GC_Use_Alloc_Win32)
+{
+    void *os_mem_map(size_t nbytes) nothrow @nogc
+    {
+        return VirtualAlloc(null, nbytes, MEM_RESERVE | MEM_COMMIT,
+                PAGE_READWRITE);
+    }
+
     int os_mem_unmap(void *base, size_t nbytes) nothrow @nogc
     {
         return cast(int)(VirtualFree(base, 0, MEM_RELEASE) == 0);
@@ -131,26 +173,30 @@ static if (is(typeof(VirtualAlloc))) // version (GC_Use_Alloc_Win32)
 }
 else static if (is(typeof(mmap)))  // else version (GC_Use_Alloc_MMap)
 {
-    enum HaveFork = true;
+    void *os_mem_map(size_t nbytes) nothrow @nogc
+    {
+        void* p = mmap(null, nbytes, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
+        return (p == MAP_FAILED) ? null : p;
+    }
 
-    void *os_mem_map(size_t nbytes, bool share = false) nothrow @nogc
-    {   void *p;
+    int os_mem_unmap(void *base, size_t nbytes) nothrow @nogc
+    {
+        return munmap(base, nbytes);
+    }
 
-        auto map_f = share ? MAP_SHARED : MAP_PRIVATE;
-        p = mmap(null, nbytes, PROT_READ | PROT_WRITE, map_f | MAP_ANON, -1, 0);
+    void *os_mem_map_shared(size_t nbytes) nothrow @nogc
+    {
+        void* p = mmap(null, nbytes, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0);
         return (p == MAP_FAILED) ? null : p;
     }
 
-
-    int os_mem_unmap(void *base, size_t nbytes) nothrow @nogc
+    int os_mem_unmap_shared(void *base, size_t nbytes) nothrow @nogc
     {
         return munmap(base, nbytes);
     }
 }
 else static if (is(typeof(valloc))) // else version (GC_Use_Alloc_Valloc)
 {
-    enum HaveFork = false;
-
     void *os_mem_map(size_t nbytes) nothrow @nogc
     {
         return valloc(nbytes);
@@ -170,8 +216,6 @@ else static if (is(typeof(malloc))) // else version (GC_Use_Alloc_Malloc)
     //       to PAGESIZE alignment, there will be space for a void* at the end
     //       after PAGESIZE bytes used by the GC.
 
-    enum HaveFork = false;
-
     import core.internal.gc.impl.conservative.gc;
 
 
@@ -200,6 +244,11 @@ else
     static assert(false, "No supported allocation methods available.");
 }
 
+/**
+* Indicates if an allocation method supports sharing between processes.
+*/
+enum AllocSupportsShared = __traits(compiles, os_mem_map_shared);
+
 /**
    Check for any kind of memory pressure.
 
@@ -306,3 +355,30 @@ else version (Posix)
         return pageSize * pages;
     }
 }
+
+/**
+   The GC signals might be blocked by `fork` when the atfork prepare
+   handler is invoked. This guards us from the scenario where we are
+   waiting for a GC action in another thread to complete, and that thread
+   decides to call thread_suspendAll, then we must be able to response to
+   that request, otherwise we end up in a deadlock situation.
+ */
+version (GCSignalsUnblock)
+{
+    void os_unblock_gc_signals() nothrow @nogc
+    {
+        import core.sys.posix.signal : pthread_sigmask, sigaddset, sigemptyset, sigset_t, SIG_UNBLOCK;
+        import core.thread : thread_getGCSignals;
+
+        int suspendSignal = void, resumeSignal = void;
+        thread_getGCSignals(suspendSignal, resumeSignal);
+
+        sigset_t set;
+        sigemptyset(&set);
+        sigaddset(&set, suspendSignal);
+        sigaddset(&set, resumeSignal);
+
+        auto sigmask = pthread_sigmask(SIG_UNBLOCK, &set, null);
+        assert(sigmask == 0, "failed to unblock GC signals");
+    }
+}
index e5c6ab1f0f33a6dfa750361c5ded948cb63d1522..f9355220df756dd6f36c828603a0248f7a15df19 100644 (file)
@@ -300,12 +300,9 @@ size_t hashOf(T)(scope const T val, size_t seed) if (__traits(isScalar, T) && !i
     static if (is(T V : V*))
     {
         pragma(inline, true);
-        if (__ctfe)
-        {
-            if (val is null) return hashOf(size_t(0), seed);
-            assert(0, "Unable to calculate hash of non-null pointer at compile time");
-        }
-        return hashOf(cast(size_t) val, seed);
+        return __ctfe ? (val is null ? hashOf(size_t(0), seed) :
+                assert(0, "Unable to calculate hash of non-null pointer at compile time"))
+            : hashOf(cast(size_t) val, seed);
     }
     else static if (is(T EType == enum) && is(typeof(val[0])))
     {
@@ -584,12 +581,9 @@ if (!is(T == enum) && (is(T == struct) || is(T == union))
 size_t hashOf(T)(scope const T val, size_t seed = 0) if (!is(T == enum) && is(T == delegate))
 {
     pragma(inline, true);
-    if (__ctfe)
-    {
-        if (val is null) return hashOf(size_t(0), hashOf(size_t(0), seed));
-        assert(0, "unable to compute hash of "~T.stringof~" at compile time");
-    }
-    return hashOf(val.ptr, hashOf(cast(void*) val.funcptr, seed));
+    return __ctfe ? (val is null ? hashOf(size_t(0), hashOf(size_t(0), seed)) :
+            assert(0, "unable to compute hash of "~T.stringof~" at compile time"))
+        : hashOf(val.ptr, hashOf(cast(void*) val.funcptr, seed));
 }
 
 //address-based class hash. CTFE only if null.
@@ -599,8 +593,8 @@ if (!is(T == enum) && (is(T == interface) || is(T == class))
     && canBitwiseHash!T)
 {
     pragma(inline, true);
-    if (__ctfe) if (val is null) return 0;
-    return hashOf(cast(const void*) val);
+    return __ctfe && (val is null) ? 0
+        : hashOf(cast(const void*) val);
 }
 
 //address-based class hash. CTFE only if null.
@@ -610,8 +604,8 @@ if (!is(T == enum) && (is(T == interface) || is(T == class))
     && canBitwiseHash!T)
 {
     pragma(inline, true);
-    if (__ctfe) if (val is null) return hashOf(size_t(0), seed);
-    return hashOf(cast(const void*) val, seed);
+    return __ctfe && (val is null) ? hashOf(size_t(0), seed)
+        : hashOf(cast(const void*) val, seed);
 }
 
 //class or interface hash. CTFE depends on toHash
index 764d1e7b5868e0459556f2e305d68e7360c0d934..55bc16e5e8b7bb5456f9c4b17486c9139fbabc24 100644 (file)
@@ -113,7 +113,8 @@ static struct Entry(K, V)
 // backward compatibility conversions
 private ref compat_key(K, K2)(ref K2 key)
 {
-    pragma(inline, true);
+    static if(!(is(K2 == struct) && __traits(isNested, K2)))
+        pragma(inline, true);
     static if (is(K2 == const(char)[]) && is(K == string))
         return (ref (ref return K2 k2) @trusted => *cast(string*)&k2)(key);
     else
@@ -132,7 +133,7 @@ private void _aaMove(V)(ref V src, ref V dst) @trusted
 }
 
 // mimick behaviour of rt.aaA for initialization
-Entry!(K, V)* _newEntry(K, V)(ref K key, ref V value)
+Entry!(K, V)* _newEntry(K, V)(ref K key, auto ref V value)
 {
     static if (__traits(compiles, new Entry!(K, V)(key, value)))
     {
@@ -180,7 +181,8 @@ Entry!(K, V)* _newEntry(K, V, K2)(ref K2 key)
 
 template pure_hashOf(K)
 {
-    static if (__traits(compiles, function hash_t(scope const ref K key) pure nothrow @nogc @trusted { return hashOf(cast()key); }))
+    static if (!(is(K == struct) && __traits(isNested, K)) &&
+               __traits(compiles, function hash_t(scope const ref K key) pure nothrow @nogc @trusted { return hashOf(cast()key); }))
     {
         // avoid wrapper call in debug builds if pure nothrow @nogc is inferred
         pragma(inline, true)
@@ -198,7 +200,9 @@ template pure_hashOf(K)
 // this also breaks cyclic inference on recursive data types
 template pure_keyEqual(K1, K2 = K1)
 {
-    static if (__traits(compiles, function bool(ref const K1 k1, ref const K2 k2) pure nothrow @nogc @trusted { return cast()k1 == cast()k2; }))
+    static if (!(is(K1 == struct) && __traits(isNested, K1)) &&
+               !(is(K2 == struct) && __traits(isNested, K2)) &&
+               __traits(compiles, function bool(ref const K1 k1, ref const K2 k2) pure nothrow @nogc @trusted { return cast()k1 == cast()k2; }))
     {
         // avoid wrapper call in debug builds if pure nothrow @nogc is inferred
         pragma(inline, true)
@@ -475,9 +479,11 @@ size_t _d_aaLen(K, V)(inout V[K] a)
 V* _d_aaGetY(K, V, T : V1[K1], K1, V1, K2)(auto ref scope T aa, auto ref K2 key, out bool found)
 {
     ref aax = cast(V[K])cast(V1[K1])aa; // remove outer const from T
-    return _aaGetX!(K, V)(aax, key, found);
+    return _aaGetX!(K, V, K2)(aax, key, found, _noV2());
 }
 
+private struct _noV2 {}
+
 /******************************
  * Lookup key in aa.
  * Called only from implementation of require, update and _d_aaGetY
@@ -485,12 +491,13 @@ V* _d_aaGetY(K, V, T : V1[K1], K1, V1, K2)(auto ref scope T aa, auto ref K2 key,
  *      a = associative array
  *      key = reference to the key value
  *      found = true if the value was found
+ *      v2 = if key not found, init new value to this (except if type _noV2)
  * Returns:
  *      if key was in the aa, a mutable pointer to the existing value.
  *      If key was not in the aa, a mutable pointer to newly inserted value which
- *      is set to V.init
+ *      is set to v2 or is zero-initialized
  */
-V* _aaGetX(K, V, K2)(auto ref scope V[K] a, auto ref K2 key, out bool found)
+V* _aaGetX(K, V, K2, V2)(auto ref scope V[K] a, auto ref K2 key, out bool found, lazy V2 v2)
 {
     ref aa = _refAA!(K, V)(a);
 
@@ -513,21 +520,26 @@ V* _aaGetX(K, V, K2)(auto ref scope V[K] a, auto ref K2 key, out bool found)
     }
 
     auto pi = aa.findSlotInsert(hash);
-    if (aa.buckets[pi].deleted)
-        --aa.deleted;
     // check load factor and possibly grow
-    else if (++aa.used * GROW_DEN > aa.dim * GROW_NUM)
+    if (!aa.buckets[pi].deleted && (aa.used + 1) * GROW_DEN > aa.dim * GROW_NUM)
     {
         aa.grow();
         pi = aa.findSlotInsert(hash);
         assert(aa.buckets[pi].empty);
     }
 
-    // update search cache and allocate entry
-    aa.firstUsed = min(aa.firstUsed, cast(uint)pi);
+    // allocate entry and update search cache (if not throwing in _newEntry)
     ref p = aa.buckets[pi];
+    static if (is(V2 == _noV2))
+        p.entry = _newEntry!(K, V)(key2);
+    else
+        p.entry = _newEntry!(K, V)(key2, v2);
+    if (p.deleted)
+        --aa.deleted;
+    else
+        aa.used++;
     p.hash = hash;
-    p.entry = _newEntry!(K, V)(key2);
+    aa.firstUsed = min(aa.firstUsed, cast(uint)pi);
     return &p.entry.value;
 }
 
index 8938a445babd02423914f6d296fc7f14f8962c1c..8e789e70510cc47440e14c377573ed496638684a 100644 (file)
@@ -60,21 +60,10 @@ else version (FreeBSD)
 
     static if (__FreeBSD_version >= 1400000)
     {
+        // FreeBSD changed qsort_r function signature to POSIX in FreeBSD 14.0
         alias extern (C) int function(scope const void*, scope const void*, scope void*) Cmp;
         extern (C) void qsort_r(scope void* base, size_t nmemb, size_t size, Cmp cmp, scope void* thunk);
 
-        // https://cgit.freebsd.org/src/tree/include/stdlib.h?h=stable/14#n350
-        pragma(mangle, "qsort_r@FBSD_1.0")
-        private extern (C) void __qsort_r_compat(scope void* base, size_t nmemb, size_t size, scope void* thunk, OldCmp cmp);
-        alias extern (C) int function(scope void*, scope const void*, scope const void*) OldCmp;
-
-        deprecated("In FreeBSD 14, qsort_r's signature was fixed to match POSIX. This extern(D) overload has been " ~
-                   "provided to avoid breaking code, but code should be updated to use the POSIX version.")
-        extern (D) void qsort_r(scope void* base, size_t nmemb, size_t size, scope void* thunk, OldCmp cmp)
-        {
-            __qsort_r_compat(base, nmemb, size, thunk, cmp);
-        }
-
         extern (C) void[] _adSort(return scope void[] a, TypeInfo ti)
         {
             extern (C) int cmp(scope const void* p1, scope const void* p2, scope void* ti)
diff --git a/libphobos/libdruntime/core/internal/vararg/gnu.d b/libphobos/libdruntime/core/internal/vararg/gnu.d
new file mode 100644 (file)
index 0000000..be3d699
--- /dev/null
@@ -0,0 +1,52 @@
+/**
+ * Varargs implementation for the GNU compilers (Gnu D Compiler)
+ * Used by core.stdc.stdarg and core.vararg.
+ *
+ * Reference: https://github.com/ARM-software/abi-aa/blob/master/aapcs64/aapcs64.rst#appendix-variable-argument-lists
+ *
+ * Copyright: Copyright D Language Foundation 2025
+ * License:   $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
+ * Authors:   Various
+ * Source: $(DRUNTIMESRC core/internal/vararg/gnu.d)
+ */
+
+module core.internal.vararg.gnu;
+
+version (GNU):
+
+import gcc.builtins;
+
+@nogc:
+nothrow:
+
+/**
+ * The argument pointer type.
+ */
+alias va_list = __gnuc_va_list;
+alias __gnuc_va_list = __builtin_va_list;
+
+/**
+ * Initialize ap.
+ * parmn should be the last named parameter.
+ */
+void va_start(T)(out va_list ap, ref T parmn);
+
+/**
+ * Retrieve and return the next value that is of type T.
+ */
+T va_arg(T)(ref va_list ap); // intrinsic
+
+/**
+ * Retrieve and store in parmn the next value that is of type T.
+ */
+void va_arg(T)(ref va_list ap, ref T parmn); // intrinsic
+
+/**
+ * End use of ap.
+ */
+alias va_end = __builtin_va_end;
+
+/**
+ * Make a copy of ap.
+ */
+alias va_copy = __builtin_va_copy;
index 49512ab14b476e058ca937156bcdb58775da3d52..ad98b2f2c562a991b7856fe6e8ba2736f8974b27 100644 (file)
@@ -2600,11 +2600,13 @@ pure nothrow @system unittest
 }
 
 // wipes source after moving
-pragma(inline, true)
 private void wipe(T, Init...)(return scope ref T source, ref const scope Init initializer) @trusted
 if (!Init.length ||
     ((Init.length == 1) && (is(immutable T == immutable Init[0]))))
 {
+    static if (!is(T == struct) || !__traits(isNested, T))
+        pragma(inline, true);
+
     static if (__traits(isStaticArray, T) && hasContextPointers!T)
     {
         for (auto i = 0; i < T.length; i++)
@@ -2793,7 +2795,7 @@ T _d_newclassTTrace(T)(string file = __FILE__, int line = __LINE__, string funcn
     version (D_TypeInfo)
     {
         import core.internal.array.utils : TraceHook, gcStatsPure, accumulatePure;
-        mixin(TraceHook!(T.stringof, "_d_newclassT"));
+        mixin(TraceHook!("T", "_d_newclassT"));
 
         return _d_newclassT!T();
     }
@@ -2999,7 +3001,7 @@ version (D_ProfileGC)
             }
 
             import core.internal.array.utils : TraceHook, gcStatsPure, accumulatePure;
-            mixin(TraceHook!(T.stringof, "_d_newitemT"));
+            mixin(TraceHook!("T", "_d_newitemT"));
 
             return _d_newitemT!T();
         }
index 357d75e9589b0c1069b397b075fbf1b195c1dcf3..50b45980dc3324ce0ad8f2912a28d538b5d177ff 100644 (file)
@@ -29,32 +29,70 @@ nothrow:
 @nogc:
 
 ///
-struct lconv
+version (Windows)
 {
-    char* decimal_point;
-    char* thousands_sep;
-    char* grouping;
-    char* int_curr_symbol;
-    char* currency_symbol;
-    char* mon_decimal_point;
-    char* mon_thousands_sep;
-    char* mon_grouping;
-    char* positive_sign;
-    char* negative_sign;
-    byte  int_frac_digits;
-    byte  frac_digits;
-    byte  p_cs_precedes;
-    byte  p_sep_by_space;
-    byte  n_cs_precedes;
-    byte  n_sep_by_space;
-    byte  p_sign_posn;
-    byte  n_sign_posn;
-    byte  int_p_cs_precedes;
-    byte  int_p_sep_by_space;
-    byte  int_n_cs_precedes;
-    byte  int_n_sep_by_space;
-    byte  int_p_sign_posn;
-    byte  int_n_sign_posn;
+    import core.stdc.wchar_ : wchar_t;
+
+    // ...Windows Kits\10\Include\10.0.14393.0\ucrt\locale.h
+    struct lconv
+    {
+        char*    decimal_point;
+        char*    thousands_sep;
+        char*    grouping;
+        char*    int_curr_symbol;
+        char*    currency_symbol;
+        char*    mon_decimal_point;
+        char*    mon_thousands_sep;
+        char*    mon_grouping;
+        char*    positive_sign;
+        char*    negative_sign;
+        char     int_frac_digits = 0; // " = 0" For zero init
+        char     frac_digits = 0;
+        char     p_cs_precedes = 0;
+        char     p_sep_by_space = 0;
+        char     n_cs_precedes = 0;
+        char     n_sep_by_space = 0;
+        char     p_sign_posn = 0;
+        char     n_sign_posn = 0;
+        wchar_t* _W_decimal_point;
+        wchar_t* _W_thousands_sep;
+        wchar_t* _W_int_curr_symbol;
+        wchar_t* _W_currency_symbol;
+        wchar_t* _W_mon_decimal_point;
+        wchar_t* _W_mon_thousands_sep;
+        wchar_t* _W_positive_sign;
+        wchar_t* _W_negative_sign;
+    }
+}
+else
+{
+    struct lconv
+    {
+        char* decimal_point;
+        char* thousands_sep;
+        char* grouping;
+        char* int_curr_symbol;
+        char* currency_symbol;
+        char* mon_decimal_point;
+        char* mon_thousands_sep;
+        char* mon_grouping;
+        char* positive_sign;
+        char* negative_sign;
+        byte  int_frac_digits;
+        byte  frac_digits;
+        byte  p_cs_precedes;
+        byte  p_sep_by_space;
+        byte  n_cs_precedes;
+        byte  n_sep_by_space;
+        byte  p_sign_posn;
+        byte  n_sign_posn;
+        byte  int_p_cs_precedes;
+        byte  int_p_sep_by_space;
+        byte  int_n_cs_precedes;
+        byte  int_n_sep_by_space;
+        byte  int_p_sign_posn;
+        byte  int_n_sign_posn;
+    }
 }
 
 version (CRuntime_Glibc)
index 220b27c4cf8e69bb0a20f5c782d5c8dc0093c911..82383ea72b992542a6f5f659adbad16776037a11 100644 (file)
@@ -15,29 +15,19 @@ module core.stdc.stdarg;
 @nogc:
 nothrow:
 
+version (GNU)
+    public import core.internal.vararg.gnu;
+else:
+
 version (X86_64)
 {
     version (Windows) { /* different ABI */ }
     else version = SysV_x64;
 }
 
-version (GNU)
-{
-    import gcc.builtins;
-}
-else version (SysV_x64)
+version (SysV_x64)
 {
     static import core.internal.vararg.sysv_x64;
-
-    version (DigitalMars)
-    {
-        align(16) struct __va_argsave_t
-        {
-            size_t[6] regs;   // RDI,RSI,RDX,RCX,R8,R9
-            real[8] fpregs;   // XMM0..XMM7
-            __va_list va;
-        }
-    }
 }
 
 version (ARM)     version = ARM_Any;
@@ -49,11 +39,7 @@ version (PPC64)   version = PPC_Any;
 version (RISCV32) version = RISCV_Any;
 version (RISCV64) version = RISCV_Any;
 
-version (GNU)
-{
-    // Uses gcc.builtins
-}
-else version (ARM_Any)
+version (ARM_Any)
 {
     // Darwin uses a simpler varargs implementation
     version (OSX) {}
@@ -69,7 +55,7 @@ else version (ARM_Any)
     else version (AArch64)
     {
         version = AAPCS64;
-        static import core.internal.vararg.aarch64;
+        static import core.internal.vararg.aapcs64;
     }
 }
 
@@ -107,15 +93,9 @@ version (BigEndian)
 /**
  * The argument pointer type.
  */
-version (GNU)
+version (SysV_x64)
 {
-    alias va_list = __gnuc_va_list;
-    alias __gnuc_va_list = __builtin_va_list;
-}
-else version (SysV_x64)
-{
-    alias va_list = core.internal.vararg.sysv_x64.va_list;
-    public import core.internal.vararg.sysv_x64 : __va_list, __va_list_tag;
+    public import core.internal.vararg.sysv_x64 : va_list, __va_list, __va_list_tag;
 }
 else version (AAPCS32)
 {
@@ -129,9 +109,9 @@ else version (AAPCS32)
 }
 else version (AAPCS64)
 {
-    alias va_list = core.internal.vararg.aarch64.va_list;
+    alias va_list = core.internal.vararg.aapcs64.va_list;
     version (DigitalMars)
-        public import core.internal.vararg.aarch64 : __va_list, __va_list_tag;
+        public import core.internal.vararg.aapcs64 : __va_list, __va_list_tag;
 }
 else version (RISCV_Any)
 {
@@ -149,11 +129,7 @@ else
  * Initialize ap.
  * parmn should be the last named parameter.
  */
-version (GNU)
-{
-    void va_start(T)(out va_list ap, ref T parmn);
-}
-else version (LDC)
+version (LDC)
 {
     pragma(LDC_va_start)
     void va_start(T)(out va_list ap, ref T parmn) @nogc;
@@ -177,9 +153,6 @@ else version (DigitalMars)
 /**
  * Retrieve and return the next value that is of type T.
  */
-version (GNU)
-    T va_arg(T)(ref va_list ap); // intrinsic
-else
 T va_arg(T)(ref va_list ap)
 {
     version (X86)
@@ -229,7 +202,7 @@ T va_arg(T)(ref va_list ap)
     }
     else version (AAPCS64)
     {
-        return core.internal.vararg.aarch64.va_arg!T(ap);
+        return core.internal.vararg.aapcs64.va_arg!T(ap);
     }
     else version (ARM_Any)
     {
@@ -295,9 +268,6 @@ T va_arg(T)(ref va_list ap)
 /**
  * Retrieve and store in parmn the next value that is of type T.
  */
-version (GNU)
-    void va_arg(T)(ref va_list ap, ref T parmn); // intrinsic
-else
 void va_arg(T)(ref va_list ap, ref T parmn)
 {
     parmn = va_arg!T(ap);
@@ -307,11 +277,7 @@ void va_arg(T)(ref va_list ap, ref T parmn)
 /**
  * End use of ap.
  */
-version (GNU)
-{
-    alias va_end = __builtin_va_end;
-}
-else version (LDC)
+version (LDC)
 {
     pragma(LDC_va_end)
     void va_end(va_list ap);
@@ -325,11 +291,7 @@ else version (DigitalMars)
 /**
  * Make a copy of ap.
  */
-version (GNU)
-{
-    alias va_copy = __builtin_va_copy;
-}
-else version (LDC)
+version (LDC)
 {
     pragma(LDC_va_copy)
     void va_copy(out va_list dest, va_list src);
index 51968af8c5fbc3dc2b41e10e6a755860214c519f..966d29d808405ef777e095fe36e28ef715ed726c 100644 (file)
@@ -165,27 +165,23 @@ void atomic_flag_clear_explicit_impl()(atomic_flag* obj, memory_order order)
 {
     assert(obj !is null);
 
-    final switch (order)
-    {
-        case memory_order.memory_order_relaxed:
-            atomicStore!(memory_order.memory_order_relaxed)(&obj.b, false);
-            break;
+    // use series of ternary expressions instead of switch to help the dmd inliner
+    order == memory_order.memory_order_relaxed ?
+        atomicStore!(memory_order.memory_order_relaxed)(&obj.b, false) :
 
-        case memory_order.memory_order_acquire:
-        case memory_order.memory_order_acq_rel:
-            // Ideally this would error at compile time but alas it is not an intrinsic.
-            // Note: this is not a valid memory order for this operation.
-            atomicStore!(memory_order.memory_order_seq_cst)(&obj.b, false);
-            break;
+    order == memory_order.memory_order_acquire ||
+    order == memory_order.memory_order_acq_rel ?
+        // Ideally this would error at compile time but alas it is not an intrinsic.
+        // Note: this is not a valid memory order for this operation.
+        atomicStore!(memory_order.memory_order_seq_cst)(&obj.b, false) :
 
-        case memory_order.memory_order_release:
-            atomicStore!(memory_order.memory_order_release)(&obj.b, false);
-            break;
+    order == memory_order.memory_order_release ?
+        atomicStore!(memory_order.memory_order_release)(&obj.b, false) :
 
-        case memory_order.memory_order_seq_cst:
-            atomicStore(&obj.b, false);
-            break;
-    }
+    order == memory_order.memory_order_seq_cst ?
+        atomicStore(&obj.b, false) :
+
+    cast(void) assert(0);
 }
 
 ///
@@ -210,25 +206,26 @@ bool atomic_flag_test_and_set_explicit_impl()(atomic_flag* obj, memory_order ord
 {
     assert(obj !is null);
 
-    final switch (order)
-    {
-        case memory_order.memory_order_relaxed:
-            return atomicExchange!(memory_order.memory_order_relaxed)(&obj.b, true);
+    // use series of ternary expressions instead of switch to help the dmd inliner
+    return
+        order == memory_order.memory_order_relaxed ?
+            atomicExchange!(memory_order.memory_order_relaxed)(&obj.b, true) :
 
-        case memory_order.memory_order_acquire:
+        order == memory_order.memory_order_acquire ?
             // Ideally this would error at compile time but alas it is not an intrinsic.
             // Note: this is not a valid memory order for this operation.
-            return atomicExchange!(memory_order.memory_order_seq_cst)(&obj.b, true);
+            atomicExchange!(memory_order.memory_order_seq_cst)(&obj.b, true) :
 
-        case memory_order.memory_order_release:
-            return atomicExchange!(memory_order.memory_order_release)(&obj.b, true);
+         order == memory_order.memory_order_release ?
+            atomicExchange!(memory_order.memory_order_release)(&obj.b, true) :
 
-        case memory_order.memory_order_acq_rel:
-            return atomicExchange!(memory_order.memory_order_acq_rel)(&obj.b, true);
+         order == memory_order.memory_order_acq_rel ?
+            atomicExchange!(memory_order.memory_order_acq_rel)(&obj.b, true) :
 
-        case memory_order.memory_order_seq_cst:
-            return atomicExchange(&obj.b, true);
-    }
+         order == memory_order.memory_order_seq_cst ?
+            atomicExchange(&obj.b, true) :
+
+         assert(0);
 }
 
 ///
@@ -271,28 +268,24 @@ A kill_dependency(A)(A y) @trusted
 pragma(inline, true)
 void atomic_signal_fence_impl()(memory_order order)
 {
-    final switch (order)
-    {
-        case memory_order.memory_order_relaxed:
-            // This is a no-op operation for relaxed memory orders.
-            break;
+    // use series of ternary expressions instead of switch to help the dmd inliner
+    order == memory_order.memory_order_relaxed ?
+        // This is a no-op operation for relaxed memory orders.
+        cast(void)0 :
 
-        case memory_order.memory_order_acquire:
-            atomicSignalFence!(memory_order.memory_order_acquire);
-            break;
+    order == memory_order.memory_order_acquire ?
+        atomicSignalFence!(memory_order.memory_order_acquire) :
 
-        case memory_order.memory_order_release:
-            atomicSignalFence!(memory_order.memory_order_release);
-            break;
+    order == memory_order.memory_order_release ?
+        atomicSignalFence!(memory_order.memory_order_release) :
 
-        case memory_order.memory_order_acq_rel:
-            atomicSignalFence!(memory_order.memory_order_acq_rel);
-            break;
+    order == memory_order.memory_order_acq_rel ?
+        atomicSignalFence!(memory_order.memory_order_acq_rel) :
 
-        case memory_order.memory_order_seq_cst:
-            atomicSignalFence!(memory_order.memory_order_seq_cst);
-            break;
-    }
+    order == memory_order.memory_order_seq_cst ?
+        atomicSignalFence!(memory_order.memory_order_seq_cst) :
+
+    cast(void) assert(0);
 }
 
 ///
@@ -305,28 +298,24 @@ unittest
 pragma(inline, true)
 void atomic_thread_fence_impl()(memory_order order)
 {
-    final switch (order)
-    {
-        case memory_order.memory_order_relaxed:
-            // This is a no-op operation for relaxed memory orders.
-            break;
+    // use series of ternary expressions instead of switch to help the dmd inliner
+    order == memory_order.memory_order_relaxed ?
+        // This is a no-op operation for relaxed memory orders.
+        cast(void)0 :
 
-        case memory_order.memory_order_acquire:
-            atomicFence!(memory_order.memory_order_acquire);
-            break;
+    order == memory_order.memory_order_acquire ?
+        atomicFence!(memory_order.memory_order_acquire) :
 
-        case memory_order.memory_order_release:
-            atomicFence!(memory_order.memory_order_release);
-            break;
+    order == memory_order.memory_order_release ?
+        atomicFence!(memory_order.memory_order_release) :
 
-        case memory_order.memory_order_acq_rel:
-            atomicFence!(memory_order.memory_order_acq_rel);
-            break;
+    order == memory_order.memory_order_acq_rel ?
+        atomicFence!(memory_order.memory_order_acq_rel) :
 
-        case memory_order.memory_order_seq_cst:
-            atomicFence!(memory_order.memory_order_seq_cst);
-            break;
-    }
+    order == memory_order.memory_order_seq_cst ?
+        atomicFence!(memory_order.memory_order_seq_cst) :
+
+    cast(void) assert(0);
 }
 
 ///
@@ -437,27 +426,23 @@ void atomic_store_explicit_impl(A, C)(shared(A)* obj, C desired, memory_order or
 {
     assert(obj !is null);
 
-    final switch (order)
-    {
-        case memory_order.memory_order_relaxed:
-            atomicStore!(memory_order.memory_order_relaxed)(cast(A*)obj, cast(A)desired);
-            break;
+    // use series of ternary expressions instead of switch to help the dmd inliner
+    order == memory_order.memory_order_relaxed ?
+        atomicStore!(memory_order.memory_order_relaxed)(cast(A*)obj, cast(A)desired) :
 
-        case memory_order.memory_order_acquire:
-        case memory_order.memory_order_acq_rel:
-            // Ideally this would error at compile time but alas it is not an intrinsic.
-            // Note: this is not a valid memory order for this operation.
-            atomicStore!(memory_order.memory_order_release)(cast(A*)obj, cast(A)desired);
-            break;
+    order == memory_order.memory_order_acquire ||
+    order == memory_order.memory_order_acq_rel ?
+        // Ideally this would error at compile time but alas it is not an intrinsic.
+        // Note: this is not a valid memory order for this operation.
+        atomicStore!(memory_order.memory_order_release)(cast(A*)obj, cast(A)desired) :
 
-        case memory_order.memory_order_release:
-            atomicStore!(memory_order.memory_order_release)(cast(A*)obj, cast(A)desired);
-            break;
+    order == memory_order.memory_order_release ?
+        atomicStore!(memory_order.memory_order_release)(cast(A*)obj, cast(A)desired) :
 
-        case memory_order.memory_order_seq_cst:
-            atomicStore!(memory_order.memory_order_seq_cst)(cast(A*)obj, cast(A)desired);
-            break;
-    }
+    order == memory_order.memory_order_seq_cst ?
+        atomicStore!(memory_order.memory_order_seq_cst)(cast(A*)obj, cast(A)desired) :
+
+    cast(void) assert(0);
 }
 
 ///
@@ -501,23 +486,24 @@ A atomic_load_explicit_impl(A)(const shared(A)* obj, memory_order order) @truste
 {
     assert(obj !is null);
 
-    final switch (order)
-    {
-        case memory_order.memory_order_relaxed:
-            return atomicLoad!(memory_order.memory_order_relaxed)(cast(A*)obj);
+    // use series of ternary expressions instead of switch to help the dmd inliner
+    return
+        order == memory_order.memory_order_relaxed ?
+            atomicLoad!(memory_order.memory_order_relaxed)(cast(A*)obj) :
 
-        case memory_order.memory_order_acquire:
-            return atomicLoad!(memory_order.memory_order_acquire)(cast(A*)obj);
+        order == memory_order.memory_order_acquire ?
+            atomicLoad!(memory_order.memory_order_acquire)(cast(A*)obj) :
 
-        case memory_order.memory_order_release:
-        case memory_order.memory_order_acq_rel:
+        order == memory_order.memory_order_release ||
+        order == memory_order.memory_order_acq_rel ?
             // Ideally this would error at compile time but alas it is not an intrinsic.
             // Note: this is not a valid memory order for this operation.
-            return atomicLoad!(memory_order.memory_order_acquire)(cast(A*)obj);
+            atomicLoad!(memory_order.memory_order_acquire)(cast(A*)obj) :
 
-        case memory_order.memory_order_seq_cst:
-            return atomicLoad!(memory_order.memory_order_seq_cst)(cast(A*)obj);
-    }
+        order == memory_order.memory_order_seq_cst ?
+            atomicLoad!(memory_order.memory_order_seq_cst)(cast(A*)obj) :
+
+        assert(0);
 }
 
 ///
@@ -554,25 +540,26 @@ A atomic_exchange_explicit_impl(A, C)(shared(A)* obj, C desired, memory_order or
 {
     assert(obj !is null);
 
-    final switch (order)
-    {
-        case memory_order.memory_order_relaxed:
-            return atomicExchange!(memory_order.memory_order_relaxed)(cast(A*)obj, cast(A)desired);
+    // use series of ternary expressions instead of switch to help the dmd inliner
+    return
+        order == memory_order.memory_order_relaxed ?
+            atomicExchange!(memory_order.memory_order_relaxed)(cast(A*)obj, cast(A)desired) :
 
-        case memory_order.memory_order_acquire:
+        order == memory_order.memory_order_acquire ?
             // Ideally this would error at compile time but alas it is not an intrinsic.
             // Note: this is not a valid memory order for this operation.
-            return atomicExchange!(memory_order.memory_order_seq_cst)(cast(A*)obj, cast(A)desired);
+            atomicExchange!(memory_order.memory_order_seq_cst)(cast(A*)obj, cast(A)desired) :
 
-        case memory_order.memory_order_release:
-            return atomicExchange!(memory_order.memory_order_release)(cast(A*)obj, cast(A)desired);
+        order == memory_order.memory_order_release ?
+            atomicExchange!(memory_order.memory_order_release)(cast(A*)obj, cast(A)desired) :
 
-        case memory_order.memory_order_acq_rel:
-            return atomicExchange!(memory_order.memory_order_acq_rel)(cast(A*)obj, cast(A)desired);
+        order == memory_order.memory_order_acq_rel ?
+            atomicExchange!(memory_order.memory_order_acq_rel)(cast(A*)obj, cast(A)desired) :
 
-        case memory_order.memory_order_seq_cst:
-            return atomicExchange!(memory_order.memory_order_seq_cst)(cast(A*)obj, cast(A)desired);
-    }
+        order == memory_order.memory_order_seq_cst ?
+            atomicExchange!(memory_order.memory_order_seq_cst)(cast(A*)obj, cast(A)desired) :
+
+        assert(0);
 }
 
 ///
@@ -641,19 +628,19 @@ bool atomic_compare_exchange_strong_explicit_impl(A, B, C)(shared(A)* obj, B* ex
     assert(obj !is null);
     // NOTE: To not have to deal with all invalid cases, the failure model is ignored for now.
 
-    final switch(succ)
-    {
-        case memory_order.memory_order_relaxed:
-            return atomicCompareExchangeStrong!(memory_order.memory_order_relaxed, memory_order.memory_order_relaxed)(cast(B*)obj, expected, cast(B)desired);
-        case memory_order.memory_order_acquire:
-            return atomicCompareExchangeStrong!(memory_order.memory_order_acquire, memory_order.memory_order_relaxed)(cast(B*)obj, expected, cast(B)desired);
-        case memory_order.memory_order_release:
-            return atomicCompareExchangeStrong!(memory_order.memory_order_release, memory_order.memory_order_relaxed)(cast(B*)obj, expected, cast(B)desired);
-        case memory_order.memory_order_acq_rel:
-            return atomicCompareExchangeStrong!(memory_order.memory_order_acq_rel, memory_order.memory_order_relaxed)(cast(B*)obj, expected, cast(B)desired);
-        case memory_order.memory_order_seq_cst:
-            return atomicCompareExchangeStrong!(memory_order.memory_order_seq_cst, memory_order.memory_order_relaxed)(cast(B*)obj, expected, cast(B)desired);
-    }
+    // use series of ternary expressions instead of switch to help the dmd inliner
+    return
+        succ == memory_order.memory_order_relaxed ?
+            atomicCompareExchangeStrong!(memory_order.memory_order_relaxed, memory_order.memory_order_relaxed)(cast(B*)obj, expected, cast(B)desired) :
+        succ == memory_order.memory_order_acquire ?
+            atomicCompareExchangeStrong!(memory_order.memory_order_acquire, memory_order.memory_order_relaxed)(cast(B*)obj, expected, cast(B)desired) :
+        succ == memory_order.memory_order_release ?
+            atomicCompareExchangeStrong!(memory_order.memory_order_release, memory_order.memory_order_relaxed)(cast(B*)obj, expected, cast(B)desired) :
+        succ == memory_order.memory_order_acq_rel ?
+            atomicCompareExchangeStrong!(memory_order.memory_order_acq_rel, memory_order.memory_order_relaxed)(cast(B*)obj, expected, cast(B)desired) :
+        succ == memory_order.memory_order_seq_cst ?
+            atomicCompareExchangeStrong!(memory_order.memory_order_seq_cst, memory_order.memory_order_relaxed)(cast(B*)obj, expected, cast(B)desired) :
+        assert(0);
 }
 
 ///
@@ -689,19 +676,19 @@ bool atomic_compare_exchange_weak_explicit_impl(A, B, C)(shared(A)* obj, B* expe
     assert(obj !is null);
     // NOTE: To not have to deal with all invalid cases, the failure model is ignored for now.
 
-    final switch(succ)
-    {
-        case memory_order.memory_order_relaxed:
-            return atomicCompareExchangeWeak!(memory_order.memory_order_relaxed, memory_order.memory_order_relaxed)(cast(B*)obj, expected, cast(B)desired);
-        case memory_order.memory_order_acquire:
-            return atomicCompareExchangeWeak!(memory_order.memory_order_acquire, memory_order.memory_order_relaxed)(cast(B*)obj, expected, cast(B)desired);
-        case memory_order.memory_order_release:
-            return atomicCompareExchangeWeak!(memory_order.memory_order_release, memory_order.memory_order_relaxed)(cast(B*)obj, expected, cast(B)desired);
-        case memory_order.memory_order_acq_rel:
-            return atomicCompareExchangeWeak!(memory_order.memory_order_acq_rel, memory_order.memory_order_relaxed)(cast(B*)obj, expected, cast(B)desired);
-        case memory_order.memory_order_seq_cst:
-            return atomicCompareExchangeWeak!(memory_order.memory_order_seq_cst, memory_order.memory_order_relaxed)(cast(B*)obj, expected, cast(B)desired);
-    }
+    // use series of ternary expressions instead of switch to help the dmd inliner
+    return
+        succ == memory_order.memory_order_relaxed ?
+            atomicCompareExchangeWeak!(memory_order.memory_order_relaxed, memory_order.memory_order_relaxed)(cast(B*)obj, expected, cast(B)desired) :
+        succ == memory_order.memory_order_acquire ?
+            atomicCompareExchangeWeak!(memory_order.memory_order_acquire, memory_order.memory_order_relaxed)(cast(B*)obj, expected, cast(B)desired) :
+        succ == memory_order.memory_order_release ?
+            atomicCompareExchangeWeak!(memory_order.memory_order_release, memory_order.memory_order_relaxed)(cast(B*)obj, expected, cast(B)desired) :
+        succ == memory_order.memory_order_acq_rel ?
+            atomicCompareExchangeWeak!(memory_order.memory_order_acq_rel, memory_order.memory_order_relaxed)(cast(B*)obj, expected, cast(B)desired) :
+        succ == memory_order.memory_order_seq_cst ?
+            atomicCompareExchangeWeak!(memory_order.memory_order_seq_cst, memory_order.memory_order_relaxed)(cast(B*)obj, expected, cast(B)desired) :
+        assert(0);
 }
 
 ///
@@ -774,19 +761,19 @@ A atomic_fetch_add_explicit_impl(A, M)(shared(A)* obj, M arg, memory_order order
 {
     assert(obj !is null);
 
-    final switch(order)
-    {
-        case memory_order.memory_order_relaxed:
-            return atomic_fetch_op!(memory_order.memory_order_relaxed, "+=")(cast(A*)obj, arg);
-        case memory_order.memory_order_acquire:
-            return atomic_fetch_op!(memory_order.memory_order_acquire, "+=")(cast(A*)obj, arg);
-        case memory_order.memory_order_release:
-            return atomic_fetch_op!(memory_order.memory_order_release, "+=")(cast(A*)obj, arg);
-        case memory_order.memory_order_acq_rel:
-            return atomic_fetch_op!(memory_order.memory_order_acq_rel, "+=")(cast(A*)obj, arg);
-        case memory_order.memory_order_seq_cst:
-            return atomic_fetch_op!(memory_order.memory_order_seq_cst, "+=")(cast(A*)obj, arg);
-    }
+    // use series of ternary expressions instead of switch to help the dmd inliner
+    return
+        order == memory_order.memory_order_relaxed ?
+            atomic_fetch_op!(memory_order.memory_order_relaxed, "+=")(cast(A*)obj, arg) :
+        order == memory_order.memory_order_acquire ?
+            atomic_fetch_op!(memory_order.memory_order_acquire, "+=")(cast(A*)obj, arg) :
+        order == memory_order.memory_order_release ?
+            atomic_fetch_op!(memory_order.memory_order_release, "+=")(cast(A*)obj, arg) :
+        order == memory_order.memory_order_acq_rel ?
+            atomic_fetch_op!(memory_order.memory_order_acq_rel, "+=")(cast(A*)obj, arg) :
+        order == memory_order.memory_order_seq_cst ?
+            atomic_fetch_op!(memory_order.memory_order_seq_cst, "+=")(cast(A*)obj, arg) :
+        assert(0);
 }
 
 ///
@@ -807,19 +794,18 @@ A atomic_fetch_sub_explicit_impl(A, M)(shared(A)* obj, M arg, memory_order order
 {
     assert(obj !is null);
 
-    final switch(order)
-    {
-        case memory_order.memory_order_relaxed:
-            return atomic_fetch_op!(memory_order.memory_order_relaxed, "-=")(cast(A*)obj, arg);
-        case memory_order.memory_order_acquire:
-            return atomic_fetch_op!(memory_order.memory_order_acquire, "-=")(cast(A*)obj, arg);
-        case memory_order.memory_order_release:
-            return atomic_fetch_op!(memory_order.memory_order_release, "-=")(cast(A*)obj, arg);
-        case memory_order.memory_order_acq_rel:
-            return atomic_fetch_op!(memory_order.memory_order_acq_rel, "-=")(cast(A*)obj, arg);
-        case memory_order.memory_order_seq_cst:
-            return atomic_fetch_op!(memory_order.memory_order_seq_cst, "-=")(cast(A*)obj, arg);
-    }
+    return
+        order == memory_order.memory_order_relaxed ?
+            atomic_fetch_op!(memory_order.memory_order_relaxed, "-=")(cast(A*)obj, arg) :
+        order == memory_order.memory_order_acquire ?
+            atomic_fetch_op!(memory_order.memory_order_acquire, "-=")(cast(A*)obj, arg) :
+        order == memory_order.memory_order_release ?
+            atomic_fetch_op!(memory_order.memory_order_release, "-=")(cast(A*)obj, arg) :
+        order == memory_order.memory_order_acq_rel ?
+            atomic_fetch_op!(memory_order.memory_order_acq_rel, "-=")(cast(A*)obj, arg) :
+        order == memory_order.memory_order_seq_cst ?
+            atomic_fetch_op!(memory_order.memory_order_seq_cst, "-=")(cast(A*)obj, arg) :
+        assert(0);
 }
 
 ///
@@ -856,19 +842,18 @@ A atomic_fetch_or_explicit_impl(A, M)(shared(A)* obj, M arg, memory_order order)
 {
     assert(obj !is null);
 
-    final switch(order)
-    {
-        case memory_order.memory_order_relaxed:
-            return atomic_fetch_op!(memory_order.memory_order_relaxed, "|=")(cast(A*)obj, arg);
-        case memory_order.memory_order_acquire:
-            return atomic_fetch_op!(memory_order.memory_order_acquire, "|=")(cast(A*)obj, arg);
-        case memory_order.memory_order_release:
-            return atomic_fetch_op!(memory_order.memory_order_release, "|=")(cast(A*)obj, arg);
-        case memory_order.memory_order_acq_rel:
-            return atomic_fetch_op!(memory_order.memory_order_acq_rel, "|=")(cast(A*)obj, arg);
-        case memory_order.memory_order_seq_cst:
-            return atomic_fetch_op!(memory_order.memory_order_seq_cst, "|=")(cast(A*)obj, arg);
-    }
+    return
+        order == memory_order.memory_order_relaxed ?
+            atomic_fetch_op!(memory_order.memory_order_relaxed, "|=")(cast(A*)obj, arg) :
+        order == memory_order.memory_order_acquire ?
+            atomic_fetch_op!(memory_order.memory_order_acquire, "|=")(cast(A*)obj, arg) :
+        order == memory_order.memory_order_release ?
+            atomic_fetch_op!(memory_order.memory_order_release, "|=")(cast(A*)obj, arg) :
+        order == memory_order.memory_order_acq_rel ?
+            atomic_fetch_op!(memory_order.memory_order_acq_rel, "|=")(cast(A*)obj, arg) :
+        order == memory_order.memory_order_seq_cst ?
+            atomic_fetch_op!(memory_order.memory_order_seq_cst, "|=")(cast(A*)obj, arg) :
+        assert(0);
 }
 
 ///
@@ -901,19 +886,18 @@ A atomic_fetch_xor_explicit_impl(A, M)(shared(A)* obj, M arg, memory_order order
 {
     assert(obj !is null);
 
-    final switch(order)
-    {
-        case memory_order.memory_order_relaxed:
-           return atomic_fetch_op!(memory_order.memory_order_relaxed, "^=")(cast(A*)obj, arg);
-        case memory_order.memory_order_acquire:
-            return atomic_fetch_op!(memory_order.memory_order_acquire, "^=")(cast(A*)obj, arg);
-        case memory_order.memory_order_release:
-            return atomic_fetch_op!(memory_order.memory_order_release, "^=")(cast(A*)obj, arg);
-        case memory_order.memory_order_acq_rel:
-            return atomic_fetch_op!(memory_order.memory_order_acq_rel, "^=")(cast(A*)obj, arg);
-        case memory_order.memory_order_seq_cst:
-            return atomic_fetch_op!(memory_order.memory_order_seq_cst, "^=")(cast(A*)obj, arg);
-    }
+    return
+        order == memory_order.memory_order_relaxed ?
+            atomic_fetch_op!(memory_order.memory_order_relaxed, "^=")(cast(A*)obj, arg) :
+        order == memory_order.memory_order_acquire ?
+            atomic_fetch_op!(memory_order.memory_order_acquire, "^=")(cast(A*)obj, arg) :
+        order == memory_order.memory_order_release ?
+            atomic_fetch_op!(memory_order.memory_order_release, "^=")(cast(A*)obj, arg) :
+        order == memory_order.memory_order_acq_rel ?
+            atomic_fetch_op!(memory_order.memory_order_acq_rel, "^=")(cast(A*)obj, arg) :
+        order == memory_order.memory_order_seq_cst ?
+            atomic_fetch_op!(memory_order.memory_order_seq_cst, "^=")(cast(A*)obj, arg) :
+        assert(0);
 }
 
 ///
@@ -946,19 +930,18 @@ A atomic_fetch_and_explicit_impl(A, M)(shared(A)* obj, M arg, memory_order order
 {
     assert(obj !is null);
 
-    final switch(order)
-    {
-        case memory_order.memory_order_relaxed:
-            return atomic_fetch_op!(memory_order.memory_order_relaxed, "&=")(cast(A*)obj, arg);
-        case memory_order.memory_order_acquire:
-            return atomic_fetch_op!(memory_order.memory_order_acquire, "&=")(cast(A*)obj, arg);
-        case memory_order.memory_order_release:
-            return atomic_fetch_op!(memory_order.memory_order_release, "&=")(cast(A*)obj, arg);
-        case memory_order.memory_order_acq_rel:
-            return atomic_fetch_op!(memory_order.memory_order_acq_rel, "&=")(cast(A*)obj, arg);
-        case memory_order.memory_order_seq_cst:
-            return atomic_fetch_op!(memory_order.memory_order_seq_cst, "&=")(cast(A*)obj, arg);
-    }
+    return
+        order == memory_order.memory_order_relaxed ?
+            atomic_fetch_op!(memory_order.memory_order_relaxed, "&=")(cast(A*)obj, arg) :
+        order == memory_order.memory_order_acquire ?
+            atomic_fetch_op!(memory_order.memory_order_acquire, "&=")(cast(A*)obj, arg) :
+        order == memory_order.memory_order_release ?
+            atomic_fetch_op!(memory_order.memory_order_release, "&=")(cast(A*)obj, arg) :
+        order == memory_order.memory_order_acq_rel ?
+            atomic_fetch_op!(memory_order.memory_order_acq_rel, "&=")(cast(A*)obj, arg) :
+        order == memory_order.memory_order_seq_cst ?
+            atomic_fetch_op!(memory_order.memory_order_seq_cst, "&=")(cast(A*)obj, arg) :
+        assert(0);
 }
 
 ///
@@ -971,11 +954,11 @@ unittest
 
 private:
 
-pragma(inline, true)
 A atomic_fetch_op(memory_order order, string op, A, M)(A* obj, M arg) @trusted
 {
     static if (is(A : ulong) && (op == "+=" || op == "-="))
     {
+        pragma(inline, true)
         // these cannot handle floats
         static if (op == "+=")
         {
index c9b6d7b6b7ec058487b209f55b4f97575b95f8fc..0c4e02f70e4b6678fccba53fdeda5fb62f163dc1 100644 (file)
@@ -212,12 +212,6 @@ else version (OpenBSD)
         ///
         L_tmpnam     = 1024
     }
-
-    struct __sbuf
-    {
-        ubyte *_base;
-        int _size;
-    }
 }
 else version (DragonFlyBSD)
 {
@@ -611,31 +605,7 @@ else version (OpenBSD)
     ///
     struct __sFILE
     {
-        ubyte*          _p;
-        int             _r;
-        int             _w;
-        short           _flags;
-        short           _file;
-        __sbuf          _bf;
-        int             _lbfsize;
-
-        void*           _cookie;
-        int     function(void*)                         _close;
-        int     function(void*, scope char*, int)       _read;
-        fpos_t  function(void*, fpos_t, int)            _seek;
-        int     function(void*, scope const char*, int) _write;
-
-        __sbuf          _ext;
-        ubyte*          _up;
-        int             _ur;
-
-        ubyte[3]        _ubuf;
-        ubyte[1]        _nbuf;
-
-        __sbuf          _lb;
-
-        int             _blksize;
-        fpos_t          _offset;
+        void* dummy;
     }
 
     ///
@@ -1019,16 +989,22 @@ else version (OpenBSD)
         _IONBF = 2,
     }
 
-    private extern shared FILE[3] __sF;
-    @property auto __stdin()() { return &__sF[0]; }
-    @property auto __stdout()() { return &__sF[1]; }
-    @property auto __stderr()() { return &__sF[2]; }
+    struct __sFstub { long _stub; }
+
+    private extern shared __sFstub[1] __stdin;
+    private extern shared __sFstub[1] __stdout;
+    private extern shared __sFstub[1] __stderr;
+
+    @property auto __stdin1()() { return cast(FILE*)__stdin; }
+    @property auto __stdout1()() { return cast(FILE*)__stdout; }
+    @property auto __stderr1()() { return cast(FILE*)__stderr; }
+
     ///
-    alias __stdin stdin;
+    alias __stdin1 stdin;
     ///
-    alias __stdout stdout;
+    alias __stdout1 stdout;
     ///
-    alias __stderr stderr;
+    alias __stderr1 stderr;
 }
 else version (DragonFlyBSD)
 {
@@ -1350,9 +1326,9 @@ else version (CRuntime_Glibc)
         alias vprintf = __vprintfieee128;
         ///
         pragma(scanf)
-        int __isoc99_vfscanfieee128(scope const char* format, va_list arg);
+        int __isoc99_vscanfieee128(scope const char* format, va_list arg);
         ///
-        alias vscanf = __isoc99_vfscanfieee128;
+        alias vscanf = __isoc99_vscanfieee128;
         ///
         pragma(printf)
         int __printfieee128(scope const char* format, scope const ...);
@@ -1727,83 +1703,14 @@ else version (OpenBSD)
     {
         ///
         void rewind(FILE*);
-    }
-    @trusted private
-    {
         ///
-        pragma(mangle, "clearerr")
-        pure void __clearerr(FILE*);
+        pure void clearerr(FILE*);
         ///
-        pragma(mangle, "feof")
-        pure int __feof(FILE*);
+        pure int  feof(FILE*);
         ///
-        pragma(mangle, "ferror")
-        pure int __ferror(FILE*);
+        pure int  ferror(FILE*);
         ///
-        pragma(mangle, "fileno")
-        int __fileno(FILE*);
-    }
-
-    enum __SLBF = 0x0001;
-    enum __SNBF = 0x0002;
-    enum __SRD  = 0x0004;
-    enum __SWR  = 0x0008;
-    enum __SRW  = 0x0010;
-    enum __SEOF = 0x0020;
-    enum __SERR = 0x0040;
-    enum __SMBF = 0x0080;
-    enum __SAPP = 0x0100;
-    enum __SSTR = 0x0200;
-    enum __SOPT = 0x0400;
-    enum __SNPT = 0x0800;
-    enum __SOFF = 0x1000;
-    enum __SMOD = 0x2000;
-    enum __SALC = 0x4000;
-    enum __SIGN = 0x8000;
-
-    extern immutable __gshared int __isthreaded;
-
-    extern (D) @trusted
-    {
-        void __sclearerr()(FILE* p)
-        {
-            p._flags = p._flags & ~(__SERR|__SEOF);
-        }
-
-        int __sfeof()(FILE* p)
-        {
-            return (p._flags & __SEOF) != 0;
-        }
-
-        int __sferror()(FILE* p)
-        {
-            return (p._flags & __SERR) != 0;
-        }
-
-        int __sfileno()(FILE* p)
-        {
-            return p._file;
-        }
-
-        pure void clearerr()(FILE* file)
-        {
-            !__isthreaded ? __sclearerr(file) : __clearerr(file);
-        }
-
-        pure int feof()(FILE* file)
-        {
-            return !__isthreaded ? __sfeof(file) : __feof(file);
-        }
-
-        pure int ferror()(FILE* file)
-        {
-            return !__isthreaded ? __sferror(file) : __ferror(file);
-        }
-
-        int fileno()(FILE* file)
-        {
-            return !__isthreaded ? __sfileno(file) : __fileno(file);
-        }
+        int  fileno(FILE*);
     }
 
     ///
index 6ed2dfe8318c90683b72b3c671d6599438b9fb95..cd6192b057bb18fc3a01a6b9949f6731abdbe857 100644 (file)
@@ -129,6 +129,23 @@ else version (CRuntime_UClibc)
         wchar_t __wc = 0;
     }
 }
+else version (Windows)
+{
+    ///
+    struct __mbstate_t
+    {
+        int __count;
+        union ___value
+        {
+            wint_t __wch = 0;
+            char[4] __wchb;
+        }
+        ___value __value;
+    }
+
+    ///
+    alias mbstate_t = __mbstate_t;
+}
 else
 {
     ///
index 9d502e52e3236f1df355c3a6636ad1f3f8f9e534..c57d2e4aa8e6fc08ace4e6af90cc105cedf3d958 100644 (file)
@@ -14,7 +14,8 @@ public import core.sys.posix.config;
 // NOTE: When adding newer versions of FreeBSD, verify all current versioned
 // bindings are still compatible with the release.
 
-     version (FreeBSD_14) enum __FreeBSD_version = 1400000;
+     version (FreeBSD_15) enum __FreeBSD_version = 1500063;
+else version (FreeBSD_14) enum __FreeBSD_version = 1400097;
 else version (FreeBSD_13) enum __FreeBSD_version = 1301000;
 else version (FreeBSD_12) enum __FreeBSD_version = 1203000;
 else version (FreeBSD_11) enum __FreeBSD_version = 1104000;
diff --git a/libphobos/libdruntime/core/sys/linux/hdlc/ioctl.d b/libphobos/libdruntime/core/sys/linux/hdlc/ioctl.d
new file mode 100644 (file)
index 0000000..f879aae
--- /dev/null
@@ -0,0 +1,92 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+module core.sys.linux.hdlc.ioctl;
+
+import core.sys.linux.net.if_ : IFNAMSIZ;
+
+version (linux):
+extern(C):
+@nogc:
+nothrow:
+
+enum GENERIC_HDLC_VERSION = 4; /* For synchronization with sethdlc utility */
+
+enum CLOCK_DEFAULT = 0;        /* Default setting */
+enum CLOCK_EXT = 1;    /* External TX and RX clock - DTE */
+enum CLOCK_INT = 2;    /* Internal TX and RX clock - DCE */
+enum CLOCK_TXINT = 3;  /* Internal TX and external RX clock */
+enum CLOCK_TXFROMRX = 4;       /* TX clock derived from external RX clock */
+
+
+enum ENCODING_DEFAULT = 0; /* Default setting */
+enum ENCODING_NRZ = 1;
+enum ENCODING_NRZI = 2;
+enum ENCODING_FM_MARK = 3;
+enum ENCODING_FM_SPACE = 4;
+enum ENCODING_MANCHESTER = 5;
+
+
+enum PARITY_DEFAULT = 0; /* Default setting */
+enum PARITY_NONE = 1; /* No parity */
+enum PARITY_CRC16_PR0 = 2; /* CRC16, initial value 0x0000 */
+enum PARITY_CRC16_PR1 = 3; /* CRC16, initial value 0xFFFF */
+enum PARITY_CRC16_PR0_CCITT = 4; /* CRC16, initial 0x0000, ITU-T version */
+enum PARITY_CRC16_PR1_CCITT = 5; /* CRC16, initial 0xFFFF, ITU-T version */
+enum PARITY_CRC32_PR0_CCITT = 6; /* CRC32, initial value 0x00000000 */
+enum PARITY_CRC32_PR1_CCITT = 7; /* CRC32, initial value 0xFFFFFFFF */
+
+enum LMI_DEFAULT = 0; /* Default setting */
+enum LMI_NONE = 1; /* No LMI, all PVCs are static */
+enum LMI_ANSI = 2; /* ANSI Annex D */
+enum LMI_CCITT = 3; /* ITU-T Annex A */
+enum LMI_CISCO = 4; /* The "original" LMI, aka Gang of Four */
+
+struct sync_serial_settings {
+       uint clock_rate; /* bits per second */
+       uint clock_type; /* internal, external, TX-internal etc. */
+       ushort loopback;
+}          /* V.35, V.24, X.21 */
+
+struct te1_settings{
+       uint clock_rate; /* bits per second */
+       uint clock_type; /* internal, external, TX-internal etc. */
+       ushort loopback;
+       uint slot_map;
+}                  /* T1, E1 */
+
+struct raw_hdlc_proto {
+       ushort encoding;
+       ushort parity;
+}
+
+struct fr_proto {
+       uint t391;
+       uint t392;
+       uint n391;
+       uint n392;
+       uint n393;
+       ushort lmi;
+       ushort dce; /* 1 for DCE (network side) operation */
+}
+
+struct fr_proto_pvc {
+       uint dlci;
+}          /* for creating/deleting FR PVCs */
+
+struct fr_proto_pvc_info {
+       uint dlci;
+       char[IFNAMSIZ] master = 0;      /* Name of master FRAD device */
+}              /* for returning PVC information only */
+
+struct cisco_proto {
+    uint interval;
+    uint timeout;
+}
+
+struct x25_hdlc_proto {
+       ushort dce; /* 1 for DCE (network side) operation */
+       uint modulo; /* modulo (8 = basic / 128 = extended) */
+       uint window; /* frame window size */
+       uint t1; /* timeout t1 */
+       uint t2; /* timeout t2 */
+       uint n2; /* frame retry counter */
+}
diff --git a/libphobos/libdruntime/core/sys/linux/net/if_.d b/libphobos/libdruntime/core/sys/linux/net/if_.d
new file mode 100644 (file)
index 0000000..62d247f
--- /dev/null
@@ -0,0 +1,134 @@
+/++
+    D header file for Linux's net/if.h.
+
+    Copyright: Copyright 2025
+    License:   $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
+ +/
+module core.sys.linux.net.if_;
+
+public import core.sys.posix.sys.socket : sockaddr;
+public import core.sys.linux.hdlc.ioctl;
+
+version (linux):
+extern(C):
+@nogc:
+nothrow:
+
+enum IFNAMSIZ = 16;
+enum IFALIASZ = 256;
+enum ALTIFNAMSIZ = 128;
+enum IFHWADDRLEN = 6;
+
+enum IFF_UP          = 0x1;
+enum IFF_BROADCAST   = 0x2;
+enum IFF_DEBUG       = 0x4;
+enum IFF_LOOPBACK    = 0x8;
+enum IFF_POINTOPOINT = 0x10;
+enum IFF_NOTRAILERS  = 0x20;
+enum IFF_RUNNING     = 0x40;
+enum IFF_NOARP       = 0x80;
+enum IFF_PROMISC     = 0x100;
+enum IFF_ALLMULTI    = 0x200;
+enum IFF_MASTER      = 0x400;
+enum IFF_SLAVE       = 0x800;
+enum IFF_MULTICAST   = 0x1000;
+enum IFF_PORTSEL     = 0x2000;
+enum IFF_AUTOMEDIA   = 0x4000;
+enum IFF_DYNAMIC     = 0x8000;
+enum IFF_LOWER_UP    = 0x10000;
+enum IFF_DORMANT     = 0x20000;
+enum IFF_ECHO        = 0x40000;
+
+enum IF_GET_IFACE = 0x0001;            /* for querying only */
+enum IF_GET_PROTO = 0x0002;
+
+/* For definitions see hdlc.h */
+enum IF_IFACE_V35 = 0x1000;            /* V.35 serial interface        */
+enum IF_IFACE_V24 = 0x1001;            /* V.24 serial interface        */
+enum IF_IFACE_X21 = 0x1002;            /* X.21 serial interface        */
+enum IF_IFACE_T1 = 0x1003;             /* T1 telco serial interface    */
+enum IF_IFACE_E1 = 0x1004;             /* E1 telco serial interface    */
+enum IF_IFACE_SYNC_SERIAL = 0x1005;    /* can't be set by software     */
+enum IF_IFACE_X21D = 0x1006;          /* X.21 Dual Clocking (FarSite) */
+
+/* For definitions see hdlc.h */
+enum IF_PROTO_HDLC = 0x2000;           /* raw HDLC protocol            */
+enum IF_PROTO_PPP = 0x2001;            /* PPP protocol                 */
+enum IF_PROTO_CISCO = 0x2002;          /* Cisco HDLC protocol          */
+enum IF_PROTO_FR = 0x2003;             /* Frame Relay protocol         */
+enum IF_PROTO_FR_ADD_PVC = 0x2004;     /*    Create FR PVC             */
+enum IF_PROTO_FR_DEL_PVC = 0x2005;     /*    Delete FR PVC             */
+enum IF_PROTO_X25 = 0x2006;            /* X.25                         */
+enum IF_PROTO_HDLC_ETH = 0x2007;       /* raw HDLC, Ethernet emulation */
+enum IF_PROTO_FR_ADD_ETH_PVC = 0x2008; /*  Create FR Ethernet-bridged PVC */
+enum IF_PROTO_FR_DEL_ETH_PVC =  0x2009;        /*  Delete FR Ethernet-bridged PVC */
+enum IF_PROTO_FR_PVC = 0x200A;         /* for reading PVC status       */
+enum IF_PROTO_FR_ETH_PVC = 0x200B;
+enum IF_PROTO_RAW = 0x200C;          /* RAW Socket                   */
+
+enum IF_OPER_UNKNOWN = 0;
+enum IF_OPER_NOTPRESENT = 1;
+enum IF_OPER_DOWN = 2;
+enum IF_OPER_LOWERLAYERDOWN = 3;
+enum IF_OPER_TESTING = 4;
+enum IF_OPER_DORMANT = 5;
+enum IF_OPER_UP = 6;
+
+enum IF_LINK_MODE_DEFAULT = 0;
+enum IF_LINK_MODE_DORMANT = 1; /* limit upward transition to dormant */
+enum IF_LINK_MODE_TESTING = 2; /* limit upward transition to testing */
+
+struct ifmap {
+       uint mem_start;
+       uint mem_end;
+       ushort base_addr;
+       ubyte irq;
+       ubyte dma;
+       ubyte port;
+       /* 3 bytes spare */
+}
+
+struct if_settings {
+       uint type;      /* Type of physical device or protocol */
+       uint size;      /* Size of the data allocated by the caller */
+       union {
+               /* {atm/eth/dsl}_settings anyone ? */
+               raw_hdlc_proto* raw_hdlc;
+               cisco_proto* cisco;
+               fr_proto* fr;
+               fr_proto_pvc* fr_pvc;
+               fr_proto_pvc_info* fr_pvc_info;
+               x25_hdlc_proto* x25;
+
+               /* interface settings */
+               sync_serial_settings* sync;
+               te1_settings* te1;
+       };
+}
+
+struct ifreq {
+       union
+       {
+               char[IFNAMSIZ]  ifrn_name = 0;          /* if name, e.g. "en0" */
+       }
+
+       union {
+
+               short   ifru_flags;
+               int     ifru_ivalue;
+               int     ifru_mtu;
+               ifmap ifru_map;
+               char[IFNAMSIZ]  ifru_slave;     /* Just fits the size */
+               char[IFNAMSIZ]  ifru_newname;
+               void*   ifru_data;
+               if_settings ifru_settings;
+       }
+}
+
+struct ifconf {
+       int     ifc_len;                        /* size of buffer       */
+       union {
+               char* ifcu_buf;
+               ifreq* ifcu_req;
+       }
+}
index 7288231730d11d76719cbb55a1c725b62e1404b7..64db6998bf7c0a4718df6d383c61329525c4f618 100644 (file)
@@ -18,4 +18,8 @@ nothrow:
 static if (_GNU_SOURCE)
 {
     pure void* memmem(return scope const void* haystack, size_t haystacklen, scope const void* needle, size_t needlelen);
+    /// return string describing error number
+    const(char)* strerrorname_np(int);
+    /// ditto
+    const(char)* strerrordesc_np(int);
 }
index 727471e45552d00a9f204547a7d98e669999e7bf..3e582262f1c60c43e3584dc5b19faabe2f8415b4 100644 (file)
@@ -54,7 +54,7 @@ struct inotify_event
 
 enum: uint
 {
-    IN_ACCESS        = 0x00000000,
+    IN_ACCESS        = 0x00000001,
     IN_MODIFY        = 0x00000002,
     IN_ATTRIB        = 0x00000004,
     IN_CLOSE_WRITE   = 0x00000008,
index 9618543d8da8b34dbd49f42d1a2cfc72bc5d828f..c6b53f876d2f711587c40471d91d5b2fcf10dc91 100644 (file)
@@ -14,7 +14,7 @@ nothrow:
 
 public import core.sys.posix.sys.types;
 
-int getentropy(void*, size_t);
+public import core.sys.posix.unistd : getentropy;
 int getthrname(pid_t, char*, size_t);
 int pledge(const scope char*, const scope char*);
 int setthrname(pid_t, const scope char*);
index 6b80d1ff0e6cdb2c378756b6f798d62559c4ec30..9463471d32fa424d032d9ad0d04a156f3c00502d 100644 (file)
@@ -20,9 +20,9 @@ public import core.stdc.config;
 version (Posix):
 extern (C) nothrow @nogc:
 
-enum _XOPEN_SOURCE     = 600;
+enum _XOPEN_SOURCE     = 700;
 enum _POSIX_SOURCE     = true;
-enum _POSIX_C_SOURCE   = 200112L;
+enum _POSIX_C_SOURCE   = 200809L;
 
 version (CRuntime_Glibc)
 {
@@ -87,8 +87,8 @@ else version (CRuntime_Musl)
     // Not present in Musl sources
     enum __REDIRECT          = false;
 
-    // Those three are irrelevant for Musl as it always uses 64 bits off_t
-    enum __USE_FILE_OFFSET64 = false;
+    // Always use code paths that are compatible with 64 bits off_t
+    enum __USE_FILE_OFFSET64 = true;
     enum __USE_LARGEFILE     = __USE_FILE_OFFSET64 && !__REDIRECT;
     enum __USE_LARGEFILE64   = __USE_FILE_OFFSET64 && !__REDIRECT;
 
index 3da9fd72e6d7da378cf52855779aa19b1b97b8cd..4f7a22592d4e6b593478199244fb36237c89928f 100644 (file)
@@ -500,6 +500,12 @@ else version (Darwin)
         short   l_type;
         short   l_whence;
     }
+
+    enum AT_FDCWD = -2;
+    enum AT_EACCESS          = 0x0010;
+    enum AT_SYMLINK_NOFOLLOW = 0x0020;
+    enum AT_SYMLINK_FOLLOW   = 0x0040;
+    enum AT_REMOVEDIR        = 0x0080;
 }
 else version (FreeBSD)
 {
@@ -559,8 +565,11 @@ else version (FreeBSD)
         short   l_whence;
     }
 
-    enum AT_SYMLINK_NOFOLLOW = 0x200;
     enum AT_FDCWD = -100;
+    enum AT_EACCESS          = 0x100;
+    enum AT_SYMLINK_NOFOLLOW = 0x200;
+    enum AT_SYMLINK_FOLLOW   = 0x400;
+    enum AT_REMOVEDIR        = 0x800;
 }
 else version (OpenBSD)
 {
@@ -676,6 +685,12 @@ else version (NetBSD)
         short   l_type;
         short   l_whence;
     }
+
+    enum AT_FDCWD = -100;
+    enum AT_EACCESS          = 0x100;
+    enum AT_SYMLINK_NOFOLLOW = 0x200;
+    enum AT_SYMLINK_FOLLOW   = 0x400;
+    enum AT_REMOVEDIR        = 0x800;
 }
 else version (DragonFlyBSD)
 {
@@ -760,6 +775,12 @@ else version (DragonFlyBSD)
     }
 
     alias oflock = flock;
+
+    enum AT_FDCWD = 0xFFFAFDCD;
+    enum AT_SYMLINK_NOFOLLOW = 1;
+    enum AT_REMOVEDIR        = 2;
+    enum AT_EACCESS          = 4;
+    enum AT_SYMLINK_FOLLOW   = 8;
 }
 else version (Solaris)
 {
@@ -843,6 +864,11 @@ else version (Solaris)
             c_long[4]   l_pad;
         }
     }
+    enum AT_FDCWD = -3041965;
+    enum AT_SYMLINK_NOFOLLOW = 0x1000;
+    enum AT_SYMLINK_FOLLOW   = 0x2000;
+    enum AT_REMOVEDIR        = 0x1;
+    enum AT_EACCESS          = 0x4;
 }
 else
 {
@@ -853,6 +879,7 @@ else
 int creat(const scope char*, mode_t);
 int fcntl(int, int, ...);
 int open(const scope char*, int, ...);
+int openat(int, const scope char*, int, ...);
 */
 version (CRuntime_Glibc)
 {
@@ -863,37 +890,46 @@ version (CRuntime_Glibc)
 
         int   open64(const scope char*, int, ...);
         alias open64 open;
+
+        int   openat64(int, const scope char*, int, ...);
+        alias openat64 openat;
     }
     else
     {
         int   creat(const scope char*, mode_t);
         int   open(const scope char*, int, ...);
+        int   openat(int, const scope char*, int, ...);
     }
 }
 else version (Darwin)
 {
     int creat(const scope char*, mode_t);
     int open(const scope char*, int, ...);
+    int openat(int, const scope char*, int, ...);
 }
 else version (FreeBSD)
 {
     int creat(const scope char*, mode_t);
     int open(const scope char*, int, ...);
+    int openat(int, const scope char*, int, ...);
 }
 else version (OpenBSD)
 {
     int creat(const scope char*, mode_t);
     int open(const scope char*, int, ...);
+    int openat(int, const scope char*, int, ...);
 }
 else version (NetBSD)
 {
     int creat(const scope char*, mode_t);
     int open(const scope char*, int, ...);
+    int openat(int, const scope char*, int, ...);
 }
 else version (DragonFlyBSD)
 {
     int creat(const scope char*, mode_t);
     int open(const scope char*, int, ...);
+    int openat(int, const scope char*, int, ...);
 }
 else version (Solaris)
 {
@@ -901,11 +937,13 @@ else version (Solaris)
     {
         int creat(const scope char*, mode_t);
         int open(const scope char*, int, ...);
+        int openat(int, const scope char*, int, ...);
 
         static if (__USE_LARGEFILE64)
         {
             alias creat creat64;
             alias open open64;
+            alias openat openat64;
         }
     }
     else
@@ -917,11 +955,15 @@ else version (Solaris)
 
             int open64(const scope char*, int, ...);
             alias open64 open;
+
+            int openat64(int, const scope char*, int, ...);
+            alias openat64 openat;
         }
         else
         {
             int creat(const scope char*, mode_t);
             int open(const scope char*, int, ...);
+            int openat(int, const scope char*, int, ...);
         }
     }
 }
@@ -929,10 +971,13 @@ else version (CRuntime_Bionic)
 {
     int   creat(const scope char*, mode_t);
     int   open(const scope char*, int, ...);
+    int   openat(int, const scope char*, int, ...);
 }
 else version (CRuntime_Musl)
 {
+    int creat(const scope char*, mode_t);
     int open(const scope char*, int, ...);
+    int openat(int, const scope char*, int, ...);
 }
 else version (CRuntime_UClibc)
 {
@@ -943,11 +988,15 @@ else version (CRuntime_UClibc)
 
         int   open64(const scope char*, int, ...);
         alias open64 open;
+
+        int openat64(int, const scope char*, int, ...);
+        alias openat64 openat;
     }
     else
     {
         int   creat(const scope char*, mode_t);
         int   open(const scope char*, int, ...);
+        int openat(int, const scope char*, int, ...);
     }
 }
 else
index 6c5f1a93e45be540243c2bfc0dff21c1b9885bc5..86844f9c5ce1350ae3bea7451a48e0c162d5e5ae 100644 (file)
@@ -371,6 +371,12 @@ else version (CRuntime_Bionic)
         PTHREAD_CREATE_DETACHED
     }
 
+    enum
+    {
+        PTHREAD_EXPLICIT_SCHED = 0,
+        PTHREAD_INHERIT_SCHED = 1,
+    }
+
     enum PTHREAD_MUTEX_INITIALIZER = pthread_mutex_t.init;
     enum PTHREAD_ONCE_INIT         = pthread_once_t.init;
 
@@ -382,12 +388,28 @@ else version (CRuntime_Bionic)
 }
 else version (CRuntime_Musl)
 {
+    enum
+    {
+        PTHREAD_CANCEL_ENABLE = 0,
+        PTHREAD_CANCEL_DISABLE = 1,
+        PTHREAD_CANCEL_DEFERRED = 0,
+        PTHREAD_CANCEL_ASYNCHRONOUS = 1,
+    }
+
+    enum PTHREAD_CANCELED = cast(void*) -1;
+
     enum
     {
         PTHREAD_CREATE_JOINABLE = 0,
         PTHREAD_CREATE_DETACHED = 1
     }
 
+    enum
+    {
+        PTHREAD_INHERIT_SCHED = 0,
+        PTHREAD_EXPLICIT_SCHED = 1,
+    }
+
     enum PTHREAD_MUTEX_INITIALIZER = pthread_mutex_t.init;
     enum PTHREAD_ONCE_INIT = pthread_once_t.init;
 
index c5a2e1b71827afd54224b1c8260e8044a21d47ab..dee81ecc3429b615a1dce6b55bb661bdc4915bb6 100644 (file)
@@ -1,5 +1,5 @@
 /**
- * D header file for C99.
+ * D header file for C99/C11.
  *
  * $(C_HEADER_DESCRIPTION pubs.opengroup.org/onlinepubs/009695399/basedefs/_time.h.html, _time.h)
  *
@@ -50,6 +50,13 @@ struct tm
 }
 
 public import core.sys.posix.sys.types : time_t, clock_t;
+public import core.sys.posix.time : timespec;
+
+/// timespec_get introduced in C11
+@system int timespec_get(timespec* ts, int base);
+
+/// Base Value used for timespec_get
+enum TIME_UTC = 1;
 
 ///
 version (CRuntime_Glibc)
index e2ee0b6085f32c73e3854d0018e072a7c08cb8ee..e3de3e283c1154b40f7c555850d3627fa96cfef2 100644 (file)
@@ -246,6 +246,7 @@ ssize_t getline(char**, size_t*, FILE*);
 char*   gets(char*);
 int     pclose(FILE*);
 FILE*   popen(const scope char*, const scope char*);
+int     renameat(int, const scope char*, int, const scope char*);
 */
 
 version (CRuntime_Glibc)
@@ -274,6 +275,7 @@ version (CRuntime_Glibc)
 
     ssize_t getdelim(char**, size_t*, int, FILE*);
     ssize_t getline(char**, size_t*, FILE*);
+    int     renameat(int, const scope char*, int, const scope char*);
 }
 else version (CRuntime_UClibc)
 {
@@ -302,6 +304,7 @@ else version (CRuntime_UClibc)
 
     ssize_t getdelim(char**, size_t*, int, FILE*);
     ssize_t getline(char**, size_t*, FILE*);
+    int     renameat(int, const scope char*, int, const scope char*);
 }
 else version (CRuntime_Musl)
 {
@@ -318,6 +321,7 @@ else version (CRuntime_Musl)
 
     ssize_t getdelim(char**, size_t*, int, FILE*);
     ssize_t getline(char**, size_t*, FILE*);
+    int     renameat(int, const scope char*, int, const scope char*);
 }
 else version (CRuntime_Bionic)
 {
@@ -345,6 +349,7 @@ else version (CRuntime_Bionic)
 
     ssize_t getdelim(char**, size_t*, int, FILE*);
     ssize_t getline(char**, size_t*, FILE*);
+    int     renameat(int, const scope char*, int, const scope char*);
 }
 else version (Darwin)
 {
@@ -355,6 +360,7 @@ else version (Darwin)
 
     ssize_t getdelim(char**, size_t*, int, FILE*);
     ssize_t getline(char**, size_t*, FILE*);
+    int     renameat(int, const scope char*, int, const scope char*);
 }
 else version (FreeBSD)
 {
@@ -369,6 +375,7 @@ else version (FreeBSD)
     {
         ssize_t getdelim(char**, size_t*, int, FILE*);
         ssize_t getline(char**, size_t*, FILE*);
+        int     renameat(int, const scope char*, int, const scope char*);
     }
 }
 else version (NetBSD)
@@ -390,6 +397,7 @@ else version (OpenBSD)
 
     ssize_t getdelim(char**, size_t*, int, FILE*);
     ssize_t getline(char**, size_t*, FILE*);
+    int     renameat(int, const scope char*, int, const scope char*);
 }
 else version (DragonFlyBSD)
 {
@@ -400,6 +408,7 @@ else version (DragonFlyBSD)
 
     ssize_t getdelim(char**, size_t*, int, FILE*);
     ssize_t getline(char**, size_t*, FILE*);
+    int     renameat(int, const scope char*, int, const scope char*);
 }
 else version (Solaris)
 {
@@ -428,6 +437,7 @@ else version (Solaris)
 
     ssize_t getdelim(char**, size_t*, int, FILE*);
     ssize_t getline(char**, size_t*, FILE*);
+    int     renameat(int, const scope char*, int, const scope char*);
 }
 else version (Posix)
 {
index 6ecdc0defeb891328fae61792c9de5aebcd731c2..7481d8cc60f9fc15049db0c93ce06fe3eed03a3d 100644 (file)
@@ -66,11 +66,11 @@ version (linux)
         ipc_perm    shm_perm;
         size_t      shm_segsz;
         time_t      shm_atime;
-        version (X86_64) {} else c_ulong     __unused1;
+        static if (time_t.sizeof == 4) c_ulong     __unused1;
         time_t      shm_dtime;
-        version (X86_64) {} else c_ulong     __unused2;
+        static if (time_t.sizeof == 4) c_ulong     __unused2;
         time_t      shm_ctime;
-        version (X86_64) {} else c_ulong     __unused3;
+        static if (time_t.sizeof == 4) c_ulong     __unused3;
         pid_t       shm_cpid;
         pid_t       shm_lpid;
         shmatt_t    shm_nattch;
index 0dbf4ebb7b6b87403da68447034e036f04bb9ced..67d4e83f2a3dd67bc5cff8981e6df8f92fc3305d 100644 (file)
@@ -72,29 +72,38 @@ version (linux)
 {
     version (X86)
     {
-        struct stat_t
+        version (CRuntime_Musl)
         {
-            dev_t       st_dev;
-            ushort      __pad1;
-            static if (!__USE_FILE_OFFSET64)
+            struct stat_t
             {
+                dev_t       st_dev;
+                ushort      __pad1;
+                static if (!__USE_FILE_OFFSET64)
+                {
+                    ino_t       st_ino;
+                }
+                else
+                {
+                    uint        __st_ino;
+                }
+                mode_t      st_mode;
+                nlink_t     st_nlink;
+                uid_t       st_uid;
+                gid_t       st_gid;
+                dev_t       st_rdev;
+                ushort      __pad2;
+                off_t       st_size;
+                blksize_t   st_blksize;
+                blkcnt_t    st_blocks;
+                private struct __timespec32
+                {
+                    c_long tv_sec;
+                    c_long tv_nsec;
+                }
+                __timespec32 __st_atim32;
+                __timespec32 __st_mtim32;
+                __timespec32 __st_ctim32;
                 ino_t       st_ino;
-            }
-            else
-            {
-                uint        __st_ino;
-            }
-            mode_t      st_mode;
-            nlink_t     st_nlink;
-            uid_t       st_uid;
-            gid_t       st_gid;
-            dev_t       st_rdev;
-            ushort      __pad2;
-            off_t       st_size;
-            blksize_t   st_blksize;
-            blkcnt_t    st_blocks;
-            static if (_DEFAULT_SOURCE || _XOPEN_SOURCE >= 700)
-            {
                 timespec    st_atim;
                 timespec    st_mtim;
                 timespec    st_ctim;
@@ -105,23 +114,61 @@ version (linux)
                     ref inout(time_t) st_ctime() return { return st_ctim.tv_sec; }
                 }
             }
-            else
-            {
-                time_t      st_atime;
-                ulong_t     st_atimensec;
-                time_t      st_mtime;
-                ulong_t     st_mtimensec;
-                time_t      st_ctime;
-                ulong_t     st_ctimensec;
-            }
-            static if (__USE_FILE_OFFSET64)
-            {
-                ino_t       st_ino;
-            }
-            else
+            static assert(stat_t.sizeof == 144);
+        }
+        else
+        {
+            struct stat_t
             {
-                c_ulong     __unused4;
-                c_ulong     __unused5;
+                dev_t       st_dev;
+                ushort      __pad1;
+                static if (!__USE_FILE_OFFSET64)
+                {
+                    ino_t       st_ino;
+                }
+                else
+                {
+                    uint        __st_ino;
+                }
+                mode_t      st_mode;
+                nlink_t     st_nlink;
+                uid_t       st_uid;
+                gid_t       st_gid;
+                dev_t       st_rdev;
+                ushort      __pad2;
+                off_t       st_size;
+                blksize_t   st_blksize;
+                blkcnt_t    st_blocks;
+                static if (_DEFAULT_SOURCE || _XOPEN_SOURCE >= 700)
+                {
+                    timespec    st_atim;
+                    timespec    st_mtim;
+                    timespec    st_ctim;
+                    extern(D) @safe @property inout pure nothrow
+                    {
+                        ref inout(time_t) st_atime() return { return st_atim.tv_sec; }
+                        ref inout(time_t) st_mtime() return { return st_mtim.tv_sec; }
+                        ref inout(time_t) st_ctime() return { return st_ctim.tv_sec; }
+                    }
+                }
+                else
+                {
+                    time_t      st_atime;
+                    ulong_t     st_atimensec;
+                    time_t      st_mtime;
+                    ulong_t     st_mtimensec;
+                    time_t      st_ctime;
+                    ulong_t     st_ctimensec;
+                }
+                static if (__USE_FILE_OFFSET64)
+                {
+                    ino_t       st_ino;
+                }
+                else
+                {
+                    c_ulong     __unused4;
+                    c_ulong     __unused5;
+                }
             }
         }
     }
@@ -261,37 +308,31 @@ version (linux)
     }
     else version (MIPS_O32)
     {
-        struct stat_t
+        version (CRuntime_Musl)
         {
-            version (CRuntime_Musl)
+            struct stat_t
             {
                 dev_t       st_dev;
-                c_long[2]   st_pad1;
-            }
-            else
-            {
-                c_ulong     st_dev;
-                c_long[3]   st_pad1;
-            }
-            ino_t       st_ino;
-            mode_t      st_mode;
-            nlink_t     st_nlink;
-            uid_t       st_uid;
-            gid_t       st_gid;
-            c_ulong     st_rdev;
-            static if (!__USE_FILE_OFFSET64)
-            {
-                c_long[2]   st_pad2;
-                off_t       st_size;
-                c_long      st_pad3;
-            }
-            else
-            {
-                c_long[3]   st_pad2;
+                c_long[2]   __pad1;
+                ino_t       st_ino;
+                mode_t      st_mode;
+                nlink_t     st_nlink;
+                uid_t       st_uid;
+                gid_t       st_gid;
+                dev_t       st_rdev;
+                c_long[2]   __pad2;
                 off_t       st_size;
-            }
-            static if (_DEFAULT_SOURCE || _XOPEN_SOURCE >= 700)
-            {
+                private struct __timespec32
+                {
+                    c_long tv_sec;
+                    c_long tv_nsec;
+                }
+                __timespec32 __st_atim32;
+                __timespec32 __st_mtim32;
+                __timespec32 __st_ctim32;
+                blksize_t   st_blksize;
+                c_long      __pad3;
+                blkcnt_t    st_blocks;
                 timespec    st_atim;
                 timespec    st_mtim;
                 timespec    st_ctim;
@@ -301,27 +342,65 @@ version (linux)
                     ref inout(time_t) st_mtime() return { return st_mtim.tv_sec; }
                     ref inout(time_t) st_ctime() return { return st_ctim.tv_sec; }
                 }
+                c_long[2]   __pad4;
             }
-            else
-            {
-                time_t      st_atime;
-                c_ulong     st_atimensec;
-                time_t      st_mtime;
-                c_ulong     st_mtimensec;
-                time_t      st_ctime;
-                c_ulong     st_ctimensec;
-            }
-            blksize_t   st_blksize;
-            static if (!__USE_FILE_OFFSET64)
-            {
-                blkcnt_t    st_blocks;
-            }
-            else
+        }
+        else
+        {
+            struct stat_t
             {
-                c_long      st_pad4;
-                blkcnt_t    st_blocks;
+                c_ulong     st_dev;
+                c_long[3]   st_pad1;
+                ino_t       st_ino;
+                mode_t      st_mode;
+                nlink_t     st_nlink;
+                uid_t       st_uid;
+                gid_t       st_gid;
+                c_ulong     st_rdev;
+                static if (!__USE_FILE_OFFSET64)
+                {
+                    c_long[2]   st_pad2;
+                    off_t       st_size;
+                    c_long      st_pad3;
+                }
+                else
+                {
+                    c_long[3]   st_pad2;
+                    off_t       st_size;
+                }
+                static if (_DEFAULT_SOURCE || _XOPEN_SOURCE >= 700)
+                {
+                    timespec    st_atim;
+                    timespec    st_mtim;
+                    timespec    st_ctim;
+                    extern(D) @safe @property inout pure nothrow
+                    {
+                        ref inout(time_t) st_atime() return { return st_atim.tv_sec; }
+                        ref inout(time_t) st_mtime() return { return st_mtim.tv_sec; }
+                        ref inout(time_t) st_ctime() return { return st_ctim.tv_sec; }
+                    }
+                }
+                else
+                {
+                    time_t      st_atime;
+                    c_ulong     st_atimensec;
+                    time_t      st_mtime;
+                    c_ulong     st_mtimensec;
+                    time_t      st_ctime;
+                    c_ulong     st_ctimensec;
+                }
+                blksize_t   st_blksize;
+                static if (!__USE_FILE_OFFSET64)
+                {
+                    blkcnt_t    st_blocks;
+                }
+                else
+                {
+                    c_long      st_pad4;
+                    blkcnt_t    st_blocks;
+                }
+                c_long[14]  st_pad6;
             }
-            c_long[14]  st_pad5;
         }
         static if (!__USE_FILE_OFFSET64)
             static assert(stat_t.sizeof == 144);
@@ -572,67 +651,46 @@ version (linux)
     }
     else version (ARM)
     {
-        private
-        {
-            alias __dev_t = ulong;
-            alias __ino_t = c_ulong;
-            alias __ino64_t = ulong;
-            alias __mode_t = uint;
-            alias __nlink_t = size_t;
-            alias __uid_t = uint;
-            alias __gid_t = uint;
-            alias __off_t = c_long;
-            alias __off64_t = long;
-            alias __blksize_t = c_long;
-            alias __blkcnt_t = c_long;
-            alias __blkcnt64_t = long;
-            alias __timespec = timespec;
-            alias __time_t = time_t;
-        }
-        struct stat_t
+        version (CRuntime_Musl)
         {
-            __dev_t st_dev;
-            ushort __pad1;
-
-            static if (!__USE_FILE_OFFSET64)
-            {
-                __ino_t st_ino;
-            }
-            else
-            {
-                __ino_t __st_ino;
-            }
-            __mode_t st_mode;
-            __nlink_t st_nlink;
-            __uid_t st_uid;
-            __gid_t st_gid;
-            __dev_t st_rdev;
-            ushort __pad2;
-
-            static if (!__USE_FILE_OFFSET64)
-            {
-                __off_t st_size;
-            }
-            else
-            {
-                __off64_t st_size;
-            }
-            __blksize_t st_blksize;
-
-            static if (!__USE_FILE_OFFSET64)
-            {
-                __blkcnt_t st_blocks;
-            }
-            else
-            {
-                __blkcnt64_t st_blocks;
-            }
-
-            static if ( _DEFAULT_SOURCE || _XOPEN_SOURCE >= 700)
-            {
-                __timespec st_atim;
-                __timespec st_mtim;
-                __timespec st_ctim;
+            // Matches struct stat from musl arch/arm/bits/stat.h
+            // See: https://git.musl-libc.org/cgit/musl/tree/arch/arm/bits/stat.h?h=v1.2.3
+            //
+            // Type definitions from https://git.musl-libc.org/cgit/musl/tree/include/alltypes.h.in?h=v1.2.3
+            // with ARM-specific _Int64=long long and _Reg=int from
+            // https://git.musl-libc.org/cgit/musl/tree/arch/arm/bits/alltypes.h.in?h=v1.2.3#n3
+            //
+            // Key 64-bit LFS types (always 64-bit on musl):
+            //   dev_t     = unsigned _Int64 = unsigned long long  (line 31)
+            //   off_t     = _Int64          = long long           (line 29)
+            //   ino_t     = unsigned _Int64 = unsigned long long  (line 30)
+            //   blkcnt_t  = _Int64          = long long           (line 33)
+            //   blksize_t = long            = long (32-bit)       (line 32)
+            //   nlink_t   = unsigned _Reg   = unsigned int        (line 28)
+            struct stat_t
+            {
+                ulong st_dev;
+                int __st_dev_padding;
+                c_long __st_ino_truncated;
+                mode_t st_mode;
+                uint st_nlink;
+                uid_t st_uid;
+                gid_t st_gid;
+                ulong st_rdev;
+                int __st_rdev_padding;
+                long st_size;
+                c_long st_blksize;
+                long st_blocks;
+                struct __timespec32
+                {
+                    c_long tv_sec;
+                    c_long tv_nsec;
+                }
+                __timespec32 __st_atim32, __st_mtim32, __st_ctim32;
+                ulong st_ino;
+                timespec st_atim;
+                timespec st_mtim;
+                timespec st_ctim;
                 extern(D) @safe @property inout pure nothrow
                 {
                     ref inout(time_t) st_atime() return { return st_atim.tv_sec; }
@@ -640,30 +698,103 @@ version (linux)
                     ref inout(time_t) st_ctime() return { return st_ctim.tv_sec; }
                 }
             }
-            else
-            {
-                __time_t st_atime;
-                c_ulong st_atimensec;
-                __time_t st_mtime;
-                c_ulong st_mtimensec;
-                __time_t st_ctime;
-                c_ulong st_ctimensec;
-            }
+            static assert(stat_t.sizeof == 152);
+        }
+        else
+        {
+            private
+            {
+                alias __dev_t = ulong;
+                alias __ino_t = c_ulong;
+                alias __ino64_t = ulong;
+                alias __mode_t = uint;
+                alias __nlink_t = size_t;
+                alias __uid_t = uint;
+                alias __gid_t = uint;
+                alias __off_t = c_long;
+                alias __off64_t = long;
+                alias __blksize_t = c_long;
+                alias __blkcnt_t = c_long;
+                alias __blkcnt64_t = long;
+                alias __timespec = timespec;
+                alias __time_t = time_t;
+            }
+            struct stat_t
+            {
+                __dev_t st_dev;
+                ushort __pad1;
 
-            static if (!__USE_FILE_OFFSET64)
-            {
-                c_ulong __unused4;
-                c_ulong __unused5;
-            }
-            else
-            {
-                __ino64_t st_ino;
+                static if (!__USE_FILE_OFFSET64)
+                {
+                    __ino_t st_ino;
+                }
+                else
+                {
+                    __ino_t __st_ino;
+                }
+                __mode_t st_mode;
+                __nlink_t st_nlink;
+                __uid_t st_uid;
+                __gid_t st_gid;
+                __dev_t st_rdev;
+                ushort __pad2;
+
+                static if (!__USE_FILE_OFFSET64)
+                {
+                    __off_t st_size;
+                }
+                else
+                {
+                    __off64_t st_size;
+                }
+                __blksize_t st_blksize;
+
+                static if (!__USE_FILE_OFFSET64)
+                {
+                    __blkcnt_t st_blocks;
+                }
+                else
+                {
+                    __blkcnt64_t st_blocks;
+                }
+
+                static if ( _DEFAULT_SOURCE || _XOPEN_SOURCE >= 700)
+                {
+                    __timespec st_atim;
+                    __timespec st_mtim;
+                    __timespec st_ctim;
+                    extern(D) @safe @property inout pure nothrow
+                    {
+                        ref inout(time_t) st_atime() return { return st_atim.tv_sec; }
+                        ref inout(time_t) st_mtime() return { return st_mtim.tv_sec; }
+                        ref inout(time_t) st_ctime() return { return st_ctim.tv_sec; }
+                    }
+                }
+                else
+                {
+                    __time_t st_atime;
+                    c_ulong st_atimensec;
+                    __time_t st_mtime;
+                    c_ulong st_mtimensec;
+                    __time_t st_ctime;
+                    c_ulong st_ctimensec;
+                }
+
+                static if (!__USE_FILE_OFFSET64)
+                {
+                    c_ulong __unused4;
+                    c_ulong __unused5;
+                }
+                else
+                {
+                    __ino64_t st_ino;
+                }
             }
+                 static if (__USE_FILE_OFFSET64)
+                     static assert(stat_t.sizeof == 104);
+                 else
+                     static assert(stat_t.sizeof == 88);
         }
-        static if (__USE_FILE_OFFSET64)
-            static assert(stat_t.sizeof == 104);
-        else
-            static assert(stat_t.sizeof == 88);
     }
     else version (AArch64)
     {
@@ -1052,9 +1183,6 @@ version (linux)
         extern bool S_TYPEISSEM( stat_t* buf ) { return false; }
         extern bool S_TYPEISSHM( stat_t* buf ) { return false; }
     }
-
-    enum UTIME_NOW = 0x3fffffff;
-    enum UTIME_OMIT = 0x3ffffffe;
 }
 else version (Darwin)
 {
@@ -1187,9 +1315,6 @@ else version (FreeBSD)
     enum S_ISUID    = 0x800; // octal 0004000
     enum S_ISGID    = 0x400; // octal 0002000
     enum S_ISVTX    = 0x200; // octal 0001000
-
-    enum UTIME_NOW = -1;
-    enum UTIME_OMIT = -2;
 }
 else version (NetBSD)
 {
@@ -1485,10 +1610,6 @@ version (CRuntime_Glibc)
     extern (D) bool S_ISREG()( mode_t mode )  { return S_ISTYPE( mode, S_IFREG );  }
     extern (D) bool S_ISLNK()( mode_t mode )  { return S_ISTYPE( mode, S_IFLNK );  }
     extern (D) bool S_ISSOCK()( mode_t mode ) { return S_ISTYPE( mode, S_IFSOCK ); }
-
-    int utimensat(int dirfd, const char *pathname,
-        ref const(timespec)[2] times, int flags);
-    int futimens(int fd, ref const(timespec)[2] times);
 }
 else version (Darwin)
 {
@@ -1555,14 +1676,6 @@ else version (FreeBSD)
     extern (D) bool S_ISREG()( mode_t mode )  { return S_ISTYPE( mode, S_IFREG );  }
     extern (D) bool S_ISLNK()( mode_t mode )  { return S_ISTYPE( mode, S_IFLNK );  }
     extern (D) bool S_ISSOCK()( mode_t mode ) { return S_ISTYPE( mode, S_IFSOCK ); }
-
-    // Since FreeBSD 11:
-    version (none)
-    {
-        int utimensat(int dirfd, const char *pathname,
-            ref const(timespec)[2] times, int flags);
-        int futimens(int fd, ref const(timespec)[2] times);
-    }
 }
 else version (NetBSD)
 {
@@ -1722,10 +1835,6 @@ else version (CRuntime_Bionic)
     extern (D) bool S_ISREG()( uint mode )  { return S_ISTYPE( mode, S_IFREG );  }
     extern (D) bool S_ISLNK()( uint mode )  { return S_ISTYPE( mode, S_IFLNK );  }
     extern (D) bool S_ISSOCK()( uint mode ) { return S_ISTYPE( mode, S_IFSOCK ); }
-
-    // Added since Lollipop
-    int utimensat(int dirfd, const char *pathname,
-        ref const(timespec)[2] times, int flags);
 }
 else version (CRuntime_Musl)
 {
@@ -1761,9 +1870,6 @@ else version (CRuntime_Musl)
     extern (D) bool S_ISREG()( mode_t mode )  { return S_ISTYPE( mode, S_IFREG );  }
     extern (D) bool S_ISLNK()( mode_t mode )  { return S_ISTYPE( mode, S_IFLNK );  }
     extern (D) bool S_ISSOCK()( mode_t mode ) { return S_ISTYPE( mode, S_IFSOCK ); }
-
-    int utimensat(int dirfd, const char *pathname,
-        ref const(timespec)[2] times, int flags);
 }
 else version (CRuntime_UClibc)
 {
@@ -1797,10 +1903,6 @@ else version (CRuntime_UClibc)
     extern (D) bool S_ISREG()( mode_t mode )  { return S_ISTYPE( mode, S_IFREG );  }
     extern (D) bool S_ISLNK()( mode_t mode )  { return S_ISTYPE( mode, S_IFLNK );  }
     extern (D) bool S_ISSOCK()( mode_t mode ) { return S_ISTYPE( mode, S_IFSOCK ); }
-
-    int utimensat(int dirfd, const char *pathname,
-    ref const(timespec)[2] times, int flags);
-    int futimens(int fd, ref const(timespec)[2] times);
 }
 else
 {
@@ -1810,12 +1912,18 @@ else
 /*
 int    chmod(const scope char*, mode_t);
 int    fchmod(int, mode_t);
-int    fstat(int, stat*);
-int    lstat(const scope char*, stat*);
+int    fchmodat(int, const scope char*, mode_t, int);
+int    fstat(int, stat_t*);
+int    fstatat(int, const scope char*, stat_t*, int);
+int    futimens(int, ref const(timespec)[2]);
+int    lstat(const scope char*, stat_t*);
 int    mkdir(const scope char*, mode_t);
+int    mkdirat(int, const scope char*, mode_t);
 int    mkfifo(const scope char*, mode_t);
-int    stat(const scope char*, stat*);
+int    mkfifoat(int, const scope char*, mode_t);
+int    stat(const scope char*, stat_t*);
 mode_t umask(mode_t);
+int    utimensat(int, const scope char*, ref const(timespec)[2], int);
 */
 
 int    chmod(const scope char*, mode_t);
@@ -1839,6 +1947,10 @@ version (CRuntime_Glibc)
 
     int   stat64(const scope char*, stat_t*);
     alias stat64 stat;
+
+    int   fstatat64(int, const scope char*, stat_t*, int);
+    alias fstatat64 fstatat;
+
   }
   else
   {
@@ -1846,6 +1958,11 @@ version (CRuntime_Glibc)
     int   lstat(const scope char*, stat_t*);
     int   stat(const scope char*, stat_t*);
   }
+    int   fchmodat(int, const scope char*, mode_t, int);
+    int   futimens(int, ref const(timespec)[2]);
+    int   mkdirat(int, const scope char*, mode_t);
+    int   mkfifoat(int, const scope char*, mode_t);
+    int   utimensat(int, const scope char*, ref const(timespec)[2], int);
 }
 else version (Solaris)
 {
@@ -1854,12 +1971,14 @@ else version (Solaris)
         int fstat(int, stat_t*) @trusted;
         int lstat(const scope char*, stat_t*);
         int stat(const scope char*, stat_t*);
+        int fstatat(int, const scope char*, stat_t*, int);
 
         static if (__USE_LARGEFILE64)
         {
             alias fstat fstat64;
             alias lstat lstat64;
             alias stat stat64;
+            alias fstatat fstatat64;
         }
     }
     else
@@ -1874,14 +1993,23 @@ else version (Solaris)
 
             int   stat64(const scope char*, stat_t*);
             alias stat64 stat;
+
+            int fstatat64(int, const scope char*, stat_t*, int);
+            alias fstatat64 fstatat;
         }
         else
         {
             int fstat(int, stat_t*) @trusted;
             int lstat(const scope char*, stat_t*);
             int stat(const scope char*, stat_t*);
+            int fstatat(int, const scope char*, stat_t*, int);
         }
     }
+    int   fchmodat(int, const scope char*, mode_t, int);
+    int   futimens(int, ref const(timespec)[2]);
+    int   mkdirat(int, const scope char*, mode_t);
+    int   mkfifoat(int, const scope char*, mode_t);
+    int   utimensat(int, const scope char*, ref const(timespec)[2], int);
 }
 else version (Darwin)
 {
@@ -1908,6 +2036,12 @@ else version (Darwin)
         int lstat(const scope char*, stat_t*);
         int stat(const scope char*, stat_t*);
     }
+    int   fchmodat(int, const scope char*, mode_t, int);
+    int   fstatat(int, const scope char*, stat_t*, int);
+    int   futimens(int, ref const(timespec)[2]);
+    int   mkdirat(int, const scope char*, mode_t);
+    int   mkfifoat(int, const scope char*, mode_t);
+    int   utimensat(int, const scope char*, ref const(timespec)[2], int);
 }
 else version (FreeBSD)
 {
@@ -1932,6 +2066,18 @@ else version (FreeBSD)
             pragma(mangle, "stat@FBSD_1.0")  int   stat(const scope char*, stat_t*);
         }
     }
+    static if (__FreeBSD_version >= 800000)
+    {
+        int fchmodat(int, const scope char*, mode_t, int);
+        int fstatat(int, const scope char*, stat_t*, int);
+        int mkdirat(int, const scope char*, mode_t);
+        int mkfifoat(int, const scope char*, mode_t);
+    }
+    static if (__FreeBSD_version >= 1003000)
+    {
+        int futimens(int, ref const(timespec)[2]);
+        int utimensat(int, const scope char*, ref const(timespec)[2], int);
+    }
 }
 else version (NetBSD)
 {
@@ -1941,24 +2087,48 @@ else version (NetBSD)
     alias __fstat50 fstat;
     alias __lstat50 lstat;
     alias __stat50 stat;
+    int   fchmodat(int, const scope char*, mode_t, int);
+    int   fstatat(int, const scope char*, stat_t*, int);
+    int   futimens(int, ref const(timespec)[2]);
+    int   mkdirat(int, const scope char*, mode_t);
+    int   mkfifoat(int, const scope char*, mode_t);
+    int   utimensat(int, const scope char*, ref const(timespec)[2], int);
 }
 else version (OpenBSD)
 {
     int   fstat(int, stat_t*);
     int   lstat(const scope char*, stat_t*);
     int   stat(const scope char*, stat_t*);
+    int   fchmodat(int, const scope char*, mode_t, int);
+    int   fstatat(int, const scope char*, stat_t*, int);
+    int   futimens(int, ref const(timespec)[2]);
+    int   mkdirat(int, const scope char*, mode_t);
+    int   mkfifoat(int, const scope char*, mode_t);
+    int   utimensat(int, const scope char*, ref const(timespec)[2], int);
 }
 else version (DragonFlyBSD)
 {
     int   fstat(int, stat_t*);
     int   lstat(const scope char*, stat_t*);
     int   stat(const scope char*, stat_t*);
+    int   fchmodat(int, const scope char*, mode_t, int);
+    int   fstatat(int, const scope char*, stat_t*, int);
+    int   futimens(int, ref const(timespec)[2]);
+    int   mkdirat(int, const scope char*, mode_t);
+    int   mkfifoat(int, const scope char*, mode_t);
+    int   utimensat(int, const scope char*, ref const(timespec)[2], int);
 }
 else version (CRuntime_Bionic)
 {
     int   fstat(int, stat_t*) @trusted;
     int   lstat(const scope char*, stat_t*);
     int   stat(const scope char*, stat_t*);
+    int   fchmodat(int, const scope char*, mode_t, int);
+    int   fstatat(int, const scope char*, stat_t*, int);
+    int   futimens(int, ref const(timespec)[2]);
+    int   mkdirat(int, const scope char*, mode_t);
+    int   mkfifoat(int, const scope char*, mode_t);
+    int   utimensat(int, const scope char*, ref const(timespec)[2], int);
 }
 else version (CRuntime_Musl)
 {
@@ -1968,10 +2138,17 @@ else version (CRuntime_Musl)
     int fstat(int, stat_t*);
     pragma(mangle, muslRedirTime64Mangle!("lstat", "__lstat_time64"))
     int lstat(const scope char*, stat_t*);
+    pragma(mangle, muslRedirTime64Mangle!("fstatat", "__fstatat_time64"))
+    int   fstatat(int, const scope char*, stat_t*, int);
 
     alias fstat fstat64;
     alias lstat lstat64;
     alias stat stat64;
+    int   fchmodat(int, const scope char*, mode_t, int);
+    int   futimens(int, ref const(timespec)[2]);
+    int   mkdirat(int, const scope char*, mode_t);
+    int   mkfifoat(int, const scope char*, mode_t);
+    int   utimensat(int, const scope char*, ref const(timespec)[2], int);
 }
 else version (CRuntime_UClibc)
 {
@@ -1985,14 +2162,68 @@ else version (CRuntime_UClibc)
 
     int   stat64(const scope char*, stat_t*);
     alias stat64 stat;
+
+    int   fstatat64(int, const scope char*, stat_t*, int);
+    alias fstatat64 fstatat;
   }
   else
   {
     int   fstat(int, stat_t*) @trusted;
     int   lstat(const scope char*, stat_t*);
     int   stat(const scope char*, stat_t*);
+    int   fstatat(int, const scope char*, stat_t*, int);
   }
+    int   fchmodat(int, const scope char*, mode_t, int);
+    int   futimens(int, ref const(timespec)[2]);
+    int   mkdirat(int, const scope char*, mode_t);
+    int   mkfifoat(int, const scope char*, mode_t);
+    int   utimensat(int, const scope char*, ref const(timespec)[2], int);
+}
+
+/*
+UTIME_NOW
+UTIME_OMIT
+*/
+version (linux)
+{
+    enum UTIME_NOW = 0x3fffffff;
+    enum UTIME_OMIT = 0x3ffffffe;
+}
+else version (Darwin)
+{
+    enum UTIME_NOW = -1;
+    enum UTIME_OMIT = -2;
+}
+else version (FreeBSD)
+{
+    enum UTIME_NOW = -1;
+    enum UTIME_OMIT = -2;
+}
+else version (NetBSD)
+{
+    enum UTIME_NOW = 0x3fffffff;
+    enum UTIME_OMIT = 0x3ffffffe;
 }
+else version (OpenBSD)
+{
+    enum UTIME_NOW = -2;
+    enum UTIME_OMIT = -1;
+}
+else version (DragonFlyBSD)
+{
+    enum UTIME_NOW = -1;
+    enum UTIME_OMIT = -2;
+}
+else version (Solaris)
+{
+    enum UTIME_NOW = -1;
+    enum UTIME_OMIT = -2;
+}
+else
+{
+    static assert(false, "Unsupported platform");
+}
+
 
 //
 // Typed Memory Objects (TYM)
@@ -2101,15 +2332,18 @@ else
 
 /*
 int mknod(const scope char*, mode_t, dev_t);
+int mknodat(int, const scope char*, mode_t, dev_t);
 */
 
 version (CRuntime_Glibc)
 {
     int mknod(const scope char*, mode_t, dev_t);
+    int mknodat(int, const scope char*, mode_t, dev_t);
 }
 else version (Darwin)
 {
     int mknod(const scope char*, mode_t, dev_t);
+    int mknodat(int, const scope char*, mode_t, dev_t);
 }
 else version (FreeBSD)
 {
@@ -2124,34 +2358,42 @@ else version (FreeBSD)
         else
             pragma(mangle, "mknod@FBSD_1.0") int mknod(const scope char*, mode_t, dev_t);
     }
+    int mknodat(int, const scope char*, mode_t, dev_t);
 }
 else version (NetBSD)
 {
     int mknod(const scope char*, mode_t, dev_t);
+    int mknodat(int, const scope char*, mode_t, dev_t);
 }
 else version (OpenBSD)
 {
     int mknod(const scope char*, mode_t, dev_t);
+    int mknodat(int, const scope char*, mode_t, dev_t);
 }
 else version (DragonFlyBSD)
 {
     int mknod(const scope char*, mode_t, dev_t);
+    int mknodat(int, const scope char*, mode_t, dev_t);
 }
 else version (Solaris)
 {
     int mknod(const scope char*, mode_t, dev_t);
+    int mknodat(int, const scope char*, mode_t, dev_t);
 }
 else version (CRuntime_Bionic)
 {
     int mknod(const scope char*, mode_t, dev_t);
+    int mknodat(int, const scope char*, mode_t, dev_t);
 }
 else version (CRuntime_Musl)
 {
     int mknod(const scope char*, mode_t, dev_t);
+    int mknodat(int, const scope char*, mode_t, dev_t);
 }
 else version (CRuntime_UClibc)
 {
     int mknod(const scope char*, mode_t, dev_t);
+    int mknodat(int, const scope char*, mode_t, dev_t);
 }
 else
 {
index 642b3839aa0877b6f904069fa4033b49e33559eb..b8a5868546fd3bf8b8a155b0c75e1e5a5decbdb7 100644 (file)
@@ -87,7 +87,19 @@ uid_t
 
 version (linux)
 {
-  static if ( __USE_FILE_OFFSET64 )
+  // Musl always uses 64-bit off_t, blkcnt_t, ino_t on all arches (LFS by default).
+  // See: https://git.musl-libc.org/cgit/musl/tree/include/alltypes.h.in?h=v1.2.3#n29
+  //   off_t:    _Int64           -> long long (signed 64-bit)
+  //   ino_t:    unsigned _Int64  -> unsigned long long (64-bit)
+  //   blkcnt_t: _Int64           -> long long (signed 64-bit)
+  // For ARM, _Int64 = long long: https://git.musl-libc.org/cgit/musl/tree/arch/arm/bits/alltypes.h.in?h=v1.2.3#n3
+  version (CRuntime_Musl)
+  {
+    alias long      blkcnt_t;
+    alias ulong     ino_t;
+    alias long      off_t;
+  }
+  else static if ( __USE_FILE_OFFSET64 )
   {
     alias long      blkcnt_t;
     alias ulong     ino_t;
@@ -99,11 +111,59 @@ version (linux)
     alias ulong_t   ino_t;
     alias slong_t   off_t;
   }
-    alias slong_t   blksize_t;
+    // musl overrides blksize_t to int on some 64-bit architectures.
+    // Default: long (https://git.musl-libc.org/cgit/musl/tree/include/alltypes.h.in?h=v1.2.3#n32)
+    // AArch64: int (https://git.musl-libc.org/cgit/musl/tree/arch/aarch64/bits/alltypes.h.in?h=v1.2.3#n18)
+    // RISCV64: int (https://git.musl-libc.org/cgit/musl/tree/arch/riscv64/bits/alltypes.h.in?h=v1.2.3#n12)
+    // LoongArch64: int (https://git.musl-libc.org/cgit/musl/tree/arch/loongarch64/bits/alltypes.h.in?id=522bd54e#n18)
+    version (CRuntime_Musl)
+    {
+        version (AArch64)
+            alias int blksize_t;
+        else version (RISCV64)
+            alias int blksize_t;
+        else version (LoongArch64)
+            alias int blksize_t;
+        else
+            alias slong_t blksize_t;
+    }
+    else
+        alias slong_t blksize_t;
+
     alias ulong     dev_t;
     alias uint      gid_t;
     alias uint      mode_t;
-    alias ulong_t   nlink_t;
+
+    // musl defines nlink_t as unsigned _Reg (= unsigned int on 32-bit, unsigned long on 64-bit),
+    // with arch-specific overrides.
+    // Default: unsigned _Reg (https://git.musl-libc.org/cgit/musl/tree/include/alltypes.h.in?h=v1.2.3#n28)
+    // MIPS64: unsigned (uint) (https://git.musl-libc.org/cgit/musl/tree/arch/mips64/bits/alltypes.h.in?h=v1.2.3#n22)
+    // X86_64: _Reg=long, so unsigned long (https://git.musl-libc.org/cgit/musl/tree/arch/x86_64/bits/alltypes.h.in?h=v1.2.3#n3)
+    version (CRuntime_Musl)
+    {
+        version (MIPS64)
+            alias uint nlink_t;
+        else version (X86_64)
+            alias ulong nlink_t;
+        else
+            alias uint nlink_t;
+    }
+    else
+    {
+        version (X86_64)
+            alias ulong nlink_t;
+        else version (S390)
+            alias size_t nlink_t;
+        else version (PPC64)
+            alias size_t nlink_t;
+        else version (MIPS64)
+            alias size_t nlink_t;
+        else version (HPPA64)
+            alias size_t nlink_t;
+        else
+            alias uint nlink_t;
+    }
+
     alias int       pid_t;
     //size_t (defined in core.stdc.stddef)
     alias c_long    ssize_t;
@@ -111,6 +171,7 @@ version (linux)
 
     version (CRuntime_Musl)
     {
+        static assert(off_t.sizeof == 8);
         /**
          * Musl versions before v1.2.0 (up to v1.1.24) had different
          * definitions for `time_t` for 32 bits.
@@ -299,7 +360,17 @@ useconds_t
 
 version (linux)
 {
-  static if ( __USE_FILE_OFFSET64 )
+  // Musl always uses 64-bit fsblkcnt_t, fsfilcnt_t on all arches (LFS by default).
+  // See: https://git.musl-libc.org/cgit/musl/tree/include/alltypes.h.in?h=v1.2.3#n34
+  //   fsblkcnt_t: unsigned _Int64  -> unsigned long long (64-bit)
+  //   fsfilcnt_t: unsigned _Int64  -> unsigned long long (64-bit)
+  // For ARM, _Int64 = long long: https://git.musl-libc.org/cgit/musl/tree/arch/arm/bits/alltypes.h.in?h=v1.2.3#n3
+  version (CRuntime_Musl)
+  {
+    alias ulong     fsblkcnt_t;
+    alias ulong     fsfilcnt_t;
+  }
+  else static if ( __USE_FILE_OFFSET64 )
   {
     alias ulong     fsblkcnt_t;
     alias ulong     fsfilcnt_t;
@@ -992,6 +1063,7 @@ else version (DragonFlyBSD)
 else version (Solaris)
 {
     alias uint pthread_t;
+    alias int lwpid_t; // non-standard
 
     struct pthread_attr_t
     {
index f49764d1964a089d904c67336a826474200e1916..732c18a97c89e5c685e5497e03691e589cdceda2 100644 (file)
@@ -16,6 +16,7 @@
 module core.sys.posix.time;
 
 import core.sys.posix.config;
+import core.sys.posix.endian;
 public import core.stdc.time;
 public import core.sys.posix.sys.types;
 public import core.sys.posix.signal; // for sigevent
@@ -201,7 +202,11 @@ version (linux)
     struct timespec
     {
         time_t  tv_sec;
+        version (CRuntime_Musl)
+            int : 8 * (time_t.sizeof - c_long.sizeof) * (BYTE_ORDER == BIG_ENDIAN);
         c_long  tv_nsec;
+        version (CRuntime_Musl)
+            int : 8 * (time_t.sizeof - c_long.sizeof) * (BYTE_ORDER != BIG_ENDIAN);
     }
 }
 else version (Darwin)
@@ -461,6 +466,8 @@ else version (CRuntime_Bionic)
 }
 else version (CRuntime_Musl)
 {
+    static assert(timespec.sizeof == 16);
+
     alias int clockid_t;
     alias void* timer_t;
 
@@ -482,6 +489,7 @@ else version (CRuntime_Musl)
     enum CLOCK_SGI_CYCLE = 10;
     enum CLOCK_TAI = 11;
 
+    pragma(mangle, muslRedirTime64Mangle!("nanosleep", "__nanosleep_time64"))
     int nanosleep(const scope timespec*, timespec*);
 
     pragma(mangle, muslRedirTime64Mangle!("clock_getres", "__clock_getres_time64"))
index ce2166307531d8f78f5365ec27df86e4314f547e..451401d3904258a928e61c77956e8d311cb3b20c 100644 (file)
@@ -1361,202 +1361,10 @@ else version (DragonFlyBSD)
 }
 else version (Solaris)
 {
-    import core.stdc.stdint;
-
-    alias uint[4] upad128_t;
-
-    version (SPARC64)
-    {
-        enum _NGREG = 21;
-        alias long greg_t;
-    }
-    else version (SPARC)
-    {
-        enum _NGREG = 19;
-        alias int greg_t;
-    }
-    else version (X86_64)
-    {
-        enum _NGREG = 28;
-        alias long greg_t;
-    }
-    else version (X86)
-    {
-        enum _NGREG = 19;
-        alias int greg_t;
-    }
-    else
-        static assert(0, "unimplemented");
-
-    alias greg_t[_NGREG] gregset_t;
-
-    version (SPARC64)
-    {
-        private
-        {
-            struct _fpq
-            {
-                uint *fpq_addr;
-                uint fpq_instr;
-            }
-
-            struct fq
-            {
-                union
-                {
-                    double whole;
-                    _fpq fpq;
-                }
-            }
-        }
-
-        struct fpregset_t
-        {
-            union
-            {
-                uint[32]   fpu_regs;
-                double[32] fpu_dregs;
-                real[16]   fpu_qregs;
-            }
-            fq    *fpu_q;
-            ulong fpu_fsr;
-            ubyte fpu_qcnt;
-            ubyte fpu_q_entrysize;
-            ubyte fpu_en;
-        }
-    }
-    else version (SPARC)
-    {
-        private
-        {
-            struct _fpq
-            {
-                uint *fpq_addr;
-                uint fpq_instr;
-            }
-
-            struct fq
-            {
-                union
-                {
-                    double whole;
-                    _fpq fpq;
-                }
-            }
-        }
-
-        struct fpregset_t
-        {
-            union
-            {
-                uint[32]   fpu_regs;
-                double[16] fpu_dregs;
-            }
-            fq    *fpu_q;
-            uint  fpu_fsr;
-            ubyte fpu_qcnt;
-            ubyte fpu_q_entrysize;
-            ubyte fpu_en;
-        }
-    }
-    else version (X86_64)
-    {
-        private
-        {
-            union _u_st
-            {
-                ushort[5]   fpr_16;
-                upad128_t   __fpr_pad;
-            }
-        }
-
-        struct fpregset_t
-        {
-            union fp_reg_set
-            {
-                struct fpchip_state
-                {
-                    ushort          cw;
-                    ushort          sw;
-                    ubyte           fctw;
-                    ubyte           __fx_rsvd;
-                    ushort          fop;
-                    ulong           rip;
-                    ulong           rdp;
-                    uint            mxcsr;
-                    uint            mxcsr_mask;
-                    _u_st[8]        st;
-                    upad128_t[16]   xmm;
-                    upad128_t[6]    __fx_ign2;
-                    uint            status;
-                    uint            xstatus;
-                }
-                uint[130]   f_fpregs;
-            }
-        }
-    }
-    else version (X86)
-    {
-        struct fpregset_t
-        {
-            union u_fp_reg_set
-            {
-                struct s_fpchip_state
-                {
-                    uint[27]        state;
-                    uint            status;
-                    uint            mxcsr;
-                    uint            xstatus;
-                    uint[2]         __pad;
-                    upad128_t[8]    xmm;
-                }
-                s_fpchip_state    fpchip_state;
-
-                struct s_fp_emul_space
-                {
-                    ubyte[246]  fp_emul;
-                    ubyte[2]    fp_epad;
-                }
-                s_fp_emul_space   fp_emul_space;
-                uint[95]        f_fpregs;
-            }
-        u_fp_reg_set fp_reg_set;
-        }
-    }
-    else
-        static assert(0, "unimplemented");
-
     version (SPARC_Any)
     {
-        private
-        {
-            struct rwindow
-            {
-                greg_t[8]     rw_local;
-                greg_t[8]     rw_in;
-            }
-
-            struct gwindows_t
-            {
-                int         wbcnt;
-                greg_t[31] *spbuf;
-                rwindow[31] wbuf;
-            }
-
-            struct xrs_t
-            {
-                uint         xrs_id;
-                caddr_t      xrs_ptr;
-            }
-
-            struct cxrs_t
-            {
-                uint         cxrs_id;
-                caddr_t      cxrs_ptr;
-            }
-
-            alias int64_t[16] asrset_t;
-        }
+        import core.sys.solaris.sys.regset : gregset_t, fpregset_t,
+               gwindows_t, xrs_t, asrset_t, cxrs_t;
 
         struct mcontext_t
         {
@@ -1579,14 +1387,7 @@ else version (Solaris)
     }
     else version (X86_Any)
     {
-        private
-        {
-            struct xrs_t
-            {
-                uint         xrs_id;
-                caddr_t      xrs_ptr;
-            }
-        }
+        import core.sys.solaris.sys.regset : gregset_t, fpregset_t, xrs_t;
 
         struct mcontext_t
         {
index d1300e1e083cac7bbc25309dd990ceff70076bf7..860630b422553807f05899388eb1e451613acb54 100644 (file)
@@ -4,7 +4,7 @@
  * Copyright: Copyright Sean Kelly 2005 - 2009.
  * License:   $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
  * Authors:   Sean Kelly
- * Standards: The Open Group Base Specifications Issue 6, IEEE Std 1003.1, 2004 Edition
+ * Standards: The Open Group Base Specifications Issue 8, IEEE Std 1003.1, 2024 Edition
  */
 
 /*          Copyright Sean Kelly 2005 - 2009.
@@ -50,18 +50,24 @@ int     close(int) @trusted;
 size_t  confstr(int, char*, size_t);
 int     dup(int) @trusted;
 int     dup2(int, int) @trusted;
+//int     dup3(int, int, int) @trusted;
 int     execl(const scope char*, const scope char*, ...);
 int     execle(const scope char*, const scope char*, ...);
 int     execlp(const scope char*, const scope char*, ...);
 int     execv(const scope char*, const scope char**);
 int     execve(const scope char*, const scope char**, const scope char**);
 int     execvp(const scope char*, const scope char**);
-void    _exit(int) @trusted;
+noreturn _exit(int) @trusted;
+//int     faccessat(int, const scope char*, int, int);
 int     fchown(int, uid_t, gid_t) @trusted;
+//int     fchownat(int, const scope char*, uid_t, gid_t, int);
+//int     fexecve(int, const scope char**, const scope char**);
 pid_t   fork() @trusted;
+//pid_t   _Fork() @trusted;
 c_long  fpathconf(int, int) @trusted;
 //int     ftruncate(int, off_t);
 char*   getcwd(char*, size_t);
+//int     getentropy(void*, size_t);
 gid_t   getegid() @trusted;
 uid_t   geteuid() @trusted;
 gid_t   getgid() @trusted;
@@ -73,31 +79,40 @@ int     getopt(int, const scope char**, const scope char*);
 pid_t   getpgrp() @trusted;
 pid_t   getpid() @trusted;
 pid_t   getppid() @trusted;
+//int     getresgid(gid_t*, gid_t*, gid_t*);
+//int     getresuid(uid_t*, uid_t*, uid_t*);
 uid_t   getuid() @trusted;
 int     isatty(int) @trusted;
 int     link(const scope char*, const scope char*);
+//int     linkat(int, const scope char*, int, const scope char*, int);
 //off_t   lseek(int, off_t, int);
 c_long  pathconf(const scope char*, int);
 int     pause() @trusted;
 int     pipe(ref int[2]) @trusted;
+//int     pipe2(ref int[2], int) @trusted;
 ssize_t read(int, void*, size_t);
 ssize_t readlink(const scope char*, char*, size_t);
+//ssize_t readlinkat(int, const scope char*, char*, size_t);
 int     rmdir(const scope char*);
 int     setegid(gid_t) @trusted;
 int     seteuid(uid_t) @trusted;
 int     setgid(gid_t) @trusted;
 int     setgroups(size_t, const scope gid_t*) @trusted;
 int     setpgid(pid_t, pid_t) @trusted;
+//int     setresgid(gid_t, gid_t, gid_t) @trusted;
+//int     setresuid(uid_t, uid_t, uid_t) @trusted;
 pid_t   setsid() @trusted;
 int     setuid(uid_t) @trusted;
 uint    sleep(uint) @trusted;
 int     symlink(const scope char*, const scope char*);
+//int     symlinkat(const scope char*, int, const scope char*);
 c_long  sysconf(int) @trusted;
 pid_t   tcgetpgrp(int) @trusted;
 int     tcsetpgrp(int, pid_t) @trusted;
 char*   ttyname(int) @trusted;
 int     ttyname_r(int, char*, size_t);
 int     unlink(const scope char*);
+//int     unlinkat(int, const scope char*, int);
 ssize_t write(int, const scope void*, size_t);
 
 version (CRuntime_Glibc)
@@ -120,26 +135,105 @@ version (CRuntime_Glibc)
   {
     int   ftruncate(int, off_t) @trusted;
   }
+    int   faccessat(int, const scope char*, int, int);
+    int   fchownat(int, const scope char*, uid_t, gid_t, int);
+    int   fexecve(int, const scope char**, const scope char**);
+    pid_t _Fork() @trusted;
+    int   getentropy(void*, size_t);
+    int   getresgid(gid_t*, gid_t*, gid_t*);
+    int   getresuid(uid_t*, uid_t*, uid_t*);
+    int   linkat(int, const scope char*, int, const scope char*, int);
+    int   pipe2(ref int[2], int) @trusted;
+    ssize_t readlinkat(int, const scope char*, char*, size_t);
+    int   setresgid(gid_t, gid_t, gid_t) @trusted;
+    int   setresuid(uid_t, uid_t, uid_t) @trusted;
+    int   symlinkat(const scope char*, int, const scope char*);
+    int   unlinkat(int, const scope char*, int);
 }
 else version (FreeBSD)
 {
     off_t lseek(int, off_t, int) @trusted;
     int   ftruncate(int, off_t) @trusted;
+    int   getresgid(gid_t*, gid_t*, gid_t*);
+    int   getresuid(uid_t*, uid_t*, uid_t*);
+    int   setresgid(gid_t, gid_t, gid_t) @trusted;
+    int   setresuid(uid_t, uid_t, uid_t) @trusted;
+
+    import core.sys.freebsd.config : __FreeBSD_version;
+  static if (__FreeBSD_version >= 800000)
+  {
+    int   faccessat(int, const scope char*, int, int);
+    int   fchownat(int, const scope char*, uid_t, gid_t, int);
+    int   fexecve(int, const scope char**, const scope char**);
+    int   linkat(int, const scope char*, int, const scope char*, int);
+    ssize_t readlinkat(int, const scope char*, char*, size_t);
+    int   symlinkat(const scope char*, int, const scope char*);
+    int   unlinkat(int, const scope char*, int);
+  }
+  static if (__FreeBSD_version >= 1000000)
+  {
+    int   dup3(int, int, int) @trusted;
+    int   pipe2(ref int[2], int) @trusted;
+  }
+  static if (__FreeBSD_version >= 1200000)
+  {
+    int   getentropy(void*, size_t);
+  }
+  static if (__FreeBSD_version >= 1301000)
+  {
+    pid_t _Fork() @trusted;
+  }
 }
 else version (NetBSD)
 {
     off_t lseek(int, off_t, int) @trusted;
     int   ftruncate(int, off_t) @trusted;
+    int   faccessat(int, const scope char*, int, int);
+    int   fchownat(int, const scope char*, uid_t, gid_t, int);
+    int   fexecve(int, const scope char**, const scope char**);
+    int   getentropy(void*, size_t);
+    int   linkat(int, const scope char*, int, const scope char*, int);
+    int   pipe2(ref int[2], int) @trusted;
+    ssize_t readlinkat(int, const scope char*, char*, size_t);
+    int   symlinkat(const scope char*, int, const scope char*);
+    int   unlinkat(int, const scope char*, int);
 }
 else version (OpenBSD)
 {
     off_t lseek(int, off_t, int) @trusted;
     int   ftruncate(int, off_t) @trusted;
+    int   dup3(int, int, int) @trusted;
+    int   faccessat(int, const scope char*, int, int);
+    int   fchownat(int, const scope char*, uid_t, gid_t, int);
+    int   getentropy(void*, size_t);
+    int   getresgid(gid_t*, gid_t*, gid_t*);
+    int   getresuid(uid_t*, uid_t*, uid_t*);
+    int   linkat(int, const scope char*, int, const scope char*, int);
+    int   pipe2(ref int[2], int) @trusted;
+    ssize_t readlinkat(int, const scope char*, char*, size_t);
+    int   setresgid(gid_t, gid_t, gid_t) @trusted;
+    int   setresuid(uid_t, uid_t, uid_t) @trusted;
+    int   symlinkat(const scope char*, int, const scope char*);
+    int   unlinkat(int, const scope char*, int);
 }
 else version (DragonFlyBSD)
 {
     off_t lseek(int, off_t, int) @trusted;
     int   ftruncate(int, off_t) @trusted;
+    int   dup3(int, int, int) @trusted;
+    int   faccessat(int, const scope char*, int, int);
+    int   fchownat(int, const scope char*, uid_t, gid_t, int);
+    int   fexecve(int, const scope char**, const scope char**);
+    int   getentropy(void*, size_t);
+    int   getresgid(gid_t*, gid_t*, gid_t*);
+    int   getresuid(uid_t*, uid_t*, uid_t*);
+    int   linkat(int, const scope char*, int, const scope char*, int);
+    int   pipe2(ref int[2], int) @trusted;
+    ssize_t readlinkat(int, const scope char*, char*, size_t);
+    int   setresgid(gid_t, gid_t, gid_t) @trusted;
+    int   setresuid(uid_t, uid_t, uid_t) @trusted;
+    int   symlinkat(const scope char*, int, const scope char*);
+    int   unlinkat(int, const scope char*, int);
 }
 else version (Solaris)
 {
@@ -167,16 +261,47 @@ else version (Solaris)
             int     ftruncate(int, off_t) @trusted;
         }
     }
+    int   dup3(int, int, int) @trusted;
+    int   faccessat(int, const scope char*, int, int);
+    int   fchownat(int, const scope char*, uid_t, gid_t, int);
+    int   fexecve(int, const scope char**, const scope char**);
+    int   getentropy(void*, size_t);
+    int   linkat(int, const scope char*, int, const scope char*, int);
+    int   pipe2(ref int[2], int) @trusted;
+    ssize_t readlinkat(int, const scope char*, char*, size_t);
+    int   symlinkat(const scope char*, int, const scope char*);
+    int   unlinkat(int, const scope char*, int);
 }
 else version (Darwin)
 {
     off_t lseek(int, off_t, int) @trusted;
     int   ftruncate(int, off_t) @trusted;
+    int   faccessat(int, const scope char*, int, int);
+    int   fchownat(int, const scope char*, uid_t, gid_t, int);
+    int   linkat(int, const scope char*, int, const scope char*, int);
+    ssize_t readlinkat(int, const scope char*, char*, size_t);
+    int   symlinkat(const scope char*, int, const scope char*);
+    int   unlinkat(int, const scope char*, int);
 }
 else version (CRuntime_Bionic)
 {
     off_t lseek(int, off_t, int) @trusted;
     int   ftruncate(int, off_t) @trusted;
+    int   dup3(int, int, int) @trusted;
+    int   faccessat(int, const scope char*, int, int);
+    int   fchownat(int, const scope char*, uid_t, gid_t, int);
+    int   fexecve(int, const scope char**, const scope char**);
+    pid_t _Fork() @trusted;
+    int   getentropy(void*, size_t);
+    int   getresgid(gid_t*, gid_t*, gid_t*);
+    int   getresuid(uid_t*, uid_t*, uid_t*);
+    int   linkat(int, const scope char*, int, const scope char*, int);
+    int   pipe2(ref int[2], int) @trusted;
+    ssize_t readlinkat(int, const scope char*, char*, size_t);
+    int   setresgid(gid_t, gid_t, gid_t) @trusted;
+    int   setresuid(uid_t, uid_t, uid_t) @trusted;
+    int   symlinkat(const scope char*, int, const scope char*);
+    int   unlinkat(int, const scope char*, int);
 }
 else version (CRuntime_Musl)
 {
@@ -184,6 +309,21 @@ else version (CRuntime_Musl)
     off_t lseek(int, off_t, int) @trusted;
     alias ftruncate ftruncate64;
     alias lseek lseek64;
+    int   dup3(int, int, int) @trusted;
+    int   faccessat(int, const scope char*, int, int);
+    int   fchownat(int, const scope char*, uid_t, gid_t, int);
+    int   fexecve(int, const scope char**, const scope char**);
+    pid_t _Fork() @trusted;
+    int   getentropy(void*, size_t);
+    int   getresgid(gid_t*, gid_t*, gid_t*);
+    int   getresuid(uid_t*, uid_t*, uid_t*);
+    int   linkat(int, const scope char*, int, const scope char*, int);
+    int   pipe2(ref int[2], int) @trusted;
+    ssize_t readlinkat(int, const scope char*, char*, size_t);
+    int   setresgid(gid_t, gid_t, gid_t) @trusted;
+    int   setresuid(uid_t, uid_t, uid_t) @trusted;
+    int   symlinkat(const scope char*, int, const scope char*);
+    int   unlinkat(int, const scope char*, int);
 }
 else version (CRuntime_UClibc)
 {
@@ -205,6 +345,18 @@ else version (CRuntime_UClibc)
   {
     int   ftruncate(int, off_t) @trusted;
   }
+    int   dup3(int, int, int) @trusted;
+    int   faccessat(int, const scope char*, int, int);
+    int   fchownat(int, const scope char*, uid_t, gid_t, int);
+    int   getresgid(gid_t*, gid_t*, gid_t*);
+    int   getresuid(uid_t*, uid_t*, uid_t*);
+    int   linkat(int, const scope char*, int, const scope char*, int);
+    int   pipe2(ref int[2], int) @trusted;
+    ssize_t readlinkat(int, const scope char*, char*, size_t);
+    int   setresgid(gid_t, gid_t, gid_t) @trusted;
+    int   setresuid(uid_t, uid_t, uid_t) @trusted;
+    int   symlinkat(const scope char*, int, const scope char*);
+    int   unlinkat(int, const scope char*, int);
 }
 
 version (CRuntime_Glibc)
diff --git a/libphobos/libdruntime/core/sys/solaris/sys/procfs.d b/libphobos/libdruntime/core/sys/solaris/sys/procfs.d
new file mode 100644 (file)
index 0000000..4fa0802
--- /dev/null
@@ -0,0 +1,252 @@
+/**
+  * D header file for Solaris sys/procfs.h.
+  *
+  * Copyright: Copyright © 2025, The D Language Foundation
+  * License: <a href="http://www.boost.org/LICENSE_1_0.txt">Boost License 1.0</a>.
+  * Authors: Iain Buclaw
+  */
+module core.sys.solaris.sys.procfs;
+
+version (Solaris):
+extern (C):
+nothrow:
+@nogc:
+
+import core.stdc.config : c_long, c_ulong;
+import core.stdc.stdint : uintptr_t;
+import core.sys.posix.signal : sigaction_t, siginfo_t, sigset_t, stack_t;
+import core.sys.posix.time : timestruc_t;
+
+version (X86)     version = X86_Any;
+version (X86_64)  version = X86_Any;
+version (SPARC)   version = SPARC_Any;
+version (SPARC64) version = SPARC_Any;
+
+version (X86_Any)
+{
+    /*
+     * Holds one i386 or and64 instruction
+     */
+    alias instr_t = ubyte;
+
+    public import core.sys.solaris.sys.regset :
+        NPRGREG = _NGREG,
+        prgreg_t = greg_t,
+        prgregset_t = gregset_t,
+        prfpregset_t = fpregset_t;
+
+    /*
+     * The following defines are for portability
+     */
+    version (X86_64)
+    {
+        public import core.sys.solaris.sys.regset :
+            R_PC = REG_RIP,
+            R_PS = REG_RFL,
+            R_SP = REG_RSP,
+            R_FP = REG_RBP,
+            R_R0 = REG_RAX,
+            R_R1 = REG_RDX;
+    }
+    else
+    {
+        public import core.sys.solaris.sys.regset :
+            R_PC = REG_EIP,
+            R_PS = REG_EFL,
+            R_SP = REG_UESP,
+            R_FP = REG_EBP,
+            R_R0 = REG_EAX,
+            R_R1 = REG_EDX;
+    }
+}
+else version (SPARC_Any)
+{
+    import core.sys.solaris.sys.regset : fq;
+
+    /*
+     * Holds one sparc instruction, both ILP32 and LP64
+     */
+    alias instr_t = uint;
+
+    /*
+     * General register access (sparc).
+     * Registers are 32 bits for ILP32, 64 bits for LP64.
+     */
+    enum NPRGREG = 38;
+
+    version (D_LP64)
+        alias prgreg_t = c_long;
+    else
+        alias prgreg_t = int;
+
+    alias prgregset_t = prgreg_t[NPRGREG];
+
+    /*
+     * Floating-point register access (sparc FPU).
+     */
+    version (SPARC64)
+    {
+        struct prfpregset_t
+        {
+            union
+            {
+                uint[32]   pr_regs;
+                double[32] pr_dregs;
+                real[16]   pr_qregs;
+            }
+            ulong pr_filler;
+            ulong pr_fsr;
+            ubyte pr_qcnt;
+            ubyte pr_q_entrysize;
+            ubyte pr_en;
+            byte[13] pr_pad;
+            fq[16] pr_q;
+        }
+    }
+    else
+    {
+        struct prfpregset_t
+        {
+            union
+            {
+                uint[32]   pr_regs;
+                double[16] pr_dregs;
+            }
+            uint  pr_filler;
+            uint  pr_fsr;
+            ubyte pr_qcnt;
+            ubyte pr_q_entrysize;
+            ubyte pr_en;
+            fq[32] pr_q;
+        }
+    }
+
+    enum R_G0 = 0;
+    enum R_G1 = 1;
+    enum R_G2 = 2;
+    enum R_G3 = 3;
+    enum R_G4 = 4;
+    enum R_G5 = 5;
+    enum R_G6 = 6;
+    enum R_G7 = 7;
+    enum R_O0 = 8;
+    enum R_O1 = 9;
+    enum R_O2 = 10;
+    enum R_O3 = 11;
+    enum R_O4 = 12;
+    enum R_O5 = 13;
+    enum R_O6 = 14;
+    enum R_O7 = 15;
+    enum R_L0 = 16;
+    enum R_L1 = 17;
+    enum R_L2 = 18;
+    enum R_L3 = 19;
+    enum R_L4 = 20;
+    enum R_L5 = 21;
+    enum R_L6 = 22;
+    enum R_L7 = 23;
+    enum R_I0 = 24;
+    enum R_I1 = 25;
+    enum R_I2 = 26;
+    enum R_I3 = 27;
+    enum R_I4 = 28;
+    enum R_I5 = 29;
+    enum R_I6 = 30;
+    enum R_I7 = 31;
+    enum R_PC = 33;
+    enum R_nPC = 34;
+    enum R_Y  = 35;
+
+    version (SPARC64)
+    {
+        enum R_CCR = 32;
+        enum R_ASI = 36;
+        enum R_FPRS = 37;
+        enum R_PS = R_CCR;
+    }
+    else
+    {
+        enum R_PSR = 32;
+        enum R_WIM = 36;
+        enum R_TBR = 37;
+        enum R_PS = R_PSR;
+    }
+
+    enum R_SP = R_O6;
+    enum R_FP = R_I6;
+    enum R_R0 = R_O0;
+    enum R_R1 = R_O1;
+}
+
+/*
+ * lwp status file.  /proc/<pid>/lwp/<lwpid>/lwpstatus
+ */
+private enum PRCLSZ    = 8;     // maximum size of scheduling class name
+private enum PRSYSARGS = 8;     // maximum number of syscall arguments
+
+struct lwpstatus_t
+{
+    int     pr_flags;               // flags (see below)
+    int     pr_lwpid;               // specific lwp identifier
+    short   pr_why;                 // reason for lwp stop, if stopped
+    short   pr_what;                // more detailed reason
+    short   pr_cursig;              // current signal, if any
+    ubyte   pr_adi;
+    byte    pr_pad1;
+    siginfo_t pr_info;              // info associated with signal or fault
+    sigset_t pr_lwppend;            // set of signals pending to the lwp
+    sigset_t pr_lwphold;            // set of signals blocked by the lwp
+    sigaction_t pr_action;          // signal action for current signal
+    stack_t pr_altstack;            // alternate signal stack info
+    uintptr_t pr_oldcontext;        // address of previous ucontext
+    short   pr_syscall;             // system call number (if in syscall)
+    short   pr_nsysarg;             // number of arguments to this syscall
+    int     pr_errno;               // errno for failed syscall, 0 if successful
+    c_long[PRSYSARGS] pr_sysarg;    // arguments to this syscall
+    c_long    pr_rval1;             // primary syscall return value
+    c_long    pr_rval2;             // second syscall return value, if any
+    char[PRCLSZ] pr_clname = void;  // scheduling class name
+    timestruc_t pr_tstamp;          // real-time time stamp of stop
+    timestruc_t pr_utime;           // lwp user cpu time
+    timestruc_t pr_stime;           // lwp system cpu time
+    int[11 - 2 * timestruc_t.sizeof / int.sizeof] pr_filler;
+    int     pr_errpriv;             // missing privilege
+    uintptr_t pr_ustack;            // address of stack boundary data (stack_t)
+    c_ulong pr_instr;               // current instruction
+    prgregset_t pr_reg;             // general registers
+    prfpregset_t pr_fpreg;          // floating-point registers
+}
+
+/*
+ * pr_flags (same values appear in both pstatus_t and lwpstatus_t pr_flags).
+ *
+ * These flags do *not* apply to psinfo_t.pr_flag or lwpsinfo_t.pr_flag
+ * (which are both deprecated).
+ */
+// The following flags apply to the specific or representative lwp
+enum PR_STOPPED = 0x00000001;   // lwp is stopped
+enum PR_ISTOP   = 0x00000002;   // lwp is stopped on an event of interest
+enum PR_DSTOP   = 0x00000004;   // lwp has a stop directive in effect
+enum PR_STEP    = 0x00000008;   // lwp has a single-step directive in effect
+enum PR_ASLEEP  = 0x00000010;   // lwp is sleeping in a system call
+enum PR_PCINVAL = 0x00000020;   // contents of pr_instr undefined
+enum PR_ASLWP   = 0x00000040;   // obsolete flag; never set
+enum PR_AGENT   = 0x00000080;   // this lwp is the /proc agent lwp
+enum PR_DETACH  = 0x00000100;   // this is a detached lwp
+enum PR_DAEMON  = 0x00000200;   // this is a daemon lwp
+enum PR_IDLE    = 0x00000400;   // lwp is a cpu's idle thread
+// The following flags apply to the process, not to an individual lwp
+enum PR_ISSYS   = 0x00001000;   // this is a system process
+enum PR_VFORKP  = 0x00002000;   // process is the parent of a vfork()d child
+enum PR_ORPHAN  = 0x00004000;   // process's process group is orphaned
+enum PR_NOSIGCHLD = 0x00008000; // process will not generate SIGCHLD on exit
+enum PR_WAITPID = 0x00010000;   // only waitid(P_PID, pid) can reap the child
+// The following process flags are modes settable by PCSET/PCUNSET
+enum PR_FORK    = 0x00100000;   // inherit-on-fork is in effect
+enum PR_RLC     = 0x00200000;   // run-on-last-close is in effect
+enum PR_KLC     = 0x00400000;   // kill-on-last-close is in effect
+enum PR_ASYNC   = 0x00800000;   // asynchronous-stop is in effect
+enum PR_MSACCT  = 0x01000000;   // micro-state usage accounting is in effect
+enum PR_BPTADJ  = 0x02000000;   // breakpoint trap pc adjustment is in effect
+enum PR_PTRACE  = 0x04000000;   // ptrace-compatibility mode is in effect
+enum PR_MSFORK  = 0x08000000;   // micro-state accounting inherited on fork
diff --git a/libphobos/libdruntime/core/sys/solaris/sys/regset.d b/libphobos/libdruntime/core/sys/solaris/sys/regset.d
new file mode 100644 (file)
index 0000000..38c5617
--- /dev/null
@@ -0,0 +1,331 @@
+/**
+  * D header file for Solaris sys/regset.h.
+  *
+  * Copyright: Copyright © 2025, The D Language Foundation
+  * License: <a href="http://www.boost.org/LICENSE_1_0.txt">Boost License 1.0</a>.
+  * Authors: Iain Buclaw
+  */
+module core.sys.solaris.sys.regset;
+
+version (Solaris):
+extern (C):
+nothrow:
+@nogc:
+
+import core.stdc.config : c_long, c_ulong;
+import core.sys.posix.sys.types : caddr_t;
+
+version (X86)     version = X86_Any;
+version (X86_64)  version = X86_Any;
+version (SPARC)   version = SPARC_Any;
+version (SPARC64) version = SPARC_Any;
+
+version (X86_64)
+{
+    enum REG_GSBASE    = 27;
+    enum REG_FSBASE    = 26;
+    enum REG_DS        = 25;
+    enum REG_ES        = 24;
+
+    enum REG_GS        = 23;
+    enum REG_FS        = 22;
+    enum REG_SS        = 21;
+    enum REG_RSP       = 20;
+    enum REG_RFL       = 19;
+    enum REG_CS        = 18;
+    enum REG_RIP       = 17;
+    enum REG_ERR       = 16;
+    enum REG_TRAPNO    = 15;
+    enum REG_RAX       = 14;
+    enum REG_RCX       = 13;
+    enum REG_RDX       = 12;
+    enum REG_RBX       = 11;
+    enum REG_RBP       = 10;
+    enum REG_RSI       = 9;
+    enum REG_RDI       = 8;
+    enum REG_R8        = 7;
+    enum REG_R9        = 6;
+    enum REG_R10       = 5;
+    enum REG_R11       = 4;
+    enum REG_R12       = 3;
+    enum REG_R13       = 2;
+    enum REG_R14       = 1;
+    enum REG_R15       = 0;
+
+    enum REG_PC = REG_RIP;
+    enum REG_FP = REG_RBP;
+    enum REG_SP = REG_RSP;
+    enum REG_PS = REG_RFL;
+    enum REG_R0 = REG_RAX;
+    enum REG_R1 = REG_RDX;
+}
+else version (X86)
+{
+    enum REG_SS        = 18;
+    enum REG_UESP      = 17;
+    enum REG_EFL       = 16;
+    enum REG_CS        = 15;
+    enum REG_EIP       = 14;
+    enum REG_ERR       = 13;
+    enum REG_TRAPNO    = 12;
+    enum REG_EAX       = 11;
+    enum REG_ECX       = 10;
+    enum REG_EDX       = 9;
+    enum REG_EBX       = 8;
+    enum REG_ESP       = 7;
+    enum REG_EBP       = 6;
+    enum REG_ESI       = 5;
+    enum REG_EDI       = 4;
+    enum REG_DS        = 3;
+    enum REG_ES        = 2;
+    enum REG_FS        = 1;
+    enum REG_GS        = 0;
+
+    enum REG_PC = REG_EIP;
+    enum REG_FP = REG_EBP;
+    enum REG_SP = REG_UESP;
+    enum REG_PS = REG_EFL;
+    enum REG_R0 = REG_EAX;
+    enum REG_R1 = REG_EDX;
+}
+else version (SPARC_Any)
+{
+    enum REG_PC  = 1;
+    enum REG_nPC = 2;
+    enum REG_Y   = 3;
+    enum REG_G1  = 4;
+    enum REG_G2  = 5;
+    enum REG_G3  = 6;
+    enum REG_G4  = 7;
+    enum REG_G5  = 8;
+    enum REG_G6  = 9;
+    enum REG_G7  = 10;
+    enum REG_O0  = 11;
+    enum REG_O1  = 12;
+    enum REG_O2  = 13;
+    enum REG_O3  = 14;
+    enum REG_O4  = 15;
+    enum REG_O5  = 16;
+    enum REG_O6  = 17;
+    enum REG_O7  = 18;
+
+    enum REG_SP = REG_O6;
+    enum REG_R0 = REG_O0;
+    enum REG_R1 = REG_O1;
+
+    version (SPARC64)
+    {
+        enum REG_CCR = 0;
+        enum REG_ASI = 19;
+        enum REG_FPRS = 20;
+    }
+    else
+    {
+        enum REG_PSR = 0;
+        enum REG_PS  = REG_PSR;
+    }
+}
+else
+    static assert(false, "Architecture not supported.");
+
+/*
+ * A gregset_t is defined as an array type.
+ */
+version (X86_64)
+{
+    enum _NGREG = 28;
+    alias greg_t = c_long;
+}
+else version (X86)
+{
+    enum _NGREG = 19;
+    alias greg_t = int;
+}
+else version (SPARC64)
+{
+    enum _NGREG = 21;
+    alias greg_t = c_long;
+}
+else version (SPARC)
+{
+    enum _NGREG = 19;
+    alias greg_t = int;
+}
+else
+    static assert(0, "unimplemented");
+
+alias gregset_t = greg_t[_NGREG];
+
+version (X86_Any)
+{
+    /*
+     * Xregs extension
+     */
+    struct xrs_t
+    {
+        c_ulong xrs_id;
+        caddr_t xrs_ptr;
+    }
+}
+else version (SPARC_Any)
+{
+    /*
+     * Defines the minimal format of a floating point instruction queue entry.
+     */
+    struct _fpq
+    {
+        uint *fpq_addr;
+        uint fpq_instr;
+    }
+
+    struct fq
+    {
+        union
+        {
+            double whole;
+            _fpq fpq;
+        }
+    }
+
+    /*
+     * Defines how a register window can appear on the stack
+     */
+    struct rwindow
+    {
+        greg_t[8] rw_local;
+        greg_t[8] rw_in;
+    }
+
+    struct gwindows_t
+    {
+        int         wbcnt;
+        greg_t[31] *spbuf;
+        rwindow[31] wbuf;
+    }
+
+    /*
+     * For associating extra register state with ucontext structure and is kept
+     * within the uc_mcontext filler area
+     */
+    struct xrs_t
+    {
+        uint    xrs_id;
+        caddr_t xrs_ptr;
+    }
+
+    struct cxrs_t
+    {
+        uint    cxrs_id;
+        caddr_t cxrs_ptr;
+    }
+
+    alias asrset_t = long[16];
+}
+
+/*
+ * The floating point processor state
+ */
+version (X86_64)
+{
+    private
+    {
+        alias upad128_t = uint[4];
+        union _u_st
+        {
+            ushort[5]   fpr_16;
+            upad128_t   __fpr_pad;
+        }
+    }
+
+    struct fpu
+    {
+        union fp_reg_set
+        {
+            struct fpchip_state
+            {
+                ushort          cw;
+                ushort          sw;
+                ubyte           fctw;
+                ubyte           __fx_rsvd;
+                ushort          fop;
+                ulong           rip;
+                ulong           rdp;
+                uint            mxcsr;
+                uint            mxcsr_mask;
+                _u_st[8]        st;
+                upad128_t[16]   xmm;
+                upad128_t[6]    __fx_ign2;
+                uint            status;
+                uint            xstatus;
+            }
+            uint[130]   f_fpregs;
+        }
+    }
+}
+else version (X86)
+{
+    private alias upad128_t = uint[4];
+
+    struct fpu
+    {
+        union u_fp_reg_set
+        {
+            struct s_fpchip_state
+            {
+                uint[27]        state;
+                uint            status;
+                uint            mxcsr;
+                uint            xstatus;
+                uint[2]         __pad;
+                upad128_t[8]    xmm;
+            }
+            s_fpchip_state    fpchip_state;
+
+            struct s_fp_emul_space
+            {
+                ubyte[246]  fp_emul;
+                ubyte[2]    fp_epad;
+            }
+            s_fp_emul_space   fp_emul_space;
+            uint[95]        f_fpregs;
+        }
+        u_fp_reg_set fp_reg_set;
+    }
+}
+else version (SPARC64)
+{
+    struct fpu
+    {
+        union
+        {
+            uint[32]   fpu_regs;
+            double[32] fpu_dregs;
+            real[16]   fpu_qregs;
+        }
+        fq    *fpu_q;
+        ulong fpu_fsr;
+        ubyte fpu_qcnt;
+        ubyte fpu_q_entrysize;
+        ubyte fpu_en;
+    }
+}
+else version (SPARC)
+{
+    struct fpu
+    {
+        union
+        {
+            uint[32]   fpu_regs;
+            double[16] fpu_dregs;
+        }
+        fq    *fpu_q;
+        uint  fpu_fsr;
+        ubyte fpu_qcnt;
+        ubyte fpu_q_entrysize;
+        ubyte fpu_en;
+    }
+}
+else
+    static assert(0, "unimplemented");
+
+alias fpregset_t = fpu;
diff --git a/libphobos/libdruntime/core/sys/solaris/thread.d b/libphobos/libdruntime/core/sys/solaris/thread.d
new file mode 100644 (file)
index 0000000..d0c496a
--- /dev/null
@@ -0,0 +1,66 @@
+/**
+  * D header file for Solaris thread.h.
+  *
+  * Copyright: Copyright © 2025, The D Language Foundation
+  * License: <a href="http://www.boost.org/LICENSE_1_0.txt">Boost License 1.0</a>.
+  * Authors: Iain Buclaw
+  */
+module core.sys.solaris.thread;
+
+version (Solaris):
+extern (C):
+nothrow:
+@nogc:
+
+import core.stdc.config : c_long;
+import core.sys.posix.signal : sigset_t, stack_t;
+
+/*
+ * definitions needed to use the thread interface except synchronization.
+ */
+alias thread_t = int;
+alias thread_key_t = int;
+
+int thr_create(void*, size_t, void* function(void*), void*, c_long, thread_t*);
+int thr_join(thread_t, thread_t*, void**);
+int thr_setconcurrency(int);
+int thr_getconcurrency();
+noreturn thr_exit(void*);
+thread_t thr_self();
+
+int thr_sigsetmask(int, const scope sigset_t*, sigset_t*);
+
+int thr_stksegment(stack_t*);
+
+int thr_main();
+int thr_kill(thread_t, int);
+int thr_suspend(thread_t);
+int thr_continue(thread_t);
+void thr_yield();
+int thr_setprio(thread_t, int);
+int thr_getprio(thread_t, int*);
+int thr_keycreate(thread_key_t*, void function(void*));
+int thr_keycreate_once(thread_key_t*, void function(void*));
+int thr_setspecific(thread_key_t, void*);
+int thr_getspecific(thread_key_t, void**);
+size_t thr_min_stack();
+
+alias THR_MIN_STACK = thr_min_stack;
+
+/*
+ * thread flags (one word bit mask)
+ */
+enum : c_long
+{
+    THR_BOUND = 0x00000001,     // = PTHREAD_SCOPE_SYSTEM
+    THR_NEW_LWP = 0x00000002,
+    THR_DETACHED = 0x00000040,  // = PTHREAD_CREATE_DETACHED
+    THR_SUSPENDED = 0x00000080,
+    THR_DAEMON = 0x00000100,
+}
+
+/*
+ * The key to be created by thr_keycreate_once()
+ * must be statically initialized with THR_ONCE_KEY.
+ */
+enum thread_key_t THR_ONCE_KEY = -1;
index 407b92c20a6f69f542661f2c1c9d2c21d50cbf23..c3fa12216c6d52c570cb37bb1b0b74bbb4f8feed 100644 (file)
@@ -1,5 +1,5 @@
 /**
- * D header file for C99.
+ * D header file for C99/C11.
  *
  * $(C_HEADER_DESCRIPTION pubs.opengroup.org/onlinepubs/009695399/basedefs/_time.h.html, _time.h)
  *
@@ -57,3 +57,36 @@ void  _tzset();                          // non-standard
 
 ///
 extern __gshared const(char)*[2] tzname; // non-standard
+
+// timespec functions, introduced in C11
+alias __time64_t = long;
+alias __time32_t = int;
+
+/// 32-bit timespec struct
+struct _timespec32
+{
+    __time32_t tv_sec;
+    c_long     tv_nsec;
+}
+
+/// 64-bit timespec struct
+struct _timespec64
+{
+    __time64_t tv_sec;
+    c_long     tv_nsec;
+}
+
+/// Timespec structure, introduced in C11
+alias timespec = _timespec64;
+
+/// Base Value used for timespec_get
+enum TIME_UTC = 1;
+
+/// 64-bit version of timespec_get for Windows
+@system int _timespec64_get(scope _timespec64* ts, int base);
+
+/// 32-bit version of timespec_get for Windows
+@system int _timespec32_get(scope _timespec32* ts, int base);
+
+/// timespec_get, introduced in C11
+alias timespec_get = _timespec64_get;
index 591b1dc146cab2b6fb872bdee549c5120b145b19..006350afd104a50b22a1713eaa89146a201ccd18 100644 (file)
@@ -759,8 +759,18 @@ protected:
         }
         else
         {
-            version (Posix) import core.sys.posix.sys.mman : MAP_ANON, MAP_FAILED, MAP_PRIVATE, mmap,
-                mprotect, PROT_NONE, PROT_READ, PROT_WRITE;
+            version (Posix)
+            {
+                static import core.sys.posix.sys.mman;
+                static if (__traits(compiles, core.sys.posix.sys.mman.mmap))
+                {
+                    import core.sys.posix.sys.mman : MAP_ANON, MAP_FAILED, MAP_PRIVATE, mmap,
+                        mprotect, PROT_NONE, PROT_READ, PROT_WRITE;
+                }
+                static import core.sys.posix.stdlib;
+                static if (__traits(compiles, core.sys.posix.stdlib.valloc))
+                    import core.sys.posix.stdlib : valloc;
+            }
             version (OpenBSD) import core.sys.posix.sys.mman : MAP_STACK;
 
             static if ( __traits( compiles, ucontext_t ) )
@@ -1066,7 +1076,16 @@ protected:
         {
             push( 0x00000000_00000000 );                            // Return address of fiber_entryPoint call
             push( cast(size_t) &fiber_entryPoint );                 // RIP
-            push( cast(size_t) m_ctxt.bstack );                     // RBP
+            version (OSX)
+            {
+                // backtrace() needs this to be null to terminate
+                // the stack walk on macOS x86_64
+                push( 0x00000000_00000000 );                        // RBP
+            }
+            else
+            {
+                push( cast(size_t) m_ctxt.bstack );                 // RBP
+            }
             push( 0x00000000_00000000 );                            // RBX
             push( 0x00000000_00000000 );                            // R12
             push( 0x00000000_00000000 );                            // R13
@@ -1323,7 +1342,9 @@ protected:
         }
         else static if ( __traits( compiles, ucontext_t ) )
         {
-            getcontext( &m_utxt );
+            const status = getcontext( &m_utxt );
+            assert( status == 0 );
+
             m_utxt.uc_stack.ss_sp   = m_pmem;
             m_utxt.uc_stack.ss_size = m_size;
             makecontext( &m_utxt, &fiber_entryPoint, 0 );
index b708af297f84343a1f867202f801bb6084ca5600..7464f648827c0a57fe24f71f2e397d32b6857b15 100644 (file)
@@ -87,6 +87,7 @@ else version (Posix)
 
     version (Darwin)
     {
+        // Use macOS threads for suspend/resume
         import core.sys.darwin.mach.kern_return : KERN_SUCCESS;
         import core.sys.darwin.mach.port : mach_port_t;
         import core.sys.darwin.mach.thread_act : mach_msg_type_number_t,
@@ -118,13 +119,19 @@ else version (Posix)
              PPC_THREAD_STATE64, PPC_THREAD_STATE64_COUNT, ppc_thread_state64_t;
         }
     }
-}
-
-version (Solaris)
-{
-    import core.sys.posix.sys.wait : idtype_t;
-    import core.sys.solaris.sys.priocntl : PC_CLNULL, PC_GETCLINFO, PC_GETPARMS, PC_SETPARMS, pcinfo_t, pcparms_t, priocntl;
-    import core.sys.solaris.sys.types : P_MYID, pri_t;
+    else version (Solaris)
+    {
+        // Use Solaris threads for suspend/resume
+        import core.sys.posix.sys.wait : idtype_t;
+        import core.sys.solaris.sys.priocntl : PC_CLNULL, PC_GETCLINFO, PC_GETPARMS, PC_SETPARMS, pcinfo_t, pcparms_t, priocntl;
+        import core.sys.solaris.sys.types : P_MYID, pri_t;
+        import core.sys.solaris.thread : thr_stksegment, thr_suspend, thr_continue;
+        import core.sys.solaris.sys.procfs : PR_STOPPED, lwpstatus_t;
+    }
+    else
+    {
+        // Use POSIX threads for suspend/resume
+    }
 }
 
 version (GNU)
@@ -393,7 +400,50 @@ class Thread : ThreadBase
             static assert(false, "Architecture not supported." );
         }
     }
+    else version (Solaris)
+    {
+        version (X86)
+        {
+            uint[8]         m_reg; // edi,esi,ebp,esp,ebx,edx,ecx,eax
+        }
+        else version (X86_64)
+        {
+            ulong[16]       m_reg; // rdi,rsi,rbp,rsp,rbx,rdx,rcx,rax
+                                   // r8,r9,r10,r11,r12,r13,r14,r15
+        }
+        else version (SPARC)
+        {
+            int[33]         m_reg; // g0-7, o0-7, l0-7, i0-7, pc
+        }
+        else version (SPARC64)
+        {
+            long[33]        m_reg; // g0-7, o0-7, l0-7, i0-7, pc
+        }
+        else
+        {
+            static assert(false, "Architecture not supported." );
+        }
+    }
 
+    override final void[] savedRegisters() nothrow @nogc
+    {
+        version (Windows)
+        {
+            return m_reg;
+        }
+        else version (Darwin)
+        {
+            return m_reg;
+        }
+        else version (Solaris)
+        {
+            return m_reg;
+        }
+        else
+        {
+            return null;
+        }
+    }
 
     ///////////////////////////////////////////////////////////////////////////
     // General Actions
@@ -1138,6 +1188,15 @@ version (CoreDdoc)
     extern (C) void thread_setGCSignals(int suspendSignalNo, int resumeSignalNo) nothrow @nogc
     {
     }
+
+    /**
+     * Get the GC signals set by the thread module. This function should be called either
+     * after thread_init() has finished, or after a call thread_setGCSignals().
+     * This function is Posix-only.
+     */
+    extern (C) void thread_getGCSignals(out int suspendSignalNo, out int resumeSignalNo) nothrow @nogc
+    {
+    }
 }
 else version (Posix)
 {
@@ -1157,6 +1216,23 @@ else version (Posix)
         suspendSignalNumber = suspendSignalNo;
         resumeSignalNumber  = resumeSignalNo;
     }
+
+    extern (C) void thread_getGCSignals(out int suspendSignalNo, out int resumeSignalNo) nothrow @nogc
+    in
+    {
+        assert(suspendSignalNumber != 0);
+        assert(resumeSignalNumber  != 0);
+    }
+    out
+    {
+        assert(suspendSignalNo != 0);
+        assert(resumeSignalNo  != 0);
+    }
+    do
+    {
+        suspendSignalNo = suspendSignalNumber;
+        resumeSignalNo  = resumeSignalNumber;
+    }
 }
 
 version (Posix)
@@ -1340,7 +1416,7 @@ in (fn)
                 enum int j = 13 + i; // source register
                 asm pure nothrow @nogc
                 {
-                    "stw "~regname~j.stringof~", %0" : "=m" (regs[i]);
+                    ("stw "~regname~j.stringof~", %0") : "=m" (regs[i]);
                 }
             }}
             sp = cast(void*)&regs[0];
@@ -1357,7 +1433,7 @@ in (fn)
                 enum int j = 13 + i; // source register
                 asm pure nothrow @nogc
                 {
-                    "std "~regname~j.stringof~", %0" : "=m" (regs[i]);
+                    ("std "~regname~j.stringof~", %0") : "=m" (regs[i]);
                 }
             }}
             sp = cast(void*)&regs[0];
@@ -1459,15 +1535,6 @@ in (fn)
     fn(sp);
 }
 
-version (Windows)
-private extern (D) void scanWindowsOnly(scope ScanAllThreadsTypeFn scan, ThreadBase _t) nothrow
-{
-    auto t = _t.toThread;
-
-    scan( ScanType.stack, t.m_reg.ptr, t.m_reg.ptr + t.m_reg.length );
-}
-
-
 /**
  * Returns the process ID of the calling process, which is guaranteed to be
  * unique on the system. This call is always successful.
@@ -1499,7 +1566,6 @@ extern (C) @nogc nothrow
 
     version (PThread_Getattr_NP)  int pthread_getattr_np(pthread_t thread, pthread_attr_t* attr);
     version (PThread_Attr_Get_NP) int pthread_attr_get_np(pthread_t thread, pthread_attr_t* attr);
-    version (Solaris) int thr_stksegment(stack_t* stk);
     version (OpenBSD) int pthread_stackseg_np(pthread_t thread, stack_t* sinfo);
 }
 
@@ -1803,6 +1869,143 @@ private extern (D) bool suspend( Thread t ) nothrow @nogc
             static assert(false, "Architecture not supported." );
         }
     }
+    else version (Solaris)
+    {
+        if (t.m_addr != pthread_self())
+        {
+            if (thr_suspend(t.m_addr) != 0)
+            {
+                if (!t.isRunning)
+                {
+                    Thread.remove(t);
+                    return false;
+                }
+                onThreadError("Unable to suspend thread");
+            }
+
+            static int getLwpStatus(ulong lwpid, out lwpstatus_t status)
+            {
+                import core.sys.posix.fcntl : open, O_RDONLY;
+                import core.sys.posix.unistd : pread, close;
+                import core.internal.string : unsignedToTempString;
+
+                char[100] path = void;
+                auto pslice = path[0 .. $];
+                immutable n = unsignedToTempString(lwpid);
+                immutable ndigits = n.length;
+
+                // Construct path "/proc/self/lwp/%u/lwpstatus"
+                pslice[0 .. 15] = "/proc/self/lwp/";
+                pslice = pslice[15 .. $];
+                pslice[0 .. ndigits] = n[];
+                pslice = pslice[ndigits .. $];
+                pslice[0 .. 10] = "/lwpstatus";
+                pslice[10] = '\0';
+
+                // Read in lwpstatus data
+                int fd = open(path.ptr, O_RDONLY, 0);
+                if (fd >= 0)
+                {
+                    while (pread(fd, &status, status.sizeof, 0) == status.sizeof)
+                    {
+                        // Should only attempt to read the thread state once it
+                        // has been stopped by thr_suspend
+                        if (status.pr_flags & PR_STOPPED)
+                        {
+                            close(fd);
+                            return 0;
+                        }
+                        // Give it a chance to stop
+                        thread_yield();
+                    }
+                    close(fd);
+                }
+                return -1;
+            }
+
+            lwpstatus_t status = void;
+            if (getLwpStatus(t.m_addr, status) != 0)
+                onThreadError("Unable to load thread state");
+
+            version (X86)
+            {
+                import core.sys.solaris.sys.regset; // REG_xxx
+
+                if (!t.m_lock)
+                    t.m_curr.tstack = cast(void*) status.pr_reg[REG_ESP];
+                // eax,ebx,ecx,edx,edi,esi,ebp,esp
+                t.m_reg[0] = status.pr_reg[REG_EAX];
+                t.m_reg[1] = status.pr_reg[REG_EBX];
+                t.m_reg[2] = status.pr_reg[REG_ECX];
+                t.m_reg[3] = status.pr_reg[REG_EDX];
+                t.m_reg[4] = status.pr_reg[REG_EDI];
+                t.m_reg[5] = status.pr_reg[REG_ESI];
+                t.m_reg[6] = status.pr_reg[REG_EBP];
+                t.m_reg[7] = status.pr_reg[REG_ESP];
+            }
+            else version (X86_64)
+            {
+                import core.sys.solaris.sys.regset; // REG_xxx
+
+                if (!t.m_lock)
+                    t.m_curr.tstack = cast(void*) status.pr_reg[REG_RSP];
+                // rax,rbx,rcx,rdx,rdi,rsi,rbp,rsp
+                t.m_reg[0] = status.pr_reg[REG_RAX];
+                t.m_reg[1] = status.pr_reg[REG_RBX];
+                t.m_reg[2] = status.pr_reg[REG_RCX];
+                t.m_reg[3] = status.pr_reg[REG_RDX];
+                t.m_reg[4] = status.pr_reg[REG_RDI];
+                t.m_reg[5] = status.pr_reg[REG_RSI];
+                t.m_reg[6] = status.pr_reg[REG_RBP];
+                t.m_reg[7] = status.pr_reg[REG_RSP];
+                // r8,r9,r10,r11,r12,r13,r14,r15
+                t.m_reg[8] = status.pr_reg[REG_R8];
+                t.m_reg[9] = status.pr_reg[REG_R9];
+                t.m_reg[10] = status.pr_reg[REG_R10];
+                t.m_reg[11] = status.pr_reg[REG_R11];
+                t.m_reg[12] = status.pr_reg[REG_R12];
+                t.m_reg[13] = status.pr_reg[REG_R13];
+                t.m_reg[14] = status.pr_reg[REG_R14];
+                t.m_reg[15] = status.pr_reg[REG_R15];
+            }
+            else version (SPARC)
+            {
+                import core.sys.solaris.sys.procfs : R_SP, R_PC;
+
+                if (!t.m_lock)
+                    t.m_curr.tstack = cast(void*) status.pr_reg[R_SP];
+                // g0..g7, o0..o7, l0..l7, i0..i7
+                t.m_reg[0 .. 32] = status.pr_reg[0 .. 32];
+                // pc
+                t.m_reg[32] = status.pr_reg[R_PC];
+            }
+            else version (SPARC64)
+            {
+                import core.sys.solaris.sys.procfs : R_SP, R_PC;
+
+                if (!t.m_lock)
+                {
+                    // SPARC V9 has a stack bias of 2047 bytes which must be added to get
+                    // the actual data of the stack frame.
+                    auto tstack = status.pr_reg[R_SP] + 2047;
+                    assert(tstack % 16 == 0);
+                    t.m_curr.tstack = cast(void*) tstack;
+                }
+                // g0..g7, o0..o7, l0..l7, i0..i7
+                t.m_reg[0 .. 32] = status.pr_reg[0 .. 32];
+                // pc
+                t.m_reg[32] = status.pr_reg[R_PC];
+            }
+            else
+            {
+                static assert(false, "Architecture not supported.");
+            }
+        }
+        else if (!t.m_lock)
+        {
+            t.m_curr.tstack = getStackTop();
+        }
+    }
     else version (Posix)
     {
         if ( t.m_addr != pthread_self() )
@@ -1886,6 +2089,8 @@ extern (C) void thread_suspendAll() nothrow
 
         version (Darwin)
         {}
+        else version (Solaris)
+        {}
         else version (Posix)
         {
             // Subtract own thread if we called suspend() on ourselves.
@@ -1958,6 +2163,22 @@ private extern (D) void resume(ThreadBase _t) nothrow @nogc
             t.m_curr.tstack = t.m_curr.bstack;
         t.m_reg[0 .. $] = 0;
     }
+    else version (Solaris)
+    {
+        if (t.m_addr != pthread_self() && thr_continue(t.m_addr) != 0)
+        {
+            if (!t.isRunning)
+            {
+                Thread.remove(t);
+                return;
+            }
+            onThreadError("Unable to resume thread");
+        }
+
+        if (!t.m_lock)
+            t.m_curr.tstack = t.m_curr.bstack;
+        t.m_reg[0 .. $] = 0;
+    }
     else version (Posix)
     {
         if ( t.m_addr != pthread_self() )
@@ -2018,6 +2239,9 @@ extern (C) void thread_init() @nogc nothrow
        }
         pthread_atfork(null, null, &initChildAfterFork);
     }
+    else version (Solaris)
+    {
+    }
     else version (Posix)
     {
         version (OpenBSD)
@@ -2127,6 +2351,10 @@ version (Windows)
 
             scope (exit)
             {
+                // allow the GC to clean up any resources it allocated for this thread.
+                import core.internal.gc.proxy : gc_getProxy;
+                gc_getProxy().cleanupThread(obj);
+
                 Thread.remove(obj);
                 obj.destroyDataStorage();
             }
@@ -2144,7 +2372,9 @@ version (Windows)
 
             void append( Throwable t )
             {
-                obj.m_unhandled = Throwable.chainTogether(obj.m_unhandled, t);
+                obj.filterCaughtThrowable(t);
+                if (t !is null)
+                    obj.m_unhandled = Throwable.chainTogether(obj.m_unhandled, t);
             }
 
             version (D_InlineAsm_X86)
@@ -2188,6 +2418,39 @@ version (Windows)
 }
 else version (Posix)
 {
+    // NOTE: A thread's cancelability state, determined by pthread_setcancelstate,
+    //       can be enabled (the default for new threads) or disabled.
+    //       If a thread has disabled cancelation, then a cancelation request remains
+    //       queued until the thread enables cancelation.  If a thread has enabled
+    //       cancelation, then its cancelability type determines when cancelation occurs.
+    //
+    // Call these routines when entering/leaving critical sections of the code that
+    // are not cancellation points.
+
+    extern (C) int thread_cancelDisable() nothrow
+    {
+        static if (__traits(compiles, core.sys.posix.pthread.PTHREAD_CANCEL_DISABLE))
+        {
+            import core.sys.posix.pthread : pthread_setcancelstate, PTHREAD_CANCEL_DISABLE;
+            int oldstate;
+            pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldstate);
+            return oldstate;
+        }
+        else
+        {
+            return 0;   // No thread cancellation on platform
+        }
+    }
+
+    extern (C) void thread_cancelRestore(int oldstate) nothrow
+    {
+        static if (__traits(compiles, core.sys.posix.pthread.PTHREAD_CANCEL_DISABLE))
+        {
+            import core.sys.posix.pthread : pthread_setcancelstate;
+            pthread_setcancelstate(oldstate, null);
+        }
+    }
+
     private
     {
         //
@@ -2223,6 +2486,10 @@ else version (Posix)
 
             scope (exit)
             {
+                // allow the GC to clean up any resources it allocated for this thread.
+                import core.internal.gc.proxy : gc_getProxy;
+                gc_getProxy().cleanupThread(obj);
+
                 Thread.remove(obj);
                 atomicStore!(MemoryOrder.raw)(obj.m_isRunning, false);
                 obj.destroyDataStorage();
@@ -2278,7 +2545,9 @@ else version (Posix)
 
             void append( Throwable t )
             {
-                obj.m_unhandled = Throwable.chainTogether(obj.m_unhandled, t);
+                obj.filterCaughtThrowable(t);
+                if (t !is null)
+                    obj.m_unhandled = Throwable.chainTogether(obj.m_unhandled, t);
             }
             try
             {
@@ -2368,6 +2637,9 @@ else version (Posix)
         {
             void op(void* sp) nothrow
             {
+                int cancel_state = thread_cancelDisable();
+                scope(exit) thread_cancelRestore(cancel_state);
+
                 bool supported = thread_preSuspend(getStackTop());
                 assert(supported, "Tried to suspend a detached thread!");
 
index 4f35cc048d82834a5b231bda78172abb44f402c9..7069bfd34e4a0c68e56bb1e99604c10d5980e7f1 100644 (file)
@@ -153,10 +153,6 @@ class ThreadBase
 
     package void destroyDataStorage() nothrow @nogc
     {
-        // allow the GC to clean up any resources it allocated for this thread.
-        import core.internal.gc.proxy : gc_getProxy;
-        gc_getProxy().cleanupThread(this);
-
         rt_tlsgc_destroy(m_tlsrtdata);
         m_tlsrtdata = null;
     }
@@ -191,6 +187,25 @@ class ThreadBase
      */
     abstract Throwable join(bool rethrow = true);
 
+    /**
+     * Filter any exceptions that escaped the thread entry point.
+     * This enables a 'grave digger' approach to exceptions.
+     *
+     * By default this method will call the global handler in core.exception.
+     *
+     * Overriding this method allows a per-thread behavior.
+     *
+     * Params:
+     *         thrown = The thrown exception, may be null after returned.
+     */
+    void filterCaughtThrowable(ref Throwable thrown) @system nothrow
+    {
+        import core.exception : filterThreadThrowableHandler;
+        if (thrown is null)
+            return;
+        else if (auto handler = filterThreadThrowableHandler())
+            handler(thrown);
+    }
 
     ///////////////////////////////////////////////////////////////////////////
     // General Properties
@@ -526,6 +541,13 @@ package(core.thread):
         return m_curr;
     }
 
+    /**
+     * Get an array of the current saved registers for this thread.
+     *
+     * Returns:
+     *  A slice of the array representing all saved registers or null.
+     */
+    public abstract void[] savedRegisters() nothrow @nogc;
 
 package(core.thread):
     ///////////////////////////////////////////////////////////////////////////
@@ -846,6 +868,11 @@ package ThreadT thread_attachThis_tpl(ThreadT)()
  * Deregisters the calling thread from use with the runtime.  If this routine
  * is called for a thread which is not registered, the result is undefined.
  *
+ * Once the thread is removed from the runtime, it must not use the GC because
+ * it does not participate in the Stop-The-World mechanisms. With the default
+ * GC, that has a global lock, this might not cause races, but in GCs with
+ * regional locks, it definitely can cause races.
+ *
  * NOTE: This routine does not run thread-local static destructors when called.
  *       If full functionality as a D thread is desired, the following function
  *       must be called before thread_detachThis, particularly if the thread is
@@ -853,6 +880,10 @@ package ThreadT thread_attachThis_tpl(ThreadT)()
  *
  *       $(D extern(C) void rt_moduleTlsDtor();)
  *
+ *       This also does not call the GC thread cleanup routine. After running
+ *       module dtors, it is recommended to call
+ *       $(D gc_getProxy().cleanupThread(Thread.getThis());)
+ *
  * See_Also:
  *     $(REF thread_attachThis, core,thread,osthread)
  */
@@ -867,12 +898,22 @@ extern (C) void thread_detachThis() nothrow @nogc
  * Deregisters the given thread from use with the runtime.  If this routine
  * is called for a thread which is not registered, the result is undefined.
  *
+ * Once the thread is removed from the runtime, it must not use the GC because
+ * it does not participate in the Stop-The-World mechanisms. With the default
+ * GC, that has a global lock, this might not cause races, but in GCs with
+ * regional locks, it definitely can cause races.
+ *
  * NOTE: This routine does not run thread-local static destructors when called.
  *       If full functionality as a D thread is desired, the following function
- *       must be called by the detached thread, particularly if the thread is
- *       being detached at some indeterminate time before program termination:
+ *       must be called by the detached thread before calling this function,
+ *       particularly if the thread is being detached at some indeterminate
+ *       time before program termination:
  *
  *       $(D extern(C) void rt_moduleTlsDtor();)
+ *
+ *       This also does not call the GC thread cleanup routine. After running
+ *       module dtors, it is recommended to call (from the thread itself)
+ *       $(D gc_getProxy().cleanupThread(Thread.getThis());)
  */
 extern (C) void thread_detachByAddr(ThreadID addr)
 {
@@ -1135,26 +1176,14 @@ private void scanAllTypeImpl(scope ScanAllThreadsTypeFn scan, void* curStackTop)
 
     for (ThreadBase t = ThreadBase.sm_tbeg; t; t = t.next)
     {
-        version (Windows)
-        {
-            // Ideally, we'd pass ScanType.regs or something like that, but this
-            // would make portability annoying because it only makes sense on Windows.
-            scanWindowsOnly(scan, t);
-        }
+        if (auto regs = t.savedRegisters())
+            scan(ScanType.stack, regs.ptr, regs.ptr + regs.length);
 
         if (t.m_tlsrtdata !is null)
             rt_tlsgc_scan(t.m_tlsrtdata, (p1, p2) => scan(ScanType.tls, p1, p2));
     }
 }
 
-version (Windows)
-{
-    // Currently scanWindowsOnly can't be handled properly by externDFunc
-    // https://github.com/dlang/druntime/pull/3135#issuecomment-643673218
-    pragma(mangle, "_D4core6thread8osthread15scanWindowsOnlyFNbMDFNbEQBvQBt10threadbase8ScanTypePvQcZvCQDdQDbQBi10ThreadBaseZv")
-    private extern (D) void scanWindowsOnly(scope ScanAllThreadsTypeFn scan, ThreadBase) nothrow;
-}
-
 /**
  * The main entry point for garbage collection.  The supplied delegate
  * will be passed ranges representing both stack and register values.
index e6dd47d06d39f3fb1af8bf41e7378472de949530..a283f1a2039d0c0075b2f62a9a506af5ae3aa81a 100644 (file)
@@ -106,8 +106,8 @@ void va_arg()(ref va_list ap, TypeInfo ti, void* parmn)
     }
     else version (AAPCS64)
     {
-        static import core.internal.vararg.aarch64;
-        core.internal.vararg.aarch64.va_arg(ap, ti, parmn);
+        static import core.internal.vararg.aapcs64;
+        core.internal.vararg.aapcs64.va_arg(ap, ti, parmn);
     }
     else version (ARM_Any)
     {
index 80629ed760acb4a13210802d4c9bd44a25dafda5..96fd06b59ebd263eeb3fdf7260acc20d43893ee4 100644 (file)
@@ -3515,11 +3515,8 @@ inout(V) get(K, V)(inout(V[K])* aa, K key, lazy inout(V) defaultValue)
 ref V require(K, V)(ref V[K] aa, K key, lazy V value = V.init)
 {
     bool found;
-    auto p = _aaGetX(aa, key, found);
-    if (found)
-        return *p;
-    *p = value; // Not `return (*p = value)` since if `=` is overloaded
-    return *p;  // this might not return a ref to the left-hand side.
+    auto p = _aaGetX(aa, key, found, value);
+    return *p;
 }
 
 ///
@@ -3552,10 +3549,8 @@ void update(K, V, C, U)(ref V[K] aa, K key, scope C create, scope U update)
 if (is(typeof(create()) : V) && (is(typeof(update(aa[K.init])) : V) || is(typeof(update(aa[K.init])) == void)))
 {
     bool found;
-    auto p = _aaGetX(aa, key, found);
-    if (!found)
-        *p = create();
-    else
+    auto p = _aaGetX(aa, key, found, create());
+    if (found)
     {
         static if (is(typeof(update(*p)) == void))
             update(*p);
@@ -3827,7 +3822,7 @@ private size_t getArrayHash(const scope TypeInfo element, const scope void* ptr,
     return hash;
 }
 
-/// Provide the .dup array property.
+/// Provide the .dup array property, which creates a duplicate.
 @property auto dup(T)(T[] a)
     if (!is(const(T) : T))
 {
@@ -3859,7 +3854,7 @@ private size_t getArrayHash(const scope TypeInfo element, const scope void* ptr,
 }
 
 
-/// Provide the .idup array property.
+/// Provide the .idup array property, which creates an immutable duplicate.
 @property immutable(T)[] idup(T)(T[] a)
 {
     import core.internal.array.duplication : _dup;
@@ -4685,6 +4680,7 @@ version (D_ProfileGC)
     public import core.internal.array.concatenation : _d_arraycatnTXTrace;
     public import core.lifetime : _d_newitemTTrace;
     public import core.internal.array.construction : _d_newarrayTTrace;
+    public import core.internal.array.construction : _d_newarrayUTrace;
     public import core.internal.array.construction : _d_newarraymTXTrace;
     public import core.internal.array.capacity: _d_arraysetlengthTTrace;
     public import core.internal.array.construction : _d_arrayliteralTXTrace;
@@ -4697,6 +4693,7 @@ public import core.internal.array.concatenation : _d_arraycatnTX;
 public import core.internal.array.construction : _d_arrayctor;
 public import core.internal.array.construction : _d_arraysetctor;
 public import core.internal.array.construction : _d_newarrayT;
+public import core.internal.array.construction : _d_newarrayU;
 public import core.internal.array.construction : _d_newarraymTX;
 public import core.internal.array.construction : _d_arrayliteralTX;
 public import core.internal.array.arrayassign : _d_arrayassign_l;
index 9c5af477cbb885403b693e4c66bceead8062ad94..f0093c8547a8f48ad679275029f9b4eb4aecf1c8 100644 (file)
@@ -39,9 +39,8 @@ pragma(inline, true)
 private int cmp3(T)(const T f1, const T f2)
 if (isComplex!T)
 {
-    if (int result = cmp3(f1.re, f2.re))
-        return result;
-    return cmp3(f1.im, f2.im);
+    int result;
+    return (result = cmp3(f1.re, f2.re)) != 0 ? result : cmp3(f1.im, f2.im);
 }
 
 unittest
index 4bf563baa9f2494f0433b6e5c8ff1042bb192714..4c8d325940b463416c6f0a80a4842ebc98cb8709 100644 (file)
@@ -1,4 +1,4 @@
-f87979028bd0e0f3e67dbf5b0cd1335ca3f7ed67
+808314eb236c7a7566d3ab798ba9b7be33f5473e
 
 The first line of this file holds the git revision number of the last
 merge done from the dlang/phobos repository.
index 60fd114da2d7bbc42a6314593b3096a55462d4dc..c00bccf9424b348e79417c15b803cf5a2fec0894 100644 (file)
@@ -82,8 +82,12 @@ Returns:
     found value plus one is returned.
 
 See_Also:
-$(REF_ALTTEXT find, find, std,algorithm,searching) and $(REF_ALTTEXT canFind, canFind, std,algorithm,searching) for finding a value in a
-range.
+    $(UL
+    $(LI $(REF_SHORT find, std,algorithm,searching) and
+    $(REF_SHORT canFind, std,algorithm,searching) for finding a value in a range.)
+    $(LI $(REF_ALTTEXT `value in iota(start, end)`, iota, std,range) to find a value in
+    a particular interval.)
+    )
 */
 uint among(alias pred = (a, b) => a == b, Value, Values...)
     (Value value, Values values)
@@ -211,31 +215,32 @@ private template indexOfFirstOvershadowingChoiceOnLast(choices...)
 }
 
 /**
-Executes and returns one of a collection of handlers based on the type of the
-switch object.
+Executes one of a series of handlers based on the dynamic type of a class instance.
 
-The first choice that `switchObject` can be casted to the type
-of argument it accepts will be called with `switchObject` casted to that
-type, and the value it'll return will be returned by `castSwitch`.
-
-If a choice's return type is void, the choice must throw an exception, unless
-all the choices are void. In that case, castSwitch itself will return void.
+$(P
+`switchObject`'s dynamic type is checked for compatibility with the argument type of each
+choice in turn until a match is found. When there's a match, the choice will be called with
+`switchObject` cast to that type. If the choice returns a value, `castSwitch` will return it.
+)
+- If one choice's return type is void and one can return a value, `SwitchError`
+  will be thrown if the void handler was matched and completed execution without throwing.
+- A void handler is allowed to return when all the choices' return types convert to void -
+  in that case, `castSwitch` itself will return void.
+- A choice's return type can be `noreturn` (e.g. when a handler always throws an exception).
 
-Throws: If none of the choice matches, a `SwitchError` will be thrown.  $(D
-SwitchError) will also be thrown if not all the choices are void and a void
-choice was executed without throwing anything.
+Throws: If none of the choices match, a `SwitchError` will be thrown.  $(D
+SwitchError) will also be thrown if one choice can return a value but a void
+choice was matched and completed execution.
 
 Params:
-    choices = The `choices` needs to be composed of function or delegate
-        handlers that accept one argument. There can also be a choice that
-        accepts zero arguments. That choice will be invoked if the $(D
+    choices = A sequence of function and/or delegate
+        handlers that each accept one argument. There can also be one choice that
+        accepts zero arguments, which will be invoked if the $(D
         switchObject) is null.
     switchObject = the object against which the tests are being made.
 
 Returns:
-    The value of the selected choice.
-
-Note: `castSwitch` can only be used with object types.
+    The return value of the selected choice.
 */
 auto castSwitch(choices...)(Object switchObject)
 {
@@ -269,7 +274,7 @@ auto castSwitch(choices...)(Object switchObject)
             {
                 alias CastClass = choiceParameterTypes[0];
                 static assert(is(CastClass == class) || is(CastClass == interface),
-                        "A choice handler can have only class or interface typed argument.");
+                        "A choice handler can only have a class or interface argument type.");
 
                 // Check for overshadowing:
                 immutable indexOfOvershadowingChoice =
@@ -380,8 +385,7 @@ auto castSwitch(choices...)(Object switchObject)
     class A
     {
         int a;
-        this(int a) {this.a = a;}
-        @property int i() { return a; }
+        this(int a) { this.a = a; }
     }
     interface I { }
     class B : I { }
@@ -402,22 +406,33 @@ auto castSwitch(choices...)(Object switchObject)
     assert(results[2] == "null reference");
 }
 
-/// Using with void handlers:
+/// Using with noreturn/void handlers:
 @system unittest
 {
+    import core.exception : SwitchError;
     import std.exception : assertThrown;
 
     class A { }
     class B { }
-    // Void handlers are allowed if they throw:
+
+    // B's handler never returns, so `i` does not need a value
+    int i;
     assertThrown!Exception(
-        new B().castSwitch!(
+        i = new B().castSwitch!(
+            (A a) => 1,
+            (B b) { throw new Exception("B is not allowed!"); }
+        )()
+    );
+
+    // Void handler call will throw if another handler returns a value
+    assertThrown!SwitchError(
+        i = new B().castSwitch!(
             (A a) => 1,
-            (B d)    { throw new Exception("B is not allowed!"); }
+            (B b) {}
         )()
     );
 
-    // Void handlers are also allowed if all the handlers are void:
+    // Void handlers are allowed if all the handlers convert to void:
     new A().castSwitch!(
         (A a) { },
         (B b) { assert(false); },
index 34d787ec2784bb67a47283f11afbd1ee9791247e..ee92b012e97053b80c91d96b2975aeb4208591a9 100644 (file)
@@ -659,7 +659,8 @@ if (fun.length >= 1)
         foreach (f; _funs)
         {
             static assert(!is(typeof(f(RE.init)) == void),
-                    "Mapping function(s) must not return void: " ~ _funs.stringof);
+                    "Mapping function(s) must not return void. " ~
+                    " Consider using `each` instead of `map!(" ~ _funs.stringof ~ ")`");
         }
 
         return MapResult!(_fun, Range)(r);
@@ -5547,7 +5548,10 @@ auto splitter(alias pred = "a == b",
               Range,
               Separator)(Range r, Separator s)
 if (is(typeof(binaryFun!pred(r.front, s)) : bool)
-        && ((hasSlicing!Range && hasLength!Range) || isNarrowString!Range))
+        && ((hasSlicing!Range && hasLength!Range) || isNarrowString!Range)
+        && (is(ElementType!Range : Separator)
+        || !(isForwardRange!Separator && (hasLength!Separator
+        || isNarrowString!Separator))))
 {
     import std.algorithm.searching : find;
     import std.conv : unsigned;
@@ -5619,6 +5623,7 @@ if (is(typeof(binaryFun!pred(r.front, s)) : bool)
             assert(!empty, "Attempting to fetch the front of an empty splitter.");
             static if (keepSeparators)
             {
+                if (_frontLength != _unComputed && !_wasSeparator) return _input[0 .. _frontLength];
                 if (!_wasSeparator)
                 {
                     _frontLength = _separatorLength;
@@ -6078,6 +6083,20 @@ if (is(typeof(binaryFun!pred(r.front, s)) : bool)
     assert("abXcdxef".splitter!((a, b) => a.toLower == b)('x').equal(["ab", "cd", "ef"]));
 }
 
+// https://github.com/dlang/phobos/issues/10759
+@safe unittest
+{
+    import std.algorithm.iteration : splitter;
+    import std.algorithm.searching : canFind;
+    import std.range.primitives;
+    import std.typecons : Yes;
+
+    auto range = "16x13+0-2".splitter!((a, b) => "x+-".canFind(a), Yes.keepSeparators)(0);
+    assert(range.front == "16");
+    assert(range.front == "16");
+    assert(range.front == "16");
+}
+
 /// ditto
 auto splitter(alias pred = "a == b",
               Flag!"keepSeparators" keepSeparators = No.keepSeparators,
@@ -6091,7 +6110,7 @@ if (is(typeof(binaryFun!pred(r.front, s.front)) : bool)
     import std.algorithm.searching : find;
     import std.conv : unsigned;
 
-    static struct Result
+    struct Result
     {
     private:
         Range _input;
@@ -6306,6 +6325,17 @@ if (is(typeof(binaryFun!pred(r.front, s.front)) : bool)
     assert(words.equal([ "i", "am", "pointing" ]));
 }
 
+// https://github.com/dlang/phobos/issues/10760
+@safe unittest
+{
+    import std.algorithm.searching : canFind;
+    import std.typecons : Yes;
+    import std.algorithm.comparison : equal;
+
+    auto r = "16x16+0-2".splitter!((a, b) => "x+-".canFind(a), Yes.keepSeparators)("x");
+    assert(r.equal(["16", "x", "16", "+", "0", "-", "2"]));
+}
+
 /++
 Lazily splits a range `r` whenever a predicate `isTerminator` returns true for an element.
 
index 363bd16a0f9ffbf19d539a2d63305a8d333a3093..10873a7cabc7012983f380473216a2079a053abc 100644 (file)
@@ -26,12 +26,11 @@ $(T2 multiwayMerge,
 $(T2 multiwayUnion,
         Computes the union of a range of sorted ranges.)
 $(T2 setDifference,
-        Lazily computes the set difference of two or more sorted ranges.)
+        Lazily computes the set difference of two sorted ranges.)
 $(T2 setIntersection,
         Lazily computes the intersection of two or more sorted ranges.)
 $(T2 setSymmetricDifference,
-        Lazily computes the symmetric set difference of two or more sorted
-        ranges.)
+        Lazily computes the symmetric set difference of two sorted ranges.)
 )
 
 Copyright: Andrei Alexandrescu 2008-.
index 53ffb06905f0538f3c5b1240cbdbbbdc08889f0e..e85eadb40909ccb30613ee3fe82786cc0958b488 100644 (file)
@@ -16,7 +16,7 @@ $(TR $(TH Function Name) $(TH Description)
         $(TD Returns a new $(LREF Appender) or $(LREF RefAppender) initialized with a given array.
     ))
     $(TR $(TD $(LREF assocArray))
-        $(TD Returns a newly allocated associative array from a range of key/value tuples.
+        $(TD Returns a newly allocated associative array from a range/ranges of keys and values.
     ))
     $(TR $(TD $(LREF byPair))
         $(TD Construct a range iterating over an associative array by key/value tuples.
@@ -562,29 +562,19 @@ if (isAutodecodableString!String)
 }
 
 /**
-Returns a newly allocated associative array from a range of key/value tuples
-or from a range of keys and a range of values.
+Creates an associative array from a range of key/value tuples.
 
 Params:
     r = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
-    of tuples of keys and values.
-    keys = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) of keys
-    values = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) of values
+    of $(REF_SHORT Tuple, std,typecons)`!(Key, Value)`.
 
-Returns:
+Duplicates: Associative arrays have unique keys. For any duplicate key in `r`,
+the result will contain the corresponding value from the last occurrence of that key in `r`.
 
-    A newly allocated associative array out of elements of the input
-    range, which must be a range of tuples (Key, Value) or
-    a range of keys and a range of values. If given two ranges of unequal
-    lengths after the elements of the shorter are exhausted the remaining
-    elements of the longer will not be considered.
-    Returns a null associative array reference when given an empty range.
-    Duplicates: Associative arrays have unique keys. If r contains duplicate keys,
-    then the result will contain the value of the last pair for that key in r.
-
-See_Also: $(REF Tuple, std,typecons), $(REF zip, std,range)
+Returns:
+    A newly allocated associative array, or a null associative array reference when
+    given an empty range. The type is `Value[Key]`.
  */
-
 auto assocArray(Range)(Range r)
 if (isInputRange!Range)
 {
@@ -604,7 +594,34 @@ if (isInputRange!Range)
     return aa;
 }
 
-/// ditto
+///
+@safe pure nothrow unittest
+{
+    import std.typecons : tuple;
+
+    auto b = assocArray([ tuple("foo", "bar"), tuple("baz", "quux") ]);
+    static assert(is(typeof(b) == string[string]));
+    assert(b == ["foo":"bar", "baz":"quux"]);
+}
+
+/**
+Creates an associative array from a range of keys and a range of values.
+
+If given two ranges of unequal lengths after the elements of the shorter are exhausted,
+the remaining elements of the longer will not be considered.
+
+Params:
+    keys = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) of keys
+    values = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) of values
+
+Duplicates: Associative arrays have unique keys. For any key with duplicates in `keys`,
+the result will have the corresponding value for the last occurrence of
+that key in `keys`.
+
+Returns:
+    A newly allocated associative array, or a null associative array reference when
+    given empty ranges.
+ */
 auto assocArray(Keys, Values)(Keys keys, Values values)
 if (isInputRange!Values && isInputRange!Keys)
 {
@@ -682,23 +699,16 @@ if (isInputRange!Values && isInputRange!Keys)
 }
 
 ///
-@safe pure /*nothrow*/ unittest
+@safe pure nothrow unittest
 {
-    import std.range : repeat, zip;
-    import std.typecons : tuple;
     import std.range.primitives : autodecodeStrings;
-    auto a = assocArray(zip([0, 1, 2], ["a", "b", "c"])); // aka zipMap
-    static assert(is(typeof(a) == string[int]));
-    assert(a == [0:"a", 1:"b", 2:"c"]);
-
-    auto b = assocArray([ tuple("foo", "bar"), tuple("baz", "quux") ]);
-    static assert(is(typeof(b) == string[string]));
-    assert(b == ["foo":"bar", "baz":"quux"]);
+    import std.range : repeat;
 
     static if (autodecodeStrings)
         alias achar = dchar;
     else
         alias achar = immutable(char);
+
     auto c = assocArray("ABCD", true.repeat);
     static assert(is(typeof(c) == bool[achar]));
     bool[achar] expected = ['D':true, 'A':true, 'B':true, 'C':true];
index a69b7bf84d9c94c38caa96f80d4fb5349c24256e..8363b3f579ebd84abb1a6d6663d479505723f50a 100644 (file)
@@ -186,6 +186,7 @@ else
     Params: c = The character to test.
     Returns: Whether `c` is a letter or a number (0 .. 9, a .. z, A .. Z).
   +/
+pragma(inline, true)
 bool isAlphaNum(dchar c) @safe pure nothrow @nogc
 {
     const hc = c | 0x20;
@@ -218,6 +219,7 @@ bool isAlphaNum(dchar c) @safe pure nothrow @nogc
     Params: c = The character to test.
     Returns: Whether `c` is an ASCII letter (A .. Z, a .. z).
   +/
+pragma(inline, true)
 bool isAlpha(dchar c) @safe pure nothrow @nogc
 {
     // Optimizer can turn this into a bitmask operation on 64 bit code
@@ -250,6 +252,7 @@ bool isAlpha(dchar c) @safe pure nothrow @nogc
     Params: c = The character to test.
     Returns: Whether `c` is a lowercase ASCII letter (a .. z).
   +/
+pragma(inline, true)
 bool isLower(dchar c) @safe pure nothrow @nogc
 {
     return c >= 'a' && c <= 'z';
@@ -282,6 +285,7 @@ bool isLower(dchar c) @safe pure nothrow @nogc
     Params: c = The character to test.
     Returns: Whether `c` is an uppercase ASCII letter (A .. Z).
   +/
+pragma(inline, true)
 bool isUpper(dchar c) @safe pure nothrow @nogc
 {
     return c <= 'Z' && 'A' <= c;
@@ -314,6 +318,7 @@ bool isUpper(dchar c) @safe pure nothrow @nogc
     Params: c = The character to test.
     Returns: Whether `c` is a digit (0 .. 9).
   +/
+pragma(inline, true)
 bool isDigit(dchar c) @safe pure nothrow @nogc
 {
     return '0' <= c && c <= '9';
@@ -347,6 +352,7 @@ bool isDigit(dchar c) @safe pure nothrow @nogc
     Params: c = The character to test.
     Returns: Whether `c` is a digit in base 8 (0 .. 7).
   +/
+pragma(inline, true)
 bool isOctalDigit(dchar c) @safe pure nothrow @nogc
 {
     return c >= '0' && c <= '7';
@@ -377,6 +383,7 @@ bool isOctalDigit(dchar c) @safe pure nothrow @nogc
     Params: c = The character to test.
     Returns: Whether `c` is a digit in base 16 (0 .. 9, A .. F, a .. f).
   +/
+pragma(inline, true)
 bool isHexDigit(dchar c) @safe pure nothrow @nogc
 {
     const hc = c | 0x20;
@@ -411,6 +418,7 @@ bool isHexDigit(dchar c) @safe pure nothrow @nogc
     space, tab, vertical tab, form feed, carriage return, and linefeed
     characters.
   +/
+pragma(inline, true)
 bool isWhite(dchar c) @safe pure nothrow @nogc
 {
     return c == ' ' || (c >= 0x09 && c <= 0x0D);
@@ -447,6 +455,7 @@ bool isWhite(dchar c) @safe pure nothrow @nogc
     Params: c = The character to test.
     Returns: Whether `c` is a control character.
   +/
+pragma(inline, true)
 bool isControl(dchar c) @safe pure nothrow @nogc
 {
     return c < 0x20 || c == 0x7F;
@@ -487,6 +496,7 @@ bool isControl(dchar c) @safe pure nothrow @nogc
     all ASCII characters which are not control characters, letters, digits, or
     whitespace.
   +/
+pragma(inline, true)
 bool isPunctuation(dchar c) @safe pure nothrow @nogc
 {
     return c <= '~' && c >= '!' && !isAlphaNum(c);
@@ -531,6 +541,7 @@ bool isPunctuation(dchar c) @safe pure nothrow @nogc
     Returns: Whether or not `c` is a printable character other than the
     space character.
   +/
+pragma(inline, true)
 bool isGraphical(dchar c) @safe pure nothrow @nogc
 {
     return '!' <= c && c <= '~';
@@ -567,6 +578,7 @@ bool isGraphical(dchar c) @safe pure nothrow @nogc
     Returns: Whether or not `c` is a printable character - including the
     space character.
   +/
+pragma(inline, true)
 bool isPrintable(dchar c) @safe pure nothrow @nogc
 {
     return c >= ' ' && c <= '~';
index 94265a2b7797e85384c9f1631ae8d854cb085bde..7c7f46572541db1cacc76d4ae194ddbd4eb44f42 100644 (file)
@@ -4,6 +4,7 @@
  * $(BOOKTABLE,
  * $(TR $(TH Category) $(TH Symbols))
  * $(TR $(TD Tid) $(TD
+ *     $(MYREF join)
  *     $(MYREF locate)
  *     $(MYREF ownerTid)
  *     $(MYREF register)
@@ -622,6 +623,7 @@ if (isSpawnable!(F, T))
     else
     {
         auto t = new Thread(&exec);
+        spawnTid.mbox.m_thread = t;
         t.start();
     }
     thisInfo.links[spawnTid] = linked;
@@ -1197,6 +1199,66 @@ Tid locate(string name)
     }
 }
 
+/**
+ * Waits for the thread associated with `tid` to complete.
+ *
+ * This function blocks until the thread represented by `tid` finishes executing
+ * and then releases all OS resources associated with it (thread stack, thread-local
+ * storage, etc.). This is essential for preventing resource leaks in long-running
+ * applications that create many short-lived threads.
+ *
+ * For threads created by $(LREF spawn), this function must be called to properly
+ * free OS resources. Without calling `join`, thread stacks (~8 MB each on typical
+ * systems) will accumulate in virtual memory for the lifetime of the process.
+ *
+ * Params:
+ *  tid = The $(LREF Tid) of the thread to join.
+ *
+ * Throws:
+ *  $(REF ThreadError, core,thread,osthread) if the thread has already been joined
+ *  or if the thread was created by a custom $(LREF Scheduler).
+ *
+ * Example:
+ * ---
+ * auto tid = spawn(&someFunction);
+ * // ... do other work ...
+ * join(tid); // Wait for thread to complete and free resources
+ * ---
+ *
+ * Note:
+ *  It is an error to call `join` on the same `Tid` more than once.
+ *  The function will throw if the thread was created by a $(LREF Scheduler)
+ *  rather than directly as a system thread.
+ */
+void join(Tid tid)
+{
+    import core.thread.threadbase : ThreadError;
+
+    if (tid.mbox is null)
+        throw new ThreadError("Cannot join Tid with null MessageBox");
+
+    if (tid.mbox.m_thread is null)
+        throw new ThreadError("Cannot join Tid created by a Scheduler");
+
+    tid.mbox.m_thread.join();
+}
+
+///
+@system unittest
+{
+    import core.time : msecs;
+    import core.thread : Thread;
+
+    auto tid = spawn((int x) {
+        Thread.sleep(10.msecs);
+        ownerTid.send(x * 2);
+    }, 21);
+
+    join(tid); // Wait for thread to finish
+    auto result = receiveOnly!int();
+    assert(result == 42);
+}
+
 /**
  * Encapsulates all implementation-level data needed for scheduling.
  *
@@ -2426,6 +2488,7 @@ private
         size_t m_localMsgs;
         size_t m_maxMsgs;
         bool m_closed;
+        package Thread m_thread; // For join(Tid)
     }
 
     /*
index fc0495057d0c5cfcc4c0d6298f5237dd0fab09bd..57373ba0aef845098ada3fdbac3c736224b190b9 100644 (file)
@@ -3,12 +3,13 @@
 /**
 This module defines generic containers.
 
-Construction:
+$(H3 $(LNAME2 construction, Construction))
 
-To implement the different containers both struct and class based
+To implement the different containers, both struct and class based
 approaches have been used. $(REF make, std,container,util) allows for
 uniform construction with either approach.
 
+$(RUNNABLE_EXAMPLE
 ---
 import std.container;
 // Construct a red-black tree and an array both containing the values 1, 2, 3.
@@ -16,27 +17,36 @@ import std.container;
 RedBlackTree!int rbTree = new RedBlackTree!int(1, 2, 3);
 // But `new` should not be used with Array
 Array!int array = Array!int(1, 2, 3);
+
 // `make` hides the differences
 RedBlackTree!int rbTree2 = make!(RedBlackTree!int)(1, 2, 3);
 Array!int array2 = make!(Array!int)(1, 2, 3);
+
+assert(rbTree == rbTree2);
+assert(array == array2);
 ---
+)
 
 Note that `make` can infer the element type from the given arguments.
 
 ---
 import std.container;
+
 auto rbTree = make!RedBlackTree(1, 2, 3); // RedBlackTree!int
 auto array = make!Array("1", "2", "3"); // Array!string
 ---
 
-Reference_semantics:
+$(H3 $(LNAME2 reference-semantics, Reference Semantics))
 
 All containers have reference semantics, which means that after
 assignment both variables refer to the same underlying data.
 
 To make a copy of a container, use the `c.dup` container primitive.
+
+$(RUNNABLE_EXAMPLE
 ---
-import std.container, std.range;
+import std.algorithm, std.container, std.range;
+
 Array!int originalArray = make!(Array!int)(1, 2, 3);
 Array!int secondArray = originalArray;
 assert(equal(originalArray[], secondArray[]));
@@ -51,6 +61,7 @@ secondArray[0] = 1;
 // assert that originalArray has not been affected
 assert(originalArray[0] == 12);
 ---
+)
 
 $(B Attention:) If the container is implemented as a class, using an
 uninitialized instance can cause a null pointer dereference.
@@ -67,6 +78,7 @@ intializes itself upon use; however, up to this point the container will not
 have an identity and assignment does not create two references to the same
 data.
 
+$(RUNNABLE_EXAMPLE
 ---
 import std.container;
 
@@ -84,6 +96,8 @@ array1 = array2;
 array1.removeBack();
 assert(array2.empty);
 ---
+)
+
 It is therefore recommended to always construct containers using
 $(REF make, std,container,util).
 
@@ -97,7 +111,7 @@ import std.container, std.range;
 auto arrOfArrs = make!Array(generate!(() => make!(Array!int)).take(10));
 ---
 
-Submodules:
+$(H3 $(LNAME2 submodules, Submodules))
 
 This module consists of the following submodules:
 
@@ -130,7 +144,7 @@ $(UL
     )
 )
 
-The_primary_range_of_a_container:
+$(H3 $(LNAME2 primary-range, The Primary Range of a Container))
 
 While some containers offer direct access to their elements e.g. via
 `opIndex`, `c.front` or `c.back`, access
@@ -147,9 +161,10 @@ these parameters $(B must) be obtained from the same container instance
 as the one being worked with. It is important to note that many generic range
 algorithms return the same range type as their input range.
 
+$(RUNNABLE_EXAMPLE
 ---
 import std.algorithm.comparison : equal;
-import std.algorithm.iteration : find;
+import std.algorithm.searching : find;
 import std.container;
 import std.range : take;
 
@@ -168,11 +183,13 @@ array.linearRemove(array[].find(2).take(1));
 
 assert(array[].equal([1, 3]));
 ---
+)
 
 When any $(MREF_ALTTEXT range, std, range) can be passed as an argument to
 a member function, the documention usually refers to the parameter's templated
 type as `Stuff`.
 
+$(RUNNABLE_EXAMPLE
 ---
 import std.algorithm.comparison : equal;
 import std.container;
@@ -186,8 +203,9 @@ array.insertBack(iota(3, 10));
 
 assert(array[].equal([1, 2, 3, 4, 5, 6, 7, 8, 9]));
 ---
+)
 
-Container_primitives:
+$(H3 $(LNAME2 primitives, Container Primitives))
 
 Containers do not form a class hierarchy, instead they implement a
 common set of primitives (see table below). These primitives each guarantee
index 17e9fc42215306d5ea89c3c1a56937801bb94bec..b5c8fa3352cb37d2d13a8d601b66a4f1f0bceb63 100644 (file)
@@ -1531,13 +1531,18 @@ if (is(typeof((ref const T a) => binaryFun!less(a, a))))
        Complexity: $(BIGOH m log(n)) (where m is the number of elements to remove)
 
        Example:
+$(RUNNABLE_EXAMPLE
 --------------------
+import std.algorithm, std.container;
+
 auto rbt = redBlackTree!true(0, 1, 1, 1, 4, 5, 7);
 rbt.removeKey(1, 4, 7);
 assert(equal(rbt[], [0, 1, 1, 5]));
+
 rbt.removeKey(1, 1, 0);
 assert(equal(rbt[], [5]));
 --------------------
+)
       +/
     size_t removeKey(U...)(U elems)
     if (allSatisfy!(isImplicitlyConvertibleToElem, U))
index ef6fbf060f1681ba7f2daf97bff3aec4c955fc8d..917d4a382fefea2ed1d89ee931026101c25f2668 100644 (file)
@@ -506,13 +506,18 @@ Complexity: $(BIGOH k + m), where `k` is the number of elements in
 `r` and `m` is the length of `stuff`.
 
 Example:
+$(RUNNABLE_EXAMPLE
 --------------------
+import std.algorithm, std.container, std.range;
+
 auto sl = SList!string(["a", "b", "d"]);
 sl.insertAfter(sl[], "e"); // insert at the end (slowest)
-assert(std.algorithm.equal(sl[], ["a", "b", "d", "e"]));
-sl.insertAfter(std.range.take(sl[], 2), "c"); // insert after "b"
-assert(std.algorithm.equal(sl[], ["a", "b", "c", "d", "e"]));
+assert(equal(sl[], ["a", "b", "d", "e"]));
+
+sl.insertAfter(take(sl[], 2), "c"); // insert after "b"
+assert(equal(sl[], ["a", "b", "c", "d", "e"]));
 --------------------
+)
      */
 
     size_t insertAfter(Stuff)(Range r, Stuff stuff)
index 0d44e5c3565d876d0539b60d7e99c0e6b836d10e..ccac8f561f7ff050ab622b0d8db5bad05b8c6d91 100644 (file)
@@ -1866,7 +1866,7 @@ if (!is(S : T) && isAssociativeArray!S &&
             assert(convFails!(Floating, Integral, ConvOverflowException)(a));
         }
         // convert to the smallest integral value
-        a = 0.0 + Integral.min;
+        a = 0.0L + Integral.min;
         static if (Integral.min < 0)
         {
             a = -a; // -Integral.min not representable as an Integral
@@ -1874,13 +1874,13 @@ if (!is(S : T) && isAssociativeArray!S &&
                     || Floating.sizeof <= Integral.sizeof
                     || floatTraits!Floating.realFormat == RealFormat.ieeeExtended53);
         }
-        a = 0.0 + Integral.min;
+        a = 0.0L + Integral.min;
         assert(to!Integral(a) == Integral.min);
         --a; // no more representable as an Integral
         assert(convFails!(Floating, Integral, ConvOverflowException)(a)
                 || Floating.sizeof <= Integral.sizeof
                 || floatTraits!Floating.realFormat == RealFormat.ieeeExtended53);
-        a = 0.0 + Integral.max;
+        a = 0.0L + Integral.max;
         assert(to!Integral(a) == Integral.max
                 || Floating.sizeof <= Integral.sizeof
                 || floatTraits!Floating.realFormat == RealFormat.ieeeExtended53);
index 9d30b5e02768df4e838ecfb56371813ad8a5b06f..decc3249612b4f37693ca1d8fc5096e4ce5c8bc4 100644 (file)
@@ -1328,6 +1328,10 @@ template SharedAllocatorList(alias factoryFunction,
     assert(a.deallocateAll());
 }
 
+//BUG: this test freezes spuriously on FreeBSD, see also https://github.com/dlang/phobos/issues/10730.
+// The lock in a.allocate() below blocks in all remaining threads causing the join to never complete.
+// This might also hint at problems in the spinlock implementation.
+version (FreeBSD) {} else
 @system unittest
 {
     import std.experimental.allocator.mallocator : Mallocator;
index 8d3288579303c606e2cf0bc0c80fe3fe70e53416..1446afb69a4c570d4e9ae3b4534d63647ea370cd 100644 (file)
@@ -2546,9 +2546,23 @@ if ((is(T == struct) || is(T == union)) && (hasToString!(T, Char) || !is(Builtin
             {
                 // ignore hidden context pointer
             }
-            else static if (0 < i && T.tupleof[i-1].offsetof == T.tupleof[i].offsetof)
+            /* https://github.com/dlang/phobos/issues/10840
+             * handle possible bitfields by doing overlap comparisons
+             * using bit counts rather than byte counts.
+             * However, the overlap
+             * check in general does not take into account staggered unions.
+             * This can be fixed using the correct algorithm implemented in
+             * the compiler function dmd.declaration.isOverlappedWith().
+             * For the moment we will not change to that because the `#(overlap ...)` output
+             * needs to be re-thought, as it was never correct.
+             */
+            else static if (0 < i &&
+                            T.tupleof[i-1].offsetof * 8 + __traits(getBitfieldOffset,T.tupleof[i-1]) ==
+                            T.tupleof[i  ].offsetof * 8 + __traits(getBitfieldOffset,T.tupleof[i  ]))
             {
-                static if (i == T.tupleof.length - 1 || T.tupleof[i].offsetof != T.tupleof[i+1].offsetof)
+                static if (i == T.tupleof.length - 1 ||
+                            T.tupleof[i  ].offsetof * 8 + __traits(getBitfieldOffset,T.tupleof[i  ]) !=
+                            T.tupleof[i+1].offsetof * 8 + __traits(getBitfieldOffset,T.tupleof[i+1]))
                 {
                     enum el = separator ~ __traits(identifier, T.tupleof[i]) ~ "}";
                     put(w, el);
@@ -2559,7 +2573,9 @@ if ((is(T == struct) || is(T == union)) && (hasToString!(T, Char) || !is(Builtin
                     put(w, el);
                 }
             }
-            else static if (i+1 < T.tupleof.length && T.tupleof[i].offsetof == T.tupleof[i+1].offsetof)
+            else static if (i+1 < T.tupleof.length &&
+                            T.tupleof[i  ].offsetof * 8 + __traits(getBitfieldOffset,T.tupleof[i  ]) ==
+                            T.tupleof[i+1].offsetof * 8 + __traits(getBitfieldOffset,T.tupleof[i+1]))
             {
                 enum el = (i > 0 ? separator : "") ~ "#{overlap " ~ __traits(identifier, T.tupleof[i]);
                 put(w, el);
@@ -3095,13 +3111,10 @@ if (isDelegate!T)
     import std.format : formatValue;
 
     void func() @system { __gshared int x; ++x; throw new Exception("msg"); }
-    version (linux)
-    {
-        FormatSpec!char f;
-        auto w = appender!string();
-        formatValue(w, &func, f);
-        assert(w.data.length >= 15 && w.data[0 .. 15] == "void delegate()");
-    }
+    FormatSpec!char f;
+    auto w = appender!string();
+    formatValue(w, &func, f);
+    assert(w.data.length >= 15 && w.data[0 .. 15] == "void delegate()");
 }
 
 // string elements are formatted like UTF-8 string literals.
index d5f5dd2e81427fdb1c6238f7bf4d24bd70d534f2..468f7eff8f5a493e01f1d02b88efd88a4f485048 100644 (file)
@@ -422,6 +422,13 @@ real gamma(real x)
         4.59084371199880305320475827592915200343410999829340L) >= real.mant_dig-1);
 }
 
+
+/* This is the lower bound on x for when the Stirling approximation can be used
+ * to compute ln(Γ(x)).
+ */
+private enum real LN_GAMMA_STIRLING_LB = 13.0L;
+
+
 /*****************************************************
  * Natural logarithm of gamma function.
  *
@@ -480,7 +487,7 @@ real logGamma(real x)
         return z;
     }
 
-    if ( x < 13.0L )
+    if ( x < LN_GAMMA_STIRLING_LB )
     {
         z = 1.0L;
         nx = floor( x +  0.5L );
@@ -579,6 +586,366 @@ real logGamma(real x)
 }
 
 
+/** sgn($(GAMMA)(x))
+ *
+ * Params:
+ *   x = the argument of $(GAMMA)
+ *
+ * Returns:
+ *   -1 if $(GAMMA)(x) < 0, +1 if $(GAMMA)(x) > 0, and $(NAN) if $(GAMMA)(x)
+ *   does not exist.
+ *
+ * Authors: Don Clugston
+ */
+real sgnGamma(in real x)
+{
+    if (isNaN(x)) return x;
+    if (x > 0) return 1.0;
+
+    // -x is so large that x + 1 is indistinguishable from x.
+    if (x < -1 / real.epsilon) return real.nan;
+
+    const n = trunc(x);
+    if (x == n) return x == 0 ? copysign(1, x) : real.nan;
+    return cast(long) n & 1 ? 1.0 : -1.0;
+}
+
+@safe unittest
+{
+    assert(sgnGamma(5.0) == 1.0);
+    assert(isNaN(sgnGamma(-3.0)));
+    assert(sgnGamma(-0.1) == -1.0);
+    assert(sgnGamma(-0.6) == -1.0);
+    assert(sgnGamma(-55.1) == 1.0);
+    assert(isNaN(sgnGamma(-real.infinity)));
+    assert(isIdentical(sgnGamma(NaN(0xABC)), NaN(0xABC)));
+}
+
+
+/* A method for computing B(x,y) when gamma would return infinity. It uses
+ * logGamma and exp instead.
+ */
+private pragma(inline, true) real betaLarge(in real x, in real y)
+{
+    const sgnB = sgnGamma(x) * sgnGamma(y) / sgnGamma(x+y);
+    return sgnB * exp(logGamma(x) + logGamma(y) - logGamma(x+y));
+}
+
+@safe unittest
+{
+    assert(betaLarge(2*MAXGAMMA, -0.5) < 0);
+    assert(betaLarge(-0.1, 2*MAXGAMMA) < 0);
+    assert(betaLarge(-1.6, 2*MAXGAMMA) > 0);
+    assert(betaLarge(+0., 2*MAXGAMMA) == real.infinity);
+    assert(betaLarge(-0., 2*MAXGAMMA) == -real.infinity);
+    assert(betaLarge(-MAXGAMMA-1.5, MAXGAMMA+1) < 0);
+    assert(isNaN(betaLarge(-1, 2*MAXGAMMA)));
+}
+
+/** B(x,y)
+ *
+ * This computes B(x,y). It will use the formula when i$(GAMMA)(x)$(GAMMA)(y)/$(GAMMA)(x+y) when
+ * `gamma` can compute the $(GAMMA) terms, otherwise it will use `logGamma`.
+ *
+ * There are many edge cases that generate NaN instead of the actual value. The main algorithm works
+ * for most (x,y) pairs and only generates a NaN when it doesn't. In order to not penalize every
+ * computation with a bunch of branching logic, the main algorithm is used, and only if it results
+ * in a NaN will the edge cases be checked.
+ *
+ * Params:
+ *   x = the first argument of B
+ *   y = the second argument of B
+ *
+ * Returns:
+ *   B(x,y) if it can be computed, otherwise $(NAN)
+ */
+real beta(in real x, in real y)
+{
+    real res;
+
+    // the main algorithm
+    if (x > MAXGAMMA || y > MAXGAMMA || x + y > MAXGAMMA)
+    {
+        res = betaLarge(x, y);
+    }
+    else
+    {
+        res = gamma(x) * gamma(y) / gamma(x+y);
+
+        // There are several regions near the asymptotes and inflection lines
+        // gamma cannot be computed but logGamma can.
+        if (!isFinite(res)) res = betaLarge(x,y);
+    }
+
+    if (!isNaN(res)) return res;
+
+    // For valid NaN results, always return the response from the main algorithm
+    // in order to preserve signaling NaNs.
+
+    if (isNaN(x) || isNaN(y)) return res;
+
+    // Take advantage of the symmetry B(x,y) = B(y,x)
+    // smaller ≤ larger
+    const larger = cmp(x, y) >= 0 ? x : y;
+    const smaller = cmp(x, y) >= 0 ? y : x;
+    const sum = larger + smaller;
+
+    // in a quadrant of the (smaller,larger) cartesian plane
+    const inQ1 = cmp(smaller, +0.0L) >= 0;
+    const inQ2 = !inQ1 && cmp(larger, +0.0L) >= 0;
+    const inQ3 = !inQ1 && !inQ2;
+
+    const nextToSmallAxis = smaller == 0;
+    const nextToLargeAxis = larger == 0;
+    const nextToOrigin = nextToSmallAxis && nextToLargeAxis;
+
+    // on an asymptote, excluding the one at the axis
+    const onSmallAsymptote = smaller < 0 && smaller == trunc(smaller);
+    const onLargeAsymptote = larger < 0 && larger == trunc(larger);
+
+    // on an inflection line segment
+    const onInflection =
+        sum <= 0 && sum == trunc(sum) && !onSmallAsymptote && !onLargeAsymptote && !nextToOrigin;
+
+    // 1) Either is -∞, B = nan
+    if (larger == -real.infinity || smaller == -real.infinity) return res;
+
+    // 2) On an asymptote, B = nan
+    if (onSmallAsymptote || onLargeAsymptote) return res;
+
+    // 3) On an inflection line segment
+    if (onInflection) return copysign(0.0L, sgnGamma(smaller)*sgnGamma(larger));
+
+    if (inQ1)
+    {
+        // 4) On the larger axis and larger is finite, B = +∞
+        // 5) On the larger axis, and larger is +∞, B = nan
+        if (nextToSmallAxis) return larger < +real.infinity ? +real.infinity : res;
+
+        // 6) Not on the larger axis, and the larger is +∞, B = +0
+        if (!nextToSmallAxis && larger == +real.infinity) return +0.;
+    }
+
+    if (inQ2)
+    {
+        // 7) Next to the origin, B = nan
+        // 8) Next to the larger axis, but not the origin, B = -∞
+        if (nextToSmallAxis) return nextToOrigin ? res : -real.infinity;
+
+        // 9) Larger is +∞, B = ∞ * sgn(Γ(smaller))
+        if (larger == +real.infinity) return copysign(real.infinity, sgnGamma(smaller));
+
+        // 10) next to smaller axis, but not on an asymptote or at the origin,
+        //     B = +∞.
+        if (nextToLargeAxis && !onSmallAsymptote && !nextToOrigin) return +real.infinity;
+
+        // larger very large, case 9
+        // larger so large that ln|Γ(larger)| and ln|Γ(sum)| are too large to
+        // represent as reals. Thus they each are approximated as ∞, and the
+        // main algorithm resolves to NaN instead of ±∞.
+        if (sum > 1) return copysign(real.infinity, sgnGamma(smaller));
+    }
+
+    if (inQ3)
+    {
+        // 11) next to the smaller axis, but not on an asymptote, B = -∞.
+        if (nextToLargeAxis && !onSmallAsymptote) return -real.infinity;
+
+        // near origin, case 11
+        // -larger and -sum are so small that ln|Γ(larger)| and ln|Γ(sum)| are
+        // too large to be represented as reals. Thus they each are approximated
+        // as ∞, and the main algorithm resolves to NaN instead of -∞.
+        if (smaller > -0.25) return -real.infinity;
+    }
+
+    // Unknown case
+    return res;
+}
+
+@safe unittest
+{
+    assert(isIdentical(beta(2, NaN(0xABC)), NaN(0xABC)));
+
+    // Test symmetry
+
+    // Test first quadrant
+    assert(beta(+0., 1) == +real.infinity);
+    assert(beta(nextUp(+0.0L), nextUp(+0.0L) > 0), "B(εₓ,ε𞁟) > 0");
+    assert(!isNaN(beta(nextUp(+0.0L), 1)), "B(ε,y), y > 0 should exist");
+    assert(beta(1, +real.infinity) is +0.0L, "lim{y→+∞} B(x,y) = 0⁺, x > 0");
+    assert(beta(1, 1) > 0);
+    assert(beta(0.6*MAXGAMMA, 0.5*MAXGAMMA) > 0);
+    assert(beta(1, 2*MAXGAMMA) > 0);
+    assert(beta(+0., 2*MAXGAMMA) == real.infinity);
+    assert(beta(nextUp(+0.0L), 2*MAXGAMMA) > 0);
+
+    // Test second quadrant above inflection lines
+    assert(isNaN(beta(-0., +0.)), "lim{x→0⁻, y→0⁺} B(x,y) should not exist");
+    assert(beta(-0., real.infinity) == -real.infinity, "lim{x→0⁻, y→+∞} B(x,y) = -∞");
+    assert(isNaN(beta(-2, 3)));
+    assert(beta(-0.5, 1) < 0);
+    assert(beta(-1.5, 3) > 0);
+    assert(beta(nextDown(-0.0L), 1) < 0);
+    assert(beta(nextUp(-1.0L), 2) < 0);
+    assert(beta(nextUp(-0.5L), 0.5) < 0);
+    assert(beta(-0.5, nextUp(0.5L)) < 0);
+    assert(beta(-0.5, real.infinity) == -real.infinity);
+    assert(cmp(beta(nextDown(-0.0L), 2*nextUp(+0.0L)), -0.0L) <= 0);
+    assert(beta(nextUp(-1.0L), 1) < 0);
+    assert(beta(nextDown(-0.0L), +real.infinity) == -real.infinity);
+    assert(beta(nextDown(-0.0L), nextDown(+real.infinity)) < 0, "B(-ε,y) < 0, y large");
+    assert(
+        beta(nextUp(-1.0L), real.infinity) == -real.infinity, "lim{y→+∞} B(-n+ε, y) = -∞, n odd");
+    assert(beta(nextUp(-1.0L), nextDown(real.infinity)) < 0, "B(-n+ε, y) < 0, n odd, y large");
+    assert(beta(nextDown(-1.0L), 2) > 0);
+    assert(beta(nextUp(-2.0L), 3) > 0);
+    assert(beta(nextUp(-1.5L), 1.5) > 0);
+    assert(beta(-1.5, nextUp(1.5L)) > 0);
+    assert(beta(nextDown(-1.0L), nextDown(real.infinity)) > 0);
+    assert(
+        beta(nextUp(-2.0L), real.infinity) == real.infinity, "lim{y→+∞} B(-n+ε, y) = +∞, n even");
+    assert(beta(nextUp(-2.0L), nextDown(real.infinity)) > 0, "B(-n+ε, y) > 0, n even, y large");
+    assert(
+        beta(-1.5, real.infinity) == +real.infinity, "lim{y→+∞} B(x,y) = +∞, -n-1 < x < -n, n odd");
+
+    // Test second quadrant within inflection lines
+    assert(beta(nextDown(-1.0L), nextUp(nextUp(1.0L))) > 0);
+    assert(beta(nextUp(-2.0L), 2) > 0);
+    assert(beta(nextDown(-0.0L), +0.) == +real.infinity);
+    assert(isNaN(beta(-1, +0)), "lim{y→0⁺} B(-n,y), should not exist");
+    assert(beta(nextUp(-1.0L), +0.) == +real.infinity);
+    assert(beta(nextDown(-1.0L), +0.) == +real.infinity);
+    assert(isNaN(beta(-real.infinity, real.infinity)));
+    assert(beta(nextDown(-0.0L), nextUp(+0.0L)) is -0.0L);
+    assert(beta(nextUp(-1.0L), nextDown(1.0L)) is -0.0L);
+    assert(beta(nextDown(-1.0L), nextUp(1.0L)) is +0.0L);
+    assert(beta(-0.5, 0.25) > 0);
+    assert(beta(nextUp(-1.0L), 0.25) > 0);
+    assert(beta(-0.5, nextUp(+0.0L)) > 0);
+    assert(beta(nextDown(-0.5L), 0.5) >= 0);
+    assert(beta(-0.5, nextDown(0.5L)) >= 0);
+    assert(beta(nextUp(-1.0L), nextDown(nextDown(1.0L))) >= 0);
+    assert(beta(nextUp(-1.0L), nextUp(+0.0L)));
+    assert(beta(-1.5, 0.25) > 0);
+    assert(beta(nextUp(2.0L), 0.25) > 0);
+    assert(beta(-1.5, nextUp(+0.0L)) > 0);
+    assert(beta(nextDown(-1.5L), 0.5) >= 0);
+    assert(beta(-1.5, nextDown(0.5L)) >= 0);
+    assert(beta(nextUp(-2.0L), nextUp(+0.0L)) > 0);
+    assert(beta(nextUp(-2.0L), nextDown(nextDown(1.0L))) >= 0);
+    assert(beta(nextDown(nextDown(-1.0L)), nextUp(+0.0L)) >= 0);
+    assert(beta(-1.5, 1) < 0);
+    assert(beta(nextDown(-1.0L), 0.5) < 0);
+    assert(beta(nextUp(-2.0L), 1.5) < 0);
+    assert(beta(nextUp(-1.5L), 0.5) <= 0);
+    assert(beta(-1.5L, nextUp(0.5L)) <= 0);
+    assert(beta(nextDown(-1.5L), 1.5) <= 0);
+    assert(beta(-1.5, nextDown(1.5L)) <= 0);
+    assert(beta(nextDown(-1.0L), 2*(nextUp(+1.0L) - 1)) <= 0);
+    assert(beta(nextUp(-2.0L), 1.) <= 0);
+    assert(beta(nextUp(-2.0L), nextDown(nextDown(2.0L))) <= 0);
+    assert(beta(nextDown(-1.0L), 1.) <= 0);
+    assert(beta(-0.0L, 2*MAXGAMMA) == -real.infinity, "lim{x→0⁻} B(x,y) = -∞, y > MAXGAMMA");
+    assert(beta(nextDown(-0.0L), 2*MAXGAMMA) < 0, "B(-ε,y) < 0, y > MAXGAMMA");
+
+    // Test third quadrant
+    assert(beta(-0., -0.) == -real.infinity);
+    assert(isNaN(beta(-2, -0.5)));
+    assert(beta(-0.5, -0.) == -real.infinity);
+    assert(isNaN(beta(-1, -0.)));
+    assert(isNaN(beta(-real.infinity, -0.)));
+    assert(beta(nextDown(-0.0L), -0.) == -real.infinity);
+    assert(isNaN(beta(-1, -0.)));
+    assert(beta(nextUp(-1.0L), -0.) == -real.infinity);
+    assert(beta(nextDown(-1.0L), -0.) == -real.infinity);
+    assert(isNaN(beta(-1.5, -1)));
+    assert(isNaN(beta(-3, -1)));
+    assert(beta(-0.75, -0.25) == +0.);
+    assert(beta(nextUp(-1.0L), nextDown(1.0L) - 1) == +0., "B(-n+ε, -ε) = 0⁺, n odd");
+    assert(beta(nextDown(-1.0L), nextUp(1.0L)) == -0.);
+    assert(beta(-0.5, -0.25) < 0);
+    assert(beta(-0.5, nextDown(-0.0L)) < 0);
+    assert(beta(-0.5, nextUp(-0.5L)) <= 0);
+    assert(beta(nextDown(-0.0L), nextDown(-0.0L)) < 0, "B(-εₓ,-ε𞁟) < 0");
+    assert(beta(nextUp(nextUp(-1.0L)), nextDown(-0.0L)) <= 0);
+    assert(beta(-2.25, -1.25) < 0);
+    assert(beta(nextUp(-2.5L), -1.5) <= 0);
+    assert(beta(-2.5, nextDown(-1.0L)) < 0);
+    assert(beta(nextDown(-2.0L), -1.5) < 0);
+    assert(beta(nextDown(-2.0L), nextDown(-1.0L)) < 0);
+    assert(beta(nextUp(nextUp(-3.0L)), nextDown(-1.0L)) <= 0);
+    assert(beta(nextDown(-2.0L), nextUp(nextUp(-2.0L))) <= 0);
+    assert(beta(-2.75, -1.75) > 0);
+    assert(beta(nextDown(-2.5L), -1.5) >= 0);
+    assert(beta(nextUp(-3.0L), -1.5) > 0);
+    assert(beta(-2.5, nextUp(-2.0L)) > 0);
+    assert(beta(nextUp(-3.0L), nextUp(-2.0L)) > 0);
+    assert(beta(nextUp(-3.0L), nextDown(nextDown(-1.0L))) >= 0);
+    assert(beta(nextDown(nextDown(-2.0L)), nextUp(-2.0L)) >= 0);
+}
+
+
+/* This is the natural logarithm of the absolute value of the beta function. It
+ * tries to eliminate reduce the loss of precision that happens when subtracting
+ * large numbers by combining the Stirling approximations of the individual
+ * logGamma calls.
+ *
+ * ln|B(x,y)| = ln|Γ(x)| + ln|Γ(y)| - ln|Γ(x+y)|. Stirling's approximation for
+ * ln|Γ(z)| is ln|Γ(z)| ~ zln(z) - z + ln(2𝜋/z)/2 + 𝚺ₙ₌₁ᴺB₂ₙ/[2n(2n-1)z²ⁿ⁻¹],
+ * where Bₙ is the nᵗʰ Bernoulli number.
+ * 𝚺ₙ₌₁ᴺB₂ₙ/[2n(2n-1)z²ⁿ⁻¹] = 𝚺ₙ₌₁ᴺB₂ₙ/[2n(2n-1)z²ⁿ⁻²]/z
+ *    = 𝚺ₙ₌₀ᴺ⁻¹B₂₍ₙ₊₁₎/[(2n+2)(2n+1)z²ⁿ]/z
+ *    = [𝚺ₙ₌₀ᴺ⁻¹Cₙ(1/z²)ⁿ]/z,
+ * where Cₙ = B₂₍ₙ₊₁₎/[(2n+2)(2n+1)].
+ * ln|Γ(z)| ~ zln(z) - z + ln(2𝜋/z)/2 +[𝚺ₙ₌₀ᴺ⁻¹Cₙ(1/z²)ⁿ]/z.
+ */
+private real logBeta(real x, in real y)
+{
+    const larger = x > y ? x : y;
+    const smaller = x < y ? x : y;
+    const sum = larger + smaller;
+
+    if (larger >= LN_GAMMA_STIRLING_LB && sum >= LN_GAMMA_STIRLING_LB && larger - smaller > 10.0L)
+    {
+        // Assume x > y
+        // ln|Γ(x)| - ln|Γ(x+y)|
+        //    ~ x⋅ln(x) - (x+y)ln(x+y) + y + ln(2𝜋/x)/2 - ln(2𝜋/[x+y])/2
+        //      + [𝚺ₙ₌₀ᴺ⁻¹Cₙ(1/x²)ⁿ]/x - [𝚺ₙ₌₀ᴺ⁻¹Cₙ(1/{x+y}²)ⁿ]/{x+y}.
+        // x⋅ln(x) - (x+y)ln(x+y) + y + ln(2𝜋/x)/2 - ln(2𝜋/[x+y])/2
+        //    = ln(xˣ) - ln([x+y]ˣ⁺ʸ) + y + ln(√[2𝜋/x]) - ln(√[2𝜋/{x+y}])
+        //    = ln(xˣ⁻¹ᐟ²/[x+y]ˣ⁺ʸ⁻¹ᐟ²) + y = ln([x/{x+y}]ˣ⁺ʸ⁻¹ᐟ²x⁻ʸ) + y
+        //    = y - y⋅ln(x) + (.5 - x - y)ln(1 + y/x)
+        // ln|B(x,y)|
+        //    ~ ln|Γ(y)| + y - y⋅ln(x) + (.5 - x - y)ln(1 + y/x)
+        //      + [𝚺ₙ₌₀ᴺ⁻¹Cₙ(1/x²)ⁿ]/x - [𝚺ₙ₌₀ᴺ⁻¹Cₙ(1/{x+y}²)ⁿ]/{x+y}.
+        const gamDiffApprox = smaller - smaller*log(larger) + (0.5L - sum)*log1p(smaller/larger);
+
+        const gamDiffCorr
+            = poly(1.0L/larger^^2, logGammaStirlingCoeffs) / larger
+            - poly(1.0L/sum^^2, logGammaStirlingCoeffs) / sum;
+
+        return logGamma(smaller) + gamDiffApprox + gamDiffCorr;
+    }
+
+    return logGamma(smaller) + logGamma(larger) - logGamma(sum);
+}
+
+@safe unittest
+{
+    assert(isClose(logBeta(1, 1), log(beta(1, 1))));
+    assert(isClose(logBeta(3, 2), logBeta(2, 3)));
+    assert(isClose(exp(logBeta(20, 4)), beta(20, 4)));
+    assert(isClose(logBeta(30, 40), log(beta(30, 40))));
+
+    // The following were generated by scipy's betaln function.
+    assert(feqrel(logBeta(-1.4, -0.4), 1.133_156_234_422_692_6) > double.mant_dig-3);
+    assert(feqrel(logBeta(-0.5, 1.0), 0.693_147_180_559_945_2) > double.mant_dig-3);
+    assert(feqrel(logBeta(1.0, 2.0), -0.693_147_180_559_945_3) > double.mant_dig-3);
+    assert(feqrel(logBeta(14.0, 3.0), -7.426_549_072_397_305) > double.mant_dig-3);
+    assert(feqrel(logBeta(20.0, 30.0), -33.968_820_791_977_386) > double.mant_dig-3);
+}
+
+
 private {
 /*
  * These value can be calculated like this:
@@ -645,19 +1012,35 @@ enum real BETA_BIGINV = 1.084202172485504434007e-19L;
  */
 real betaIncomplete(real aa, real bb, real xx )
 {
-    if ( !(aa>0 && bb>0) )
+    // If any parameters are NaN, return the NaN with the largest payload.
+    if (isNaN(aa) || isNaN(bb) || isNaN(xx))
     {
-         if ( isNaN(aa) ) return aa;
-         if ( isNaN(bb) ) return bb;
-         return real.nan; // domain error
+        // With cmp,
+        // -NaN(larger) < -NaN(smaller) < -inf < inf < NaN(smaller) < NaN(larger).
+        const largerParam = cmp(abs(aa), abs(bb)) >= 0 ? aa : bb;
+        return cmp(abs(xx), abs(largerParam)) >= 0 ? xx : largerParam;
     }
-    if (!(xx>0 && xx<1.0))
+
+    // domain errors
+    if (signbit(aa) == 1 || signbit(bb) == 1) return real.nan;
+    if (xx < 0.0L || xx > 1.0L) return real.nan;
+
+    // edge cases
+    if ( xx == 0.0L ) return 0.0L;
+    if ( xx == 1.0L )  return 1.0L;
+
+    // degenerate cases
+    if (aa is +0.0L || aa is real.infinity || bb is +0.0L || bb is real.infinity)
     {
-        if (isNaN(xx)) return xx;
-        if ( xx == 0.0L ) return 0.0;
-        if ( xx == 1.0L )  return 1.0;
-        return real.nan; // domain error
+        if (aa is +0.0L && bb is +0.0L) return real.nan;
+        if (aa is real.infinity && bb is real.infinity) return real.nan;
+        if (aa is +0.0L || bb is real.infinity) return 1.0L;
+        if (aa is real.infinity || bb is +0.0L) return 0.0L;
     }
+
+    // symmetry
+    if (aa == bb && xx == 0.5L) return 0.5L;
+
     if ( (bb * xx) <= 1.0L && xx <= 0.95L)
     {
         return betaDistPowerSeries(aa, bb, xx);
@@ -677,6 +1060,7 @@ real betaIncomplete(real aa, real bb, real xx )
         b = aa;
         xc = xx;
         x = 1.0L - xx;
+        if (x == 1.0L) x = nextDown(x);
     }
     else
     {
@@ -718,12 +1102,12 @@ real betaIncomplete(real aa, real bb, real xx )
         t *= pow(x,a);
         t /= a;
         t *= w;
-        t *= gamma(a+b) / (gamma(a) * gamma(b));
+        t /= beta(a, b);
     }
     else
     {
         /* Resort to logarithms.  */
-        y += t + logGamma(a+b) - logGamma(a) - logGamma(b);
+        y += t - logBeta(a, b);
         y += log(w/a);
 
         t = exp(y);
@@ -1030,11 +1414,31 @@ done:
     assert(isIdentical(betaIncompleteInv(2,NaN(0xABC),8), NaN(0xABC)));
     assert(isIdentical(betaIncompleteInv(2,3, NaN(0xABC)), NaN(0xABC)));
 
-    assert(isNaN(betaIncomplete(-1, 2, 3)));
+    assert(isNaN(betaIncomplete(-0., 1, .5)));
+    assert(isNaN(betaIncomplete(1, -0., .5)));
+    assert(isNaN(betaIncomplete(1, 1, -1)));
+    assert(isNaN(betaIncomplete(1, 1, 2)));
+
+    assert(betaIncomplete(+0., +0., 0) == 0);
+    assert(isNaN(betaIncomplete(+0., +0., .5)));
+    assert(betaIncomplete(+0., +0., 1) == 1);
+    assert(betaIncomplete(+0., 1, .5) == 1);
+    assert(betaIncomplete(1, +0., 0) == 0);
+    assert(betaIncomplete(1, +0., .5) == 0);
+    assert(betaIncomplete(1, real.infinity, .5) == 1);
+    assert(betaIncomplete(real.infinity, real.infinity, 0) == 0);
+    assert(isNaN(betaIncomplete(real.infinity, real.infinity, .5)));
 
     assert(betaIncomplete(1, 2, 0)==0);
     assert(betaIncomplete(1, 2, 1)==1);
-    assert(isNaN(betaIncomplete(1, 2, 3)));
+    assert(betaIncomplete(9.99999984824320730e+30, 9.99999984824320730e+30, 0.5) == 0.5L);
+    assert(betaIncomplete(1.17549435082228751e-38, 9.99999977819630836e+22, 9.99999968265522539e-22) == 1.0L);
+    assert(betaIncomplete(1.00000001954148138e-25, 1.00000001490116119e-01, 1.17549435082228751e-38) == 1.0L);
+    assert(isClose(betaIncomplete(9.99999983775159024e-18, 9.99999977819630836e+22, 1.00000001954148138e-25), 1.0L));
+    assert(isClose(
+        betaIncomplete(9.99999974737875164e-06, 9.99999998050644787e+18, 9.99999968265522539e-22),
+        0.9999596214389047L));
+
     assert(betaIncompleteInv(1, 1, 0)==0);
     assert(betaIncompleteInv(1, 1, 1)==1);
 
@@ -1065,23 +1469,38 @@ done:
         assert(betaIncompleteInv(0.01L, 8e-48L, 9e-26L) == 1-real.epsilon);
 
         // Beware: a one-bit change in pow() changes almost all digits in the result!
+        // scipy says that this is 0.99999_99995_89020_6 (0x1.ffff_fffc_783f_2a7ap-1)
+        // in double precision.
         assert(feqrel(
             betaIncompleteInv(0x1.b3d151fbba0eb18p+1L, 1.2265e-19L, 2.44859e-18L),
-            0x1.c0110c8531d0952cp-1L
+            0x1.ffff_fffc_783f_2a7ap-1
         ) > 10);
         // This next case uncovered a one-bit difference in the FYL2X instruction
         // between Intel and AMD processors. This difference gets magnified by 2^^38.
-        // WolframAlpha crashes attempting to calculate this.
-        assert(feqrel(betaIncompleteInv(0x1.ff1275ae5b939bcap-41L, 4.6713e18L, 0.0813601L),
-            0x1.f97749d90c7adba8p-63L) >= real.mant_dig - 39);
+        // WolframAlpha fails to calculate this.
+        // scipy says that this is 2.225073858507201e-308 in double precision,
+        // essentially double.min-normal.
+        assert(isClose(
+            betaIncompleteInv(0x1.ff1275ae5b939bcap-41L, 4.6713e18L, 0.0813601L),
+            2.225_073_858_507_201e-308L,
+            0,
+            1e-40));
+
+        // scipy says that this is 8.068764506083944e-20 to double precision. Since this is a
+        // regression test where the original value isn't a known good value, I' updating the
+        // test value to the current generated value, which is closer to the scipy value.
         real a1 = 3.40483L;
-        assert(betaIncompleteInv(a1, 4.0640301659679627772e19L, 0.545113L) == 0x1.ba8c08108aaf5d14p-109L);
+        assert(betaIncompleteInv(a1, 4.0640301659679627772e19L, 0.545113L) == 0x1.2a867b1e12b9bdf0p-64L);
+
         real b1 = 2.82847e-25L;
         assert(feqrel(betaIncompleteInv(0.01L, b1, 9e-26L), 0x1.549696104490aa9p-830L) >= real.mant_dig-10);
 
         // --- Problematic cases ---
-        // This is a situation where the series expansion fails to converge
-        assert( isNaN(betaIncompleteInv(0.12167L, 4.0640301659679627772e19L, 0.0813601L)));
+        // In the past, this was a situation where the series expansion failed
+        // to converge.
+        assert(!isNaN(betaIncompleteInv(0.12167L, 4.0640301659679627772e19L, 0.0813601L)));
+        // Using scipy, the result should be 1.683301919972747e-29.
+
         // This next result is almost certainly erroneous.
         // Mathematica states: "(cannot be determined by current methods)"
         assert(betaIncomplete(1.16251e20L, 2.18e39L, 5.45e-20L) == -real.infinity);
@@ -1293,12 +1712,39 @@ real betaDistPowerSeries(real a, real b, real x )
     u = a * log(x);
     if ( (a+b) < MAXGAMMA && fabs(u) < MAXLOG )
     {
-        t = gamma(a+b)/(gamma(a)*gamma(b));
-        s = s * t * pow(x,a);
+        s = s * pow(x,a) / beta(a, b);
     }
     else
     {
-        t = logGamma(a+b) - logGamma(a) - logGamma(b) + u + log(s);
+        if (abs(a*s - 1.0L) < 0.01L)
+        {
+            // Compute logGamma(a+b) - logGamma(b)
+            real lnGamma_apb_m_lnGamma_b;
+
+            if (b >= LN_GAMMA_STIRLING_LB)
+            {
+                const gamDiffApprox = a - a*log(b) + (0.5L - a - b)*log1p(a/b);
+
+                const gamDiffCorr
+                    = poly(1.0L/b^^2, logGammaStirlingCoeffs) / b
+                    - poly(1.0L/(a+b)^^2, logGammaStirlingCoeffs) / (a+b);
+
+                lnGamma_apb_m_lnGamma_b = -gamDiffApprox - gamDiffCorr;
+            }
+            else
+            {
+                lnGamma_apb_m_lnGamma_b = logGamma(a+b) - logGamma(b);
+            }
+
+            // Compute log(s) - logGamma(a)
+            const ln_s_m_lnGamma_a = log1p(a*s - 1.0L) - log(a) - logGamma(a);
+
+            t = lnGamma_apb_m_lnGamma_b + u + ln_s_m_lnGamma_a;
+        }
+        else
+        {
+            t = u + log(s) - logBeta(a, b);
+        }
 
         if ( t < MINLOG )
         {
@@ -1306,6 +1752,8 @@ real betaDistPowerSeries(real a, real b, real x )
         } else
             s = exp(t);
     }
+
+    if (s > 1.0L) return (s - 2*real.epsilon <= 1.0L) ? 1.0L : real.nan;
     return s;
 }
 
index d14d9b32ed32ff1a533c1e3e359b3a955e3b57f0..8a92c4ae72eae7dd4a47045b787ea9054d71e8e2 100644 (file)
@@ -158,6 +158,13 @@ real NaN(ulong payload) @trusted pure nothrow @nogc
  * For 80-bit or 128-bit reals, it is 0x3FFF_FFFF_FFFF_FFFF.
  */
 ulong getNaNPayload(real x) @trusted pure nothrow @nogc
+in
+{
+    // Precondition: Input must be NaN
+    import std.math.traits : isNaN;
+    assert(isNaN(x), "getNaNPayload called on a non-NaN value");
+}
+do
 {
     import std.math.traits : floatTraits, RealFormat;
 
index 10c4a2d5310d083f24f8a1314710c80545a7c3ad..8889cbf6c67d7725e03113a67be1f558983c7a0a 100644 (file)
@@ -37,6 +37,7 @@
  *      SUB = $1<sub>$2</sub>
  *      BIGSUM = $(BIG &Sigma; <sup>$2</sup><sub>$(SMALL $1)</sub>)
  *      CHOOSE = $(BIG &#40;) <sup>$(SMALL $1)</sup><sub>$(SMALL $2)</sub> $(BIG &#41;)
+ *      CEIL = &#8968;$1&#8969;
  *      PLUSMN = &plusmn;
  *      MNPLUS = &mnplus;
  *      INFIN = &infin;
  *      MNPLUSINF = &mnplus;&infin;
  *      PI = &pi;
  *      LT = &lt;
+ *      LE = &le;
  *      GT = &gt;
  *      SQRT = &radic;
  *      HALF = &frac12;
- *
+ *      COMPLEX = &#8450;
  *
  * Copyright: Based on the CEPHES math library, which is
  *            Copyright (C) 1994 Stephen L. Moshier (moshier@world.std.com).
@@ -114,59 +116,89 @@ real logGamma(real x)
 
 /** The sign of $(GAMMA)(x).
  *
- * Returns -1 if $(GAMMA)(x) < 0,  +1 if $(GAMMA)(x) > 0,
- * $(NAN) if sign is indeterminate.
+ * Params:
+ *   x = the argument of $(GAMMA)
+ *
+ * Returns:
+ *   -1 if $(GAMMA)(x) < 0, +1 if $(GAMMA)(x) > 0, and $(NAN) if $(GAMMA)(x)
+ *   does not exist.
  *
- * Note that this function can be used in conjunction with logGamma(x) to
- * evaluate gamma for very large values of x.
+ * Note:
+ *   This function can be used in conjunction with `logGamma` to evaluate
+ *   $(GAMMA)(x) when `gamma(x)` is too large to be represented as a `real`.
  */
-real sgnGamma(real x)
+pragma(inline, true) real sgnGamma(real x)
 {
-    import core.math : rndtol;
-    /* Author: Don Clugston. */
-    if (isNaN(x)) return x;
-    if (x > 0) return 1.0;
-    if (x < -1/real.epsilon)
-    {
-        // Large negatives lose all precision
-        return real.nan;
-    }
-    long n = rndtol(x);
-    if (x == n)
-    {
-        return x == 0 ?  copysign(1, x) : real.nan;
-    }
-    return n & 1 ? 1.0 : -1.0;
+    return std.internal.math.gammafunction.sgnGamma(x);
 }
 
+///
 @safe unittest
 {
-    assert(sgnGamma(5.0) == 1.0);
-    assert(isNaN(sgnGamma(-3.0)));
-    assert(sgnGamma(-0.1) == -1.0);
-    assert(sgnGamma(-55.1) == 1.0);
-    assert(isNaN(sgnGamma(-real.infinity)));
-    assert(isIdentical(sgnGamma(NaN(0xABC)), NaN(0xABC)));
+    assert(sgnGamma(10_000) == 1);
 }
 
-/** Beta function
+/** Beta function, B(x,y)
+ *
+ * Mathematically, if x $(GT) 0 and y $(GT) 0 then
+ * B(x,y) = $(INTEGRATE 0, 1)$(POWER t, x-1)$(POWER (l-t), y-1)dt. Through analytic continuation, it
+ * is extended to $(COMPLEX)$(SUP 2) where it can be expressed in terms of $(GAMMA)(z).
+ *
+ * B(x,y) = $(GAMMA)(x)$(GAMMA)(y) / $(GAMMA)(x+y).
+ *
+ * This implementation restricts x and y to the set of real numbers.
+ *
+ * Params:
+ *   x = the first argument of B
+ *   y = the second argument of B
+ *
+ * Returns:
+ *   It returns B(x,y) if it can be computed, otherwise $(NAN).
  *
- * The beta function is defined as
+ * $(TABLE_SV
+ *   $(TR $(TH x)                                   $(TH y)                $(TH beta(x, y))   )
+ *   $(TR $(TD $(NAN))                              $(TD y)                $(TD $(NAN))       )
+ *   $(TR $(TD -$(INFIN))                           $(TD y)                $(TD $(NAN))       )
+ *   $(TR $(TD integer $(LT) 0)                     $(TD y)                $(TD $(NAN))       )
+ *   $(TR $(TD noninteger and x+y even $(LE) 0)     $(TD noninteger)       $(TD -0)           )
+ *   $(TR $(TD noninteger and x+y odd $(LE) 0)      $(TD noninteger)       $(TD +0)           )
+ *   $(TR $(TD +0)                                  $(TD positive finite)  $(TD +$(INFIN))    )
+ *   $(TR $(TD +0)                                  $(TD +$(INFIN))        $(TD $(NAN))       )
+ *   $(TR $(TD $(GT) 0)                             $(TD +$(INFIN))        $(TD +0)           )
+ *   $(TR $(TD -0)                                  $(TD +0)               $(TD $(NAN))       )
+ *   $(TR $(TD -0)                                  $(TD $(GT) 0)          $(TD -$(INFIN))    )
+ *   $(TR $(TD noninteger $(LT) 0, $(CEIL x) odd)   $(TD +$(INFIN))        $(TD -$(INFIN))    )
+ *   $(TR $(TD noninteger $(LT) 0, $(CEIL x) even)  $(TD +$(INFIN))        $(TD +$(INFIN))    )
+ *   $(TR $(TD noninteger $(LT) 0)                  $(TD $(PLUSMN)0)       $(TD $(PLUSMNINF)) )
+ * )
  *
- * beta(x, y) = ($(GAMMA)(x) * $(GAMMA)(y)) / $(GAMMA)(x + y)
+ * Since B(x,y) = B(y,x), if the table states that beta(x, y) is a special value, then beta(y, x) is
+ * one as well.
  */
-real beta(real x, real y)
+pragma(inline, true) real beta(real x, real y)
 {
-    if ((x+y)> MAXGAMMA)
-    {
-        return exp(logGamma(x) + logGamma(y) - logGamma(x+y));
-    } else return gamma(x) * gamma(y) / gamma(x+y);
+    return std.internal.math.gammafunction.beta(x, y);
 }
 
+///
 @safe unittest
 {
+    assert(beta(1, 2) == 0.5);
     assert(isIdentical(beta(NaN(0xABC), 4), NaN(0xABC)));
-    assert(isIdentical(beta(2, NaN(0xABC)), NaN(0xABC)));
+    assert(beta(3, 4) == beta(4, 3));
+    assert(isNaN(beta(-real.infinity, +0.)));
+    assert(isNaN(beta(-1, 2)));
+    assert(beta(-0.5, 0.5) is -0.0L);
+    assert(beta(-1.5, 0.5) is +0.0L);
+    assert(beta(+0., +0.) == +real.infinity);
+    assert(isNaN(beta(+0., +real.infinity)));
+    assert(beta(1, +real.infinity) is +0.0L);
+    assert(isNaN(beta(-0., +0.)));
+    assert(beta(-0., nextUp(+0.0L)) == -real.infinity);
+    assert(beta(-0.5, +real.infinity) == -real.infinity);
+    assert(beta(nextDown(-1.0L), real.infinity) == real.infinity);
+    assert(beta(nextDown(-0.0L), +0.) == +real.infinity);
+    assert(beta(-0.5, -0.) == -real.infinity);
 }
 
 /** Digamma function, $(PSI)(x)
@@ -232,32 +264,125 @@ real logmdigammaInverse(real x)
     return std.internal.math.gammafunction.logmdigammaInverse(x);
 }
 
-/** Incomplete beta integral
+/** Regularized incomplete beta function $(SUB I, x)(a,b)
  *
- * Returns regularized incomplete beta integral of the arguments, evaluated
- * from zero to x. The regularized incomplete beta function is defined as
+ * Mathematically, if a and b are positive real numbers, and 0 $(LE) x $(LE) 1, then
+ * $(SUB I, x)(a,b) = $(INTEGRATE 0, x)$(POWER t, a-1)$(POWER (1-t), b-1)dt/B(a,b) where B is the
+ * beta function. It is also the cumulative distribution function of the beta distribution.
  *
- * betaIncomplete(a, b, x) = $(GAMMA)(a + b) / ( $(GAMMA)(a) $(GAMMA)(b) ) *
- * $(INTEGRATE 0, x) $(POWER t, a-1)$(POWER (1-t), b-1) dt
+ * `betaIncomplete(a, b, x)` evaluates $(SUB I, `x`)(`a`,`b`).
  *
- * and is the same as the cumulative distribution function of the Beta
- * distribution.
+ * Params:
+ *   a = the first argument of B, must be positive
+ *   b = the second argument of B, must be positive
+ *   x = the fraction of integration completion from below, 0 $(LE) x $(LE) 1
  *
- * The domain of definition is 0 <= x <= 1.  In this
- * implementation a and b are restricted to positive values.
- * The integral from x to 1 may be obtained by the symmetry
- * relation
+ * Returns:
+ *   It returns $(SUB I, x)(a,b), an element of [0,1].
+ *
+  * $(TABLE_SV
+ *   $(TR $(TH a)         $(TH b)         $(TH x)        $(TH betaIncomplete(a, b, x)) )
+ *   $(TR $(TD negative)  $(TD b)         $(TD x)        $(TD $(NAN))                  )
+ *   $(TR $(TD a)         $(TD negative)  $(TD x)        $(TD $(NAN))                  )
+ *   $(TR $(TD a)         $(TD b)         $(TD $(LT) 0)  $(TD $(NAN))                  )
+ *   $(TR $(TD a)         $(TD b)         $(TD $(GT) 1)  $(TD $(NAN))                  )
+ *   $(TR $(TD +0)        $(TD +0)        $(TD (0,1))    $(TD $(NAN))                  )
+ *   $(TR $(TD $(INFIN))  $(TD $(INFIN))  $(TD (0,1))    $(TD $(NAN))                  )
+ * )
  *
- *    betaIncompleteCompl(a, b, x )  =  betaIncomplete( b, a, 1-x )
+ * If one or more of the input parameters are $(NAN), the one with the largest payload is returned.
+ * For equal payloads but with possibly different signs, the order of preference is x, a, b.
  *
- * The integral is evaluated by a continued fraction expansion
- * or, when b * x is small, by a power series.
+ * Note:
+ *   The integral is evaluated by a continued fraction expansion or, when `b * x` is small, by a
+ *   power series.
+ *
+ * See_Also: $(LREF beta) $(LREF betaIncompleteCompl)
  */
 real betaIncomplete(real a, real b, real x )
+in
+{
+    if (!isNaN(a) && !isNaN(b) && !isNaN(x))
+    {
+        assert(signbit(a) == 0, "a must be positive");
+        assert(signbit(b) == 0, "b must be positive");
+        assert(x >= 0 && x <= 1, "x must be in [0,1]");
+    }
+}
+out(i; isNaN(i) || (i >=0 && i <= 1))
+do
 {
     return std.internal.math.gammafunction.betaIncomplete(a, b, x);
 }
 
+///
+@safe unittest
+{
+    assert(betaIncomplete(1, 1, .5) == .5);
+    assert(betaIncomplete(+0., +0., 0) == 0);
+    assert(isNaN(betaIncomplete(+0., +0., .5)));
+    assert(isNaN(betaIncomplete(real.infinity, real.infinity, .5)));
+    assert(betaIncomplete(real.infinity, real.infinity, 1) == 1);
+    assert(betaIncomplete(NaN(0x1), 1, NaN(0x2)) is NaN(0x2));
+    assert(betaIncomplete(1, NaN(0x3), -NaN(0x3)) is -NaN(0x3));
+}
+
+/** Regularized incomplete beta function complement $(SUB I$(SUP C), x)(a,b)
+ *
+ * Mathematically, if a $(GT) 0, b $(GT) 0, and 0 $(LE) x $(LE) 1, then
+ * $(SUB I$(SUP C), x)(a,b) = $(INTEGRATE x, 1)$(POWER t, a-1)$(POWER (1-t), b-1)dt/B(a,b) where B
+ * is the beta function. It is also the complement of the cumulative distribution function of the
+ * beta distribution. It can be shown that $(SUB I$(SUP C), x)(a,b) = $(SUB I, 1-x)(b,a).
+ *
+ * `betaIncompleteCompl(a, b, x)` evaluates $(SUB I$(SUP C), `x`)(`a`,`b`).
+ *
+ * Params:
+ *   a = the first argument of B, must be positive
+ *   b = the second argument of B, must be positive
+ *   x = the fraction of integration completion from above, 0 $(LE) x $(LE) 1
+ *
+ * Returns:
+ *   It returns $(SUB I$(SUP C), x)(a,b), an element of [0,1].
+ *
+   * $(TABLE_SV
+ *   $(TR $(TH a)         $(TH b)         $(TH x)        $(TH betaIncompleteCompl(a, b, x)) )
+ *   $(TR $(TD negative)  $(TD b)         $(TD x)        $(TD $(NAN))                       )
+ *   $(TR $(TD a)         $(TD negative)  $(TD x)        $(TD $(NAN))                       )
+ *   $(TR $(TD a)         $(TD b)         $(TD $(LT) 0)  $(TD $(NAN))                       )
+ *   $(TR $(TD a)         $(TD b)         $(TD $(GT) 1)  $(TD $(NAN))                       )
+ *   $(TR $(TD +0)        $(TD +0)        $(TD (0,1))    $(TD $(NAN))                       )
+ *   $(TR $(TD $(INFIN))  $(TD $(INFIN))  $(TD (0,1))    $(TD $(NAN))                       )
+ * )
+ *
+ * If one or more of the input parameters are $(NAN), the one with the largest payload is returned.
+ * For equal payloads but with possibly different signs, the order of preference is x, a, b.
+ *
+ * See_Also: $(LREF beta) $(LREF betaIncomplete)
+ */
+real betaIncompleteCompl(real a, real b, real x)
+in
+{
+    // allow NaN input to pass through so that it can be addressed by the
+    // internal NaN payload propagation logic
+    if (!isNaN(a) && !isNaN(b) && !isNaN(x))
+    {
+        assert(signbit(a) == 0, "a must be positive");
+        assert(signbit(b) == 0, "b must be positive");
+        assert(x >= 0 && x <= 1, "x must be in [0, 1]");
+    }
+}
+out(i; isNaN(i) || (i >=0 && i <= 1))
+do
+{
+    return std.internal.math.gammafunction.betaIncomplete(b, a, 1-x);
+}
+
+///
+@safe unittest
+{
+    assert(betaIncompleteCompl(.1, .2, 0) == betaIncomplete(.2, .1, 1));
+}
+
 /** Inverse of incomplete beta integral
  *
  * Given y, the function finds x such that
index b2cab3157d07349f8b9997222aba7167726f6e58..d0860ca7037fa90c0c61676ecf6af4fe8e054af4 100644 (file)
@@ -422,6 +422,7 @@ class MmFile
         version (Windows)
         {
             FlushViewOfFile(data.ptr, data.length);
+            FlushFileBuffers(hFile);
         }
         else version (Posix)
         {
index 1a020c88fef9e06f4eee638c9b685166d17dd70d..d359ca0bae411648714f430b02c0ff5dc680d0e1 100644 (file)
@@ -1247,8 +1247,19 @@ private Pid spawnProcessPosix(scope const(char[])[] args,
     else
     {
         closePipeWriteEnds();
+
+        T retryInterrupted(T)(scope T delegate() syscall)
+        {
+            import core.stdc.errno : errno, EINTR;
+            T result;
+            do
+                result = syscall();
+            while (result == -1 && .errno == EINTR);
+            return result;
+        }
+
         auto status = InternalError.noerror;
-        auto readExecResult = core.sys.posix.unistd.read(forkPipe[0], &status, status.sizeof);
+        auto readExecResult = retryInterrupted(() => core.sys.posix.unistd.read(forkPipe[0], &status, status.sizeof));
         // Save error number just in case if subsequent "waitpid" fails and overrides errno
         immutable lastError = .errno;
 
@@ -1257,7 +1268,7 @@ private Pid spawnProcessPosix(scope const(char[])[] args,
             // Forked child exits right after creating second fork. So it should be safe to wait here.
             import core.sys.posix.sys.wait : waitpid;
             int waitResult;
-            waitpid(id, &waitResult, 0);
+            retryInterrupted(() => waitpid(id, &waitResult, 0));
         }
 
         if (readExecResult == -1)
@@ -1267,7 +1278,7 @@ private Pid spawnProcessPosix(scope const(char[])[] args,
         if (status != InternalError.noerror)
         {
             int error;
-            readExecResult = read(forkPipe[0], &error, error.sizeof);
+            readExecResult = retryInterrupted(() => read(forkPipe[0], &error, error.sizeof));
             string errorMsg;
             final switch (status)
             {
index 0176b0ae653282cdff9d89967e5d9757ef4e8035..5ca346e89ab3f8a2a4cc82efba637121078ab957 100644 (file)
@@ -247,23 +247,17 @@ version (StdUnittest)
  *   $(LI it has a 'bool isUniformRandom' field readable in CTFE)
  * )
  */
-template isUniformRNG(Rng, ElementType)
-{
-    enum bool isUniformRNG = .isUniformRNG!Rng &&
-        is(std.range.primitives.ElementType!Rng == ElementType);
-}
+enum isUniformRNG(Rng, ElementType) = .isUniformRNG!Rng &&
+       is(std.range.primitives.ElementType!Rng == ElementType);
 
 /**
  * ditto
  */
-template isUniformRNG(Rng)
-{
-    enum bool isUniformRNG = isInputRange!Rng &&
-        is(typeof(
-        {
-            static assert(Rng.isUniformRandom); //tag
-        }));
-}
+enum isUniformRNG(Rng) = isInputRange!Rng &&
+       is(typeof(
+       {
+        static assert(Rng.isUniformRandom); //tag
+    }));
 
 ///
 @safe unittest
@@ -311,29 +305,23 @@ template isUniformRNG(Rng)
  *   $(LI it has a 'seed(ElementType)' function)
  * )
  */
-template isSeedable(Rng, SeedType)
-{
-    enum bool isSeedable = isUniformRNG!(Rng) &&
-        is(typeof(
-        {
-            Rng r = void;              // can define a Rng object
-            SeedType s = void;
-            r.seed(s); // can seed a Rng
-        }));
-}
+enum isSeedable(Rng, SeedType) = isUniformRNG!(Rng) &&
+       is(typeof(
+    {
+        Rng r = void;              ///< can define a Rng object
+        SeedType s = void; ///< Dummy doc to silence D-scanner.
+        r.seed(s); // can seed a Rng
+    }));
 
 ///ditto
-template isSeedable(Rng)
-{
-    enum bool isSeedable = isUniformRNG!Rng &&
-        is(typeof(
-        {
-            Rng r = void;                     // can define a Rng object
-            alias SeedType = typeof(r.front);
-            SeedType s = void;
-            r.seed(s); // can seed a Rng
-        }));
-}
+enum isSeedable(Rng) = isUniformRNG!Rng &&
+       is(typeof(
+    {
+        Rng r = void;                     ///< can define a Rng object
+        alias SeedType = typeof(r.front);
+        SeedType s = void; ///< Dummy doc to silence D-scanner.
+        r.seed(s); // can seed a Rng
+    }));
 
 ///
 @safe unittest
index 4620b98071e971f00d73bc3b4415c441558fbe31..16bf0bf9ee991e551506f9ea39e1324c898fa274 100644 (file)
@@ -86,7 +86,7 @@ $(BOOKTABLE ,
         $(TD Creates the range that results from discarding
         the first element from the given range.
     ))
-    $(TR $(TD $(D $(LREF dropBackOne)))
+    $(TR $(TD $(LREF dropBackOne))
         $(TD Creates the range that results from discarding
         the last element from the given range.
     ))
@@ -162,7 +162,7 @@ $(BOOKTABLE ,
         $(TD Similar to `recurrence`, except that a random-access range is
         created.
     ))
-    $(TR $(TD $(D $(LREF slide)))
+    $(TR $(TD $(LREF slide))
         $(TD Creates a range that returns a fixed-size sliding window
         over the original range. Unlike chunks,
         it advances a configurable number of items at a time,
@@ -4446,7 +4446,7 @@ if (isForwardRange!R && !isInfinite!R)
             return _original[_index];
         }
 
-        static if (is(typeof((cast(const R)_original)[_index])))
+        static if (__traits(compiles, (const R r) => r[0]))
         {
             /// ditto
             @property auto ref front() const
@@ -4484,8 +4484,8 @@ if (isForwardRange!R && !isInfinite!R)
             return _original[(n + _index) % _original.length];
         }
 
-        static if (is(typeof((cast(const R)_original)[_index])) &&
-                   is(typeof((cast(const R)_original).length)))
+        static if (__traits(compiles, (const R r) => r[0]) &&
+            __traits(compiles, (const R r) => r.length))
         {
             /// ditto
             auto ref opIndex(size_t n) const
@@ -4559,7 +4559,7 @@ if (isForwardRange!R && !isInfinite!R)
             return _current.front;
         }
 
-        static if (is(typeof((cast(const R)_current).front)))
+        static if (__traits(compiles, (const R r) => r.front))
         {
             /// ditto
             @property auto ref front() const
@@ -4948,6 +4948,23 @@ pure @safe unittest
     }
 }
 
+// https://github.com/dlang/phobos/issues/10852
+@safe unittest
+{
+    // forward range
+    struct R
+    {
+        int i;
+        int front() => i;
+        bool empty() => i == 0;
+        void popFront() {--i;}
+        R save() => this;
+    }
+
+    auto r = R(10).cycle.take(20);
+    assert(r.array == [10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1]);
+}
+
 private alias lengthType(R) = typeof(R.init.length.init);
 
 /**
@@ -6830,12 +6847,15 @@ pure @safe nothrow unittest
    user-defined types that support `++`, the range is an input
    range.
 
-   An integral iota also supports `in` operator from the right. It takes
-   the stepping into account, the integral won't be considered
-   contained if it falls between two consecutive values of the range.
-   `contains` does the same as in, but from lefthand side.
+   $(DDOC_SECTION_H `in` operator and `contains`:)
+   `iota` over an integral/pointer type defines the `in` operator from the right.
+   `val in iota(...)` is true when `val` occurs in the range. When present, it takes
+   `step` into account - `val` won't be considered
+   contained if it falls between two consecutive elements of the range.
+   The `contains` method does the same as `in`, but from the left-hand side.
 
     Example:
+    $(RUNNABLE_EXAMPLE
     ---
     void main()
     {
@@ -6862,6 +6882,7 @@ pure @safe nothrow unittest
         writeln();
     }
     ---
+    )
 */
 auto iota(B, E, S)(B begin, E end, S step)
 if ((isIntegral!(CommonType!(B, E)) || isPointer!(CommonType!(B, E)))
index 99ac97ac1e3a0a262626272acbc2fe0caa003632..e48bf565971347de18060ee69d858b3f33b52e3d 100644 (file)
@@ -1284,7 +1284,7 @@ abstract class Address
         // libraries shipped with DMD. Thus, we check for getnameinfo at
         // runtime in the shared module constructor, and use it if it's
         // available in the base class method. Classes for specific network
-        // families (e.g. InternetHost) override this method and use a
+        // families (e.g. InternetAddress) override this method and use a
         // deprecated, albeit commonly-available method when getnameinfo()
         // is not available.
         // http://technet.microsoft.com/en-us/library/aa450403.aspx
@@ -1863,6 +1863,19 @@ public:
         sin6 = addr;
     }
 
+    version (Posix)
+    {
+        /// Human readable string representing the IPv6 address in RFC 2373 form.
+        override string toAddrString() @trusted const
+        {
+            char[INET6_ADDRSTRLEN] buf;
+            string addrString = to!string(
+                .inet_ntop(AddressFamily.INET6, &sin6.sin6_addr, buf.ptr, INET6_ADDRSTRLEN)
+            );
+            return addrString;
+        }
+    }
+
    /**
      * Parse an IPv6 host address string as described in RFC 2373, and return the
      * address.
index 1a26235a08bf689d8957eba8f2306198b19b8f6f..afc4ddb42d1e6022beb614a4d9c072efe0843209 100644 (file)
@@ -842,6 +842,19 @@ public:
             return this.match!hashOf;
         }
     }
+
+    /**
+     * Returns the index of the current value's type in the `SumType`'s
+     * $(LREF Types).
+     *
+     * Note that $(LREF Types) does not include type qualifiers that are
+     * applied to the `SumType` itself. To obtain the properly-qualified type
+     * of a qualified `SumType`'s value, use $(REF CopyTypeQualifiers, std,traits).
+     */
+    size_t typeIndex() const
+    {
+        return tag;
+    }
 }
 
 // Construction
@@ -1577,6 +1590,59 @@ version (D_BetterC) {} else
     static assert(SumType!int.sizeof == int.sizeof);
 }
 
+// typeIndex
+@safe unittest
+{
+    alias MySum = SumType!(int, string);
+
+    MySum a = 42;
+    MySum b = "hello";
+
+    assert(a.typeIndex == IndexOf!(int, MySum.Types));
+    assert(b.typeIndex == IndexOf!(string, MySum.Types));
+}
+
+// typeIndex with qualified SumTypes
+@safe unittest
+{
+    alias MySum = SumType!(int[], const int[], immutable int[]);
+
+    int[] a;
+    const int[] ca;
+    immutable int[] ia;
+
+    MySum s1 = a;
+    MySum s2 = ca;
+    MySum s3 = ia;
+
+    const MySum cs1 = s1;
+    const MySum cs2 = s2;
+    const MySum cs3 = s3;
+
+    // Copying a SumType doesn't change its typeIndex
+    assert(cs1.typeIndex == s1.typeIndex);
+    assert(cs2.typeIndex == s2.typeIndex);
+    assert(cs3.typeIndex == s3.typeIndex);
+
+    static bool isIndexOf(Target, Types...)(size_t i)
+    {
+        switch (i)
+        {
+            static foreach (tid, T; Types)
+                case tid: return is(T == Target);
+            default: return false;
+        }
+    }
+
+    // const(int[]) appears twice in ConstTypes.
+    // Both indices are valid return values for typeIndex.
+    alias ConstTypes = Map!(ConstOf, MySum.Types);
+
+    assert(isIndexOf!(const int[], ConstTypes)(cs1.typeIndex));
+    assert(isIndexOf!(const int[], ConstTypes)(cs2.typeIndex));
+    assert(isIndexOf!(immutable int[], ConstTypes)(cs3.typeIndex));
+}
+
 /// True if `T` is an instance of the `SumType` template, otherwise false.
 private enum bool isSumTypeInstance(T) = is(T == SumType!Args, Args...);
 
@@ -2663,13 +2729,18 @@ template has(T)
     bool has(Self)(auto ref Self self)
     if (isSumType!Self)
     {
-        return self.match!checkType;
-    }
+        import std.meta : ApplyLeft;
+        import std.traits : CopyTypeQualifiers;
 
-    // Helper to avoid redundant template instantiations
-    private bool checkType(Value)(ref Value value)
-    {
-        return is(Value == T);
+        alias AddQualifiers = ApplyLeft!(CopyTypeQualifiers, Self);
+        alias ValueTypes = Map!(AddQualifiers, Self.Types);
+
+        final switch (self.typeIndex)
+        {
+            static foreach (valueTid, Value; ValueTypes)
+                case valueTid:
+                    return is(Value == T);
+        }
     }
 }
 
@@ -2769,9 +2840,9 @@ template get(T)
         import std.typecons : No;
 
         static if (__traits(isRef, self))
-            return self.match!(getLvalue!(No.try_, T));
+            return self.getLvalue!(No.try_, T);
         else
-            return self.match!(getRvalue!(No.try_, T));
+            return self.getRvalue!(No.try_, T);
     }
 }
 
@@ -2879,9 +2950,9 @@ template tryGet(T)
         import std.typecons : Yes;
 
         static if (__traits(isRef, self))
-            return self.match!(getLvalue!(Yes.try_, T));
+            return self.getLvalue!(Yes.try_, T);
         else
-            return self.match!(getRvalue!(Yes.try_, T));
+            return self.getRvalue!(Yes.try_, T);
     }
 }
 
@@ -3007,48 +3078,81 @@ private template failedGetMessage(Expected, Actual)
 
 private template getLvalue(Flag!"try_" try_, T)
 {
-    ref T getLvalue(Value)(ref Value value)
+    ref T getLvalue(Self)(ref Self self)
+    if (isSumType!Self)
     {
-        static if (is(Value == T))
-        {
-            return value;
-        }
-        else
+        import std.meta : ApplyLeft;
+        import std.traits : CopyTypeQualifiers;
+
+        alias AddQualifiers = ApplyLeft!(CopyTypeQualifiers, Self);
+        alias ValueTypes = Map!(AddQualifiers, Self.Types);
+
+        final switch (self.typeIndex)
         {
-            static if (try_)
-                throw new MatchException(failedGetMessage!(T, Value));
-            else
-                assert(false, failedGetMessage!(T, Value));
+            static foreach (valueTid, Value; ValueTypes)
+            {
+                case valueTid:
+                    static if (is(Value == T))
+                    {
+                        return self.getByIndex!valueTid;
+                    }
+                    else
+                    {
+                        static if (try_)
+                            throw new MatchException(failedGetMessage!(T, Value));
+                        else
+                            assert(false, failedGetMessage!(T, Value));
+                    }
+            }
         }
     }
 }
 
 private template getRvalue(Flag!"try_" try_, T)
 {
-    T getRvalue(Value)(ref Value value)
+    T getRvalue(Self)(ref Self self)
+    if (isSumType!Self)
     {
-        static if (is(Value == T))
-        {
-            import core.lifetime : move;
+        import std.meta : ApplyLeft;
+        import std.traits : CopyTypeQualifiers;
 
-            // Move if possible; otherwise fall back to copy
-            static if (is(typeof(move(value))))
+        alias AddQualifiers = ApplyLeft!(CopyTypeQualifiers, Self);
+        alias ValueTypes = Map!(AddQualifiers, Self.Types);
+
+        final switch (self.typeIndex)
+        {
+            static foreach (valueTid, Value; ValueTypes)
             {
-                static if (isCopyable!Value)
-                    // Workaround for https://issues.dlang.org/show_bug.cgi?id=21542
-                    return __ctfe ? value : move(value);
-                else
-                    return move(value);
+                case valueTid:
+                    static if (is(Value == T))
+                    {
+                        import core.lifetime : move;
+
+                        // Move if possible; otherwise fall back to copy
+                        static if (is(typeof(move(self.getByIndex!valueTid))))
+                        {
+                            static if (isCopyable!Value)
+                            {
+                                // Workaround for https://issues.dlang.org/show_bug.cgi?id=21542
+                                if (__ctfe)
+                                    return self.getByIndex!valueTid;
+                                else
+                                    return move(self.getByIndex!valueTid);
+                            }
+                            else
+                                return move(self.getByIndex!valueTid);
+                        }
+                        else
+                            return self.getByIndex!valueTid;
+                    }
+                    else
+                    {
+                        static if (try_)
+                            throw new MatchException(failedGetMessage!(T, Value));
+                        else
+                            assert(false, failedGetMessage!(T, Value));
+                    }
             }
-            else
-                return value;
-        }
-        else
-        {
-            static if (try_)
-                throw new MatchException(failedGetMessage!(T, Value));
-            else
-                assert(false, failedGetMessage!(T, Value));
         }
     }
 }
index aa672a59dd3acea91b32fbf91b2b33eddb0ec04f..d563aba636a504ef72b75fd325a74c49d65c9b29 100644 (file)
@@ -1,4 +1,6 @@
 // Written in the D programming language.
+//
+// NOTE: This must be kept in sync with phobos/sys/system.d
 
 /**
  * Information about the target operating system, environment, and CPU.
@@ -39,6 +41,7 @@ immutable
         openBSD,   /// OpenBSD
         dragonFlyBSD, /// DragonFlyBSD
         solaris,   /// Solaris
+        haiku,     /// HaikuOS
         android,   /// Android
         otherPosix, /// Other Posix Systems
         unknown,   /// Unknown
@@ -57,6 +60,8 @@ immutable
     else version (NetBSD)  OS os = OS.netBSD;
     else version (OpenBSD) OS os = OS.openBSD;
     else version (DragonFlyBSD) OS os = OS.dragonFlyBSD;
+    else version (Solaris) OS os = OS.solaris;
+    else version (Haiku) OS os = OS.haiku;
     else version (Posix)   OS os = OS.otherPosix;
     else OS os = OS.unknown;
 
@@ -123,6 +128,9 @@ immutable
         sh, /// The SuperH architecture, 32-bit
         webAssembly, /// The WebAssembly virtual ISA (instruction set architecture), 32-bit
         alpha, /// The Alpha architecture
+        loongArch32, /// The LoongAtch architecture, 32-bit
+        loongArch64, /// The LoongArch architecture, 64-bit
+        xtensa, /// The Xtensa architecture, 32-bit
         unknown, /// Unknown
     }
 
@@ -153,6 +161,9 @@ immutable
     else version (SH)       ISA instructionSetArchitecture = ISA.sh;
     else version (WebAssembly) ISA instructionSetArchitecture = ISA.webAssembly;
     else version (Alpha)    ISA instructionSetArchitecture = ISA.alpha;
+    else version (LoongArch32) ISA instructionSetArchitecture = ISA.loongArch32;
+    else version (loongArch64) ISA instructionSetArchitecture = ISA.loongArch64;
+    else version (Xtensa)   ISA instructionSetArchitecture = ISA.xtensa;
     else ISA instructionSetArchitecture = ISA.unknown;
 }
 
index 400df02aaac61d2a9e4cc6ceb876f2a4101a4ea8..759ccb94fcfbc4daf6f6a15264d85025da86f272 100644 (file)
@@ -1182,14 +1182,7 @@ if (isCallable!func)
     static assert(pstc.length == 4); // number of parameters
     static assert(pstc[0] == STC.ref_);
     static assert(pstc[1] == STC.out_);
-    version (none)
-    {
-        // TODO: When the DMD PR (dlang/dmd#11474) gets merged,
-        // remove the versioning and the second test
-        static assert(pstc[2] == STC.in_);
-        // This is the current behavior, before `in` is fixed to not be an alias
-        static assert(pstc[2] == STC.scope_);
-    }
+    static assert(pstc[2] == STC.in_);
     static assert(pstc[3] == STC.none);
 }
 
@@ -7609,7 +7602,7 @@ enum bool isSomeFunction(alias T) =
 }
 
 /**
-Detect whether `T` is a callable object, which can be called with the
+Detect whether `callable` is a callable object, which can be called with the
 function call operator `$(LPAREN)...$(RPAREN)`.
 
 $(NOTE Implicit Function Template Instantiation is *not* attempted - see below.)
@@ -7617,10 +7610,10 @@ $(NOTE Implicit Function Template Instantiation is *not* attempted - see below.)
 template isCallable(alias callable)
 {
     static if (is(typeof(&callable.opCall) == delegate))
-        // T is a object which has a member function opCall().
+        // callable is a object which has a member function opCall().
         enum bool isCallable = true;
     else static if (is(typeof(&callable.opCall) V : V*) && is(V == function))
-        // T is a type which has a static member function opCall().
+        // callable is a type which has a static member function opCall().
         enum bool isCallable = true;
     else static if (is(typeof(&callable.opCall!()) TemplateInstanceType))
     {
@@ -7636,7 +7629,7 @@ template isCallable(alias callable)
     }
 }
 
-/// Functions, function pointers, delegates, lambdas.
+/// Functions, function pointers, delegates, non-template function literals.
 @safe unittest
 {
     void f() { }
@@ -7651,6 +7644,8 @@ template isCallable(alias callable)
 
     int x;
     static assert(!isCallable!x);
+    auto d = () => x;
+    static assert( isCallable!d);
 }
 
 /// Aggregate types with (static) opCall.
@@ -7665,7 +7660,6 @@ template isCallable(alias callable)
     static assert( isCallable!(c.opCall));
     static assert( isCallable!S);
     static assert( isCallable!(I.value));
-    static assert( isCallable!((int a) { return a; }));
 
     static assert(!isCallable!I);
 }
@@ -7675,11 +7669,13 @@ template isCallable(alias callable)
 {
     void f()() { }
     T g(T = int)(T x) { return x; }
+    int h(T)();
     struct S1 { static void opCall()() { } }
     struct S2 { static T opCall(T = int)(T x) {return x; } }
 
     static assert( isCallable!f);
     static assert( isCallable!g);
+    static assert(!isCallable!h);
     static assert( isCallable!S1);
     static assert( isCallable!S2);
 
@@ -9150,9 +9146,9 @@ enum isType(alias X) = is(X);
 }
 
 /**
- * Detect whether symbol or type `X` is a function. This is different that finding
- * if a symbol is callable or satisfying `is(X == function)`, it finds
- * specifically if the symbol represents a normal function declaration, i.e.
+ * Detect whether symbol or type `X` is a function.
+ * This is different from finding if a symbol is callable or satisfying `is(X == return)`.
+ * It finds specifically if the symbol represents a normal function (or method) declaration, i.e.
  * not a delegate or a function pointer.
  *
  * Returns:
@@ -9170,10 +9166,10 @@ template isFunction(alias X)
         // x is a (nested) function symbol.
         enum isFunction = true;
     }
-    else static if (is(X T))
+    else static if (is(X))
     {
-        // x is a type.  Take the type of it and examine.
-        enum isFunction = is(T == function);
+        // x is a type
+        enum isFunction = is(X == function);
     }
     else
         enum isFunction = false;
@@ -9184,6 +9180,14 @@ template isFunction(alias X)
 {
     static void func(){}
     static assert(isFunction!func);
+    static assert(isFunction!(typeof(func)));
+
+    auto fp = &func; // function pointer
+    static assert(!isFunction!fp);
+
+    int i;
+    int f2() => i; // nested function
+    static assert(isFunction!f2);
 
     struct S
     {
index cd2772109c044b3814fe7cffe28496cbe854566a..5138efa5fa78039bba2b32ee43c9a7b12fc057e6 100644 (file)
@@ -21,10 +21,15 @@ $(TR $(TD Flags) $(TD
     $(LREF No)
     $(LREF Yes)
 ))
-$(TR $(TD Memory allocation) $(TD
+$(TR $(TD Reference Counting) $(TD
+    $(LREF borrow)
+    $(LREF RefCountedAutoInitialize)
+    $(LREF RefCounted)
+    $(LREF refCounted)
     $(LREF SafeRefCounted)
     $(LREF safeRefCounted)
-    $(LREF RefCountedAutoInitialize)
+))
+$(TR $(TD Memory allocation) $(TD
     $(LREF scoped)
     $(LREF Unique)
 ))
@@ -33,9 +38,11 @@ $(TR $(TD Code generation) $(TD
     $(LREF BlackHole)
     $(LREF generateAssertTrap)
     $(LREF generateEmptyFunction)
+    $(LREF NotImplementedError)
     $(LREF WhiteHole)
 ))
 $(TR $(TD Nullable) $(TD
+    $(LREF apply)
     $(LREF Nullable)
     $(LREF nullable)
     $(LREF NullableRef)
@@ -45,12 +52,13 @@ $(TR $(TD Proxies) $(TD
     $(LREF Proxy)
     $(LREF rebindable)
     $(LREF Rebindable)
-    $(LREF ReplaceType)
     $(LREF unwrap)
     $(LREF wrap)
 ))
 $(TR $(TD Types) $(TD
     $(LREF alignForSize)
+    $(LREF ReplaceType)
+    $(LREF ReplaceTypeUnless)
     $(LREF Ternary)
     $(LREF Typedef)
     $(LREF TypedefType)
@@ -3814,13 +3822,16 @@ struct Nullable(T)
         assert(e != 12);
     }
 
-    size_t toHash() const @safe nothrow
+    static if (!isAggregateType!T || hasMember!(T, "toHash"))
     {
-        static if (__traits(compiles, .hashOf(_value.payload)))
-            return _isNull ? 0 : .hashOf(_value.payload);
-        else
-            // Workaround for when .hashOf is not both @safe and nothrow.
-            return _isNull ? 0 : typeid(T).getHash(&_value.payload);
+        size_t toHash() const @safe nothrow
+        {
+            static if (__traits(compiles, .hashOf(_value.payload)))
+                return _isNull ? 0 : .hashOf(_value.payload);
+            else
+                // Workaround for when .hashOf is not both @safe and nothrow.
+                return _isNull ? 0 : typeid(T).getHash(&_value.payload);
+        }
     }
 
     /**
@@ -4826,6 +4837,18 @@ auto nullable(T)(T t)
     auto result = cast(immutable(Nullable!(int*))) a;
 }
 
+// https://github.com/dlang/phobos/issues/10758
+@safe unittest
+{
+    struct F
+    {
+        bool opEquals(ref const F rhs) const { return true; }
+    }
+
+    static assert(!__traits(compiles, bool[F]));
+    static assert(!__traits(compiles, bool[Nullable!F]));
+}
+
 /**
 Just like `Nullable!T`, except that the null state is defined as a
 particular value. For example, $(D Nullable!(uint, uint.max)) is an
@@ -5240,7 +5263,7 @@ if (is (typeof(nullValue) == T))
 
 // apply
 /**
-Unpacks the content of a `Nullable`, performs an operation and packs it again. Does nothing if isNull.
+Unpacks the content of a $(LREF Nullable), performs an operation and packs it again. Does nothing if $(LREF isNull).
 
 When called on a `Nullable`, `apply` will unpack the value contained in the `Nullable`,
 pass it to the function you provide and wrap the result in another `Nullable` (if necessary).
@@ -5260,6 +5283,7 @@ template apply(alias fun)
 {
     import std.functional : unaryFun;
 
+    ///
     auto apply(T)(auto ref T t)
     if (isInstanceOf!(Nullable, T))
     {
@@ -8311,6 +8335,7 @@ template borrow(alias fun)
 {
     import std.functional : unaryFun;
 
+    ///
     auto ref borrow(RC)(RC refCount)
     if
     (
index 3a054a5aef34663b1315de2a83e0f854bdc60d09..77948fc88387173cf8f911c67a817ec9e6a2d3dd 100644 (file)
@@ -23,6 +23,7 @@ $(TR $(TDNW Generating UUIDs)
      $(TD $(MYREF sha1UUID)
           $(MYREF randomUUID)
           $(MYREF md5UUID)
+          $(MYREF timestampRandomUUID)
           )
      )
 $(TR $(TDNW Using UUIDs)
@@ -119,6 +120,10 @@ module std.uuid;
     assert(id.empty);
 }
 
+import core.time : dur;
+import std.bitmanip : bigEndianToNative, nativeToBigEndian;
+import std.datetime.systime : SysTime;
+import std.datetime : Clock, DateTime, UTC;
 import std.range.primitives;
 import std.traits;
 
@@ -193,7 +198,9 @@ public struct UUID
             ///Version 4 (Random)
             randomNumberBased = 4,
             ///Version 5 (Name based + SHA-1)
-            nameBasedSHA1 = 5
+            nameBasedSHA1 = 5,
+            ///Version 7 (milliseconds since unix epoch + random)
+            timestampRandom = 7
         }
 
         union
@@ -309,6 +316,46 @@ public struct UUID
             assert(!__traits(compiles, typeof(UUID(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,1))));
         }
 
+        /**
+         * UUID V7 constructor
+         *
+         * This implementation is not guaranteed to use a cryptographically secure PRNG.
+         * For more information please see: std.random.unpredictableSeed
+         *
+         * Params:
+         *   timestamp = the timestamp part of the UUID V7
+         *   random = UUID V7 has 74 bits of random data, which rounds to 10 ubyte's.
+         *    If no random data is given, random data is generated.
+         */
+        @safe pure this(SysTime timestamp, ubyte[10] random = generateRandomData!10)
+        {
+            ulong epoch = (timestamp - SysTime.fromUnixTime(0)).total!"msecs";
+            this(epoch, random);
+        }
+
+        /// ditto
+        @safe pure this(ulong epoch_msecs, ubyte[10] random = generateRandomData!10)
+        {
+            ubyte[8] epoch = epoch_msecs.nativeToBigEndian;
+
+            this.data[0 .. 6] = epoch[2 .. 8];
+            this.data[6 .. $] = random;
+
+            // version and variant
+            this.data[6] = (this.data[6] & 0x0F) | 0x70;
+            this.data[8] = (this.data[8] & 0x3F) | 0x80;
+        }
+
+        ///
+        @system unittest
+        {
+            import std.datetime : DateTime, SysTime;
+            SysTime st = DateTime(2025, 8, 19, 10, 38, 45);
+            UUID u = UUID(st);
+            SysTime o = u.v7Timestamp();
+            assert(o == st, st.toString() ~ " | " ~ o.toString());
+        }
+
         /**
          * <a name="UUID(string)"></a>
          * Parse a UUID from its canonical string form. An UUID in its
@@ -515,6 +562,47 @@ public struct UUID
             enum res = ctfeTest();
         }
 
+        /**
+         * If the UUID is of version 7 it has a timestamp that this function
+         * returns, otherwise an UUIDParsingException is thrown.
+         */
+        SysTime v7Timestamp() const {
+            if (this.uuidVersion != Version.timestampRandom)
+            {
+                throw new UUIDParsingException("The UUID is not of version" ~
+                    " v7 therefore no timestamp exist", 0);
+            }
+
+            import std.bitmanip : bigEndianToNative;
+
+            ubyte[8] tmp = void;
+            tmp[0 .. 2] = 0;
+            tmp[2 .. 8] = data[0 .. 6];
+
+            ulong milli = tmp.bigEndianToNative!ulong;
+
+            return SysTime(DateTime(1970, 1, 1), UTC()) + dur!"msecs"(milli);
+        }
+
+        /**
+         * If the UUID is of version 7 it has a timestamp that this function
+         * returns as described in RFC 9562 (Method 3), otherwise an
+         * UUIDParsingException is thrown.
+         */
+        SysTime v7Timestamp_method3() const {
+            auto ret = v7Timestamp();
+
+            const ubyte[2] rand_a = [
+                data[6] & 0x0f, // masks version bits
+                data[7]
+            ];
+
+            const float hnsecs = rand_a.bigEndianToNative!ushort / MonotonicUUIDsFactory.subMsecsPart;
+            ret += dur!"hnsecs"(cast(ulong) hnsecs);
+
+            return ret;
+        }
+
         /**
          * RFC 4122 defines different internal data layouts for UUIDs.
          * Returns the format used by this UUID.
@@ -601,6 +689,8 @@ public struct UUID
                 return Version.randomNumberBased;
             else if ((octet9 & 0xF0) == 0x50)
                 return Version.nameBasedSHA1;
+            else if ((octet9 & 0xF0) == 0x70)
+                return Version.timestampRandom;
             else
                 return Version.unknown;
         }
@@ -622,7 +712,7 @@ public struct UUID
                 0x40 : UUID.Version.randomNumberBased,
                 0x50 : UUID.Version.nameBasedSHA1,
                 0x60 : UUID.Version.unknown,
-                0x70 : UUID.Version.unknown,
+                0x70 : UUID.Version.timestampRandom,
                 0x80 : UUID.Version.unknown,
                 0x90 : UUID.Version.unknown,
                 0xa0 : UUID.Version.unknown,
@@ -1317,6 +1407,164 @@ if (isInputRange!RNG && isIntegral!(ElementType!RNG))
     assert(u1.uuidVersion == UUID.Version.randomNumberBased);
 }
 
+///
+class MonotonicUUIDsFactory
+{
+    import core.sync.mutex : Mutex;
+    import core.time : Duration;
+    import std.datetime.stopwatch : StopWatch;
+
+    private shared Mutex mtx;
+    private StopWatch startTimePoint;
+
+    ///
+    this(in SysTime startTime = SysTime.fromUnixTime(0)) shared
+    {
+        this(Clock.currTime(UTC()) - startTime);
+    }
+
+    ///
+    this(in Duration timeElapsed, bool autostartDisabledForTesting = false) shared
+    {
+        mtx = new shared Mutex();
+
+        (cast() startTimePoint).setTimeElapsed = timeElapsed;
+
+        if (!autostartDisabledForTesting)
+            (cast() startTimePoint).start();
+    }
+
+    private auto peek() shared
+    {
+        mtx.lock();
+        scope(exit) mtx.unlock();
+
+        return (cast() startTimePoint).peek;
+    }
+
+    // hnsecs is 1/10_000 of millisecond
+    // rand_a size is 12 bits (4096 values)
+    private enum float subMsecsPart = 1.0f / 10_000 * 4096;
+
+    /**
+     * Returns a monotonic timestamp + random based UUIDv7
+     * as described in RFC 9562 (Method 3).
+     */
+    UUID createUUIDv7_method3(ubyte[8] externalRandom = generateRandomData!8) shared
+    {
+        const curr = peek.split!("msecs", "hnsecs");
+        const qhnsecs = cast(ushort) (curr.hnsecs * subMsecsPart);
+
+        ubyte[10] rand;
+
+        // Whole rand_a is 16 bit, but usable only 12 MSB.
+        // additional 4 less significant bits consumed
+        // by a version value
+        rand[0 .. 2] = qhnsecs.nativeToBigEndian;
+        rand[2 .. $] = externalRandom;
+
+        return UUID(curr.msecs, rand);
+    }
+}
+
+/// Generate monotone UUIDs
+@system unittest
+{
+    auto f = new shared MonotonicUUIDsFactory;
+
+    UUID[10] monotonic;
+
+    foreach (ref u; monotonic)
+        u = f.createUUIDv7_method3;
+}
+
+@system unittest
+{
+    import std.conv : to;
+    import std.datetime;
+
+    const currTime = SysTime(DateTime(2025, 9, 12, 21, 38, 45), UTC());
+    Duration d = currTime - SysTime.fromUnixTime(0) + dur!"msecs"(123);
+
+    auto f = new shared MonotonicUUIDsFactory(d, true);
+
+    const u1 = f.createUUIDv7_method3();
+    assert(u1.uuidVersion == UUID.Version.timestampRandom);
+
+    // sub-millisecond part zeroed
+    assert((u1.data[6] & 0b0000_1111) == 0);
+    assert(u1.data[7] == 0);
+
+    const uuidv7_milli_1 = u1.v7Timestamp;
+
+    {
+        const st = u1.v7Timestamp_method3;
+        assert(cast(DateTime) st == cast(DateTime) currTime, st.to!string);
+
+        const sp = st.fracSecs.split!("msecs", "usecs", "hnsecs");
+        assert(sp.msecs == 123, sp.to!string);
+        assert(sp.usecs == 0, sp.to!string);
+    }
+
+    // 0.3 usecs, but Method 3 precision is only 0.25 of usec,
+    // thus, expected value is 2
+    d += dur!"hnsecs"(3);
+    f = new shared MonotonicUUIDsFactory(d, true);
+
+    const u2 = f.createUUIDv7_method3();
+    const uuidv7_milli_2 = u2.v7Timestamp;
+    assert(uuidv7_milli_1 == uuidv7_milli_2);
+
+    {
+        const st = u2.v7Timestamp_method3;
+        assert(cast(DateTime) st == cast(DateTime) currTime, st.to!string);
+
+        const sp = st.fracSecs.split!("msecs", "usecs", "hnsecs");
+        assert(sp.msecs == 123, sp.to!string);
+        assert(sp.usecs == 0, sp.to!string);
+        assert(sp.hnsecs == 2, sp.to!string);
+    }
+}
+
+@system unittest
+{
+    import core.thread.osthread : Thread;
+    import std.datetime;
+
+    scope f = new shared MonotonicUUIDsFactory;
+
+    UUID[1000] uuids;
+
+    foreach (ref u; uuids)
+    {
+        // UUIDv7 Method 3 monotonicity is only guaranteed if UUIDs are
+        // generated slower than 2.5 microseconds
+        Thread.sleep(dur!("hnsecs")(25));
+        u = f.createUUIDv7_method3;
+    }
+
+    foreach (i; 1 .. uuids.length)
+    {
+        assert(uuids[i-1].v7Timestamp_method3 < uuids[i].v7Timestamp_method3);
+        assert(uuids[i-1].data[8 .. $] != uuids[i].data[8 .. $], "random parts are equal");
+    }
+}
+
+/**
+ * This function returns a timestamp + random based UUID aka. uuid v7.
+ */
+UUID timestampRandomUUID()
+{
+    return UUID(Clock.currTime(UTC()));
+}
+
+///
+@system unittest
+{
+    UUID u = timestampRandomUUID();
+    assert(u.uuidVersion == UUID.Version.timestampRandom);
+}
+
 /**
  * This is a less strict parser compared to the parser used in the
  * UUID constructor. It enforces the following rules:
@@ -1718,6 +1966,20 @@ enum uuidRegex = "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}"~
     ]);
 }
 
+private ubyte[Size] generateRandomData(ubyte Size)() {
+    import std.random : Random, uniform, unpredictableSeed;
+
+    auto rnd = Random(unpredictableSeed);
+
+    ubyte[Size] bytes;
+    foreach (idx; 0 .. bytes.length)
+    {
+        bytes[idx] = uniform!(ubyte)(rnd);
+        rnd.popFront();
+    }
+    return bytes;
+}
+
 /**
  * This exception is thrown if an error occurs when parsing a UUID
  * from a string.
@@ -1777,3 +2039,40 @@ public class UUIDParsingException : Exception
     assert(ex.position == 10);
     assert(ex.reason == UUIDParsingException.Reason.tooMuch);
 }
+
+/// uuidv7
+@system unittest
+{
+    import std.datetime : DateTime, SysTime;
+
+    SysTime st = DateTime(2025, 8, 19, 10, 38, 45);
+    UUID u = UUID(st);
+    assert(u.uuidVersion == UUID.Version.timestampRandom);
+    SysTime o = u.v7Timestamp();
+    assert(o == st, st.toString() ~ " | " ~ o.toString());
+    string s = u.toString();
+    UUID u2 = UUID(s);
+    SysTime o2 = u2.v7Timestamp();
+    assert(o2 == st, st.toString() ~ " | " ~ o2.toString());
+}
+
+@system unittest
+{
+    import std.datetime : SysTime;
+
+    UUID u = timestampRandomUUID();
+    assert(u.uuidVersion == UUID.Version.timestampRandom);
+
+    SysTime o = u.v7Timestamp();
+    assert(o.year > 2024);
+    assert(o.year < 3024);
+}
+
+/// uuid v7 generated by external tool
+@system unittest
+{
+    import std.datetime : DateTime, SysTime;
+    UUID u = UUID("0198c2b2-c5a8-7a0f-a1db-86aac7906c7b");
+    auto d = DateTime(2025,8,19);
+    assert((cast(DateTime) u.v7Timestamp()).year == d.year);
+}
index 4f8cac52d3c122957ac49051c0a53d37e02a2f5f..214bc9a12b2ec5289039fdd4744041fc1c3281a9 100644 (file)
@@ -7,8 +7,10 @@ void main()
     testRequire1();
     testRequire2();
     testRequire3();
+    testRequire4();
     testUpdate1();
     testUpdate2();
+    testUpdate3();
     testByKey1();
     testByKey2();
     testByKey3();
@@ -43,6 +45,7 @@ void main()
     testTypeInfoCollect();
     testNew();
     testAliasThis();
+    testAliasThis2();
 }
 
 void testKeysValues1()
@@ -227,6 +230,20 @@ void testRequire3() pure
     assert("foo" in aa);
 }
 
+void testRequire4() pure
+{
+    int[int] aa;
+    try
+        aa.require(5, {
+            if (true)
+                throw new Exception("oops");
+            else
+                return 1;
+        }());
+    catch (Exception e) {}
+    assert(5 !in aa);
+}
+
 
 void testUpdate1()
 {
@@ -291,6 +308,23 @@ void testUpdate2()
     assert(updated);
 }
 
+void testUpdate3() pure
+{
+    int[int] aa;
+    try
+        aa.update(5, {
+            if (true)
+                throw new Exception("oops");
+            else
+                return 1;
+        }, (ref int v) {
+            throw new Exception("unexpected update");
+        });
+    catch (Exception e) {}
+    assert(5 !in aa);
+}
+
+
 void testByKey1() @safe
 {
     static struct BadValue
@@ -988,3 +1022,29 @@ void testAliasThis()
     s.remove(1);
     assert(S.numCopies == 0);
 }
+
+void testAliasThis2()
+{
+    static struct A
+    {
+        bool extra;
+        uint id;
+    }
+
+    static struct B
+    {
+        uint id;
+
+        A toA() const pure nothrow
+        {
+            return A(false, id);
+        }
+
+        alias toA this;
+    }
+
+    bool[A] aa;
+    aa[B(5)] = true;
+    assert(B(5) in aa);
+    assert(A(false, 5) in aa);
+}
diff --git a/libphobos/testsuite/libphobos.gc/forkgc2.d b/libphobos/testsuite/libphobos.gc/forkgc2.d
deleted file mode 100644 (file)
index 38d0d0c..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-// { dg-skip-if "test hangs the testsuite PR103944" { *-*-darwin* } }
-import core.stdc.stdlib : exit;
-import core.sys.posix.sys.wait : waitpid;
-import core.sys.posix.unistd : fork;
-import core.thread : Thread;
-
-void main()
-{
-    foreach (t; 0 .. 10)
-        new Thread({
-            foreach (n; 0 .. 100)
-            {
-                foreach (x; 0 .. 100)
-                    new ubyte[x];
-                auto f = fork();
-                assert(f >= 0);
-                if (f == 0)
-                    exit(0);
-                else
-                    waitpid(f, null, 0);
-            }
-        }).start();
-}
diff --git a/libphobos/testsuite/libphobos.thread/filterthrownglobal.d b/libphobos/testsuite/libphobos.thread/filterthrownglobal.d
new file mode 100644 (file)
index 0000000..5b9fa02
--- /dev/null
@@ -0,0 +1,31 @@
+import core.exception;
+import core.thread;
+
+__gshared bool caught;
+
+void main()
+{
+    filterThreadThrowableHandler = (ref Throwable t) {
+        if (auto t2 = cast(AssertError) t)
+        {
+            if (t2.message != "Hey!")
+                return;
+        }
+        else
+            return;
+
+        caught = true;
+        t = null;
+    };
+
+    Thread t = new Thread(&entry);
+    t.start();
+    t.join();
+
+    assert(caught);
+}
+
+void entry()
+{
+    throw new AssertError("Hey!");
+}
diff --git a/libphobos/testsuite/libphobos.thread/filterthrownmethod.d b/libphobos/testsuite/libphobos.thread/filterthrownmethod.d
new file mode 100644 (file)
index 0000000..8cb43b8
--- /dev/null
@@ -0,0 +1,41 @@
+import core.exception;
+import core.thread;
+
+__gshared bool caught;
+
+void main()
+{
+    Thread t = new MyThread(&entry);
+    t.start();
+    t.join();
+
+    assert(caught);
+}
+
+class MyThread : Thread
+{
+    this( void function() fn, size_t sz = 0 ) @safe pure nothrow @nogc
+    {
+        super(fn, sz);
+    }
+
+    override void filterCaughtThrowable(ref Throwable t) @system nothrow
+    {
+        if (auto t2 = cast(AssertError) t)
+        {
+            if (t2.message == "Hey!")
+            {
+                caught = true;
+                t = null;
+                return;
+            }
+        }
+
+        super.filterCaughtThrowable(t);
+    }
+}
+
+void entry()
+{
+    throw new AssertError("Hey!");
+}