diff --git a/.github/actions/1-setup/action.yml b/.github/actions/1-setup/action.yml index 48726d0ae9..0abefc45d2 100644 --- a/.github/actions/1-setup/action.yml +++ b/.github/actions/1-setup/action.yml @@ -145,6 +145,12 @@ runs: if: runner.os == 'Windows' shell: cmd run: make --version + - name: 'macOS: Install a more recent GNU make' + if: runner.os == 'macOS' + shell: bash + run: | + brew install make + gmake --version - name: 'Windows: Download & extract libcurl' # into ../libcurl/ldc2 if: runner.os == 'Windows' diff --git a/.github/actions/4c-test-dmd/action.yml b/.github/actions/4c-test-dmd/action.yml index cd7368dc85..22dbea1f94 100644 --- a/.github/actions/4c-test-dmd/action.yml +++ b/.github/actions/4c-test-dmd/action.yml @@ -9,20 +9,7 @@ runs: - name: 'Posix: Run DMD testsuite' if: runner.os != 'Windows' shell: bash - run: | - set -eux - repoDir=$PWD - cd ../build - if type -P apk &>/dev/null; then - # Alpine: run full dmd-testsuite-debug - ctest -V -R 'dmd-testsuite' -E '^dmd-testsuite$' - # these two tests require extra flags "-link-defaultlib-debug -frame-pointer=all": https://github.com/ldc-developers/ldc/issues/4694 - # => remove before running optimized dmd-testsuite separately - rm $repoDir/tests/dmd/runnable/{test17559.d,test19086.d} - ctest -V -R '^dmd-testsuite$' - else - ctest -V -R "dmd-testsuite" - fi + run: cd ../build && ctest -V -R "dmd-testsuite" - name: 'Windows: Run DMD testsuite' if: runner.os == 'Windows' diff --git a/.github/actions/4d-test-libs/action.yml b/.github/actions/4d-test-libs/action.yml index 52257977a9..cd592ff0b4 100644 --- a/.github/actions/4d-test-libs/action.yml +++ b/.github/actions/4d-test-libs/action.yml @@ -27,8 +27,6 @@ runs: excludes+='|^std.internal.math.gammafunction' # FIXME: failing unittest(s) with enabled optimizations excludes+='|^std.math.exponential(-shared)?$' - # FIXME: subtest rt_trap_exceptions fails - excludes+='|^druntime-test-exceptions-debug$' # FIXME: sporadically hanging excludes+='|^core.thread-shared$' fi diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 099f9497fb..2efbf10a07 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -2,8 +2,12 @@ name: Main on: - - pull_request - - push + pull_request: + push: + branches-ignore: + - 'merge-*' # don't run on pushes to merge-X.Y.Z branches, they are usually PRs + tags: # explicitly needed to run for all tags, due to the branches filter above + - '**' concurrency: group: ${{ github.workflow }}-${{ github.ref }} diff --git a/.github/workflows/supported_llvm_versions.yml b/.github/workflows/supported_llvm_versions.yml index a6f83ddaad..657619b45b 100644 --- a/.github/workflows/supported_llvm_versions.yml +++ b/.github/workflows/supported_llvm_versions.yml @@ -2,8 +2,12 @@ name: Vanilla LLVM on: - - pull_request - - push + pull_request: + push: + branches-ignore: + - 'merge-*' # don't run on pushes to merge-X.Y.Z branches, they are usually PRs + tags: # explicitly needed to run for all tags, due to the branches filter above + - '**' concurrency: group: ${{ github.workflow }}-${{ github.ref }} @@ -69,6 +73,12 @@ jobs: sudo apt-get update sudo apt-get install gdb lld-${{ matrix.llvm_version }} llvm-${{ matrix.llvm_version }}-dev libclang-common-${{ matrix.llvm_version }}-dev + - name: 'macOS: Install a more recent GNU make' + if: runner.os == 'macOS' + run: | + brew install make + gmake --version + - name: 'macOS: Try to restore cached LLVM' if: runner.os == 'macOS' uses: actions/cache@v4 @@ -129,6 +139,14 @@ jobs: if [[ '${{ runner.os }}' == 'Linux' ]]; then # FIXME: lsan_interceptors.cpp:82 "((!lsan_init_is_running)) != (0)" rm tests/sanitizers/lsan_memleak.d + elif [[ '${{ runner.os }}' == 'macOS' ]]; then + # work around weird lit-test `plugins/addFuncEntryCall/testPlugin.d` + # regression after switching to homebrew make (default CXX is clang++) + export CXX=c++ + if [[ '${{ matrix.cmake_flags }}' =~ -DRT_SUPPORT_SANITIZERS=ON ]]; then + # FIXME: regressed with druntime v2.111 on macOS arm64 (but works on Linux x86_64) + rm tests/sanitizers/asan_fiber.d + fi fi ctest -V -R "lit-tests" - name: Run DMD testsuite diff --git a/CHANGELOG.md b/CHANGELOG.md index ea3a1a2b34..9a8e0fc9e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ # LDC master #### Big news +- Frontend, druntime and Phobos are at version [2.111.0](https://dlang.org/changelog/2.111.0.html). (#4877) +- Keep frame pointers by default with `-O` for some targets, notably AArch64 (except Windows), x86_64 (except Windows and glibc Linux), Windows x86, and Android. This fixes druntime backtraces with optimized code (incl. prebuilt druntime/Phobos). (#4889) - The prebuilt (non-musl) Linux packages are now generated on Ubuntu 22.04; the minimum glibc version has accordingly been raised from v2.31 to v2.35. (#4893) - ldc2.conf: Arrays can now be appended to via the `~=` operator. (#4848, #4856) - New `--installWithSuffix` command-line option for the `ldc-build-runtime` tool, to simplify copying the libraries to an existing LDC installation. (#4870) diff --git a/CMakeLists.txt b/CMakeLists.txt index d471dc087d..6f169f85d5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -117,9 +117,9 @@ include(GetLinuxDistribution) # # Version information -set(LDC_VERSION "1.40.1") # May be overridden by git hash tag +set(LDC_VERSION "1.41.0") # May be overridden by git hash tag set(DMDFE_MAJOR_VERSION 2) -set(DMDFE_MINOR_VERSION 110) +set(DMDFE_MINOR_VERSION 111) set(DMDFE_PATCH_VERSION 0) set(DMD_VERSION ${DMDFE_MAJOR_VERSION}.${DMDFE_MINOR_VERSION}.${DMDFE_PATCH_VERSION}) @@ -414,7 +414,6 @@ set(DRV_HDR driver/linker.h driver/plugins.h driver/targetmachine.h - driver/timetrace.h driver/toobj.h driver/tool.h ) diff --git a/dmd/README.md b/dmd/README.md index 0787594315..2e93d26fe8 100644 --- a/dmd/README.md +++ b/dmd/README.md @@ -41,8 +41,11 @@ Note that these groups have no strict meaning, the category assignments are a bi | [frontend.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/frontend.d) | An interface for using DMD as a library | | [errors.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/errors.d) | Error reporting implementation | | [errorsink.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/errorsink.d) | Error reporting interface | +| [sarif.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/sarif.d) | Generates SARIF reports for errors and warnings. | | [target.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/target.d) | Manage target-specific parameters for cross-compiling (for LDC/GDC) | | [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 | ### Lexing / parsing @@ -96,14 +99,14 @@ Note that these groups have no strict meaning, the category assignments are a bi | File | Purpose | |-----------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------| -| [parsetimevisitor.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/parsetimevisitor.d) | General [visitor](https://en.wikipedia.org/wiki/Visitor_pattern) for AST nodes | -| [permissivevisitor.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/permissivevisitor.d) | Subclass of ParseTimeVisitor that does not `assert(0)` on unimplemented nodes | -| [strictvisitor.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/strictvisitor.d) | Visitor that forces derived classes to implement `visit` for every possible node | -| [visitor.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/visitor.d) | A visitor implementing `visit` for all nodes present in the compiler | -| [transitivevisitor.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/transitivevisitor.d) | Provide a mixin template with visit methods for the parse time AST | -| [postordervisitor.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/postordervisitor.d) | Depth-first expression visitor | -| [sapply.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/sapply.d) | Depth-first statement visitor | -| [statement_rewrite_walker.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/statement_rewrite_walker.d) | Statement visitor that allows replacing the currently visited node | +| [visitor/parsetime.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/visitor/parsetime.d) | General [visitor](https://en.wikipedia.org/wiki/Visitor_pattern) for AST nodes | +| [visitor/permissive.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/visitor/permissive.d) | Subclass of ParseTimeVisitor that does not `assert(0)` on unimplemented nodes | +| [visitor/strict.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/visitor/strict.d) | Visitor that forces derived classes to implement `visit` for every possible node | +| [visitor/package.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/visitor/package.d) | A visitor implementing `visit` for all nodes present in the compiler | +| [visitor/transitive.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/visitor/transitive.d) | Provide a mixin template with visit methods for the parse time AST | +| [visitor/postorder.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/visitor/postorder.d) | Depth-first expression & statement visitor | +| [visitor/statement_rewrite_walker.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/visitor/statement_rewrite_walker.d) | Statement visitor that allows replacing the currently visited node | +| [visitor/foreachvar.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/visitor/foreachvar.d) | Used in `ob.d` to iterate over all variables in an expression | **Semantic passes** @@ -197,13 +200,13 @@ Note that these groups have no strict meaning, the category assignments are a bi | File | Purpose | |-------------------------------------------------------------------------------|------------------------------------------------------| -| [lib.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/lib.d) | Abstract library class | -| [libelf.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/libelf.d) | Library in ELF format (Unix) | -| [libmach.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/libmach.d) | Library in Mach-O format (macOS) | -| [libmscoff.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/libmscoff.d) | Library in COFF format (32/64-bit Windows) | -| [scanelf.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/scanelf.d) | Extract symbol names from a library in ELF format | -| [scanmach.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/scanmach.d) | Extract symbol names from a library in Mach-O format | -| [scanmscoff.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/scanmscoff.d) | Extract symbol names from a library in COFF format | +| [lib/package.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/lib/package.d) | Abstract library class | +| [lib/elf.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/lib/elf.d) | Library in ELF format (Unix) | +| [lib/mach.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/lib/mach.d) | Library in Mach-O format (macOS) | +| [lib/mscoff.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/lib/mscoff.d) | Library in COFF format (32/64-bit Windows) | +| [lib/scanelf.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/lib/scanelf.d) | Extract symbol names from a library in ELF format | +| [lib/scanmach.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/lib/scanmach.d) | Extract symbol names from a library in Mach-O format | +| [lib/scanmscoff.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/lib/scanmscoff.d) | Extract symbol names from a library in COFF format | ### Code generation / back-end interfacing @@ -231,10 +234,10 @@ Note that these groups have no strict meaning, the category assignments are a bi | File | Purpose | |-----------------------------------------------------------------------------------|------------------------------------------------------------------| -| [cppmangle.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/cppmangle.d) | C++ name mangling | -| [cppmanglewin.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/cppmanglewin.d) | C++ name mangling for Windows | -| [basicmangle.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/basicmangle.d) | D name mangling for basic types | -| [dmangle.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/dmangle.d) | D [name mangling](https://dlang.org/spec/abi.html#name_mangling) | +| [mangle/cpp.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/mangle/cpp.d) | C++ name mangling | +| [mangle/cppwin.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/mangle/cppwin.d) | C++ name mangling for Windows | +| [mangle/basic.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/mangle/basic.d) | D name mangling for basic types | +| [mangle/package.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/mangle/package.d) | D [name mangling](https://dlang.org/spec/abi.html#name_mangling) | ### Linking @@ -251,7 +254,7 @@ Note that these groups have no strict meaning, the category assignments are a bi | [hdrgen.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/hdrgen.d) | Convert an AST into D source code for `.di` header generation, as well as `-vcg-ast` and error messages | | [json.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/json.d) | Describe the module in a `.json` file for the `-X` flag | | [dtoh.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/dtoh.d) | C++ header generation from D source files | -| [disasm86.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/backend/disasm86.d) | x86-64 dissassembly generation +| [disasm86.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/backend/x86/disasm86.d) | x86-64 disassembly generation | [disasmarm.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/backend/arm/disasmarm.d) | AArch64 disassembly generation ### Utility @@ -268,4 +271,3 @@ Note: many other utilities are in [dmd/root](https://github.com/dlang/dmd/tree/m |---------------------------------------------------------------------------------|---------------------------------------------------------------| | [asttypename.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/asttypename.d) | Print the internal name of an AST node (for debugging only) | | [printast.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/printast.d) | Print the AST data structure | -| [foreachvar.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/foreachvar.d) | Used in `ob.d` to iterate over all variables in an expression | diff --git a/dmd/access.d b/dmd/access.d index 8d02203017..eaaa288cb4 100644 --- a/dmd/access.d +++ b/dmd/access.d @@ -3,12 +3,12 @@ * * Specification: $(LINK2 https://dlang.org/spec/attribute.html#visibility_attributes, Visibility Attributes) * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/access.d, _access.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/access.d, _access.d) * Documentation: https://dlang.org/phobos/dmd_access.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/access.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/access.d */ module dmd.access; @@ -22,6 +22,7 @@ import dmd.dstruct; import dmd.dsymbol; import dmd.errors; import dmd.expression; +import dmd.funcsem : overloadApply; import dmd.location; import dmd.tokens; @@ -141,7 +142,7 @@ private bool hasPackageAccess(Module mod, Dsymbol s) /**************************************** * Determine if scope sc has protected level access to cd. */ -private bool hasProtectedAccess(Scope *sc, Dsymbol s) +private bool hasProtectedAccess(Scope* sc, Dsymbol s) { if (auto cd = s.isClassMember()) // also includes interfaces { @@ -163,7 +164,7 @@ private bool hasProtectedAccess(Scope *sc, Dsymbol s) */ bool checkAccess(Loc loc, Scope* sc, Expression e, Dsymbol d) { - if (sc.flags & SCOPE.noaccesscheck) + if (sc.noAccessCheck) return false; static if (LOG) { @@ -272,7 +273,7 @@ bool symbolIsVisible(Dsymbol origin, Dsymbol s) * s = symbol to check for visibility * Returns: true if s is visible by origin */ -bool symbolIsVisible(Scope *sc, Dsymbol s) +bool symbolIsVisible(Scope* sc, Dsymbol s) { s = mostVisibleOverload(s); return checkSymbolAccess(sc, s); @@ -287,7 +288,7 @@ bool symbolIsVisible(Scope *sc, Dsymbol s) * s = symbol to check for visibility * Returns: true if s is visible by origin */ -bool checkSymbolAccess(Scope *sc, Dsymbol s) +bool checkSymbolAccess(Scope* sc, Dsymbol s) { final switch (s.visible().kind) { diff --git a/dmd/aggregate.d b/dmd/aggregate.d index 7475954a32..b7b0b0993c 100644 --- a/dmd/aggregate.d +++ b/dmd/aggregate.d @@ -4,12 +4,12 @@ * Specification: $(LINK2 https://dlang.org/spec/struct.html, Structs, Unions), * $(LINK2 https://dlang.org/spec/class.html, Class). * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/aggregate.d, _aggregate.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/aggregate.d, _aggregate.d) * Documentation: https://dlang.org/phobos/dmd_aggregate.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/aggregate.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/aggregate.d */ module dmd.aggregate; @@ -25,7 +25,7 @@ import dmd.declaration; import dmd.dscope; import dmd.dstruct; import dmd.dsymbol; -import dmd.dsymbolsem; +import dmd.dsymbolsem : dsymbolSemantic, determineFields, search, determineSize, include; import dmd.dtemplate; import dmd.errors; import dmd.expression; @@ -96,10 +96,10 @@ struct MangleOverride extern (C++) abstract class AggregateDeclaration : ScopeDsymbol { Type type; /// - StorageClass storage_class; /// + STC storage_class; /// uint structsize; /// size of struct uint alignsize; /// size of struct for alignment purposes - VarDeclarations fields; /// VarDeclaration fields + VarDeclarations fields; /// VarDeclaration fields including flattened AnonDeclaration members Dsymbol deferred; /// any deferred semantic2() or semantic3() symbol /// specifies whether this is a D, C++, Objective-C or anonymous struct/class/interface @@ -154,7 +154,7 @@ extern (C++) abstract class AggregateDeclaration : ScopeDsymbol bool disableNew; /// disallow allocations using `new` Sizeok sizeok = Sizeok.none; /// set when structsize contains valid data - final extern (D) this(const ref Loc loc, Identifier id) + final extern (D) this(Loc loc, Identifier id) { super(loc, id); visibility = Visibility(Visibility.Kind.public_); @@ -187,70 +187,12 @@ extern (C++) abstract class AggregateDeclaration : ScopeDsymbol return fields.length - isNested() - (vthis2 !is null); } - /*************************************** - * Collect all instance fields, then determine instance size. - * Returns: - * false if failed to determine the size. - */ - extern (D) final bool determineSize(const ref Loc loc) - { - //printf("AggregateDeclaration::determineSize() %s, sizeok = %d\n", toChars(), sizeok); - - // The previous instance size finalizing had: - if (type.ty == Terror || errors) - return false; // failed already - if (sizeok == Sizeok.done) - return true; // succeeded - - if (!members) - { - .error(loc, "%s `%s` unknown size", kind, toPrettyChars); - return false; - } - - if (_scope) - dsymbolSemantic(this, null); - - // Determine the instance size of base class first. - if (auto cd = isClassDeclaration()) - { - cd = cd.baseClass; - if (cd && !cd.determineSize(loc)) - goto Lfail; - } - - // Determine instance fields when sizeok == Sizeok.none - if (!this.determineFields()) - goto Lfail; - if (sizeok != Sizeok.done) - finalizeSize(); - - // this aggregate type has: - if (type.ty == Terror) - return false; // marked as invalid during the finalizing. - if (sizeok == Sizeok.done) - return true; // succeeded to calculate instance size. - - Lfail: - // There's unresolvable forward reference. - if (type != Type.terror) - error(loc, "%s `%s` no size because of forward reference", kind, toPrettyChars); - // Don't cache errors from speculative semantic, might be resolvable later. - // https://issues.dlang.org/show_bug.cgi?id=16574 - if (!global.gag) - { - type = Type.terror; - errors = true; - } - return false; - } - abstract void finalizeSize(); - override final uinteger_t size(const ref Loc loc) + override final uinteger_t size(Loc loc) { //printf("+AggregateDeclaration::size() %s, scope = %p, sizeok = %d\n", toChars(), _scope, sizeok); - bool ok = determineSize(loc); + bool ok = determineSize(this, loc); //printf("-AggregateDeclaration::size() %s, scope = %p, sizeok = %d\n", toChars(), _scope, sizeok); return ok ? structsize : SIZE_INVALID; } @@ -336,161 +278,6 @@ extern (C++) abstract class AggregateDeclaration : ScopeDsymbol return errors; } - /*************************************** - * Fill out remainder of elements[] with default initializers for fields[]. - * Params: - * loc = location - * elements = explicit arguments which given to construct object. - * ctorinit = true if the elements will be used for default initialization. - * Returns: - * false if any errors occur. - * Otherwise, returns true and the missing arguments will be pushed in elements[]. - */ - final bool fill(const ref Loc loc, ref Expressions elements, bool ctorinit) - { - //printf("AggregateDeclaration::fill() %s\n", toChars()); - assert(sizeok == Sizeok.done); - const nfields = nonHiddenFields(); - bool errors = false; - - size_t dim = elements.length; - elements.setDim(nfields); - foreach (size_t i; dim .. nfields) - elements[i] = null; - - // Fill in missing any elements with default initializers - foreach (i; 0 .. nfields) - { - if (elements[i]) - continue; - - auto vd = fields[i]; - auto vx = vd; - if (vd._init && vd._init.isVoidInitializer()) - vx = null; - - // Find overlapped fields with the hole [vd.offset .. vd.offset.size()]. - size_t fieldi = i; - foreach (j; 0 .. nfields) - { - if (i == j) - continue; - auto v2 = fields[j]; - if (!vd.isOverlappedWith(v2)) - continue; - - if (elements[j]) - { - vx = null; - break; - } - if (v2._init && v2._init.isVoidInitializer()) - continue; - - version (all) - { - /* Prefer first found non-void-initialized field - * union U { int a; int b = 2; } - * U u; // Error: overlapping initialization for field a and b - */ - if (!vx) - { - vx = v2; - fieldi = j; - } - else if (v2._init) - { - .error(loc, "overlapping initialization for field `%s` and `%s`", v2.toChars(), vd.toChars()); - errors = true; - } - } - else - { - // fixes https://issues.dlang.org/show_bug.cgi?id=1432 by enabling this path always - - /* Prefer explicitly initialized field - * union U { int a; int b = 2; } - * U u; // OK (u.b == 2) - */ - if (!vx || !vx._init && v2._init) - { - vx = v2; - fieldi = j; - } - else if (vx != vd && !vx.isOverlappedWith(v2)) - { - // Both vx and v2 fills vd, but vx and v2 does not overlap - } - else if (vx._init && v2._init) - { - .error(loc, "overlapping default initialization for field `%s` and `%s`", - v2.toChars(), vd.toChars()); - errors = true; - } - else - assert(vx._init || !vx._init && !v2._init); - } - } - if (vx) - { - Expression e; - if (vx.type.size() == 0) - { - e = null; - } - else if (vx._init) - { - assert(!vx._init.isVoidInitializer()); - if (vx.inuse) // https://issues.dlang.org/show_bug.cgi?id=18057 - { - .error(loc, "%s `%s` recursive initialization of field", vx.kind(), vx.toPrettyChars()); - errors = true; - } - else - e = vx.getConstInitializer(false); - } - else - { - if ((vx.storage_class & STC.nodefaultctor) && !ctorinit) - { - .error(loc, "field `%s.%s` must be initialized because it has no default constructor", - type.toChars(), vx.toChars()); - errors = true; - } - /* https://issues.dlang.org/show_bug.cgi?id=12509 - * Get the element of static array type. - */ - Type telem = vx.type; - if (telem.ty == Tsarray) - { - /* We cannot use Type::baseElemOf() here. - * If the bottom of the Tsarray is an enum type, baseElemOf() - * will return the base of the enum, and its default initializer - * would be different from the enum's. - */ - TypeSArray tsa; - while ((tsa = telem.toBasetype().isTypeSArray()) !is null) - telem = tsa.next; - if (telem.ty == Tvoid) - telem = Type.tuns8.addMod(telem.mod); - } - if (telem.needsNested() && ctorinit) - e = telem.defaultInit(loc); - else - e = telem.defaultInitLiteral(loc); - } - elements[fieldi] = e; - } - } - foreach (e; elements) - { - if (e && e.op == EXP.error) - return false; - } - - return !errors; - } - override final Type getType() { /* Apply storage classes to forward references. (Issue 22254) @@ -660,7 +447,8 @@ extern (C++) abstract class AggregateDeclaration : ScopeDsymbol s.isTemplateDeclaration() || s.isOverloadSet())) { - .error(s.loc, "%s `%s` is not a constructor; identifiers starting with `__` are reserved for the implementation", s.kind(), s.toPrettyChars()); + error(s.loc, "%s name `__ctor` is not allowed", s.kind); + errorSupplemental(s.loc, "identifiers starting with `__` are reserved for internal use"); errors = true; s = null; } @@ -710,11 +498,6 @@ version (IN_LLVM) {} else void* sinit; /// initializer symbol } - override final inout(AggregateDeclaration) isAggregateDeclaration() inout - { - return this; - } - override void accept(Visitor v) { v.visit(this); @@ -796,6 +579,7 @@ public uint alignmember(structalign_t alignment, uint memalignsize, uint offset) /**************************************** * Place a field (mem) into an aggregate (agg), which can be a struct, union or class * Params: + * loc = source location for error messages * nextoffset = location just past the end of the previous field in the aggregate. * Updated to be just past the end of this field to be placed, i.e. the future nextoffset * memsize = size of field @@ -808,8 +592,8 @@ public uint alignmember(structalign_t alignment, uint memalignsize, uint offset) * aligned offset to place field at * */ -public uint placeField(ref uint nextoffset, uint memsize, uint memalignsize, - structalign_t alignment, ref uint aggsize, ref uint aggalignsize, bool isunion) @safe pure nothrow +public uint placeField(Loc loc, ref uint nextoffset, uint memsize, uint memalignsize, + structalign_t alignment, ref uint aggsize, ref uint aggalignsize, bool isunion) @trusted nothrow { static if (0) { @@ -832,7 +616,12 @@ public uint placeField(ref uint nextoffset, uint memsize, uint memalignsize, bool overflow; const sz = addu(memsize, actualAlignment, overflow); addu(ofs, sz, overflow); - if (overflow) assert(0); + if (overflow) + { + error(loc, "max object size %u exceeded from adding field size %u + alignment adjustment %u + field offset %u when placing field in aggregate", + uint.max, memsize, actualAlignment, ofs); + return 0; + } // Skip no-op for noreturn without custom aligment if (memalignsize != 0 || !alignment.isDefault()) diff --git a/dmd/aggregate.h b/dmd/aggregate.h index 45b1840d1b..cb114f8b98 100644 --- a/dmd/aggregate.h +++ b/dmd/aggregate.h @@ -1,6 +1,6 @@ /* Compiler implementation of the D programming language - * Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * written by Walter Bright * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. @@ -45,6 +45,9 @@ namespace dmd { FuncDeclaration *search_toString(StructDeclaration *sd); void semanticTypeInfoMembers(StructDeclaration *sd); + bool fill(StructDeclaration* sd, Loc loc, Expressions &elements, bool ctorinit); + bool isFuncHidden(ClassDeclaration* cd, FuncDeclaration* fd); + Dsymbol* vtblSymbol(ClassDeclaration *cd); } enum class ClassKind : uint8_t @@ -118,8 +121,7 @@ class AggregateDeclaration : public ScopeDsymbol virtual Scope *newScope(Scope *sc); virtual void finalizeSize() = 0; - uinteger_t size(const Loc &loc) override final; - bool fill(const Loc &loc, Expressions &elements, bool ctorinit); + uinteger_t size(Loc loc) override final; Type *getType() override final; bool isDeprecated() const override final; // is aggregate deprecated? bool isNested() const; @@ -137,7 +139,6 @@ class AggregateDeclaration : public ScopeDsymbol void *sinit; #endif - AggregateDeclaration *isAggregateDeclaration() override final { return this; } void accept(Visitor *v) override { v->visit(this); } }; @@ -170,7 +171,7 @@ class StructDeclaration : public AggregateDeclaration private: uint16_t bitFields; public: - static StructDeclaration *create(const Loc &loc, Identifier *id, bool inObject); + static StructDeclaration *create(Loc loc, Identifier *id, bool inObject); StructDeclaration *syntaxCopy(Dsymbol *s) override; const char *kind() const override; void finalizeSize() override final; @@ -195,7 +196,6 @@ class StructDeclaration : public AggregateDeclaration bool requestTypeInfo(bool v); #endif - StructDeclaration *isStructDeclaration() override final { return this; } void accept(Visitor *v) override { v->visit(this); } unsigned numArgTypes() const; @@ -209,7 +209,6 @@ class UnionDeclaration final : public StructDeclaration UnionDeclaration *syntaxCopy(Dsymbol *s) override; const char *kind() const override; - UnionDeclaration *isUnionDeclaration() override { return this; } void accept(Visitor *v) override { v->visit(this); } }; @@ -284,7 +283,7 @@ class ClassDeclaration : public AggregateDeclaration Symbol *cpp_type_info_ptr_sym; // cached instance of class Id.cpp_type_info_ptr #endif - static ClassDeclaration *create(const Loc &loc, Identifier *id, BaseClasses *baseclasses, Dsymbols *members, bool inObject); + 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; @@ -296,7 +295,6 @@ class ClassDeclaration : public AggregateDeclaration bool isBaseInfoComplete(); void finalizeSize() override; bool hasMonitor(); - bool isFuncHidden(FuncDeclaration *fd); bool isCOMclass() const; virtual bool isCOMinterface() const; bool isCPPclass() const; @@ -310,10 +308,8 @@ class ClassDeclaration : public AggregateDeclaration #if !IN_LLVM // Back end Dsymbol *vtblsym; - Dsymbol *vtblSymbol(); #endif - ClassDeclaration *isClassDeclaration() override final { return (ClassDeclaration *)this; } void accept(Visitor *v) override { v->visit(this); } }; @@ -328,6 +324,5 @@ class InterfaceDeclaration final : public ClassDeclaration bool isCPPinterface() const override; bool isCOMinterface() const override; - InterfaceDeclaration *isInterfaceDeclaration() override { return this; } void accept(Visitor *v) override { v->visit(this); } }; diff --git a/dmd/aliasthis.d b/dmd/aliasthis.d index 0e063caa2f..632cf95e80 100644 --- a/dmd/aliasthis.d +++ b/dmd/aliasthis.d @@ -3,12 +3,12 @@ * * Specification: $(LINK2 https://dlang.org/spec/class.html#alias-this, Alias This) * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/aliasthis.d, _aliasthis.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/aliasthis.d, _aliasthis.d) * Documentation: https://dlang.org/phobos/dmd_aliasthis.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/aliasthis.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/aliasthis.d */ module dmd.aliasthis; @@ -31,9 +31,9 @@ extern (C++) final class AliasThis : Dsymbol /// Whether this `alias this` is deprecated or not bool isDeprecated_; - extern (D) this(const ref Loc loc, Identifier ident) @safe + extern (D) this(Loc loc, Identifier ident) @safe { - super(loc, null); // it's anonymous (no identifier) + super(DSYM.aliasThis, loc, null); // it's anonymous (no identifier) this.ident = ident; } diff --git a/dmd/aliasthis.h b/dmd/aliasthis.h index 88ba35319b..249da70fd6 100644 --- a/dmd/aliasthis.h +++ b/dmd/aliasthis.h @@ -1,6 +1,6 @@ /* Compiler implementation of the D programming language - * Copyright (C) 2009-2024 by The D Language Foundation, All Rights Reserved + * Copyright (C) 2009-2025 by The D Language Foundation, All Rights Reserved * written by Walter Bright * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. diff --git a/dmd/argtypes.h b/dmd/argtypes.h index 8d018ea324..968bf5d627 100644 --- a/dmd/argtypes.h +++ b/dmd/argtypes.h @@ -1,6 +1,6 @@ /* Compiler implementation of the D programming language - * Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * written by Walter Bright * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. diff --git a/dmd/argtypes_aarch64.d b/dmd/argtypes_aarch64.d index f1cda8d18d..545f11edfb 100644 --- a/dmd/argtypes_aarch64.d +++ b/dmd/argtypes_aarch64.d @@ -1,12 +1,12 @@ /** * Break down a D type into basic (register) types for the AArch64 ABI. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: Martin Kinkelin * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/argtypes_aarch64.d, _argtypes_aarch64.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/argtypes_aarch64.d, _argtypes_aarch64.d) * Documentation: https://dlang.org/phobos/dmd_argtypes_aarch64.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/argtypes_aarch64.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/argtypes_aarch64.d */ module dmd.argtypes_aarch64; @@ -38,7 +38,7 @@ TypeTuple toArgTypes_aarch64(Type t) return null; Type tb = t.toBasetype(); - const isAggregate = tb.ty == Tstruct || tb.ty == Tsarray || tb.ty == Tarray || tb.ty == Tdelegate || tb.iscomplex(); + const isAggregate = tb.ty == Tstruct || tb.isStaticOrDynamicArray() || tb.ty == Tdelegate || tb.isComplex(); if (!isAggregate) return new TypeTuple(t); @@ -88,7 +88,7 @@ TypeTuple toArgTypes_aarch64(Type t) bool isHFVA(Type t, int maxNumElements = 4, Type* rewriteType = null) { t = t.toBasetype(); - if ((t.ty != Tstruct && t.ty != Tsarray && !t.iscomplex()) || !isPOD(t)) + if ((t.ty != Tstruct && t.ty != Tsarray && !t.isComplex()) || !isPOD(t)) return false; Type fundamentalType; @@ -165,12 +165,12 @@ size_t getNestedHFVA(Type t, ref Type fundamentalType) thisFundamentalType = t; N = 1; } - else if (t.isfloating()) // incl. imaginary and complex + else if (t.isFloating()) // incl. imaginary and complex { auto ftSize = t.size(); N = 1; - if (t.iscomplex()) + if (t.isComplex()) { ftSize /= 2; N = 2; diff --git a/dmd/argtypes_sysv_x64.d b/dmd/argtypes_sysv_x64.d index 47c2cf930b..9cd4e514a6 100644 --- a/dmd/argtypes_sysv_x64.d +++ b/dmd/argtypes_sysv_x64.d @@ -1,12 +1,12 @@ /** * Break down a D type into basic (register) types for the x86_64 System V ABI. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: Martin Kinkelin * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/argtypes_sysv_x64.d, _argtypes_sysv_x64.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/argtypes_sysv_x64.d, _argtypes_sysv_x64.d) * Documentation: https://dlang.org/phobos/dmd_argtypes_sysv_x64.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/argtypes_sysv_x64.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/argtypes_sysv_x64.d */ module dmd.argtypes_sysv_x64; diff --git a/dmd/argtypes_x86.d b/dmd/argtypes_x86.d index 179831e138..9f5adcf21b 100644 --- a/dmd/argtypes_x86.d +++ b/dmd/argtypes_x86.d @@ -1,12 +1,12 @@ /** * Break down a D type into basic (register) types for the 32-bit x86 ABI. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/argtypes_x86.d, _argtypes_x86.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/argtypes_x86.d, _argtypes_x86.d) * Documentation: https://dlang.org/phobos/dmd_argtypes_x86.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/argtypes_x86.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/argtypes_x86.d */ module dmd.argtypes_x86; @@ -135,8 +135,7 @@ TypeTuple toArgTypes_x86(Type t) { if (t2) return twoTypes(t1, t2); - else - return oneType(t1); + return oneType(t1); } else return memory(); @@ -211,12 +210,12 @@ TypeTuple toArgTypes_x86(Type t) if (t1.ty == Tfloat32 && t2.ty == Tfloat32 && offset2 == 4) return Type.tfloat64; // Merging floating and non-floating types produces the non-floating type - if (t1.isfloating()) + if (t1.isFloating()) { - if (!t2.isfloating()) + if (!t2.isFloating()) t1 = mergeFloatToInt(t1); } - else if (t2.isfloating()) + else if (t2.isFloating()) t2 = mergeFloatToInt(t2); Type t; // Pick type with larger size diff --git a/dmd/arrayop.d b/dmd/arrayop.d index e30160d72b..7d25e18512 100644 --- a/dmd/arrayop.d +++ b/dmd/arrayop.d @@ -3,12 +3,12 @@ * * Specification: $(LINK2 https://dlang.org/spec/arrays.html#array-operations, Array Operations) * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/arrayop.d, _arrayop.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/arrayop.d, _arrayop.d) * Documentation: https://dlang.org/phobos/dmd_arrayop.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/arrayop.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/arrayop.d */ module dmd.arrayop; @@ -44,12 +44,12 @@ bool isArrayOpValid(Expression e) if (e.op == EXP.arrayLiteral) { Type t = e.type.toBasetype(); - while (t.ty == Tarray || t.ty == Tsarray) + while (t.isStaticOrDynamicArray()) t = t.nextOf().toBasetype(); return (t.ty != Tvoid); } Type tb = e.type.toBasetype(); - if (tb.ty == Tarray || tb.ty == Tsarray) + if (tb.isStaticOrDynamicArray()) { if (isUnaArrayOp(e.op)) { @@ -80,7 +80,7 @@ bool isNonAssignmentArrayOp(Expression e) return isNonAssignmentArrayOp(e.isSliceExp().e1); Type tb = e.type.toBasetype(); - if (tb.ty == Tarray || tb.ty == Tsarray) + if (tb.isStaticOrDynamicArray()) { return (isUnaArrayOp(e.op) || isBinArrayOp(e.op)); } @@ -119,7 +119,7 @@ Expression arrayOp(BinExp e, Scope* sc) { //printf("BinExp.arrayOp() %s\n", e.toChars()); Type tb = e.type.toBasetype(); - assert(tb.ty == Tarray || tb.ty == Tsarray); + assert(tb.isStaticOrDynamicArray()); Type tbn = tb.nextOf().toBasetype(); if (tbn.ty == Tvoid) { @@ -287,9 +287,8 @@ bool isUnaArrayOp(EXP op) @safe case EXP.tilde: return true; default: - break; + return false; } - return false; } /*********************************************** @@ -310,9 +309,8 @@ bool isBinArrayOp(EXP op) @safe case EXP.pow: return true; default: - break; + return false; } - return false; } /*********************************************** @@ -333,9 +331,8 @@ bool isBinAssignArrayOp(EXP op) @safe case EXP.powAssign: return true; default: - break; + return false; } - return false; } /*********************************************** @@ -349,7 +346,7 @@ bool isArrayOpOperand(Expression e) if (e.op == EXP.arrayLiteral) { Type t = e.type.toBasetype(); - while (t.ty == Tarray || t.ty == Tsarray) + while (t.isStaticOrDynamicArray()) t = t.nextOf().toBasetype(); return (t.ty != Tvoid); } diff --git a/dmd/arraytypes.d b/dmd/arraytypes.d index feab9a49c7..2cd446ea5e 100644 --- a/dmd/arraytypes.d +++ b/dmd/arraytypes.d @@ -1,12 +1,12 @@ /** * Provide aliases for arrays of certain declarations or statements. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/arraytypes.d, _arraytypes.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/arraytypes.d, _arraytypes.d) * Documentation: https://dlang.org/phobos/dmd_arraytypes.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/arraytypes.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/arraytypes.d */ module dmd.arraytypes; diff --git a/dmd/arraytypes.h b/dmd/arraytypes.h index 779642813f..28165f3ed8 100644 --- a/dmd/arraytypes.h +++ b/dmd/arraytypes.h @@ -1,6 +1,6 @@ /* Compiler implementation of the D programming language - * Copyright (C) 2006-2024 by The D Language Foundation, All Rights Reserved + * Copyright (C) 2006-2025 by The D Language Foundation, All Rights Reserved * written by Walter Bright * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. diff --git a/dmd/ast_node.d b/dmd/ast_node.d index c8c95fa078..ad49169947 100644 --- a/dmd/ast_node.d +++ b/dmd/ast_node.d @@ -1,12 +1,12 @@ /** * Defines the base class for all nodes which are part of the AST. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/ast_node.d, _ast_node.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/ast_node.d, _ast_node.d) * Documentation: https://dlang.org/phobos/dmd_ast_node.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/ast_node.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/ast_node.d */ module dmd.ast_node; diff --git a/dmd/ast_node.h b/dmd/ast_node.h index db8608e7cd..0782d7eb46 100644 --- a/dmd/ast_node.h +++ b/dmd/ast_node.h @@ -1,6 +1,6 @@ /* Compiler implementation of the D programming language - * Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * written by Walter Bright * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. diff --git a/dmd/astenums.d b/dmd/astenums.d index 1e30b9f8fc..b71b6c40ae 100644 --- a/dmd/astenums.d +++ b/dmd/astenums.d @@ -1,11 +1,11 @@ /** * Defines enums common to dmd and dmd as parse library. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/astenums.d, _astenums.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/astenums.d, _astenums.d) * Documentation: https://dlang.org/phobos/dmd_astenums.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/astenums.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/astenums.d */ module dmd.astenums; @@ -50,7 +50,7 @@ alias MOD = ubyte; enum STC : ulong // transfer changes to declaration.h { - undefined_ = 0, + none = 0, static_ = 1, /// `static` extern_ = 2, /// `extern` @@ -143,6 +143,9 @@ enum STC : ulong // transfer changes to declaration.h } +// Alias for C++ interface functions which use plain integer instead of enum class, +// since C++ enum class doesn't support | & and conversion to bool. Maybe this can +// be refactored to a struct with operator overloads or bit fields at some point. alias StorageClass = ulong; /******** @@ -302,7 +305,7 @@ enum ThreeState : ubyte enum TRUST : ubyte { default_ = 0, - system = 1, // @system (same as TRUST.default) + system = 1, // @system (same as TRUST.default_ unless feature "safer" is enabled) trusted = 2, // @trusted safe = 3, // @safe } diff --git a/dmd/asttypename.d b/dmd/asttypename.d index da30c2f0e3..8642288f58 100644 --- a/dmd/asttypename.d +++ b/dmd/asttypename.d @@ -1,12 +1,12 @@ /** * Development utility for printing AST nodes by their internal name, instead of as D source code. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: Stefan Koch * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/asttypename.d, _asttypename.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/asttypename.d, _asttypename.d) * Documentation: https://dlang.org/phobos/dmd_asttypename.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/asttypename.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/asttypename.d */ module dmd.asttypename; diff --git a/dmd/attrib.d b/dmd/attrib.d index ca6682b24b..1bfe7902a9 100644 --- a/dmd/attrib.d +++ b/dmd/attrib.d @@ -14,12 +14,12 @@ * - Protection (`private`, `public`) * - Deprecated declarations (`@deprecated`) * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/attrib.d, _attrib.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/attrib.d, _attrib.d) * Documentation: https://dlang.org/phobos/dmd_attrib.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/attrib.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/attrib.d */ module dmd.attrib; @@ -32,8 +32,7 @@ import dmd.declaration; import dmd.dmodule; import dmd.dscope; import dmd.dsymbol; -import dmd.dsymbolsem; -import dmd.errors; +import dmd.dsymbolsem : include; import dmd.expression; import dmd.func; import dmd.globals; @@ -58,36 +57,29 @@ extern (C++) abstract class AttribDeclaration : Dsymbol extern (D) this(Dsymbols* decl) @safe { + super(DSYM.attribDeclaration); this.decl = decl; } - extern (D) this(const ref Loc loc, Dsymbols* decl) @safe + extern (D) this(Loc loc, Dsymbols* decl) @safe { - super(loc, null); + super(DSYM.attribDeclaration, loc, null); this.decl = decl; } - extern (D) this(const ref Loc loc, Identifier ident, Dsymbols* decl) @safe + extern (D) this(Loc loc, Identifier ident, Dsymbols* decl) @safe { - super(loc, ident); + super(DSYM.attribDeclaration, loc, ident); this.decl = decl; } - Dsymbols* include(Scope* sc) - { - if (errors) - return null; - - return decl; - } - /**************************************** * Create a new scope if one or more given attributes * are different from the sc's. * If the returned scope != sc, the caller should pop * the scope after it used. */ - extern (D) static Scope* createNewScope(Scope* sc, StorageClass stc, LINK linkage, + extern (D) static Scope* createNewScope(Scope* sc, STC stc, LINK linkage, CPPMANGLE cppmangle, Visibility visibility, int explicitVisibility, AlignDeclaration aligndecl, PragmaDeclaration inlining) { @@ -113,48 +105,14 @@ extern (C++) abstract class AttribDeclaration : Dsymbol return sc2; } - /**************************************** - * A hook point to supply scope for members. - * addMember, setScope, importAll, semantic, semantic2 and semantic3 will use this. - */ - Scope* newScope(Scope* sc) - { - return sc; - } - - override void addComment(const(char)* comment) - { - //printf("AttribDeclaration::addComment %s\n", comment); - if (comment) - { - include(null).foreachDsymbol( s => s.addComment(comment) ); - } - } - override const(char)* kind() const { return "attribute"; } - override bool oneMember(out Dsymbol ps, Identifier ident) - { - Dsymbols* d = include(null); - return Dsymbol.oneMembers(d, ps, ident); - } - override final bool hasPointers() { - return include(null).foreachDsymbol( (s) { return s.hasPointers(); } ) != 0; - } - - override final bool hasStaticCtorOrDtor() - { - return include(null).foreachDsymbol( (s) { return s.hasStaticCtorOrDtor(); } ) != 0; - } - - override final void checkCtorConstInit() - { - include(null).foreachDsymbol( s => s.checkCtorConstInit() ); + return this.include(null).foreachDsymbol( (s) { return s.hasPointers(); } ) != 0; } /**************************************** @@ -164,11 +122,6 @@ extern (C++) abstract class AttribDeclaration : Dsymbol objc.addSymbols(this, classes, categories); } - override inout(AttribDeclaration) isAttribDeclaration() inout pure @safe - { - return this; - } - override void accept(Visitor v) { v.visit(this); @@ -182,18 +135,20 @@ extern (C++) abstract class AttribDeclaration : Dsymbol */ extern (C++) class StorageClassDeclaration : AttribDeclaration { - StorageClass stc; + STC stc; - extern (D) this(StorageClass stc, Dsymbols* decl) @safe + extern (D) this(STC stc, Dsymbols* decl) @safe { super(decl); this.stc = stc; + this.dsym = DSYM.storageClassDeclaration; } - extern (D) this(const ref Loc loc, StorageClass stc, Dsymbols* decl) @safe + extern (D) this(Loc loc, STC stc, Dsymbols* decl) @safe { super(loc, decl); this.stc = stc; + this.dsym = DSYM.storageClassDeclaration; } override StorageClassDeclaration syntaxCopy(Dsymbol s) @@ -202,60 +157,6 @@ extern (C++) class StorageClassDeclaration : AttribDeclaration return new StorageClassDeclaration(stc, Dsymbol.arraySyntaxCopy(decl)); } - override Scope* newScope(Scope* sc) - { - StorageClass scstc = sc.stc; - /* These sets of storage classes are mutually exclusive, - * so choose the innermost or most recent one. - */ - if (stc & (STC.auto_ | STC.scope_ | STC.static_ | STC.extern_ | STC.manifest)) - scstc &= ~(STC.auto_ | STC.scope_ | STC.static_ | STC.extern_ | STC.manifest); - if (stc & (STC.auto_ | STC.scope_ | STC.static_ | STC.manifest | STC.gshared)) - scstc &= ~(STC.auto_ | STC.scope_ | STC.static_ | STC.manifest | STC.gshared); - if (stc & (STC.const_ | STC.immutable_ | STC.manifest)) - scstc &= ~(STC.const_ | STC.immutable_ | STC.manifest); - if (stc & (STC.gshared | STC.shared_)) - scstc &= ~(STC.gshared | STC.shared_); - if (stc & (STC.safe | STC.trusted | STC.system)) - scstc &= ~(STC.safe | STC.trusted | STC.system); - scstc |= stc; - //printf("scstc = x%llx\n", scstc); - return createNewScope(sc, scstc, sc.linkage, sc.cppmangle, - sc.visibility, sc.explicitVisibility, sc.aligndecl, sc.inlining); - } - - override final bool oneMember(out Dsymbol ps, Identifier ident) - { - bool t = Dsymbol.oneMembers(decl, ps, ident); - if (t && ps) - { - /* This is to deal with the following case: - * struct Tick { - * template to(T) { const T to() { ... } } - * } - * For eponymous function templates, the 'const' needs to get attached to 'to' - * before the semantic analysis of 'to', so that template overloading based on the - * 'this' pointer can be successful. - */ - FuncDeclaration fd = ps.isFuncDeclaration(); - if (fd) - { - /* Use storage_class2 instead of storage_class otherwise when we do .di generation - * we'll wind up with 'const const' rather than 'const'. - */ - /* Don't think we need to worry about mutually exclusive storage classes here - */ - fd.storage_class2 |= stc; - } - } - return t; - } - - override inout(StorageClassDeclaration) isStorageClassDeclaration() inout - { - return this; - } - override void accept(Visitor v) { v.visit(this); @@ -287,25 +188,6 @@ extern (C++) final class DeprecatedDeclaration : StorageClassDeclaration return new DeprecatedDeclaration(msg.syntaxCopy(), Dsymbol.arraySyntaxCopy(decl)); } - /** - * Provides a new scope with `STC.deprecated_` and `Scope.depdecl` set - * - * Calls `StorageClassDeclaration.newScope` (as it must be called or copied - * in any function overriding `newScope`), then set the `Scope`'s depdecl. - * - * Returns: - * Always a new scope, to use for this `DeprecatedDeclaration`'s members. - */ - override Scope* newScope(Scope* sc) - { - auto scx = super.newScope(sc); - // The enclosing scope is deprecated as well - if (scx == sc) - scx = sc.push(); - scx.depdecl = this; - return scx; - } - override void accept(Visitor v) { v.visit(this); @@ -322,14 +204,15 @@ extern (C++) final class LinkDeclaration : AttribDeclaration { LINK linkage; /// either explicitly set or `default_` - extern (D) this(const ref Loc loc, LINK linkage, Dsymbols* decl) @safe + extern (D) this(Loc loc, LINK linkage, Dsymbols* decl) @safe { super(loc, null, decl); //printf("LinkDeclaration(linkage = %d, decl = %p)\n", linkage, decl); this.linkage = linkage; + this.dsym = DSYM.linkDeclaration; } - static LinkDeclaration create(const ref Loc loc, LINK p, Dsymbols* decl) @safe + static LinkDeclaration create(Loc loc, LINK p, Dsymbols* decl) @safe { return new LinkDeclaration(loc, p, decl); } @@ -340,22 +223,6 @@ extern (C++) final class LinkDeclaration : AttribDeclaration return new LinkDeclaration(loc, linkage, Dsymbol.arraySyntaxCopy(decl)); } - override Scope* newScope(Scope* sc) - { - return createNewScope(sc, sc.stc, this.linkage, sc.cppmangle, sc.visibility, sc.explicitVisibility, - sc.aligndecl, sc.inlining); - } - - override const(char)* toChars() const - { - return toString().ptr; - } - - extern(D) override const(char)[] toString() const - { - return "extern ()"; - } - override void accept(Visitor v) { v.visit(this); @@ -374,11 +241,12 @@ extern (C++) final class CPPMangleDeclaration : AttribDeclaration { CPPMANGLE cppmangle; - extern (D) this(const ref Loc loc, CPPMANGLE cppmangle, Dsymbols* decl) @safe + extern (D) this(Loc loc, CPPMANGLE cppmangle, Dsymbols* decl) @safe { super(loc, null, decl); //printf("CPPMangleDeclaration(cppmangle = %d, decl = %p)\n", cppmangle, decl); this.cppmangle = cppmangle; + this.dsym = DSYM.cppMangleDeclaration; } override CPPMangleDeclaration syntaxCopy(Dsymbol s) @@ -387,22 +255,6 @@ extern (C++) final class CPPMangleDeclaration : AttribDeclaration return new CPPMangleDeclaration(loc, cppmangle, Dsymbol.arraySyntaxCopy(decl)); } - override Scope* newScope(Scope* sc) - { - return createNewScope(sc, sc.stc, LINK.cpp, cppmangle, sc.visibility, sc.explicitVisibility, - sc.aligndecl, sc.inlining); - } - - override const(char)* toChars() const - { - return toString().ptr; - } - - extern(D) override const(char)[] toString() const - { - return "extern ()"; - } - override void accept(Visitor v) { v.visit(this); @@ -437,21 +289,24 @@ extern (C++) final class CPPNamespaceDeclaration : AttribDeclaration /// CTFE-able expression, resolving to `TupleExp` or `StringExp` Expression exp; - extern (D) this(const ref Loc loc, Identifier ident, Dsymbols* decl) @safe + extern (D) this(Loc loc, Identifier ident, Dsymbols* decl) @safe { super(loc, ident, decl); + this.dsym = DSYM.cppNamespaceDeclaration; } - extern (D) this(const ref Loc loc, Expression exp, Dsymbols* decl) @safe + extern (D) this(Loc loc, Expression exp, Dsymbols* decl) @safe { super(loc, null, decl); + this.dsym = DSYM.cppNamespaceDeclaration; this.exp = exp; } - extern (D) this(const ref Loc loc, Identifier ident, Expression exp, Dsymbols* decl, + extern (D) this(Loc loc, Identifier ident, Expression exp, Dsymbols* decl, CPPNamespaceDeclaration parent) @safe { super(loc, ident, decl); + this.dsym = DSYM.cppNamespaceDeclaration; this.exp = exp; this.cppnamespace = parent; } @@ -463,34 +318,10 @@ extern (C++) final class CPPNamespaceDeclaration : AttribDeclaration this.loc, this.ident, this.exp, Dsymbol.arraySyntaxCopy(this.decl), this.cppnamespace); } - /** - * Returns: - * A copy of the parent scope, with `this` as `namespace` and C++ linkage - */ - override Scope* newScope(Scope* sc) - { - auto scx = sc.copy(); - scx.linkage = LINK.cpp; - scx.namespace = this; - return scx; - } - - override const(char)* toChars() const - { - return toString().ptr; - } - - extern(D) override const(char)[] toString() const - { - return "extern (C++, `namespace`)"; - } - override void accept(Visitor v) { v.visit(this); } - - override inout(CPPNamespaceDeclaration) isCPPNamespaceDeclaration() inout { return this; } } /*********************************************************** @@ -510,9 +341,10 @@ extern (C++) final class VisibilityDeclaration : AttribDeclaration * visibility = visibility attribute data * decl = declarations which are affected by this visibility attribute */ - extern (D) this(const ref Loc loc, Visibility visibility, Dsymbols* decl) @safe + extern (D) this(Loc loc, Visibility visibility, Dsymbols* decl) @safe { super(loc, null, decl); + this.dsym = DSYM.visibilityDeclaration; this.visibility = visibility; //printf("decl = %p\n", decl); } @@ -523,9 +355,10 @@ extern (C++) final class VisibilityDeclaration : AttribDeclaration * pkg_identifiers = list of identifiers for a qualified package name * decl = declarations which are affected by this visibility attribute */ - extern (D) this(const ref Loc loc, Identifier[] pkg_identifiers, Dsymbols* decl) + extern (D) this(Loc loc, Identifier[] pkg_identifiers, Dsymbols* decl) { super(loc, null, decl); + this.dsym = DSYM.visibilityDeclaration; this.visibility.kind = Visibility.Kind.package_; this.pkg_identifiers = pkg_identifiers; if (pkg_identifiers.length > 0) @@ -546,13 +379,6 @@ extern (C++) final class VisibilityDeclaration : AttribDeclaration return new VisibilityDeclaration(this.loc, visibility, Dsymbol.arraySyntaxCopy(decl)); } - override Scope* newScope(Scope* sc) - { - if (pkg_identifiers) - dsymbolSemantic(this, sc); - return createNewScope(sc, sc.stc, sc.linkage, sc.cppmangle, this.visibility, 1, sc.aligndecl, sc.inlining); - } - override const(char)* kind() const { return "visibility attribute"; @@ -566,11 +392,6 @@ extern (C++) final class VisibilityDeclaration : AttribDeclaration return buf.extractChars(); } - override inout(VisibilityDeclaration) isVisibilityDeclaration() inout - { - return this; - } - override void accept(Visitor v) { v.visit(this); @@ -592,9 +413,10 @@ extern (C++) final class AlignDeclaration : AttribDeclaration structalign_t salign; - extern (D) this(const ref Loc loc, Expression exp, Dsymbols* decl) + extern (D) this(Loc loc, Expression exp, Dsymbols* decl) { super(loc, null, decl); + this.dsym = DSYM.alignDeclaration; if (exp) { exps = new Expressions(); @@ -602,15 +424,17 @@ extern (C++) final class AlignDeclaration : AttribDeclaration } } - extern (D) this(const ref Loc loc, Expressions* exps, Dsymbols* decl) @safe + extern (D) this(Loc loc, Expressions* exps, Dsymbols* decl) @safe { super(loc, null, decl); + this.dsym = DSYM.alignDeclaration; this.exps = exps; } - extern (D) this(const ref Loc loc, structalign_t salign, Dsymbols* decl) @safe + extern (D) this(Loc loc, structalign_t salign, Dsymbols* decl) @safe { super(loc, null, decl); + this.dsym = DSYM.alignDeclaration; this.salign = salign; } @@ -622,11 +446,6 @@ extern (C++) final class AlignDeclaration : AttribDeclaration Dsymbol.arraySyntaxCopy(decl)); } - override Scope* newScope(Scope* sc) - { - return createNewScope(sc, sc.stc, sc.linkage, sc.cppmangle, sc.visibility, sc.explicitVisibility, this, sc.inlining); - } - override void accept(Visitor v) { v.visit(this); @@ -644,9 +463,10 @@ extern (C++) final class AnonDeclaration : AttribDeclaration uint anonstructsize; /// size of anonymous struct uint anonalignsize; /// size of anonymous struct for alignment purposes - extern (D) this(const ref Loc loc, bool isunion, Dsymbols* decl) @safe + extern (D) this(Loc loc, bool isunion, Dsymbols* decl) @safe { super(loc, null, decl); + this.dsym = DSYM.anonDeclaration; this.isunion = isunion; } @@ -661,11 +481,6 @@ extern (C++) final class AnonDeclaration : AttribDeclaration return (isunion ? "anonymous union" : "anonymous struct"); } - override inout(AnonDeclaration) isAnonDeclaration() inout - { - return this; - } - override void accept(Visitor v) { v.visit(this); @@ -682,9 +497,10 @@ extern (C++) final class PragmaDeclaration : AttribDeclaration { Expressions* args; /// parameters of this pragma - extern (D) this(const ref Loc loc, Identifier ident, Expressions* args, Dsymbols* decl) @safe + extern (D) this(Loc loc, Identifier ident, Expressions* args, Dsymbols* decl) @safe { super(loc, ident, decl); + this.dsym = DSYM.pragmaDeclaration; this.args = args; } @@ -695,38 +511,6 @@ extern (C++) final class PragmaDeclaration : AttribDeclaration return new PragmaDeclaration(loc, ident, Expression.arraySyntaxCopy(args), Dsymbol.arraySyntaxCopy(decl)); } - override Scope* newScope(Scope* sc) - { - if (ident == Id.Pinline) - { - // We keep track of this pragma inside scopes, - // then it's evaluated on demand in function semantic - return createNewScope(sc, sc.stc, sc.linkage, sc.cppmangle, sc.visibility, sc.explicitVisibility, sc.aligndecl, this); - } - if (IN_LLVM && ident == Id.LDC_profile_instr) - { - import gen.dpragma : DtoCheckProfileInstrPragma; - - bool emitInstr = true; - if (!args || args.length != 1 || !DtoCheckProfileInstrPragma((*args)[0], emitInstr)) - { - error(loc, "pragma(LDC_profile_instr, true or false) expected"); - (*args)[0] = ErrorExp.get(); - } - else - { - // Only create a new scope if the emitInstrumentation flag is changed - if (sc.emitInstrumentation != emitInstr) - { - auto newscope = sc.copy(); - newscope.emitInstrumentation = emitInstr; - return newscope; - } - } - } - return sc; - } - override const(char)* kind() const { return "pragma"; @@ -749,9 +533,10 @@ extern (C++) class ConditionalDeclaration : AttribDeclaration Condition condition; /// condition deciding whether decl or elsedecl applies Dsymbols* elsedecl; /// array of Dsymbol's for else block - extern (D) this(const ref Loc loc, Condition condition, Dsymbols* decl, Dsymbols* elsedecl) @safe + extern (D) this(Loc loc, Condition condition, Dsymbols* decl, Dsymbols* elsedecl) @safe { super(loc, null, decl); + this.dsym = DSYM.conditionalDeclaration; //printf("ConditionalDeclaration::ConditionalDeclaration()\n"); this.condition = condition; this.elsedecl = elsedecl; @@ -763,48 +548,6 @@ extern (C++) class ConditionalDeclaration : AttribDeclaration return new ConditionalDeclaration(loc, condition.syntaxCopy(), Dsymbol.arraySyntaxCopy(decl), Dsymbol.arraySyntaxCopy(elsedecl)); } - override final bool oneMember(out Dsymbol ps, Identifier ident) - { - //printf("ConditionalDeclaration::oneMember(), inc = %d\n", condition.inc); - if (condition.inc != Include.notComputed) - { - Dsymbols* d = condition.include(null) ? decl : elsedecl; - return Dsymbol.oneMembers(d, ps, ident); - } - else - { - bool res = (Dsymbol.oneMembers(decl, ps, ident) && ps is null && Dsymbol.oneMembers(elsedecl, ps, ident) && ps is null); - ps = null; - return res; - } - } - - // Decide if 'then' or 'else' code should be included - override Dsymbols* include(Scope* sc) - { - //printf("ConditionalDeclaration::include(sc = %p) scope = %p\n", sc, _scope); - - if (errors) - return null; - - assert(condition); - return condition.include(_scope ? _scope : sc) ? decl : elsedecl; - } - - override final void addComment(const(char)* comment) - { - /* Because addComment is called by the parser, if we called - * include() it would define a version before it was used. - * But it's no problem to drill down to both decl and elsedecl, - * so that's the workaround. - */ - if (comment) - { - decl .foreachDsymbol( s => s.addComment(comment) ); - elsedecl.foreachDsymbol( s => s.addComment(comment) ); - } - } - override void accept(Visitor v) { v.visit(this); @@ -819,12 +562,13 @@ extern (C++) class ConditionalDeclaration : AttribDeclaration extern (C++) final class StaticIfDeclaration : ConditionalDeclaration { ScopeDsymbol scopesym; /// enclosing symbol (e.g. module) where symbols will be inserted - private bool addisdone = false; /// true if members have been added to scope - private bool onStack = false; /// true if a call to `include` is currently active + bool addisdone = false; /// true if members have been added to scope + bool onStack = false; /// true if a call to `include` is currently active - extern (D) this(const ref Loc loc, Condition condition, Dsymbols* decl, Dsymbols* elsedecl) @safe + extern (D) this(Loc loc, Condition condition, Dsymbols* decl, Dsymbols* elsedecl) @safe { super(loc, condition, decl, elsedecl); + this.dsym = DSYM.staticIfDeclaration; //printf("StaticIfDeclaration::StaticIfDeclaration()\n"); } @@ -834,52 +578,11 @@ extern (C++) final class StaticIfDeclaration : ConditionalDeclaration return new StaticIfDeclaration(loc, condition.syntaxCopy(), Dsymbol.arraySyntaxCopy(decl), Dsymbol.arraySyntaxCopy(elsedecl)); } - /**************************************** - * Different from other AttribDeclaration subclasses, include() call requires - * the completion of addMember and setScope phases. - */ - override Dsymbols* include(Scope* sc) - { - //printf("StaticIfDeclaration::include(sc = %p) scope = %p\n", sc, _scope); - - if (errors || onStack) - return null; - onStack = true; - scope(exit) onStack = false; - - if (sc && condition.inc == Include.notComputed) - { - assert(scopesym); // addMember is already done - assert(_scope); // setScope is already done - Dsymbols* d = ConditionalDeclaration.include(_scope); - if (d && !addisdone) - { - // Add members lazily. - d.foreachDsymbol( s => s.addMember(_scope, scopesym) ); - - // Set the member scopes lazily. - d.foreachDsymbol( s => s.setScope(_scope) ); - - addisdone = true; - } - return d; - } - else - { - return ConditionalDeclaration.include(sc); - } - } - override const(char)* kind() const { return "static if"; } - override inout(StaticIfDeclaration) isStaticIfDeclaration() inout pure @safe - { - return this; - } - override void accept(Visitor v) { v.visit(this); @@ -910,6 +613,7 @@ extern (C++) final class StaticForeachDeclaration : AttribDeclaration extern (D) this(StaticForeach sfe, Dsymbols* decl) @safe { super(sfe.loc, null, decl); + this.dsym = DSYM.staticForeachDeclaration; this.sfe = sfe; } @@ -921,64 +625,6 @@ extern (C++) final class StaticForeachDeclaration : AttribDeclaration Dsymbol.arraySyntaxCopy(decl)); } - override bool oneMember(out Dsymbol ps, Identifier ident) - { - // Required to support IFTI on a template that contains a - // `static foreach` declaration. `super.oneMember` calls - // include with a `null` scope. As `static foreach` requires - // the scope for expansion, `oneMember` can only return a - // precise result once `static foreach` has been expanded. - if (cached) - { - return super.oneMember(ps, ident); - } - ps = null; // a `static foreach` declaration may in general expand to multiple symbols - return false; - } - - override Dsymbols* include(Scope* sc) - { - if (errors || onStack) - return null; - if (cached) - { - assert(!onStack); - return cache; - } - onStack = true; - scope(exit) onStack = false; - - if (_scope) - { - sfe.prepare(_scope); // lower static foreach aggregate - } - if (!sfe.ready()) - { - return null; // TODO: ok? - } - - // expand static foreach - import dmd.statementsem: makeTupleForeach; - Dsymbols* d = makeTupleForeach(_scope, true, true, sfe.aggrfe, decl, sfe.needExpansion).decl; - if (d) // process generated declarations - { - // Add members lazily. - d.foreachDsymbol( s => s.addMember(_scope, scopesym) ); - - // Set the member scopes lazily. - d.foreachDsymbol( s => s.setScope(_scope) ); - } - cached = true; - cache = d; - return d; - } - - override void addComment(const(char)* comment) - { - // do nothing - // change this to give semantics to documentation comments on static foreach declarations - } - override const(char)* kind() const { return "static foreach"; @@ -1005,7 +651,7 @@ extern (C++) final class StaticForeachDeclaration : AttribDeclaration * pragma(msg, mixin("x" ~ to!string(i))); // ok, all 10 symbols are visible as they were forwarded to the global scope * } * - * static assert (!is(typeof(i))); // loop index variable is not visible outside of the static foreach loop + * static assert(!is(typeof(i))); // loop index variable is not visible outside of the static foreach loop * * A StaticForeachDeclaration generates one * ForwardingAttribDeclaration for each expansion of its body. The @@ -1023,30 +669,17 @@ extern(C++) final class ForwardingAttribDeclaration : AttribDeclaration this(Dsymbols* decl) @safe { super(decl); + this.dsym = DSYM.forwardingAttribDeclaration; sym = new ForwardingScopeDsymbol(); sym.symtab = new DsymbolTable(); } - /************************************** - * Use the ForwardingScopeDsymbol as the parent symbol for members. - */ - override Scope* newScope(Scope* sc) - { - return sc.push(sym); - } - - override inout(ForwardingAttribDeclaration) isForwardingAttribDeclaration() inout - { - return this; - } - override void accept(Visitor v) { v.visit(this); } } - /*********************************************************** * Mixin declarations, like: * mixin("int x"); @@ -1059,10 +692,11 @@ extern (C++) final class MixinDeclaration : AttribDeclaration ScopeDsymbol scopesym; bool compiled; - extern (D) this(const ref Loc loc, Expressions* exps) @safe + extern (D) this(Loc loc, Expressions* exps) @safe { super(loc, null, null); //printf("MixinDeclaration(loc = %d)\n", loc.linnum); + this.dsym = DSYM.mixinDeclaration; this.exps = exps; } @@ -1077,11 +711,6 @@ extern (C++) final class MixinDeclaration : AttribDeclaration return "mixin"; } - override inout(MixinDeclaration) isMixinDeclaration() inout - { - return this; - } - override void accept(Visitor v) { v.visit(this); @@ -1100,6 +729,7 @@ extern (C++) final class UserAttributeDeclaration : AttribDeclaration extern (D) this(Expressions* atts, Dsymbols* decl) @safe { super(decl); + this.dsym = DSYM.userAttributeDeclaration; this.atts = atts; } @@ -1110,18 +740,6 @@ extern (C++) final class UserAttributeDeclaration : AttribDeclaration return new UserAttributeDeclaration(Expression.arraySyntaxCopy(this.atts), Dsymbol.arraySyntaxCopy(decl)); } - override Scope* newScope(Scope* sc) - { - Scope* sc2 = sc; - if (atts && atts.length) - { - // create new one for changes - sc2 = sc.copy(); - sc2.userAttribDecl = this; - } - return sc2; - } - extern (D) static Expressions* concat(Expressions* udas1, Expressions* udas2) { Expressions* udas; @@ -1150,70 +768,6 @@ extern (C++) final class UserAttributeDeclaration : AttribDeclaration { v.visit(this); } - - /** - * Check if the provided expression references `core.attribute.gnuAbiTag` - * - * This should be called after semantic has been run on the expression. - * Semantic on UDA happens in semantic2 (see `dmd.semantic2`). - * - * Params: - * e = Expression to check (usually from `UserAttributeDeclaration.atts`) - * - * Returns: - * `true` if the expression references the compiler-recognized `gnuAbiTag` - */ - static bool isGNUABITag(Expression e) - { - if (global.params.cplusplus < CppStdRevision.cpp11) - return false; - - auto ts = e.type ? e.type.isTypeStruct() : null; - if (!ts) - return false; - if (ts.sym.ident != Id.udaGNUAbiTag || !ts.sym.parent) - return false; - // Can only be defined in druntime - Module m = ts.sym.parent.isModule(); - if (!m || !m.isCoreModule(Id.attribute)) - return false; - return true; - } - - /** - * Called from a symbol's semantic to check if `gnuAbiTag` UDA - * can be applied to them - * - * Directly emits an error if the UDA doesn't work with this symbol - * - * Params: - * sym = symbol to check for `gnuAbiTag` - * linkage = Linkage of the symbol (Declaration.link or sc.link) - */ - static void checkGNUABITag(Dsymbol sym, LINK linkage) - { - if (global.params.cplusplus < CppStdRevision.cpp11) - return; - - foreachUdaNoSemantic(sym, (exp) { - if (isGNUABITag(exp)) - { - if (sym.isCPPNamespaceDeclaration() || sym.isNspace()) - { - .error(exp.loc, "`@%s` cannot be applied to namespaces", Id.udaGNUAbiTag.toChars()); - sym.errors = true; - } - else if (linkage != LINK.cpp) - { - .error(exp.loc, "`@%s` can only apply to C++ symbols", Id.udaGNUAbiTag.toChars()); - sym.errors = true; - } - // Only one `@gnuAbiTag` is allowed by semantic2 - return 1; // break - } - return 0; // continue - }); - } } /** @@ -1265,7 +819,6 @@ int foreachUdaNoSemantic(Dsymbol sym, int delegate(Expression) dg) return 0; } - /** * Returns: true if the given expression is an enum from `core.attribute` named `id` */ diff --git a/dmd/attrib.h b/dmd/attrib.h index d4c41ec94f..ef37e0adec 100644 --- a/dmd/attrib.h +++ b/dmd/attrib.h @@ -1,6 +1,6 @@ /* Compiler implementation of the D programming language - * Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * written by Walter Bright * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. @@ -28,17 +28,8 @@ class AttribDeclaration : public Dsymbol { public: Dsymbols *decl; // array of Dsymbol's - - virtual Dsymbols *include(Scope *sc); - virtual Scope *newScope(Scope *sc); - void addComment(const utf8_t *comment) override; const char *kind() const override; - bool oneMember(Dsymbol *&ps, Identifier *ident) override; bool hasPointers() override final; - bool hasStaticCtorOrDtor() override final; - void checkCtorConstInit() override final; - AttribDeclaration *isAttribDeclaration() override { return this; } - void accept(Visitor *v) override { v->visit(this); } }; @@ -48,9 +39,6 @@ class StorageClassDeclaration : public AttribDeclaration StorageClass stc; StorageClassDeclaration *syntaxCopy(Dsymbol *s) override; - Scope *newScope(Scope *sc) override; - bool oneMember(Dsymbol *&ps, Identifier *ident) override final; - StorageClassDeclaration *isStorageClassDeclaration() override { return this; } void accept(Visitor *v) override { v->visit(this); } }; @@ -62,7 +50,6 @@ class DeprecatedDeclaration final : public StorageClassDeclaration const char *msgstr; DeprecatedDeclaration *syntaxCopy(Dsymbol *s) override; - Scope *newScope(Scope *sc) override; void accept(Visitor *v) override { v->visit(this); } }; @@ -71,10 +58,8 @@ class LinkDeclaration final : public AttribDeclaration public: LINK linkage; - static LinkDeclaration *create(const Loc &loc, LINK p, Dsymbols *decl); + static LinkDeclaration *create(Loc loc, LINK p, Dsymbols *decl); LinkDeclaration *syntaxCopy(Dsymbol *s) override; - Scope *newScope(Scope *sc) override; - const char *toChars() const override; void accept(Visitor *v) override { v->visit(this); } }; @@ -84,8 +69,6 @@ class CPPMangleDeclaration final : public AttribDeclaration CPPMANGLE cppmangle; CPPMangleDeclaration *syntaxCopy(Dsymbol *s) override; - Scope *newScope(Scope *sc) override; - const char *toChars() const override; void accept(Visitor *v) override { v->visit(this); } }; @@ -95,8 +78,6 @@ class CPPNamespaceDeclaration final : public AttribDeclaration Expression *exp; CPPNamespaceDeclaration *syntaxCopy(Dsymbol *s) override; - Scope *newScope(Scope *sc) override; - const char *toChars() const override; void accept(Visitor *v) override { v->visit(this); } }; @@ -107,10 +88,8 @@ class VisibilityDeclaration final : public AttribDeclaration DArray pkg_identifiers; VisibilityDeclaration *syntaxCopy(Dsymbol *s) override; - Scope *newScope(Scope *sc) override; const char *kind() const override; const char *toPrettyChars(bool unused) override; - VisibilityDeclaration *isVisibilityDeclaration() override { return this; } void accept(Visitor *v) override { v->visit(this); } }; @@ -121,7 +100,6 @@ class AlignDeclaration final : public AttribDeclaration structalign_t salign; AlignDeclaration *syntaxCopy(Dsymbol *s) override; - Scope *newScope(Scope *sc) override; void accept(Visitor *v) override { v->visit(this); } }; @@ -136,7 +114,6 @@ class AnonDeclaration final : public AttribDeclaration AnonDeclaration *syntaxCopy(Dsymbol *s) override; const char *kind() const override; - AnonDeclaration *isAnonDeclaration() override { return this; } void accept(Visitor *v) override { v->visit(this); } }; @@ -146,7 +123,6 @@ class PragmaDeclaration final : public AttribDeclaration Expressions *args; // array of Expression's PragmaDeclaration *syntaxCopy(Dsymbol *s) override; - Scope *newScope(Scope *sc) override; const char *kind() const override; void accept(Visitor *v) override { v->visit(this); } }; @@ -158,9 +134,6 @@ class ConditionalDeclaration : public AttribDeclaration Dsymbols *elsedecl; // array of Dsymbol's for else block ConditionalDeclaration *syntaxCopy(Dsymbol *s) override; - bool oneMember(Dsymbol *&ps, Identifier *ident) override final; - Dsymbols *include(Scope *sc) override; - void addComment(const utf8_t *comment) override final; void accept(Visitor *v) override { v->visit(this); } }; @@ -172,8 +145,6 @@ class StaticIfDeclaration final : public ConditionalDeclaration d_bool onStack; StaticIfDeclaration *syntaxCopy(Dsymbol *s) override; - Dsymbols *include(Scope *sc) override; - StaticIfDeclaration *isStaticIfDeclaration() override { return this; } const char *kind() const override; void accept(Visitor *v) override { v->visit(this); } }; @@ -188,9 +159,6 @@ class StaticForeachDeclaration final : public AttribDeclaration Dsymbols *cache; StaticForeachDeclaration *syntaxCopy(Dsymbol *s) override; - bool oneMember(Dsymbol *&ps, Identifier *ident) override; - Dsymbols *include(Scope *sc) override; - void addComment(const utf8_t *comment) override; const char *kind() const override; void accept(Visitor *v) override { v->visit(this); } }; @@ -200,8 +168,6 @@ class ForwardingAttribDeclaration final : public AttribDeclaration public: ForwardingScopeDsymbol *sym; - Scope *newScope(Scope *sc) override; - ForwardingAttribDeclaration *isForwardingAttribDeclaration() override { return this; } void accept(Visitor *v) override { v->visit(this); } }; @@ -230,7 +196,6 @@ class UserAttributeDeclaration final : public AttribDeclaration Expressions *atts; UserAttributeDeclaration *syntaxCopy(Dsymbol *s) override; - Scope *newScope(Scope *sc) override; const char *kind() const override; void accept(Visitor *v) override { v->visit(this); } }; diff --git a/dmd/attribsem.d b/dmd/attribsem.d index 44647eee62..bc966bb8eb 100644 --- a/dmd/attribsem.d +++ b/dmd/attribsem.d @@ -14,12 +14,12 @@ * - Protection (`private`, `public`) * - Deprecated declarations (`@deprecated`) * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/attribsem.d, _attrib.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/attribsem.d, _attrib.d) * Documentation: https://dlang.org/phobos/dmd_attribsem.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/attribsem.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/attribsem.d */ module dmd.attribsem; diff --git a/dmd/blockexit.d b/dmd/blockexit.d index d77af7e1c5..ae2f12f323 100644 --- a/dmd/blockexit.d +++ b/dmd/blockexit.d @@ -1,12 +1,12 @@ /** * Find out in what ways control flow can exit a statement block. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/blockexit.d, _blockexit.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/blockexit.d, _blockexit.d) * Documentation: https://dlang.org/phobos/dmd_blockexit.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/blockexit.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/blockexit.d */ module dmd.blockexit; @@ -80,26 +80,25 @@ int blockExit(Statement s, FuncDeclaration func, ErrorSink eSink) void visitExp(ExpStatement s) { result = BE.fallthru; - if (s.exp) + if (!s.exp) + return; + + if (s.exp.op == EXP.halt) { - if (s.exp.op == EXP.halt) + result = BE.halt; + return; + } + if (AssertExp a = s.exp.isAssertExp()) + { + if (a.e1.toBool().hasValue(false)) // if it's an assert(0) { result = BE.halt; return; } - if (AssertExp a = s.exp.isAssertExp()) - { - if (a.e1.toBool().hasValue(false)) // if it's an assert(0) - { - result = BE.halt; - return; - } - } - if (s.exp.type && s.exp.type.toBasetype().isTypeNoreturn()) - result = BE.halt; - - result |= canThrow(s.exp, func, eSink); } + if (s.exp.type && s.exp.type.toBasetype().isTypeNoreturn()) + result = BE.halt; + result |= canThrow(s.exp, func, eSink); } void visitDtorExp(DtorExpStatement s) @@ -120,44 +119,39 @@ int blockExit(Statement s, FuncDeclaration func, ErrorSink eSink) Statement slast = null; foreach (s; *cs.statements) { - if (s) + if (!s) + continue; + + //printf("result = x%x\n", result); + //printf("s: %s\n", s.toChars()); + if (result & BE.fallthru && slast) { - //printf("result = x%x\n", result); - //printf("s: %s\n", s.toChars()); - if (result & BE.fallthru && slast) + slast = slast.last(); + if (slast && (slast.isCaseStatement() || slast.isDefaultStatement()) && (s.isCaseStatement() || s.isDefaultStatement())) { - slast = slast.last(); - if (slast && (slast.isCaseStatement() || slast.isDefaultStatement()) && (s.isCaseStatement() || s.isDefaultStatement())) + // Allow if last case/default was empty + CaseStatement sc = slast.isCaseStatement(); + DefaultStatement sd = slast.isDefaultStatement(); + auto sl = (sc ? sc.statement : (sd ? sd.statement : null)); + + if (sl && (!sl.hasCode() || sl.isErrorStatement())) { - // Allow if last case/default was empty - CaseStatement sc = slast.isCaseStatement(); - DefaultStatement sd = slast.isDefaultStatement(); - auto sl = (sc ? sc.statement : (sd ? sd.statement : null)); - - if (sl && (!sl.hasCode() || sl.isErrorStatement())) - { - } - else if (func.getModule().filetype != FileType.c) - { - const(char)* gototype = s.isCaseStatement() ? "case" : "default"; - // @@@DEPRECATED_2.110@@@ https://issues.dlang.org/show_bug.cgi?id=22999 - // Deprecated in 2.100 - // Make an error in 2.110 - if (sl && sl.isCaseStatement()) - global.errorSink.deprecation(s.loc, "switch case fallthrough - use 'goto %s;' if intended", gototype); - else - global.errorSink.error(s.loc, "switch case fallthrough - use 'goto %s;' if intended", gototype); - } + } + else if (func.getModule().filetype != FileType.c) + { + const(char)* gototype = s.isCaseStatement() ? "case" : "default"; + // https://issues.dlang.org/show_bug.cgi?id=22999 + global.errorSink.error(s.loc, "switch case fallthrough - use 'goto %s;' if intended", gototype); } } + } - if ((result & BE.fallthru) || s.comeFrom()) - { - result &= ~BE.fallthru; - result |= blockExit(s, func, eSink); - } - slast = s; + if ((result & BE.fallthru) || s.comeFrom()) + { + result &= ~BE.fallthru; + result |= blockExit(s, func, eSink); } + slast = s; } } @@ -166,13 +160,12 @@ int blockExit(Statement s, FuncDeclaration func, ErrorSink eSink) result = BE.fallthru; foreach (s; *uls.statements) { - if (s) - { - int r = blockExit(s, func, eSink); - result |= r & ~(BE.break_ | BE.continue_ | BE.fallthru); - if ((r & (BE.fallthru | BE.continue_ | BE.break_)) == 0) - result &= ~BE.fallthru; - } + if (!s) + continue; + int r = blockExit(s, func, eSink); + result |= r & ~(BE.break_ | BE.continue_ | BE.fallthru); + if ((r & (BE.fallthru | BE.continue_ | BE.break_)) == 0) + result &= ~BE.fallthru; } } @@ -485,7 +478,7 @@ int blockExit(Statement s, FuncDeclaration func, ErrorSink eSink) if (!(s.stc & STC.nothrow_)) { if(func) - func.setThrow(s.loc, "`asm` statement is assumed to throw - mark it with `nothrow` if it does not"); + func.setThrow(s.loc, "executing an `asm` statement without a `nothrow` annotation"); if (eSink) eSink.error(s.loc, "`asm` statement is assumed to throw - mark it with `nothrow` if it does not"); // TODO else @@ -517,7 +510,7 @@ int blockExit(Statement s, FuncDeclaration func, ErrorSink eSink) + + Returns: `BE.[err]throw` depending on the type of `exp` +/ -BE checkThrow(ref const Loc loc, Expression exp, FuncDeclaration func, ErrorSink eSink) +BE checkThrow(Loc loc, Expression exp, FuncDeclaration func, ErrorSink eSink) { Type t = exp.type.toBasetype(); ClassDeclaration cd = t.isClassHandle(); @@ -530,7 +523,7 @@ BE checkThrow(ref const Loc loc, Expression exp, FuncDeclaration func, ErrorSink if (eSink) eSink.error(loc, "`%s` is thrown but not caught", exp.type.toChars()); else if (func) - func.setThrow(loc, "`%s` is thrown but not caught", exp.type); + func.setThrow(loc, "`%s` being thrown but not caught", exp.type); return BE.throw_; } diff --git a/dmd/builtin.d b/dmd/builtin.d index 4acad88ed9..047c71bbe0 100644 --- a/dmd/builtin.d +++ b/dmd/builtin.d @@ -3,24 +3,24 @@ * * Currently includes functions from `std.math`, `core.math` and `core.bitop`. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/builtin.d, _builtin.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/builtin.d, _builtin.d) * Documentation: https://dlang.org/phobos/dmd_builtin.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/builtin.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/builtin.d */ module dmd.builtin; import dmd.arraytypes; import dmd.astenums; -import dmd.dmangle; import dmd.errors; import dmd.expression; import dmd.func; import dmd.globals; import dmd.location; +import dmd.mangle; import dmd.mtype; import dmd.root.ctfloat; import dmd.tokens; @@ -44,7 +44,7 @@ public extern (C++) BUILTIN isBuiltin(FuncDeclaration fd) * Evaluate builtin function. * Return result; NULL if cannot evaluate it. */ -public extern (C++) Expression eval_builtin(const ref Loc loc, FuncDeclaration fd, Expressions* arguments) +public extern (C++) Expression eval_builtin(Loc loc, FuncDeclaration fd, Expressions* arguments) { if (fd.builtin == BUILTIN.unimp) return null; @@ -203,8 +203,8 @@ else // Only match pow(fp,fp) where fp is a floating point type if (id3 == Id._pow) { - if ((*fd.parameters)[0].type.isfloating() && - (*fd.parameters)[1].type.isfloating()) + if ((*fd.parameters)[0].type.isFloating() && + (*fd.parameters)[1].type.isFloating()) return BUILTIN.pow; return BUILTIN.unimp; } diff --git a/dmd/canthrow.d b/dmd/canthrow.d index 31155f188d..96acf05529 100644 --- a/dmd/canthrow.d +++ b/dmd/canthrow.d @@ -3,12 +3,12 @@ * * Specification: $(LINK2 https://dlang.org/spec/function.html#nothrow-functions, Nothrow Functions) * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/canthrow.d, _canthrow.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/canthrow.d, _canthrow.d) * Documentation: https://dlang.org/phobos/dmd_canthrow.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/canthrow.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/canthrow.d */ module dmd.canthrow; @@ -20,15 +20,17 @@ import dmd.astenums; import dmd.blockexit : BE, checkThrow; import dmd.declaration; import dmd.dsymbol; +import dmd.dsymbolsem : include; import dmd.errorsink; import dmd.expression; +import dmd.expressionsem : errorSupplementalInferredAttr; import dmd.func; import dmd.globals; import dmd.init; import dmd.mtype; -import dmd.postordervisitor; import dmd.tokens; import dmd.visitor; +import dmd.visitor.postorder; /** * Status indicating what kind of throwable might be caused by an expression. @@ -72,16 +74,16 @@ CT canThrow(Expression e, FuncDeclaration func, ErrorSink eSink) void checkFuncThrows(Expression e, FuncDeclaration f) { auto tf = f.type.toBasetype().isTypeFunction(); - if (tf && !tf.isnothrow) + if (tf && !tf.isNothrow) { if (eSink) { eSink.error(e.loc, "%s `%s` is not `nothrow`", f.kind(), f.toPrettyChars()); if (!f.isDtorDeclaration()) - errorSupplementalInferredAttr(f, 10, false, STC.nothrow_); + errorSupplementalInferredAttr(f, 10, false, STC.nothrow_, eSink); import dmd.expressionsem : checkOverriddenDtor; - f.checkOverriddenDtor(null, e.loc, dd => dd.type.toTypeFunction().isnothrow, "not nothrow"); + f.checkOverriddenDtor(null, e.loc, dd => dd.type.toTypeFunction().isNothrow, "not nothrow"); } else if (func) { @@ -111,11 +113,9 @@ CT canThrow(Expression e, FuncDeclaration func, ErrorSink eSink) if (ce.f && ce.arguments.length > 0) { Type tb = (*ce.arguments)[0].type.toBasetype(); - auto tbNext = tb.nextOf(); - if (tbNext) + if (auto tbNext = tb.nextOf()) { - auto ts = tbNext.baseElemOf().isTypeStruct(); - if (ts) + if (auto ts = tbNext.baseElemOf().isTypeStruct()) { auto sd = ts.sym; const id = ce.f.ident; @@ -135,7 +135,7 @@ CT canThrow(Expression e, FuncDeclaration func, ErrorSink eSink) if (ce.f && ce.f == func) return; const tf = ce.calledFunctionType(); - if (tf && tf.isnothrow) + if (tf && tf.isNothrow) return; if (ce.f) diff --git a/dmd/chkformat.d b/dmd/chkformat.d index bbc7b867c5..8b2d5b4dd4 100644 --- a/dmd/chkformat.d +++ b/dmd/chkformat.d @@ -1,12 +1,12 @@ /** * Check the arguments to `printf` and `scanf` against the `format` string. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/chkformat.d, _chkformat.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/chkformat.d, _chkformat.d) * Documentation: https://dlang.org/phobos/dmd_chkformat.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/chkformat.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/chkformat.d */ module dmd.chkformat; @@ -63,7 +63,7 @@ import dmd.target; * https://www.cplusplus.com/reference/cstdio/printf/ */ public -bool checkPrintfFormat(ref const Loc loc, scope const char[] format, scope Expression[] args, bool isVa_list, ErrorSink eSink) +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); size_t n; // index in args @@ -174,13 +174,13 @@ bool checkPrintfFormat(ref const Loc loc, scope const char[] format, scope Expre case Format.lu: // unsigned long int case Format.ld: // long int - if (!(t.isintegral() && t.size() == c_longsize)) + if (!(t.isIntegral() && t.size() == c_longsize)) { if (fmt == Format.lu) errorMsg(null, e, (c_longsize == 4 ? "uint" : "ulong"), t); else errorMsg(null, e, (c_longsize == 4 ? "int" : "long"), t); - if (t.isintegral() && t.size() != c_longsize) + if (t.isIntegral() && t.size() != c_longsize) eSink.errorSupplemental(e.loc, "C `long` is %d bytes on your system", c_longsize); } break; @@ -203,12 +203,12 @@ bool checkPrintfFormat(ref const Loc loc, scope const char[] format, scope Expre break; case Format.zd: // size_t - if (!(t.isintegral() && t.size() == ptrsize)) + if (!(t.isIntegral() && t.size() == ptrsize)) errorMsg(null, e, "size_t", t); break; case Format.td: // ptrdiff_t - if (!(t.isintegral() && t.size() == ptrsize)) + if (!(t.isIntegral() && t.size() == ptrsize)) errorMsg(null, e, "ptrdiff_t", t); break; @@ -234,7 +234,7 @@ bool checkPrintfFormat(ref const Loc loc, scope const char[] format, scope Expre break; case Format.ln: // pointer to long int - if (!(t.ty == Tpointer && tnext.isintegral() && tnext.size() == c_longsize)) + if (!(t.ty == Tpointer && tnext.isIntegral() && tnext.size() == c_longsize)) errorMsg(null, e, (c_longsize == 4 ? "int*" : "long*"), t); break; @@ -259,12 +259,12 @@ bool checkPrintfFormat(ref const Loc loc, scope const char[] format, scope Expre break; case Format.zn: // pointer to size_t - if (!(t.ty == Tpointer && tnext.isintegral() && tnext.isunsigned() && tnext.size() == ptrsize)) + if (!(t.ty == Tpointer && tnext.isIntegral() && tnext.isUnsigned() && tnext.size() == ptrsize)) errorMsg(null, e, "size_t*", t); break; case Format.tn: // pointer to ptrdiff_t - if (!(t.ty == Tpointer && tnext.isintegral() && !tnext.isunsigned() && tnext.size() == ptrsize)) + if (!(t.ty == Tpointer && tnext.isIntegral() && !tnext.isUnsigned() && tnext.size() == ptrsize)) errorMsg(null, e, "ptrdiff_t*", t); break; @@ -339,7 +339,7 @@ bool checkPrintfFormat(ref const Loc loc, scope const char[] format, scope Expre * https://www.cplusplus.com/reference/cstdio/scanf/ */ public -bool checkScanfFormat(ref const Loc loc, scope const char[] format, scope Expression[] args, bool isVa_list, ErrorSink eSink) +bool checkScanfFormat(Loc loc, scope const char[] format, scope Expression[] args, bool isVa_list, ErrorSink eSink) { size_t n = 0; for (size_t i = 0; i < format.length;) @@ -414,7 +414,7 @@ bool checkScanfFormat(ref const Loc loc, scope const char[] format, scope Expres case Format.ln: case Format.ld: // pointer to long int - if (!(t.ty == Tpointer && tnext.isintegral() && !tnext.isunsigned() && tnext.size() == c_longsize)) + if (!(t.ty == Tpointer && tnext.isIntegral() && !tnext.isUnsigned() && tnext.size() == c_longsize)) errorMsg(null, e, (c_longsize == 4 ? "int*" : "long*"), t); break; @@ -432,13 +432,13 @@ bool checkScanfFormat(ref const Loc loc, scope const char[] format, scope Expres case Format.zn: case Format.zd: // pointer to size_t - if (!(t.ty == Tpointer && tnext.isintegral() && tnext.isunsigned() && tnext.size() == ptrsize)) + if (!(t.ty == Tpointer && tnext.isIntegral() && tnext.isUnsigned() && tnext.size() == ptrsize)) errorMsg(null, e, "size_t*", t); break; case Format.tn: case Format.td: // pointer to ptrdiff_t - if (!(t.ty == Tpointer && tnext.isintegral() && !tnext.isunsigned() && tnext.size() == ptrsize)) + if (!(t.ty == Tpointer && tnext.isIntegral() && !tnext.isUnsigned() && tnext.size() == ptrsize)) errorMsg(null, e, "ptrdiff_t*", t); break; @@ -458,7 +458,7 @@ bool checkScanfFormat(ref const Loc loc, scope const char[] format, scope Expres break; case Format.lu: // pointer to unsigned long int - if (!(t.ty == Tpointer && tnext.isintegral() && tnext.isunsigned() && tnext.size() == c_longsize)) + if (!(t.ty == Tpointer && tnext.isIntegral() && tnext.isUnsigned() && tnext.size() == c_longsize)) errorMsg(null, e, (c_longsize == 4 ? "uint*" : "ulong*"), t); break; diff --git a/dmd/cli.d b/dmd/cli.d index e8a4815b1c..01b8348bb8 100644 --- a/dmd/cli.d +++ b/dmd/cli.d @@ -5,12 +5,12 @@ * However, this file will be used to generate the * $(LINK2 https://dlang.org/dmd-linux.html, online documentation) and MAN pages. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/cli.d, _cli.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/cli.d, _cli.d) * Documentation: https://dlang.org/phobos/dmd_cli.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/cli.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/cli.d */ module dmd.cli; @@ -365,6 +365,8 @@ dmd -cov -unittest myprog.d Sets `__traits(getTargetInfo, \"cppStd\")` to `201703`) $(LI $(I c++20): Use C++20 name mangling, Sets `__traits(getTargetInfo, \"cppStd\")` to `202002`) + $(LI $(I c++23): Use C++23 name mangling, + Sets `__traits(getTargetInfo, \"cppStd\")` to `202302`) )", ), Option("extern-std=[h|help|?]", @@ -381,6 +383,27 @@ dmd -cov -unittest myprog.d "generate position independent executables", cast(TargetOS) (TargetOS.all & ~(TargetOS.Windows | TargetOS.OSX)) ), + Option("ftime-trace", + "turn on compile time profiler, generate JSON file with results", + "Per function, the time to analyze it, call it from CTFE, generate code for it etc. will be measured, + and events with a time longer than 500 microseconds (adjustable with `-ftime-trace-granularity`) + will be recorded. + The profiling result is output in the Chrome Trace Event Format, + $(LINK2 https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/preview, described here). + This can be turned into a more readable text file with the included tool `timetrace2txt`, or inspected + with an interactive viewer such as $(LINK2 https://ui.perfetto.dev/, Perfetto)." + ), + Option("ftime-trace-granularity=", + "Minimum time granularity (in microseconds) traced by time profiler (default: 500)", + "Measured events shorter than the specified time will be discarded from the output. + Set it too high, and interesting events may not show up in the output. + Set too low, and the profiler overhead will be larger, and the output will be cluttered with tiny events." + ), + Option("ftime-trace-file=", + "specify output file for -ftime-trace", + "By default, the output name is the same as the first object file name, but with the `.time-trace` extension appended. + A different filename can be chosen with this option, including a path relative to the current directory or an absolute path." + ), Option("g", "add symbolic debug info", `$(WINDOWS @@ -675,6 +698,12 @@ dmd -cov -unittest myprog.d off when generating an object, interface, or Ddoc file name. $(SWLINK -op) will leave it on.`, ), + Option("oq", + "Write object files with fully qualified file names", + `When compiling pkg/app.d, the resulting object file name will be pkg_app.obj + instead of app.o. This helps to prevent name conflicts when compiling multiple + packages in the same directory with the $(SWLINK -od) flag.`, + ), Option("os=", "sets target operating system to ", `Set the target operating system as other than the host. @@ -804,13 +833,14 @@ dmd -cov -unittest myprog.d Option("vcolumns", "print character (column) numbers in diagnostics" ), - Option("verror-style=[digitalmars|gnu]", + Option("verror-style=[digitalmars|gnu|sarif]", "set the style for file/line number annotations on compiler messages", `Set the style for file/line number annotations on compiler messages, where: $(DL $(DT digitalmars)$(DD 'file(line[,column]): message'. This is the default.) $(DT gnu)$(DD 'file:line[:column]: message', conforming to the GNU standard used by gcc and clang.) + $(DT sarif)$(DD 'Generates JSON output conforming to the SARIF (Static Analysis Results Interchange Format) standard, useful for integration with tools like GitHub and other SARIF readers.') )`, ), Option("verror-supplements=", @@ -819,8 +849,13 @@ dmd -cov -unittest myprog.d Option("verrors=", "limit the number of error/deprecation messages (0 means unlimited)" ), - Option("verrors=context", - "show error messages with the context of the erroring source line" + Option("verrors=[context|simple]", + "set the verbosity of error messages", + `Set the verbosity of error messages: + $(DL + $(DT context)$(DD Show error messages with the context of the erroring source line (including caret).) + $(DT simple)$(DD Show error messages without the context of the erroring source line.) + )`, ), Option("verrors=spec", "show errors from speculative compiles such as __traits(compiles,...)" @@ -951,6 +986,9 @@ dmd -cov -unittest myprog.d Feature("rvaluerefparam", "rvalueRefParam", "enable rvalue arguments to ref parameters", "https://gist.github.com/andralex/e5405a5d773f07f73196c05f8339435a"), + Feature("safer", "safer", + "more safety checks by default", + "https://github.com/WalterBright/documents/blob/38f0a846726b571f8108f6e63e5e217b91421c86/safer.md", true, false), Feature("nosharedaccess", "noSharedAccess", "disable access to shared memory objects", "https://dlang.org/spec/const3.html#shared"), @@ -1098,6 +1136,7 @@ version (IN_LLVM) {} else =c++14 Sets `__traits(getTargetInfo, \"cppStd\")` to `201402` =c++17 Sets `__traits(getTargetInfo, \"cppStd\")` to `201703` =c++20 Sets `__traits(getTargetInfo, \"cppStd\")` to `202002` + =c++23 Sets `__traits(getTargetInfo, \"cppStd\")` to `202302` "; /// Options supported by -HC diff --git a/dmd/clone.d b/dmd/clone.d index 17b33d839d..a21f5a05be 100644 --- a/dmd/clone.d +++ b/dmd/clone.d @@ -2,12 +2,12 @@ * Builds struct member functions if needed and not defined by the user. * Includes `opEquals`, `opAssign`, post blit, copy constructor and destructor. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/clone.d, _clone.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/clone.d, _clone.d) * Documentation: https://dlang.org/phobos/dmd_clone.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/clone.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/clone.d */ module dmd.clone; @@ -51,13 +51,13 @@ import dmd.tokens; * Returns: * merged storage class */ -StorageClass mergeFuncAttrs(StorageClass s1, const FuncDeclaration f) pure +STC mergeFuncAttrs(STC s1, const FuncDeclaration f) pure @safe { if (!f) return s1; - StorageClass s2 = (f.storage_class & STC.disable); + STC s2 = (f.storage_class & STC.disable); - auto tf = cast(TypeFunction)f.type; + auto tf = f.type.isTypeFunction(); if (tf.trust == TRUST.safe) s2 |= STC.safe; else if (tf.trust == TRUST.system) @@ -67,15 +67,15 @@ StorageClass mergeFuncAttrs(StorageClass s1, const FuncDeclaration f) pure if (tf.purity != PURE.impure) s2 |= STC.pure_; - if (tf.isnothrow) + if (tf.isNothrow) s2 |= STC.nothrow_; - if (tf.isnogc) + if (tf.isNogc) s2 |= STC.nogc; const sa = s1 & s2; const so = s1 | s2; - StorageClass stc = (sa & (STC.pure_ | STC.nothrow_ | STC.nogc)) | (so & STC.disable); + STC stc = (sa & (STC.pure_ | STC.nothrow_ | STC.nogc)) | (so & STC.disable); if (so & STC.system) stc |= STC.system; @@ -96,56 +96,60 @@ StorageClass mergeFuncAttrs(StorageClass s1, const FuncDeclaration f) pure * sc = current scope * Returns: * if found, returns FuncDeclaration of opAssign, otherwise null + * References: + * https://dlang.org/spec/operatoroverloading.html#assignment */ FuncDeclaration hasIdentityOpAssign(AggregateDeclaration ad, Scope* sc) { - Dsymbol assign = search_function(ad, Id.assign); - if (assign) - { - /* check identity opAssign exists - */ - scope er = new NullExp(ad.loc, ad.type); // dummy rvalue - scope el = new IdentifierExp(ad.loc, Id.p); // dummy lvalue - el.type = ad.type; - auto a = new Expressions(1); - const errors = global.startGagging(); // Do not report errors, even if the template opAssign fbody makes it. - sc = sc.push(); - sc.tinst = null; - sc.minst = null; + Dsymbol assign = search_function(ad, Id.opAssign); + if (!assign) + return null; - (*a)[0] = er; - auto f = resolveFuncCall(ad.loc, sc, assign, null, ad.type, ArgumentList(a), FuncResolveFlag.quiet); - if (!f) - { - (*a)[0] = el; - f = resolveFuncCall(ad.loc, sc, assign, null, ad.type, ArgumentList(a), FuncResolveFlag.quiet); - } + /* check identity opAssign exists + */ + scope er = new NullExp(ad.loc, ad.type); // dummy rvalue + scope el = new IdentifierExp(ad.loc, Id.p); // dummy lvalue + el.type = ad.type; + const errors = global.startGagging(); // Do not report errors, even if the template opAssign fbody makes it. + sc = sc.push(); + sc.tinst = null; + sc.minst = null; - sc = sc.pop(); - global.endGagging(errors); - if (f) - { - if (f.errors) - return null; - auto fparams = f.getParameterList(); - if (fparams.length) - { - auto fparam0 = fparams[0]; - if (fparam0.type.toDsymbol(null) != ad) - f = null; - } - } - // BUGS: This detection mechanism cannot find some opAssign-s like follows: - // struct S { void opAssign(ref immutable S) const; } - return f; + auto a = new Expressions(1); + (*a)[0] = er; + auto f = resolveFuncCall(ad.loc, sc, assign, null, ad.type, ArgumentList(a), FuncResolveFlag.quiet); + if (!f) + { + (*a)[0] = el; + f = resolveFuncCall(ad.loc, sc, assign, null, ad.type, ArgumentList(a), FuncResolveFlag.quiet); } - return null; + + sc = sc.pop(); + global.endGagging(errors); + if (!f) + return null; + if (f.errors) + return null; + auto fparams = f.getParameterList(); + if (fparams.length) + { + auto fparam0 = fparams[0]; + if (fparam0.type.toDsymbol(null) != ad) + f = null; + } + // BUGS: This detection mechanism cannot find some opAssign-s like follows: + // struct S { void opAssign(ref immutable S) const; } + return f; } /******************************************* * We need an opAssign for the struct if * it has a destructor or a postblit. - * We need to generate one if a user-specified one does not exist. + * (We will later generate one if a user-specified one does not exist) + * Params: + * sd = struct to check + * Returns: + * true if an opAssign is needed */ private bool needOpAssign(StructDeclaration sd) { @@ -175,9 +179,8 @@ private bool needOpAssign(StructDeclaration sd) if (v.overlapped) // if field of a union continue; // user must handle it themselves Type tv = v.type.baseElemOf(); - if (tv.ty == Tstruct) + if (auto ts = tv.isTypeStruct()) { - auto ts = cast(TypeStruct)tv; if (ts.sym.isUnionDeclaration()) continue; if (needOpAssign(ts.sym)) @@ -251,7 +254,7 @@ private bool needOpAssign(StructDeclaration sd) * sd = struct to generate opAssign for * sc = context * Returns: - * generated `opAssign` function + * generated `opAssign` function, or null if it is not needed */ FuncDeclaration buildOpAssign(StructDeclaration sd, Scope* sc) { @@ -266,7 +269,7 @@ FuncDeclaration buildOpAssign(StructDeclaration sd, Scope* sc) return null; //printf("StructDeclaration::buildOpAssign() %s\n", sd.toChars()); - StorageClass stc = STC.safe | STC.nothrow_ | STC.pure_ | STC.nogc; + STC stc = STC.safe; Loc declLoc = sd.loc; Loc loc; // internal code should have no loc to prevent coverage @@ -281,10 +284,10 @@ FuncDeclaration buildOpAssign(StructDeclaration sd, Scope* sc) if (v.overlapped) continue; Type tv = v.type.baseElemOf(); - if (tv.ty != Tstruct) - continue; - StructDeclaration sdv = (cast(TypeStruct)tv).sym; - stc = mergeFuncAttrs(stc, hasIdentityOpAssign(sdv, sc)); + if (auto tvs = tv.isTypeStruct()) + { + stc = mergeFuncAttrs(stc, hasIdentityOpAssign(tvs.sym, sc)); + } } if (sd.dtor || sd.postblit) @@ -300,7 +303,7 @@ FuncDeclaration buildOpAssign(StructDeclaration sd, Scope* sc) auto fparams = new Parameters(); fparams.push(new Parameter(loc, STC.nodtor, sd.type, Id.p, null, null)); auto tf = new TypeFunction(ParameterList(fparams), sd.handleType(), LINK.d, stc | STC.ref_); - auto fop = new FuncDeclaration(declLoc, Loc.initial, Id.assign, stc, tf); + auto fop = new FuncDeclaration(declLoc, Loc.initial, Id.opAssign, stc, tf); fop.storage_class |= STC.inference; fop.isGenerated = true; Expression e; @@ -314,8 +317,7 @@ FuncDeclaration buildOpAssign(StructDeclaration sd, Scope* sc) else if (sd.dtor) { //printf("\tswap copy\n"); - auto tdtor = cast(TypeFunction)sd.dtor.type; - assert(tdtor.ty == Tfunction); + auto tdtor = sd.dtor.type.isTypeFunction(); auto idswap = Identifier.generateId("__swap"); auto swap = new VarDeclaration(loc, sd.type, idswap, new VoidInitializer(loc)); @@ -378,14 +380,14 @@ FuncDeclaration buildOpAssign(StructDeclaration sd, Scope* sc) auto er = new ThisExp(loc); Statement s2 = new ReturnStatement(loc, er); fop.fbody = new CompoundStatement(loc, s1, s2); - tf.isreturn = true; + tf.isReturn = true; } sd.members.push(fop); fop.addMember(sc, sd); sd.hasIdentityAssign = true; // temporary mark identity assignable const errors = global.startGagging(); // Do not report errors, even if the template opAssign fbody makes it. Scope* sc2 = sc.push(); - sc2.stc = 0; + sc2.stc = STC.none; sc2.linkage = LINK.d; fop.dsymbolSemantic(sc2); fop.semantic2(sc2); @@ -407,21 +409,34 @@ FuncDeclaration buildOpAssign(StructDeclaration sd, Scope* sc) /******************************************* * We need an opEquals for the struct if - * any fields has an opEquals. - * Generate one if a user-specified one does not exist. + * any field has an opEquals and a user-specified one does not exist. + * Params: + * sd = struct to check + * Returns: + * true if need to generate one */ bool needOpEquals(StructDeclaration sd) { + bool dontneed() + { + //printf("\tdontneed\n"); + return false; + } + bool need() + { + //printf("\tneed\n"); + return true; + } //printf("StructDeclaration::needOpEquals() %s\n", sd.toChars()); if (sd.isUnionDeclaration()) { /* If a union has only one field, treat it like a struct */ if (sd.fields.length != 1) - goto Ldontneed; + return dontneed(); } if (sd.hasIdentityEquals) - goto Lneed; + return need(); /* If any of the fields has an opEquals, then we * need it too. */ @@ -433,84 +448,84 @@ bool needOpEquals(StructDeclaration sd) continue; Type tv = v.type.toBasetype(); auto tvbase = tv.baseElemOf(); - if (tvbase.ty == Tstruct) + if (auto ts = tvbase.isTypeStruct()) { - auto ts = cast(TypeStruct)tvbase; if (ts.sym.isUnionDeclaration() && ts.sym.fields.length != 1) continue; if (needOpEquals(ts.sym)) - goto Lneed; + return need(); } - if (tvbase.isfloating()) + if (tvbase.isFloating()) { // This is necessray for: // 1. comparison of +0.0 and -0.0 should be true. // 2. comparison of NANs should be false always. - goto Lneed; + return need(); } if (tvbase.ty == Tarray) - goto Lneed; + return need(); if (tvbase.ty == Taarray) - goto Lneed; + return need(); if (tvbase.ty == Tclass) - goto Lneed; + return need(); } -Ldontneed: - //printf("\tdontneed\n"); - return false; -Lneed: - //printf("\tneed\n"); - return true; + return dontneed(); } /******************************************* * Check given aggregate actually has an identity opEquals or not. + * ad = aggregate to check + * sc = context + * Returns: + * identity opEquals if it is there, null if not */ private FuncDeclaration hasIdentityOpEquals(AggregateDeclaration ad, Scope* sc) { FuncDeclaration f; - if (Dsymbol eq = search_function(ad, Id.eq)) + Dsymbol eq = search_function(ad, Id.opEquals); + if (!eq) + return null; + + /* check identity opEquals exists + */ + scope er = new NullExp(ad.loc, null); // dummy rvalue + scope el = new IdentifierExp(ad.loc, Id.p); // dummy lvalue + auto a = new Expressions(1); + + bool hasIt(Type tthis) { - /* check identity opEquals exists - */ - scope er = new NullExp(ad.loc, null); // dummy rvalue - scope el = new IdentifierExp(ad.loc, Id.p); // dummy lvalue - auto a = new Expressions(1); + const errors = global.startGagging(); // Do not report errors, even if the template opAssign fbody makes it + sc = sc.push(); + sc.tinst = null; + sc.minst = null; - bool hasIt(Type tthis) + FuncDeclaration rfc(Expression e) { - const errors = global.startGagging(); // Do not report errors, even if the template opAssign fbody makes it - sc = sc.push(); - sc.tinst = null; - sc.minst = null; - - FuncDeclaration rfc(Expression e) - { - (*a)[0] = e; - (*a)[0].type = tthis; - return resolveFuncCall(ad.loc, sc, eq, null, tthis, ArgumentList(a), FuncResolveFlag.quiet); - } + (*a)[0] = e; + (*a)[0].type = tthis; + return resolveFuncCall(ad.loc, sc, eq, null, tthis, ArgumentList(a), FuncResolveFlag.quiet); + } - f = rfc(er); - if (!f) - f = rfc(el); + f = rfc(er); + if (!f) + f = rfc(el); - sc = sc.pop(); - global.endGagging(errors); + sc = sc.pop(); + global.endGagging(errors); - return f !is null; - } + return f !is null; + } - if (hasIt(ad.type) || - hasIt(ad.type.constOf()) || - hasIt(ad.type.immutableOf()) || - hasIt(ad.type.sharedOf()) || - hasIt(ad.type.sharedConstOf())) - { - if (f.errors) - return null; - } + if (hasIt(ad.type) || + hasIt(ad.type.constOf()) || + hasIt(ad.type.immutableOf()) || + hasIt(ad.type.sharedOf()) || + hasIt(ad.type.sharedConstOf())) + { + if (f.errors) + return null; } + return f; } @@ -522,7 +537,7 @@ private FuncDeclaration hasIdentityOpEquals(AggregateDeclaration ad, Scope* sc) * opEquals is changed to be never implicitly generated. * Now, struct objects comparison s1 == s2 is translated to: * s1.tupleof == s2.tupleof - * to calculate structural equality. See EqualExp.op_overload. + * to calculate structural equality. See `opOverloadEquals`. */ FuncDeclaration buildOpEquals(StructDeclaration sd, Scope* sc) { @@ -549,7 +564,7 @@ FuncDeclaration buildXopEquals(StructDeclaration sd, Scope* sc) return null; // bitwise comparison would work //printf("StructDeclaration::buildXopEquals() %s\n", sd.toChars()); - if (Dsymbol eq = search_function(sd, Id.eq)) + if (Dsymbol eq = search_function(sd, Id.opEquals)) { if (FuncDeclaration fd = eq.isFuncDeclaration()) { @@ -563,7 +578,7 @@ FuncDeclaration buildXopEquals(StructDeclaration sd, Scope* sc) parameters.push(new Parameter(Loc.initial, STC.ref_ | STC.const_, sd.type, null, null, null)); tfeqptr = new TypeFunction(ParameterList(parameters), Type.tbool, LINK.d); tfeqptr.mod = MODFlags.const_; - tfeqptr = cast(TypeFunction)tfeqptr.typeSemantic(Loc.initial, &scx); + tfeqptr = tfeqptr.typeSemantic(Loc.initial, &scx).isTypeFunction(); } fd = fd.overloadExactMatch(tfeqptr); if (fd) @@ -592,16 +607,16 @@ FuncDeclaration buildXopEquals(StructDeclaration sd, Scope* sc) auto tf = new TypeFunction(ParameterList(parameters), Type.tbool, LINK.d, STC.const_); tf = tf.addSTC(STC.const_).toTypeFunction(); Identifier id = Id.xopEquals; - auto fop = new FuncDeclaration(declLoc, Loc.initial, id, 0, tf); + auto fop = new FuncDeclaration(declLoc, Loc.initial, id, STC.none, tf); fop.isGenerated = true; fop.parent = sd; Expression e1 = new IdentifierExp(loc, Id.This); Expression e2 = new IdentifierExp(loc, Id.p); Expression e = new EqualExp(EXP.equal, loc, e1, e2); fop.fbody = new ReturnStatement(loc, e); - uint errors = global.startGagging(); // Do not report errors + const errors = global.startGagging(); // Do not report errors Scope* sc2 = sc.push(); - sc2.stc = 0; + sc2.stc = STC.none; sc2.linkage = LINK.d; fop.dsymbolSemantic(sc2); fop.semantic2(sc2); @@ -624,7 +639,7 @@ FuncDeclaration buildXopEquals(StructDeclaration sd, Scope* sc) FuncDeclaration buildXopCmp(StructDeclaration sd, Scope* sc) { //printf("StructDeclaration::buildXopCmp() %s\n", toChars()); - if (Dsymbol cmp = search_function(sd, Id.cmp)) + if (Dsymbol cmp = search_function(sd, Id.opCmp)) { if (FuncDeclaration fd = cmp.isFuncDeclaration()) { @@ -638,11 +653,10 @@ FuncDeclaration buildXopCmp(StructDeclaration sd, Scope* sc) parameters.push(new Parameter(Loc.initial, STC.ref_ | STC.const_, sd.type, null, null, null)); tfcmpptr = new TypeFunction(ParameterList(parameters), Type.tint32, LINK.d); tfcmpptr.mod = MODFlags.const_; - tfcmpptr = cast(TypeFunction)tfcmpptr.typeSemantic(Loc.initial, &scx); + tfcmpptr = tfcmpptr.typeSemantic(Loc.initial, &scx).isTypeFunction(); } - fd = fd.overloadExactMatch(tfcmpptr); - if (fd) - return fd; + if (auto fdo = fd.overloadExactMatch(tfcmpptr)) + return fdo; } } else @@ -653,7 +667,7 @@ FuncDeclaration buildXopCmp(StructDeclaration sd, Scope* sc) * Consider 'alias this', but except opDispatch. */ Expression e = new DsymbolExp(sd.loc, sd); - e = new DotIdExp(sd.loc, e, Id.cmp); + e = new DotIdExp(sd.loc, e, Id.opCmp); Scope* sc2 = sc.push(); e = e.trySemantic(sc2); sc2.pop(); @@ -674,7 +688,7 @@ FuncDeclaration buildXopCmp(StructDeclaration sd, Scope* sc) default: break; } - if (!s || s.ident != Id.cmp) + if (!s || s.ident != Id.opCmp) e = null; // there's no valid member 'opCmp' } if (!e) @@ -717,16 +731,16 @@ FuncDeclaration buildXopCmp(StructDeclaration sd, Scope* sc) auto tf = new TypeFunction(ParameterList(parameters), Type.tint32, LINK.d, STC.const_); tf = tf.addSTC(STC.const_).toTypeFunction(); Identifier id = Id.xopCmp; - auto fop = new FuncDeclaration(declLoc, Loc.initial, id, 0, tf); + auto fop = new FuncDeclaration(declLoc, Loc.initial, id, STC.none, tf); fop.isGenerated = true; fop.parent = sd; Expression e1 = new IdentifierExp(loc, Id.This); Expression e2 = new IdentifierExp(loc, Id.p); - Expression e = new CallExp(loc, new DotIdExp(loc, e1, Id.cmp), e2); + Expression e = new CallExp(loc, new DotIdExp(loc, e1, Id.opCmp), e2); fop.fbody = new ReturnStatement(loc, e); - uint errors = global.startGagging(); // Do not report errors + const errors = global.startGagging(); // Do not report errors Scope* sc2 = sc.push(); - sc2.stc = 0; + sc2.stc = STC.none; sc2.linkage = LINK.d; fop.dsymbolSemantic(sc2); fop.semantic2(sc2); @@ -739,15 +753,31 @@ FuncDeclaration buildXopCmp(StructDeclaration sd, Scope* sc) /******************************************* * We need a toHash for the struct if * any fields has a toHash. - * Generate one if a user-specified one does not exist. + * (will generate one if a user-specified one does not exist) + * Params: + * sd = struct to check + * Returns: + * need to generate toHash() + * References: + * https://dlang.org/spec/hash-map.html#using_struct_as_key */ private bool needToHash(StructDeclaration sd) { + bool dontneed() + { + //printf("\tdontneed\n"); + return false; + } + bool need() + { + //printf("\tneed\n"); + return true; + } //printf("StructDeclaration::needToHash() %s\n", sd.toChars()); if (sd.isUnionDeclaration()) - goto Ldontneed; + return dontneed(); if (sd.xhash) - goto Lneed; + return need(); /* If any of the fields has an toHash, then we * need it too. @@ -760,34 +790,28 @@ private bool needToHash(StructDeclaration sd) continue; Type tv = v.type.toBasetype(); auto tvbase = tv.baseElemOf(); - if (tvbase.ty == Tstruct) + if (auto ts = tvbase.isTypeStruct()) { - auto ts = cast(TypeStruct)tvbase; if (ts.sym.isUnionDeclaration()) continue; if (needToHash(ts.sym)) - goto Lneed; + return need(); } - if (tvbase.isfloating()) + if (tvbase.isFloating()) { /* This is necessary because comparison of +0.0 and -0.0 should be true, * i.e. not a bit compare. */ - goto Lneed; + return need(); } if (tvbase.ty == Tarray) - goto Lneed; + return need(); if (tvbase.ty == Taarray) - goto Lneed; + return need(); if (tvbase.ty == Tclass) - goto Lneed; + return need(); } -Ldontneed: - //printf("\tdontneed\n"); - return false; -Lneed: - //printf("\tneed\n"); - return true; + return dontneed(); } /****************************************** @@ -803,13 +827,12 @@ FuncDeclaration buildXtoHash(StructDeclaration sd, Scope* sc) { tftohash = new TypeFunction(ParameterList(), Type.thash_t, LINK.d); tftohash.mod = MODFlags.const_; - tftohash = cast(TypeFunction)tftohash.merge(); + tftohash = tftohash.merge().isTypeFunction(); } if (FuncDeclaration fd = s.isFuncDeclaration()) { - fd = fd.overloadExactMatch(tftohash); - if (fd) - return fd; + if (auto fdo = fd.overloadExactMatch(tftohash)) + return fdo; } } if (!needToHash(sd)) @@ -825,7 +848,7 @@ FuncDeclaration buildXtoHash(StructDeclaration sd, Scope* sc) * Note that it would only be necessary if it has floating point fields. * For now, we'll just not generate a toHash() for C files. */ - if (sc.flags & SCOPE.Cfile) + if (sc.inCfile) return null; //printf("StructDeclaration::buildXtoHash() %s\n", sd.toPrettyChars()); @@ -854,7 +877,7 @@ FuncDeclaration buildXtoHash(StructDeclaration sd, Scope* sc) "return h;"; fop.fbody = new MixinStatement(loc, new StringExp(loc, code)); Scope* sc2 = sc.push(); - sc2.stc = 0; + sc2.stc = STC.none; sc2.linkage = LINK.d; fop.dsymbolSemantic(sc2); fop.semantic2(sc2); @@ -882,7 +905,7 @@ void buildDtors(AggregateDeclaration ad, Scope* sc) if (ad.isUnionDeclaration()) return; // unions don't have destructors - StorageClass stc = STC.safe | STC.nothrow_ | STC.pure_ | STC.nogc; + STC stc = STC.safe | STC.nothrow_ | STC.pure_ | STC.nogc; Loc declLoc = ad.userDtors.length ? ad.userDtors[0].loc : ad.loc; Loc loc; // internal code should have no loc to prevent coverage FuncDeclaration xdtor_fwd = null; @@ -900,10 +923,10 @@ void buildDtors(AggregateDeclaration ad, Scope* sc) continue; if (v.overlapped) continue; - auto tv = v.type.baseElemOf(); - if (tv.ty != Tstruct) + auto tvs = v.type.baseElemOf().isTypeStruct(); + if (!tvs) continue; - auto sdv = (cast(TypeStruct)tv).sym; + auto sdv = tvs.sym; if (!sdv.dtor) continue; @@ -925,8 +948,8 @@ void buildDtors(AggregateDeclaration ad, Scope* sc) } Expression ex; - tv = v.type.toBasetype(); - if (tv.ty == Tstruct) + Type tv = v.type.toBasetype(); + if (tv.isTypeStruct()) { // this.v.__xdtor() @@ -1086,7 +1109,7 @@ private DtorDeclaration buildWindowsCppDtor(AggregateDeclaration ad, DtorDeclara // // TODO: if (del) delete (char*)this; // return (void*) this; // } - Parameter delparam = new Parameter(Loc.initial, STC.undefined_, Type.tuns32, Identifier.idPool("del"), new IntegerExp(dtor.loc, 0, Type.tuns32), null); + Parameter delparam = new Parameter(Loc.initial, STC.none, Type.tuns32, Identifier.idPool("del"), new IntegerExp(dtor.loc, 0, Type.tuns32), null); Parameters* params = new Parameters; params.push(delparam); const stc = dtor.storage_class & ~STC.scope_; // because we add the `return this;` later @@ -1179,6 +1202,14 @@ private DtorDeclaration buildExternDDtor(AggregateDeclaration ad, Scope* sc) * invs[0](), invs[1](), ...; * } * --- + * Params: + * ad = aggregate for creating invariant + * sc = context + * Returns: + * generated invariant, null if not needed + * References: + * https://dlang.org/spec/class.html#invariants + * https://dlang.org/spec/struct.html#Invariant */ FuncDeclaration buildInv(AggregateDeclaration ad, Scope* sc) { @@ -1193,8 +1224,8 @@ FuncDeclaration buildInv(AggregateDeclaration ad, Scope* sc) default: Expression e = null; - StorageClass stcx = 0; - StorageClass stc = STC.safe | STC.nothrow_ | STC.pure_ | STC.nogc; + STC stcx = STC.none; + STC stc = STC.safe | STC.nothrow_ | STC.pure_ | STC.nogc; foreach (i, inv; ad.invs) { stc = mergeFuncAttrs(stc, inv); @@ -1203,7 +1234,7 @@ FuncDeclaration buildInv(AggregateDeclaration ad, Scope* sc) // What should do? } const stcy = (inv.storage_class & STC.synchronized_) | - (inv.type.mod & MODFlags.shared_ ? STC.shared_ : 0); + (inv.type.mod & MODFlags.shared_ ? STC.shared_ : STC.none); if (i == 0) stcx = stcy; else if (stcx ^ stcy) @@ -1232,6 +1263,11 @@ FuncDeclaration buildInv(AggregateDeclaration ad, Scope* sc) * all the members. * Note the close similarity with AggregateDeclaration::buildDtor(), * and the ordering changes (runs forward instead of backwards). + * Params: + * sd = struct to create postblit for + * sc = context + * Returns: + * generated postblit, or null if not */ FuncDeclaration buildPostBlit(StructDeclaration sd, Scope* sc) { @@ -1242,7 +1278,7 @@ FuncDeclaration buildPostBlit(StructDeclaration sd, Scope* sc) const hasUserDefinedPosblit = sd.postblits.length && !sd.postblits[0].isDisabled ? true : false; // by default, the storage class of the created postblit - StorageClass stc = STC.safe | STC.nothrow_ | STC.pure_ | STC.nogc; + STC stc = STC.safe; Loc declLoc = sd.postblits.length ? sd.postblits[0].loc : sd.loc; Loc loc; // internal code should have no loc to prevent coverage @@ -1262,10 +1298,10 @@ FuncDeclaration buildPostBlit(StructDeclaration sd, Scope* sc) if (structField.overlapped) continue; // if it's a struct declaration or an array of structs - Type tv = structField.type.baseElemOf(); - if (tv.ty != Tstruct) + TypeStruct tvs = structField.type.baseElemOf().isTypeStruct(); + if (!tvs) continue; - auto sdv = (cast(TypeStruct)tv).sym; + auto sdv = tvs.sym; // which has a postblit declaration if (!sdv.postblit) continue; @@ -1275,15 +1311,15 @@ FuncDeclaration buildPostBlit(StructDeclaration sd, Scope* sc) // block to destroy any prior successfully postblitted fields should // this field's postblit fail. // Don't generate it for betterC code since it cannot throw exceptions. - if (fieldsToDestroy.length > 0 && !(cast(TypeFunction)sdv.postblit.type).isnothrow && global.params.useExceptions) + if (fieldsToDestroy.length > 0 && !sdv.postblit.type.isTypeFunction().isNothrow && global.params.useExceptions) { // create a list of destructors that need to be called Expression[] dtorCalls; foreach(sf; fieldsToDestroy) { Expression ex; - tv = sf.type.toBasetype(); - if (tv.ty == Tstruct) + Type tv = sf.type.toBasetype(); + if (auto tvs2 = tv.isTypeStruct()) { // this.v.__xdtor() @@ -1297,9 +1333,7 @@ FuncDeclaration buildPostBlit(StructDeclaration sd, Scope* sc) if (stc & STC.safe) stc = (stc & ~STC.safe) | STC.trusted; - auto sfv = (cast(TypeStruct)sf.type.baseElemOf()).sym; - - ex = new DotVarExp(loc, ex, sfv.dtor, false); + ex = new DotVarExp(loc, ex, tvs2.sym.dtor, false); ex = new CallExp(loc, ex); dtorCalls ~= ex; @@ -1346,6 +1380,7 @@ FuncDeclaration buildPostBlit(StructDeclaration sd, Scope* sc) // perform semantic on the member postblit in order to // be able to aggregate it later on with the rest of the // postblits + sdv.postblit.isGenerated = true; functionSemantic(sdv.postblit); stc = mergeFuncAttrs(stc, sdv.postblit); @@ -1361,8 +1396,8 @@ FuncDeclaration buildPostBlit(StructDeclaration sd, Scope* sc) } Expression ex; - tv = structField.type.toBasetype(); - if (tv.ty == Tstruct) + Type tv = structField.type.toBasetype(); + if (tv.isTypeStruct()) { // this.v.__xpostblit() @@ -1411,6 +1446,7 @@ FuncDeclaration buildPostBlit(StructDeclaration sd, Scope* sc) */ if (sdv.dtor) { + sdv.dtor.isGenerated = true; functionSemantic(sdv.dtor); // keep a list of fields that need to be destroyed in case @@ -1511,25 +1547,27 @@ FuncDeclaration buildPostBlit(StructDeclaration sd, Scope* sc) } /** - * Generates a copy constructor declaration with the specified storage + * Generates a copy or move constructor declaration with the specified storage * class for the parameter and the function. * * Params: - * sd = the `struct` that contains the copy constructor - * paramStc = the storage class of the copy constructor parameter - * funcStc = the storage class for the copy constructor declaration + * sd = the `struct` that contains the constructor + * paramStc = the storage class of the constructor parameter + * funcStc = the storage class for the constructor declaration + * move = true for move constructor, false for copy constructor * * Returns: * The copy constructor declaration for struct `sd`. */ -private CtorDeclaration generateCopyCtorDeclaration(StructDeclaration sd, const StorageClass paramStc, const StorageClass funcStc) +private CtorDeclaration generateCtorDeclaration(StructDeclaration sd, const STC paramStc, const STC funcStc, bool move) { auto fparams = new Parameters(); auto structType = sd.type; - fparams.push(new Parameter(Loc.initial, paramStc | STC.ref_ | STC.return_ | STC.scope_, structType, Id.p, null, null)); + STC stc = move ? STC.none : STC.ref_; // the only difference between copy or move + fparams.push(new Parameter(Loc.initial, paramStc | stc, structType, Id.p, null, null)); ParameterList pList = ParameterList(fparams); auto tf = new TypeFunction(pList, structType, LINK.d, STC.ref_); - auto ccd = new CtorDeclaration(sd.loc, Loc.initial, STC.ref_, tf, true); + auto ccd = new CtorDeclaration(sd.loc, Loc.initial, STC.ref_, tf); ccd.storage_class |= funcStc; ccd.storage_class |= STC.inference; ccd.isGenerated = true; @@ -1537,28 +1575,37 @@ private CtorDeclaration generateCopyCtorDeclaration(StructDeclaration sd, const } /** - * Generates a trivial copy constructor body that simply does memberwise - * initialization: + * Generates a trivial copy or move constructor body that simply does memberwise + * initialization. * + * for copy construction: * this.field1 = rhs.field1; * this.field2 = rhs.field2; * ... + * for move construction: + * this.field1 = __rvalue(rhs.field1); + * this.field2 = __rvalue(rhs.field2); + * ... * * Params: - * sd = the `struct` declaration that contains the copy constructor + * sd = the `struct` declaration that contains the constructor + * move = true for move constructor, false for copy constructor * * Returns: - * A `CompoundStatement` containing the body of the copy constructor. + * A `CompoundStatement` containing the body of the constructor. */ -private Statement generateCopyCtorBody(StructDeclaration sd) +private Statement generateCtorBody(StructDeclaration sd, bool move) { Loc loc; Expression e; foreach (v; sd.fields) { + Expression rhs = new DotVarExp(loc, new IdentifierExp(loc, Id.p), v); + if (move) + rhs.rvalue = true; auto ec = new AssignExp(loc, new DotVarExp(loc, new ThisExp(loc), v), - new DotVarExp(loc, new IdentifierExp(loc, Id.p), v)); + rhs); e = Expression.combine(e, ec); //printf("e.toChars = %s\n", e.toChars()); } @@ -1566,27 +1613,18 @@ private Statement generateCopyCtorBody(StructDeclaration sd) return new CompoundStatement(loc, s1); } -/** - * Determine if a copy constructor is needed for struct sd, - * if the following conditions are met: - * - * 1. sd does not define a copy constructor - * 2. at least one field of sd defines a copy constructor - * +/****************************************** + * Find root `this` constructor for struct sd. + * (root is starting position for overloaded constructors) * Params: - * sd = the `struct` for which the copy constructor is generated - * hasCpCtor = set to true if a copy constructor is already present - * - * Returns: - * `true` if one needs to be generated - * `false` otherwise + * sd = the `struct` to be searched + * ctor = `this` if found, otherwise null + * Result: + * false means `this` found in overload set */ -bool needCopyCtor(StructDeclaration sd, out bool hasCpCtor) +private bool findStructConstructorRoot(StructDeclaration sd, out Dsymbol ctor) { - if (global.errors) - return false; - - auto ctor = sd.search(sd.loc, Id.ctor); + ctor = sd.search(sd.loc, Id.ctor); // Aggregate.searchCtor() ? if (ctor) { if (ctor.isOverloadSet()) @@ -1594,13 +1632,18 @@ bool needCopyCtor(StructDeclaration sd, out bool hasCpCtor) if (auto td = ctor.isTemplateDeclaration()) ctor = td.funcroot; } + return true; +} - CtorDeclaration cpCtor; - CtorDeclaration rvalueCtor; - - if (!ctor) - goto LcheckFields; - +/*********************************************** + * Find move and copy constructors (if any) starting at `ctor` + * Params: + * ctor = `this` constructor root + * copyCtor = set to first copy constructor found, or null + * moveCtor = set to first move constructor found, or null + */ +private void findMoveAndCopyConstructors(Dsymbol ctor, out CtorDeclaration copyCtor, out CtorDeclaration moveCtor) +{ overloadApply(ctor, (Dsymbol s) { if (s.isTemplateDeclaration()) @@ -1609,38 +1652,63 @@ bool needCopyCtor(StructDeclaration sd, out bool hasCpCtor) assert(ctorDecl); if (ctorDecl.isCpCtor) { - if (!cpCtor) - cpCtor = ctorDecl; - return 0; + if (!copyCtor) + copyCtor = ctorDecl; } - - auto tf = ctorDecl.type.toTypeFunction(); - const dim = tf.parameterList.length; - if (dim == 1 || (dim > 1 && tf.parameterList[1].defaultArg)) + else if (ctorDecl.isMoveCtor) { - auto param = tf.parameterList[0]; - if (param.type.mutableOf().unSharedOf() == sd.type.mutableOf().unSharedOf()) - { - rvalueCtor = ctorDecl; - } + if (!moveCtor) + moveCtor = ctorDecl; } return 0; }); +} - if (cpCtor) - { - if (rvalueCtor) - { - .error(sd.loc, "`struct %s` may not define both a rvalue constructor and a copy constructor", sd.toChars()); - errorSupplemental(rvalueCtor.loc,"rvalue constructor defined here"); - errorSupplemental(cpCtor.loc, "copy constructor defined here"); - } - hasCpCtor = true; - return false; - } +/** + * Determine if a copy constructor is needed for struct sd, + * if the following conditions are met: + * + * 1. sd does not define a copy constructor + * 2. at least one field of sd defines a copy constructor + * + * Params: + * sd = the `struct` for which the copy constructor is generated + * hasCopyCtor = set to true if a copy constructor is already present + * hasMoveCtor = set to true if a move constructor is already present + * needCopyCtor = set to true if a copy constructor is not present, but needed + * needMoveCtor = set to true if a move constructor is not present, but needed + * + * Returns: + * `true` if one needs to be generated + * `false` otherwise + */ +void needCopyOrMoveCtor(StructDeclaration sd, out bool hasCopyCtor, out bool hasMoveCtor, out bool needCopyCtor, out bool needMoveCtor) +{ + //printf("needCopyOrMoveCtor() %s\n", sd.toChars()); + if (global.errors) + return; + + Dsymbol ctor; + if (!findStructConstructorRoot(sd, ctor)) + return; + + CtorDeclaration copyCtor; + CtorDeclaration moveCtor; + + if (ctor) + findMoveAndCopyConstructors(ctor, copyCtor, moveCtor); + + if (moveCtor) + hasMoveCtor = true; + + if (copyCtor) + hasCopyCtor = true; + + if (hasMoveCtor && hasCopyCtor) + return; -LcheckFields: VarDeclaration fieldWithCpCtor; + VarDeclaration fieldWithMoveCtor; // see if any struct members define a copy constructor foreach (v; sd.fields) { @@ -1655,20 +1723,25 @@ LcheckFields: if (ts.sym.hasCopyCtor) { fieldWithCpCtor = v; - break; + } + if (ts.sym.hasMoveCtor) + { + fieldWithMoveCtor = v; } } - if (fieldWithCpCtor && rvalueCtor) + if (0 && fieldWithCpCtor && moveCtor) { .error(sd.loc, "`struct %s` may not define a rvalue constructor and have fields with copy constructors", sd.toChars()); - errorSupplemental(rvalueCtor.loc,"rvalue constructor defined here"); + errorSupplemental(moveCtor.loc,"rvalue constructor defined here"); errorSupplemental(fieldWithCpCtor.loc, "field with copy constructor defined here"); - return false; + return; } - else if (!fieldWithCpCtor) - return false; - return true; + + if (fieldWithCpCtor && !hasCopyCtor) + needCopyCtor = true; + if (fieldWithMoveCtor && !hasMoveCtor) + needMoveCtor = true; } /** @@ -1682,30 +1755,25 @@ LcheckFields: * } * * Params: - * sd = the `struct` for which the copy constructor is generated - * sc = the scope where the copy constructor is generated - * - * Returns: - * `true` if `struct` sd defines a copy constructor (explicitly or generated), - * `false` otherwise. + * sd = the `struct` for which the constructor is generated + * sc = the scope where the constructor is generated + * move = true means generate the move constructor, otherwise copy constructor + * References: + * https://dlang.org/spec/struct.html#struct-copy-constructor */ -bool buildCopyCtor(StructDeclaration sd, Scope* sc) +void buildCopyOrMoveCtor(StructDeclaration sd, Scope* sc, bool move) { - bool hasCpCtor; - if (!needCopyCtor(sd, hasCpCtor)) - return hasCpCtor; - - //printf("generating copy constructor for %s\n", sd.toChars()); + //printf("buildCopyOrMoveCtor() generating %s constructor for %s\n", move ? "move".ptr : "copy".ptr, sd.toChars()); const MOD paramMod = MODFlags.wild; const MOD funcMod = MODFlags.wild; - auto ccd = generateCopyCtorDeclaration(sd, ModToStc(paramMod), ModToStc(funcMod)); - auto copyCtorBody = generateCopyCtorBody(sd); - ccd.fbody = copyCtorBody; + auto ccd = generateCtorDeclaration(sd, ModToStc(paramMod), ModToStc(funcMod), move); + auto ctorBody = generateCtorBody(sd, move); + ccd.fbody = ctorBody; sd.members.push(ccd); ccd.addMember(sc, sd); const errors = global.startGagging(); Scope* sc2 = sc.push(); - sc2.stc = 0; + sc2.stc = STC.none; sc2.linkage = LINK.d; ccd.dsymbolSemantic(sc2); ccd.semantic2(sc2); @@ -1717,5 +1785,4 @@ bool buildCopyCtor(StructDeclaration sd, Scope* sc) ccd.storage_class |= STC.disable; ccd.fbody = null; } - return true; } diff --git a/dmd/common/bitfields.d b/dmd/common/bitfields.d index 01aa56d180..72cb3e7d91 100644 --- a/dmd/common/bitfields.d +++ b/dmd/common/bitfields.d @@ -1,12 +1,12 @@ /** * A library bitfields utility * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: Dennis Korpel * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/common/bitfields.d, common/bitfields.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/common/bitfields.d, common/bitfields.d) * Documentation: https://dlang.org/phobos/dmd_common_bitfields.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/common/bitfields.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/common/bitfields.d */ module dmd.common.bitfields; @@ -20,43 +20,79 @@ module dmd.common.bitfields; extern (D) string generateBitFields(S, T)() if (__traits(isUnsigned, T)) { + import core.bitop: bsr; + string result = "extern (C++) pure nothrow @nogc @safe final {"; - enum structName = __traits(identifier, S); - string initialValue = ""; + struct BitInfo + { + int[] offset; + int[] size; + T initialValue; + int totalSize; + } + + // Iterate over members to compute bit offset and bit size for each of them + enum BitInfo bitInfo = () { + BitInfo result; + int bitOffset = 0; + foreach (size_t i, mem; __traits(allMembers, S)) + { + alias memType = typeof(__traits(getMember, S, mem)); + enum int bitSize = bsr(memType.max | 1) + 1; + result.offset ~= bitOffset; + result.size ~= bitSize; + result.initialValue |= cast(T) __traits(getMember, S.init, mem) << bitOffset; + bitOffset += bitSize; + } + result.totalSize = bitOffset; + return result; + } (); + + alias TP = typeof(T.init + 0u); // type that `T` gets promoted to, uint or ulong + enum string toString(TP i) = i.stringof; // compile time 'integer to string' + + static assert(bitInfo.totalSize <= T.sizeof * 8, + "sum of bit field size "~toString!(bitInfo.totalSize)~" exceeds storage type `"~T.stringof~"`"); + foreach (size_t i, mem; __traits(allMembers, S)) { - static assert(is(typeof(__traits(getMember, S, mem)) == bool)); - static assert(i < T.sizeof * 8, "too many fields for bit field storage of type `"~T.stringof~"`"); - enum mask = "(1 << "~i.stringof~")"; + enum typeName = typeof(__traits(getMember, S, mem)).stringof; + enum shift = toString!(bitInfo.offset[i]); + enum sizeMask = toString!((1 << bitInfo.size[i]) - 1); // 0x01 for bool, 0xFF for ubyte etc. result ~= " - /// set or get the corresponding "~structName~" member - bool "~mem~"() const scope { return !!(bitFields & "~mask~"); } - /// ditto - bool "~mem~"(bool v) + "~typeName~" "~mem~"() const scope { return cast("~typeName~") ((bitFields >>> "~shift~") & "~sizeMask~"); } + "~typeName~" "~mem~"("~typeName~" v) scope { - v ? (bitFields |= "~mask~") : (bitFields &= ~"~mask~"); + bitFields &= ~("~sizeMask~" << "~shift~"); + bitFields |= v << "~shift~"; return v; }"; - - initialValue = (__traits(getMember, S.init, mem) ? "1" : "0") ~ initialValue; } - return result ~ "}\n private "~T.stringof~" bitFields = 0b" ~ initialValue ~ ";\n"; + enum TP initVal = bitInfo.initialValue; + return result ~ "\n}\n private "~T.stringof~" bitFields = " ~ toString!(initVal) ~ ";\n"; } /// unittest { + enum E + { + a, b, c, + } + static struct B { bool x; bool y; + E e = E.c; bool z = 1; + private ubyte w = 77; } static struct S { - mixin(generateBitFields!(B, ubyte)); + mixin(generateBitFields!(B, ushort)); } S s; @@ -69,5 +105,13 @@ unittest s.y = true; assert(s.y); assert(!s.x); + + assert(s.e == E.c); + s.e = E.a; + assert(s.e == E.a); + assert(s.z); + assert(s.w == 77); + s.w = 3; + assert(s.w == 3); } diff --git a/dmd/common/blake3.d b/dmd/common/blake3.d index c040483a64..8390a67b75 100644 --- a/dmd/common/blake3.d +++ b/dmd/common/blake3.d @@ -51,7 +51,7 @@ public ubyte[32] blake3(scope const ubyte[] data) with(state) { //can't be end since we handle the last block separately below - uint flag = blocksCompressed == 0 ? ChunkStartFlag : 0; + const flag = blocksCompressed == 0 ? ChunkStartFlag : 0; uint[16] compressed = compress(chainingValue, blockWords, 64, //full block chunkCounter, flag); chainingValue = compressed[0 .. 8]; @@ -61,7 +61,7 @@ public ubyte[32] blake3(scope const ubyte[] data) } //handle last block, which could be the first block too - uint flag = ChunkEndFlag | (state.blocksCompressed == 0 ? ChunkStartFlag : 0); + const flag = ChunkEndFlag | (state.blocksCompressed == 0 ? ChunkStartFlag : 0); //cast is safe bc this must be <= BlockLength const remainingBytes = cast(uint)(data.length - cursor); diff --git a/dmd/common/charactertables.d b/dmd/common/charactertables.d index 951c335817..7df5234904 100644 --- a/dmd/common/charactertables.d +++ b/dmd/common/charactertables.d @@ -3,12 +3,12 @@ * * Supports UAX31, C99, C11 and least restrictive (All). * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://cattermole.co.nz, Richard (Rikki) Andrew Cattermole) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/common/charactertables.d, common/charactertables.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/common/charactertables.d, common/charactertables.d) * Documentation: https://dlang.org/phobos/dmd_common_charactertables.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/common/charactertables.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/common/charactertables.d */ module dmd.common.charactertables; @@ -35,14 +35,15 @@ struct IdentifierCharLookup extern(C++) bool function(dchar) isContinue; /// Lookup the table given the table name - static IdentifierCharLookup forTable(IdentifierTable table) + extern(C++) static IdentifierCharLookup forTable(IdentifierTable table) { import dmd.common.identifiertables; // Awful solution to require these lambdas. // However without them the extern(C++) ABI issues crop up for isInRange, // and then it can't access the tables. - final switch(table) { + final switch(table) + { case IdentifierTable.UAX31: return IdentifierCharLookup( (c) => isInRange!UAX31_Start(c), diff --git a/dmd/common/charactertables.h b/dmd/common/charactertables.h index 4afd9891bb..b38409e302 100644 --- a/dmd/common/charactertables.h +++ b/dmd/common/charactertables.h @@ -3,18 +3,27 @@ * * Supports UAX31, C99, C11 and least restrictive (All). * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://cattermole.co.nz, Richard (Rikki) Andrew Cattermole) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/common/charactertables.d, common/charactertables.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/common/charactertables.h, common/charactertables.d) */ #pragma once +enum class IdentifierTable +{ + UAX31, + C99, + C11, + LR, // Least Restrictive aka All +}; + struct IdentifierCharLookup final { bool(*isStart)(char32_t); bool(*isContinue)(char32_t); // constructor not provided here. + static IdentifierCharLookup forTable(IdentifierTable table); }; diff --git a/dmd/common/file.d b/dmd/common/file.d index 9a3be4cdf7..c8e9b1d13a 100644 --- a/dmd/common/file.d +++ b/dmd/common/file.d @@ -4,12 +4,12 @@ * Functions and objects dedicated to file I/O and management. TODO: Move here artifacts * from places such as root/ so both the frontend and the backend have access to them. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: Walter Bright, https://www.digitalmars.com * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/common/file.d, common/_file.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/common/file.d, common/_file.d) * Documentation: https://dlang.org/phobos/dmd_common_file.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/common/file.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/common/file.d */ module dmd.common.file; diff --git a/dmd/common/int128.d b/dmd/common/int128.d index 796687b1f7..aaf38c5157 100644 --- a/dmd/common/int128.d +++ b/dmd/common/int128.d @@ -2,12 +2,12 @@ * * Not optimized for speed. * - * Copyright: Copyright (C) 2022-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 2022-2025 by The D Language Foundation, All Rights Reserved * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) * Authors: Walter Bright - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/common/int128.d, root/_int128.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/common/int128.d, root/_int128.d) * Documentation: https://dlang.org/phobos/dmd_common_int128.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/common/int128.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/common/int128.d */ module dmd.common.int128; diff --git a/dmd/common/outbuffer.d b/dmd/common/outbuffer.d index f3801cb69c..01fc377400 100644 --- a/dmd/common/outbuffer.d +++ b/dmd/common/outbuffer.d @@ -1,12 +1,12 @@ /** * An expandable buffer in which you can write text or binary data. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: Walter Bright, https://www.digitalmars.com * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/root/outbuffer.d, root/_outbuffer.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/common/outbuffer.d, root/_outbuffer.d) * Documentation: https://dlang.org/phobos/dmd_root_outbuffer.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/root/outbuffer.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/common/outbuffer.d */ module dmd.common.outbuffer; @@ -247,7 +247,7 @@ struct OutBuffer /** * Writes a 16 bit value, no reserve check. */ - nothrow + nothrow @safe void write16n(int v) { auto x = cast(ushort) v; @@ -367,7 +367,7 @@ struct OutBuffer } // Position buffer to accept the specified number of bytes at offset - void position(size_t where, size_t nbytes) nothrow + void position(size_t where, size_t nbytes) nothrow @safe { if (where + nbytes > data.length) { @@ -785,10 +785,11 @@ struct OutBuffer Returns: `true` iff the operation succeeded. */ - extern(D) bool moveToFile(const char* filename) @system + extern(D) bool moveToFile(const char[] filename) @system { bool result = true; - const bool identical = this[] == FileMapping!(const ubyte)(filename)[]; + const filenameZ = (filename ~ "\0").ptr; + const bool identical = this[] == FileMapping!(const ubyte)(filenameZ)[]; if (fileMapping && fileMapping.active) { @@ -801,7 +802,7 @@ struct OutBuffer { // Resize to fit to get rid of the slack bytes at the end fileMapping.resize(offset); - result = fileMapping.moveToFile(filename); + result = fileMapping.moveToFile(filenameZ); } // Can't call destroy() here because the file mapping is already closed. data = null; @@ -810,12 +811,12 @@ struct OutBuffer else { if (!identical) - writeFile(filename, this[]); + writeFile(filenameZ, this[]); destroy(); } return identical - ? result && touchFile(filename) + ? result && touchFile(filenameZ) : result; } } diff --git a/dmd/common/outbuffer.h b/dmd/common/outbuffer.h index 2250497c38..62aca7a801 100644 --- a/dmd/common/outbuffer.h +++ b/dmd/common/outbuffer.h @@ -1,5 +1,5 @@ -/* Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved +/* Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * written by Walter Bright * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. diff --git a/dmd/common/smallbuffer.d b/dmd/common/smallbuffer.d index 3d8336c487..fe0afcc72c 100644 --- a/dmd/common/smallbuffer.d +++ b/dmd/common/smallbuffer.d @@ -1,12 +1,12 @@ /** * Common string functions including filename manipulation. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: Walter Bright, https://www.digitalmars.com * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/common/smallbuffer.d, common/_smallbuffer.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/common/smallbuffer.d, common/_smallbuffer.d) * Documentation: https://dlang.org/phobos/dmd_common_smallbuffer.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/common/smallbuffer + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/common/smallbuffer.d */ module dmd.common.smallbuffer; diff --git a/dmd/compiler.d b/dmd/compiler.d index 8d42e28d28..cccf90061b 100644 --- a/dmd/compiler.d +++ b/dmd/compiler.d @@ -1,12 +1,12 @@ /** * Describes a back-end compiler and implements compiler-specific actions. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/compiler.d, _compiler.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/compiler.d, _compiler.d) * Documentation: https://dlang.org/phobos/dmd_compiler.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/compiler.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/compiler.d */ module dmd.compiler; @@ -138,7 +138,7 @@ extern (C++) struct Compiler */ extern(C++) static bool onImport(Module m) { - if (includeImports && m.filetype == FileType.d) + if (includeImports && (m.filetype == FileType.d || m.filetype == FileType.c)) { if (includeImportedModuleCheck(ModuleComponentRange( m.md ? m.md.packages : [], m.ident, m.isPackageFile))) @@ -164,14 +164,14 @@ extern (C++) struct Compiler * semantic analysis of a statement when importing DMD as a library */ __gshared OnStatementSemanticStart onStatementSemanticStart - = function void(Statement s, Scope *sc) {}; + = function void(Statement s, Scope* sc) {}; /** * Used to insert functionality after the end of the * semantic analysis of a statement when importing DMD as a library */ __gshared OnStatementSemanticDone onStatementSemanticDone - = function void(Statement s, Scope *sc) {}; + = function void(Statement s, Scope* sc) {}; } } @@ -194,8 +194,7 @@ private struct ModuleComponentRange return packages[index]; if (index == packages.length) return name; - else - return Identifier.idPool("package"); + return Identifier.idPool("package"); } void popFront() @safe { index++; } } @@ -299,39 +298,37 @@ private void createMatchNodes() return index; } - if (matchNodes.length == 0) + if (matchNodes.length != 0) + return; + foreach (modulePattern; includeModulePatterns) { - foreach (modulePattern; includeModulePatterns) + const depth = parseModulePatternDepth(modulePattern[0 .. strlen(modulePattern)]); + const entryIndex = findSortedIndexToAddForDepth(depth); + matchNodes.split(entryIndex, depth + 1); + parseModulePattern(modulePattern, &matchNodes[entryIndex], depth); + // if at least 1 "include pattern" is given, then it is assumed the + // user only wants to include modules that were explicitly given, which + // changes the default behavior from inclusion to exclusion. + if (includeByDefault && !matchNodes[entryIndex].isExclude) { - auto depth = parseModulePatternDepth(modulePattern[0 .. strlen(modulePattern)]); - auto entryIndex = findSortedIndexToAddForDepth(depth); - matchNodes.split(entryIndex, depth + 1); - parseModulePattern(modulePattern, &matchNodes[entryIndex], depth); - // if at least 1 "include pattern" is given, then it is assumed the - // user only wants to include modules that were explicitly given, which - // changes the default behavior from inclusion to exclusion. - if (includeByDefault && !matchNodes[entryIndex].isExclude) - { - //printf("Matcher: found 'include pattern', switching default behavior to exclusion\n"); - includeByDefault = false; - } - } - - // Add the default 1 depth matchers - MatcherNode[10] defaultDepth1MatchNodes = [ - MatcherNode(true, 1), MatcherNode(Id.std), - MatcherNode(true, 1), MatcherNode(Id.core), - MatcherNode(true, 1), MatcherNode(Id.etc), - MatcherNode(true, 1), MatcherNode(Id.ldc), // IN_LLVM - MatcherNode(true, 1), MatcherNode(Id.object), - ]; - { - auto index = findSortedIndexToAddForDepth(1); - matchNodes.split(index, defaultDepth1MatchNodes.length); - auto slice = matchNodes[]; - slice[index .. index + defaultDepth1MatchNodes.length] = defaultDepth1MatchNodes[]; + //printf("Matcher: found 'include pattern', switching default behavior to exclusion\n"); + includeByDefault = false; } } + + // Add the default 1 depth matchers + MatcherNode[10] defaultDepth1MatchNodes = [ + MatcherNode(true, 1), MatcherNode(Id.std), + MatcherNode(true, 1), MatcherNode(Id.core), + MatcherNode(true, 1), MatcherNode(Id.etc), + MatcherNode(true, 1), MatcherNode(Id.ldc), // IN_LLVM + MatcherNode(true, 1), MatcherNode(Id.object), + ]; + + const index = findSortedIndexToAddForDepth(1); + matchNodes.split(index, defaultDepth1MatchNodes.length); + auto slice = matchNodes[]; + slice[index .. index + defaultDepth1MatchNodes.length] = defaultDepth1MatchNodes[]; } /* @@ -345,7 +342,7 @@ pure @safe private ushort parseModulePatternDepth(const char[] modulePattern) { const length = modulePattern.length; - size_t i = (length && modulePattern[0] == '-'); // skip past leading '-' + size_t i = length && modulePattern[0] == '-'; // skip past leading '-' // handle special case if (i + 1 == length && modulePattern[i] == '.') diff --git a/dmd/compiler.h b/dmd/compiler.h index 74351edb44..ca085c6649 100644 --- a/dmd/compiler.h +++ b/dmd/compiler.h @@ -1,6 +1,6 @@ /* Compiler implementation of the D programming language - * Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * written by Walter Bright * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. diff --git a/dmd/cond.d b/dmd/cond.d index 02dde1b4c0..1b11a9f180 100644 --- a/dmd/cond.d +++ b/dmd/cond.d @@ -3,12 +3,12 @@ * * Specification: $(LINK2 https://dlang.org/spec/version.html, Conditional Compilation) * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/cond.d, _cond.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/cond.d, _cond.d) * Documentation: https://dlang.org/phobos/dmd_cond.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/cond.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/cond.d */ module dmd.cond; @@ -24,7 +24,7 @@ import dmd.dscope; import dmd.dsymbol; import dmd.errors; import dmd.expression; -import dmd.expressionsem; +import dmd.expressionsem : evalStaticCondition; import dmd.globals; import dmd.identifier; import dmd.location; @@ -64,7 +64,7 @@ extern (C++) abstract class Condition : ASTNode return DYNCAST.condition; } - extern (D) this(const ref Loc loc) @safe + extern (D) this(Loc loc) @safe { this.loc = loc; } @@ -126,7 +126,7 @@ extern (C++) final class StaticForeach : RootObject */ bool needExpansion = false; - extern (D) this(const ref Loc loc, ForeachStatement aggrfe, ForeachRangeStatement rangefe) @safe + extern (D) this(Loc loc, ForeachStatement aggrfe, ForeachRangeStatement rangefe) @safe { assert(!!aggrfe ^ !!rangefe); @@ -144,52 +144,6 @@ extern (C++) final class StaticForeach : RootObject ); } - /***************************************** - * Turn an aggregate which is an array into an expression tuple - * of its elements. I.e., lower - * static foreach (x; [1, 2, 3, 4]) { ... } - * to - * static foreach (x; AliasSeq!(1, 2, 3, 4)) { ... } - */ - private extern(D) void lowerArrayAggregate(Scope* sc) - { - auto aggr = aggrfe.aggr; - Expression el = new ArrayLengthExp(aggr.loc, aggr); - sc = sc.startCTFE(); - el = el.expressionSemantic(sc); - sc = sc.endCTFE(); - el = el.optimize(WANTvalue); - el = el.ctfeInterpret(); - if (el.op == EXP.int64) - { - Expressions *es = void; - if (auto ale = aggr.isArrayLiteralExp()) - { - // Directly use the elements of the array for the TupleExp creation - es = ale.elements; - } - else - { - const length = cast(size_t)el.toInteger(); - es = new Expressions(length); - foreach (i; 0 .. length) - { - auto index = new IntegerExp(loc, i, Type.tsize_t); - auto value = new IndexExp(aggr.loc, aggr, index); - (*es)[i] = value; - } - } - aggrfe.aggr = new TupleExp(aggr.loc, es); - aggrfe.aggr = aggrfe.aggr.expressionSemantic(sc); - aggrfe.aggr = aggrfe.aggr.optimize(WANTvalue); - aggrfe.aggr = aggrfe.aggr.ctfeInterpret(); - } - else - { - aggrfe.aggr = ErrorExp.get(); - } - } - /***************************************** * Wrap a statement into a function literal and call it. * @@ -199,9 +153,9 @@ extern (C++) final class StaticForeach : RootObject * Returns: * AST of the expression `(){ s; }()` with location loc. */ - private extern(D) Expression wrapAndCall(const ref Loc loc, Statement s) + extern(D) Expression wrapAndCall(Loc loc, Statement s) { - auto tf = new TypeFunction(ParameterList(), null, LINK.default_, 0); + auto tf = new TypeFunction(ParameterList(), null, LINK.default_, STC.none); auto fd = new FuncLiteralDeclaration(loc, loc, tf, TOK.reserved, null); fd.fbody = s; auto fe = new FuncExp(loc, fd); @@ -222,7 +176,7 @@ extern (C++) final class StaticForeach : RootObject * `foreach (parameters; lower .. upper) s;` * Where aggregate/lower, upper are as for the current StaticForeach. */ - private extern(D) Statement createForeach(const ref Loc loc, Parameters* parameters, Statement s) + extern(D) Statement createForeach(Loc loc, Parameters* parameters, Statement s) { if (aggrfe) { @@ -255,7 +209,7 @@ extern (C++) final class StaticForeach : RootObject * } */ - private extern(D) TypeStruct createTupleType(const ref Loc loc, Expressions* e, Scope* sc) + extern(D) TypeStruct createTupleType(Loc loc, Expressions* e, Scope* sc) { // TODO: move to druntime? auto sid = Identifier.generateId("Tuple"); auto sdecl = new StructDeclaration(loc, sid, false); @@ -263,7 +217,7 @@ extern (C++) final class StaticForeach : RootObject sdecl.members = new Dsymbols(); auto fid = Identifier.idPool(tupleFieldName); auto ty = new TypeTypeof(loc, new TupleExp(loc, e)); - sdecl.members.push(new VarDeclaration(loc, ty, fid, null, 0)); + sdecl.members.push(new VarDeclaration(loc, ty, fid, null, STC.none)); auto r = cast(TypeStruct)sdecl.type; if (global.params.useTypeInfo && Type.dtypeinfo) r.vtinfo = TypeInfoStructDeclaration.create(r); // prevent typeinfo from going to object file @@ -281,205 +235,11 @@ extern (C++) final class StaticForeach : RootObject * An AST for the expression `Tuple(e)`. */ - private extern(D) Expression createTuple(const ref Loc loc, TypeStruct type, Expressions* e) @safe + extern(D) Expression createTuple(Loc loc, TypeStruct type, Expressions* e) @safe { // TODO: move to druntime? return new CallExp(loc, new TypeExp(loc, type), e); } - - /***************************************** - * Lower any aggregate that is not an array to an array using a - * regular foreach loop within CTFE. If there are multiple - * `static foreach` loop variables, an array of tuples is - * generated. In thise case, the field `needExpansion` is set to - * true to indicate that the static foreach loop expansion will - * need to expand the tuples into multiple variables. - * - * For example, `static foreach (x; range) { ... }` is lowered to: - * - * static foreach (x; { - * typeof({ - * foreach (x; range) return x; - * }())[] __res; - * foreach (x; range) __res ~= x; - * return __res; - * }()) { ... } - * - * Finally, call `lowerArrayAggregate` to turn the produced - * array into an expression tuple. - * - * Params: - * sc = The current scope. - */ - - private void lowerNonArrayAggregate(Scope* sc) - { - auto nvars = aggrfe ? aggrfe.parameters.length : 1; - auto aloc = aggrfe ? aggrfe.aggr.loc : rangefe.lwr.loc; - // We need three sets of foreach loop variables because the - // lowering contains three foreach loops. - Parameters*[3] pparams = [new Parameters(), new Parameters(), new Parameters()]; - foreach (i; 0 .. nvars) - { - foreach (params; pparams) - { - auto p = aggrfe ? (*aggrfe.parameters)[i] : rangefe.prm; - params.push(new Parameter(aloc, p.storageClass, p.type, p.ident, null, null)); - } - } - Expression[2] res; - TypeStruct tplty = null; - if (nvars == 1) // only one `static foreach` variable, generate identifiers. - { - foreach (i; 0 .. 2) - { - res[i] = new IdentifierExp(aloc, (*pparams[i])[0].ident); - } - } - else // multiple `static foreach` variables, generate tuples. - { - foreach (i; 0 .. 2) - { - auto e = new Expressions(pparams[0].length); - foreach (j, ref elem; *e) - { - auto p = (*pparams[i])[j]; - elem = new IdentifierExp(aloc, p.ident); - } - if (!tplty) - { - tplty = createTupleType(aloc, e, sc); - } - res[i] = createTuple(aloc, tplty, e); - } - needExpansion = true; // need to expand the tuples later - } - // generate remaining code for the new aggregate which is an - // array (see documentation comment). - if (rangefe) - { - sc = sc.startCTFE(); - rangefe.lwr = rangefe.lwr.expressionSemantic(sc); - rangefe.lwr = resolveProperties(sc, rangefe.lwr); - rangefe.upr = rangefe.upr.expressionSemantic(sc); - rangefe.upr = resolveProperties(sc, rangefe.upr); - sc = sc.endCTFE(); - rangefe.lwr = rangefe.lwr.optimize(WANTvalue); - rangefe.lwr = rangefe.lwr.ctfeInterpret(); - rangefe.upr = rangefe.upr.optimize(WANTvalue); - rangefe.upr = rangefe.upr.ctfeInterpret(); - } - auto s1 = new Statements(); - auto sfe = new Statements(); - if (tplty) sfe.push(new ExpStatement(loc, tplty.sym)); - sfe.push(new ReturnStatement(aloc, res[0])); - s1.push(createForeach(aloc, pparams[0], new CompoundStatement(aloc, sfe))); - s1.push(new ExpStatement(aloc, new AssertExp(aloc, IntegerExp.literal!0))); - Type ety = new TypeTypeof(aloc, wrapAndCall(aloc, new CompoundStatement(aloc, s1))); - auto aty = ety.arrayOf(); - auto idres = Identifier.generateId("__res"); - auto vard = new VarDeclaration(aloc, aty, idres, null, STC.temp); - auto s2 = new Statements(); - - // Run 'typeof' gagged to avoid duplicate errors and if it fails just create - // an empty foreach to expose them. - uint olderrors = global.startGagging(); - ety = ety.typeSemantic(aloc, sc); - if (global.endGagging(olderrors)) - s2.push(createForeach(aloc, pparams[1], null)); - else - { - s2.push(new ExpStatement(aloc, vard)); - auto catass = new CatAssignExp(aloc, new IdentifierExp(aloc, idres), res[1]); - s2.push(createForeach(aloc, pparams[1], new ExpStatement(aloc, catass))); - s2.push(new ReturnStatement(aloc, new IdentifierExp(aloc, idres))); - } - - Expression aggr = void; - Type indexty = void; - - if (rangefe && (indexty = ety).isintegral()) - { - rangefe.lwr.type = indexty; - rangefe.upr.type = indexty; - auto lwrRange = getIntRange(rangefe.lwr); - auto uprRange = getIntRange(rangefe.upr); - - const lwr = rangefe.lwr.toInteger(); - auto upr = rangefe.upr.toInteger(); - size_t length = 0; - - if (lwrRange.imin <= uprRange.imax) - length = cast(size_t) (upr - lwr); - - auto exps = new Expressions(length); - - if (rangefe.op == TOK.foreach_) - { - foreach (i; 0 .. length) - (*exps)[i] = new IntegerExp(aloc, lwr + i, indexty); - } - else - { - --upr; - foreach (i; 0 .. length) - (*exps)[i] = new IntegerExp(aloc, upr - i, indexty); - } - aggr = new ArrayLiteralExp(aloc, indexty.arrayOf(), exps); - } - else - { - aggr = wrapAndCall(aloc, new CompoundStatement(aloc, s2)); - sc = sc.startCTFE(); - aggr = aggr.expressionSemantic(sc); - aggr = resolveProperties(sc, aggr); - sc = sc.endCTFE(); - aggr = aggr.optimize(WANTvalue); - aggr = aggr.ctfeInterpret(); - } - - assert(!!aggrfe ^ !!rangefe); - aggrfe = new ForeachStatement(loc, TOK.foreach_, pparams[2], aggr, - aggrfe ? aggrfe._body : rangefe._body, - aggrfe ? aggrfe.endloc : rangefe.endloc); - rangefe = null; - lowerArrayAggregate(sc); // finally, turn generated array into expression tuple - } - - /***************************************** - * Perform `static foreach` lowerings that are necessary in order - * to finally expand the `static foreach` using - * `dmd.statementsem.makeTupleForeach`. - */ - extern(D) void prepare(Scope* sc) - { - assert(sc); - - if (aggrfe) - { - sc = sc.startCTFE(); - aggrfe.aggr = aggrfe.aggr.expressionSemantic(sc); - sc = sc.endCTFE(); - } - - if (aggrfe && aggrfe.aggr.type.toBasetype().ty == Terror) - { - return; - } - - if (!ready()) - { - if (aggrfe && aggrfe.aggr.type.toBasetype().ty == Tarray) - { - lowerArrayAggregate(sc); - } - else - { - lowerNonArrayAggregate(sc); - } - } - } - /***************************************** * Returns: * `true` iff ready to call `dmd.statementsem.makeTupleForeach`. @@ -494,15 +254,13 @@ extern (C++) final class StaticForeach : RootObject */ extern (C++) class DVCondition : Condition { - uint level; Identifier ident; Module mod; - extern (D) this(const ref Loc loc, Module mod, uint level, Identifier ident) @safe + extern (D) this(Loc loc, Module mod, Identifier ident) @safe { super(loc); this.mod = mod; - this.level = level; this.ident = ident; } @@ -557,45 +315,45 @@ extern (C++) final class DebugCondition : DVCondition * * Params: * mod = Module this node belongs to - * level = Minimum global level this condition needs to pass. - * Only used if `ident` is `null`. * ident = Identifier required for this condition to pass. * If `null`, this conditiion will use an integer level. * loc = Location in the source file */ - extern (D) this(const ref Loc loc, Module mod, uint level, Identifier ident) @safe + extern (D) this(Loc loc, Module mod, Identifier ident) @safe { - super(loc, mod, level, ident); + super(loc, mod, ident); } override int include(Scope* sc) { //printf("DebugCondition::include() level = %d, debuglevel = %d\n", level, global.params.debuglevel); - if (inc == Include.notComputed) + if (inc != Include.notComputed) { - inc = Include.no; - bool definedInModule = false; - if (ident) + return inc == Include.yes; + } + inc = Include.no; + bool definedInModule = false; + if (ident) + { + if (mod.debugids && findCondition(*mod.debugids, ident)) { - if (mod.debugids && findCondition(*mod.debugids, ident)) - { - inc = Include.yes; - definedInModule = true; - } - else if (findCondition(global.debugids, ident)) - inc = Include.yes; - else - { - if (!mod.debugidsNot) - mod.debugidsNot = new Identifiers(); - mod.debugidsNot.push(ident); - } + inc = Include.yes; + definedInModule = true; } - else if (level <= global.params.debuglevel || level <= mod.debuglevel) + else if (findCondition(global.debugids, ident)) inc = Include.yes; - if (!definedInModule) - printDepsConditional(sc, this, "depsDebug "); + else + { + if (!mod.debugidsNot) + mod.debugidsNot = new Identifiers(); + mod.debugidsNot.push(ident); + } } + else if (global.params.debugEnabled) + inc = Include.yes; + + if (!definedInModule) + printDepsConditional(sc, this, "depsDebug "); return (inc == Include.yes); } @@ -608,11 +366,6 @@ extern (C++) final class DebugCondition : DVCondition { v.visit(this); } - - override const(char)* toChars() const - { - return ident ? ident.toChars() : "debug".ptr; - } } /** @@ -744,6 +497,7 @@ extern (C++) final class VersionCondition : DVCondition case "Win32": case "Win64": case "Windows": + case "Xtensa": case "X86": case "X86_64": return true; @@ -764,7 +518,7 @@ extern (C++) final class VersionCondition : DVCondition * loc = Where the identifier is set * ident = identifier being checked (ident[$] must be '\0') */ - extern(D) static void checkReserved(const ref Loc loc, const(char)[] ident) + extern(D) static void checkReserved(Loc loc, const(char)[] ident) { if (isReserved(ident)) error(loc, "version identifier `%s` is reserved and cannot be set", @@ -836,49 +590,47 @@ extern (C++) final class VersionCondition : DVCondition * * Params: * mod = Module this node belongs to - * level = Minimum global level this condition needs to pass. - * Only used if `ident` is `null`. * ident = Identifier required for this condition to pass. * If `null`, this conditiion will use an integer level. * loc = Location in the source file */ - extern (D) this(const ref Loc loc, Module mod, uint level, Identifier ident) @safe + extern (D) this(Loc loc, Module mod, Identifier ident) @safe { - super(loc, mod, level, ident); + super(loc, mod, ident); } override int include(Scope* sc) { //printf("VersionCondition::include() level = %d, versionlevel = %d\n", level, global.params.versionlevel); //if (ident) printf("\tident = '%s'\n", ident.toChars()); - if (inc == Include.notComputed) + if (inc != Include.notComputed) { - inc = Include.no; - bool definedInModule = false; - if (ident) + return inc == Include.yes; + } + + inc = Include.no; + bool definedInModule = false; + if (ident) + { + if (mod.versionids && findCondition(*mod.versionids, ident)) { - if (mod.versionids && findCondition(*mod.versionids, ident)) - { - inc = Include.yes; - definedInModule = true; - } - else if (findCondition(global.versionids, ident)) - inc = Include.yes; - else - { - if (!mod.versionidsNot) - mod.versionidsNot = new Identifiers(); - mod.versionidsNot.push(ident); - } + inc = Include.yes; + definedInModule = true; } - else if (level <= global.params.versionlevel || level <= mod.versionlevel) + else if (findCondition(global.versionids, ident)) inc = Include.yes; - if (!definedInModule && - (!ident || (!isReserved(ident.toString()) && ident != Id._unittest && ident != Id._assert))) + else { - printDepsConditional(sc, this, "depsVersion "); + if (!mod.versionidsNot) + mod.versionidsNot = new Identifiers(); + mod.versionidsNot.push(ident); } } + if (!definedInModule && + (!ident || (!isReserved(ident.toString()) && ident != Id._unittest && ident != Id._assert))) + { + printDepsConditional(sc, this, "depsVersion "); + } return (inc == Include.yes); } @@ -891,11 +643,6 @@ extern (C++) final class VersionCondition : DVCondition { v.visit(this); } - - override const(char)* toChars() const - { - return ident ? ident.toChars() : "version".ptr; - } } /*********************************************************** @@ -904,7 +651,7 @@ extern (C++) final class StaticIfCondition : Condition { Expression exp; - extern (D) this(const ref Loc loc, Expression exp) @safe + extern (D) this(Loc loc, Expression exp) @safe { super(loc); this.exp = exp; @@ -926,31 +673,33 @@ extern (C++) final class StaticIfCondition : Condition return 0; } - if (inc == Include.notComputed) + if (inc != Include.notComputed) { - if (!sc) - { - error(loc, "`static if` conditional cannot be at global scope"); - inc = Include.no; - return 0; - } + return inc == Include.yes; + } - import dmd.staticcond; - bool errors; + if (!sc) + { + error(loc, "`static if` conditional cannot be at global scope"); + inc = Include.no; + return 0; + } - bool result = evalStaticCondition(sc, exp, exp, errors); + import dmd.staticcond; + bool errors; - // Prevent repeated condition evaluation. - // See: fail_compilation/fail7815.d - if (inc != Include.notComputed) - return (inc == Include.yes); - if (errors) - return errorReturn(); - if (result) - inc = Include.yes; - else - inc = Include.no; - } + bool result = evalStaticCondition(sc, exp, exp, errors); + + // Prevent repeated condition evaluation. + // See: fail_compilation/fail7815.d + if (inc != Include.notComputed) + return (inc == Include.yes); + if (errors) + return errorReturn(); + if (result) + inc = Include.yes; + else + inc = Include.no; return (inc == Include.yes); } @@ -963,11 +712,6 @@ extern (C++) final class StaticIfCondition : Condition { return this; } - - override const(char)* toChars() const - { - return exp ? exp.toChars() : "static if".ptr; - } } @@ -1005,7 +749,5 @@ private void printDepsConditional(Scope* sc, DVCondition condition, const(char)[ ob.writestring(") : "); if (condition.ident) ob.writestring(condition.ident.toString()); - else - ob.print(condition.level); ob.writeByte('\n'); } diff --git a/dmd/cond.h b/dmd/cond.h index fc3ebd4625..174a8fb9aa 100644 --- a/dmd/cond.h +++ b/dmd/cond.h @@ -1,6 +1,6 @@ /* Compiler implementation of the D programming language - * Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * written by Walter Bright * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. @@ -58,7 +58,6 @@ class StaticForeach final : public RootObject class DVCondition : public Condition { public: - unsigned level; Identifier *ident; Module *mod; @@ -72,7 +71,6 @@ class DebugCondition final : public DVCondition static void addGlobalIdent(const char *ident); int include(Scope *sc) override; - DebugCondition *isDebugCondition() override { return this; } void accept(Visitor *v) override { v->visit(this); } }; @@ -83,7 +81,6 @@ class VersionCondition final : public DVCondition static void addPredefinedGlobalIdent(const char *ident); int include(Scope *sc) override; - VersionCondition *isVersionCondition() override { return this; } void accept(Visitor *v) override { v->visit(this); } }; diff --git a/dmd/console.d b/dmd/console.d index 4c37043390..29dc9fb300 100644 --- a/dmd/console.d +++ b/dmd/console.d @@ -2,12 +2,12 @@ * Control the various text mode attributes, such as color, when writing text * to the console. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/console.d, _console.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/console.d, _console.d) * Documentation: https://dlang.org/phobos/dmd_console.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/console.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/console.d */ module dmd.console; @@ -90,7 +90,7 @@ private final class WindowsConsole : Console HANDLE handle; FILE* _fp; - static HANDLE getStdHandle(FILE *fp) + static HANDLE getStdHandle(FILE* fp) { /* Determine if stream fp is a console */ @@ -111,10 +111,9 @@ private final class WindowsConsole : Console if (fp == stdout) return GetStdHandle(STD_OUTPUT_HANDLE); - else if (fp == stderr) + if (fp == stderr) return GetStdHandle(STD_ERROR_HANDLE); - else - return null; + return null; } public: diff --git a/dmd/constfold.d b/dmd/constfold.d index 54d50cf4a3..fad9c9ac44 100644 --- a/dmd/constfold.d +++ b/dmd/constfold.d @@ -5,12 +5,12 @@ * * Specification: $(LINK2 https://dlang.org/spec/float.html#fp_const_folding, Floating Point Constant Folding) * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/constfold.d, _constfold.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/constfold.d, _constfold.d) * Documentation: https://dlang.org/phobos/dmd_constfold.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/constfold.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/constfold.d */ module dmd.constfold; @@ -69,15 +69,15 @@ UnionExp Neg(Type type, Expression e1) { UnionExp ue = void; Loc loc = e1.loc; - if (e1.type.isreal()) + if (e1.type.isReal()) { emplaceExp!(RealExp)(&ue, loc, -e1.toReal(), type); } - else if (e1.type.isimaginary()) + else if (e1.type.isImaginary()) { emplaceExp!(RealExp)(&ue, loc, -e1.toImaginary(), type); } - else if (e1.type.iscomplex()) + else if (e1.type.isComplex()) { emplaceExp!(ComplexExp)(&ue, loc, -e1.toComplex(), type); } @@ -107,22 +107,22 @@ UnionExp Not(Type type, Expression e1) return ue; } -UnionExp Add(const ref Loc loc, Type type, Expression e1, Expression e2) +UnionExp Add(Loc loc, Type type, Expression e1, Expression e2) { UnionExp ue = void; static if (LOG) { printf("Add(e1 = %s, e2 = %s)\n", e1.toChars(), e2.toChars()); } - if (type.isreal()) + if (type.isReal()) { emplaceExp!(RealExp)(&ue, loc, e1.toReal() + e2.toReal(), type); } - else if (type.isimaginary()) + else if (type.isImaginary()) { emplaceExp!(RealExp)(&ue, loc, e1.toImaginary() + e2.toImaginary(), type); } - else if (type.iscomplex()) + else if (type.isComplex()) { // This rigamarole is necessary so that -0.0 doesn't get // converted to +0.0 by doing an extraneous add with +0.0 @@ -134,12 +134,12 @@ UnionExp Add(const ref Loc loc, Type type, Expression e1, Expression e2) real_t i2 = CTFloat.zero; auto v = complex_t(CTFloat.zero); int x; - if (e1.type.isreal()) + if (e1.type.isReal()) { r1 = e1.toReal(); x = 0; } - else if (e1.type.isimaginary()) + else if (e1.type.isImaginary()) { i1 = e1.toImaginary(); x = 3; @@ -149,11 +149,11 @@ UnionExp Add(const ref Loc loc, Type type, Expression e1, Expression e2) c1 = e1.toComplex(); x = 6; } - if (e2.type.isreal()) + if (e2.type.isReal()) { r2 = e2.toReal(); } - else if (e2.type.isimaginary()) + else if (e2.type.isImaginary()) { i2 = e2.toImaginary(); x += 1; @@ -212,7 +212,7 @@ UnionExp Add(const ref Loc loc, Type type, Expression e1, Expression e2) return ue; } -UnionExp Min(const ref Loc loc, Type type, Expression e1, Expression e2) +UnionExp Min(Loc loc, Type type, Expression e1, Expression e2) { // Compute e1-e2 as e1+(-e2) UnionExp neg = Neg(e2.type, e2); @@ -220,32 +220,32 @@ UnionExp Min(const ref Loc loc, Type type, Expression e1, Expression e2) return ue; } -UnionExp Mul(const ref Loc loc, Type type, Expression e1, Expression e2) +UnionExp Mul(Loc loc, Type type, Expression e1, Expression e2) { UnionExp ue = void; - if (type.isfloating()) + if (type.isFloating()) { auto c = complex_t(CTFloat.zero); real_t r = CTFloat.zero; - if (e1.type.isreal()) + if (e1.type.isReal()) { r = e1.toReal(); c = e2.toComplex(); c = complex_t(r * creall(c), r * cimagl(c)); } - else if (e1.type.isimaginary()) + else if (e1.type.isImaginary()) { r = e1.toImaginary(); c = e2.toComplex(); c = complex_t(-r * cimagl(c), r * creall(c)); } - else if (e2.type.isreal()) + else if (e2.type.isReal()) { r = e2.toReal(); c = e1.toComplex(); c = complex_t(r * creall(c), r * cimagl(c)); } - else if (e2.type.isimaginary()) + else if (e2.type.isImaginary()) { r = e2.toImaginary(); c = e1.toComplex(); @@ -253,11 +253,11 @@ UnionExp Mul(const ref Loc loc, Type type, Expression e1, Expression e2) } else c = e1.toComplex() * e2.toComplex(); - if (type.isreal()) + if (type.isReal()) emplaceExp!(RealExp)(&ue, loc, creall(c), type); - else if (type.isimaginary()) + else if (type.isImaginary()) emplaceExp!(RealExp)(&ue, loc, cimagl(c), type); - else if (type.iscomplex()) + else if (type.isComplex()) emplaceExp!(ComplexExp)(&ue, loc, c, type); else assert(0); @@ -269,15 +269,15 @@ UnionExp Mul(const ref Loc loc, Type type, Expression e1, Expression e2) return ue; } -UnionExp Div(const ref Loc loc, Type type, Expression e1, Expression e2) +UnionExp Div(Loc loc, Type type, Expression e1, Expression e2) { UnionExp ue = void; - if (type.isfloating()) + if (type.isFloating()) { auto c = complex_t(CTFloat.zero); - if (e2.type.isreal()) + if (e2.type.isReal()) { - if (e1.type.isreal()) + if (e1.type.isReal()) { emplaceExp!(RealExp)(&ue, loc, e1.toReal() / e2.toReal(), type); return ue; @@ -286,7 +286,7 @@ UnionExp Div(const ref Loc loc, Type type, Expression e1, Expression e2) c = e1.toComplex(); c = complex_t(creall(c) / r, cimagl(c) / r); } - else if (e2.type.isimaginary()) + else if (e2.type.isImaginary()) { const r = e2.toImaginary(); c = e1.toComplex(); @@ -297,11 +297,11 @@ UnionExp Div(const ref Loc loc, Type type, Expression e1, Expression e2) c = e1.toComplex() / e2.toComplex(); } - if (type.isreal()) + if (type.isReal()) emplaceExp!(RealExp)(&ue, loc, creall(c), type); - else if (type.isimaginary()) + else if (type.isImaginary()) emplaceExp!(RealExp)(&ue, loc, cimagl(c), type); - else if (type.iscomplex()) + else if (type.isComplex()) emplaceExp!(ComplexExp)(&ue, loc, c, type); else assert(0); @@ -319,7 +319,7 @@ UnionExp Div(const ref Loc loc, Type type, Expression e1, Expression e2) emplaceExp!(ErrorExp)(&ue); return ue; } - if (n2 == -1 && !type.isunsigned()) + if (n2 == -1 && !type.isUnsigned()) { // Check for int.min / -1 if (n1 == 0xFFFFFFFF80000000UL && type.toBasetype().ty != Tint64) @@ -335,7 +335,7 @@ UnionExp Div(const ref Loc loc, Type type, Expression e1, Expression e2) return ue; } } - if (e1.type.isunsigned() || e2.type.isunsigned()) + if (e1.type.isUnsigned() || e2.type.isUnsigned()) n = (cast(dinteger_t)n1) / (cast(dinteger_t)n2); else n = n1 / n2; @@ -344,29 +344,29 @@ UnionExp Div(const ref Loc loc, Type type, Expression e1, Expression e2) return ue; } -UnionExp Mod(const ref Loc loc, Type type, Expression e1, Expression e2) +UnionExp Mod(Loc loc, Type type, Expression e1, Expression e2) { UnionExp ue = void; - if (type.isfloating()) + if (type.isFloating()) { auto c = complex_t(CTFloat.zero); - if (e2.type.isreal()) + if (e2.type.isReal()) { const r2 = e2.toReal(); c = complex_t(e1.toReal() % r2, e1.toImaginary() % r2); } - else if (e2.type.isimaginary()) + else if (e2.type.isImaginary()) { const i2 = e2.toImaginary(); c = complex_t(e1.toReal() % i2, e1.toImaginary() % i2); } else assert(0); - if (type.isreal()) + if (type.isReal()) emplaceExp!(RealExp)(&ue, loc, creall(c), type); - else if (type.isimaginary()) + else if (type.isImaginary()) emplaceExp!(RealExp)(&ue, loc, cimagl(c), type); - else if (type.iscomplex()) + else if (type.isComplex()) emplaceExp!(ComplexExp)(&ue, loc, c, type); else assert(0); @@ -384,7 +384,7 @@ UnionExp Mod(const ref Loc loc, Type type, Expression e1, Expression e2) emplaceExp!(ErrorExp)(&ue); return ue; } - if (n2 == -1 && !type.isunsigned()) + if (n2 == -1 && !type.isUnsigned()) { // Check for int.min % -1 if (n1 == 0xFFFFFFFF80000000UL && type.toBasetype().ty != Tint64) @@ -400,7 +400,7 @@ UnionExp Mod(const ref Loc loc, Type type, Expression e1, Expression e2) return ue; } } - if (e1.type.isunsigned() || e2.type.isunsigned()) + if (e1.type.isUnsigned() || e2.type.isUnsigned()) n = (cast(dinteger_t)n1) % (cast(dinteger_t)n2); else n = n1 % n2; @@ -409,18 +409,18 @@ UnionExp Mod(const ref Loc loc, Type type, Expression e1, Expression e2) return ue; } -UnionExp Pow(const ref Loc loc, Type type, Expression e1, Expression e2) +UnionExp Pow(Loc loc, Type type, Expression e1, Expression e2) { //printf("Pow()\n"); UnionExp ue; // Handle integer power operations. - if (e2.type.isintegral()) + if (e2.type.isIntegral()) { dinteger_t n = e2.toInteger(); bool neg; - if (!e2.type.isunsigned() && cast(sinteger_t)n < 0) + if (!e2.type.isUnsigned() && cast(sinteger_t)n < 0) { - if (e1.type.isintegral()) + if (e1.type.isIntegral()) { cantExp(ue); return ue; @@ -432,12 +432,12 @@ UnionExp Pow(const ref Loc loc, Type type, Expression e1, Expression e2) else neg = false; UnionExp ur, uv; - if (e1.type.iscomplex()) + if (e1.type.isComplex()) { emplaceExp!(ComplexExp)(&ur, loc, e1.toComplex(), e1.type); emplaceExp!(ComplexExp)(&uv, loc, complex_t(CTFloat.one), e1.type); } - else if (e1.type.isfloating()) + else if (e1.type.isFloating()) { emplaceExp!(RealExp)(&ur, loc, e1.toReal(), e1.type); emplaceExp!(RealExp)(&uv, loc, CTFloat.one, e1.type); @@ -467,14 +467,14 @@ UnionExp Pow(const ref Loc loc, Type type, Expression e1, Expression e2) emplaceExp!(RealExp)(&one, loc, CTFloat.one, v.type); uv = Div(loc, v.type, one.exp(), v); } - if (type.iscomplex()) + if (type.isComplex()) emplaceExp!(ComplexExp)(&ue, loc, v.toComplex(), type); - else if (type.isintegral()) + else if (type.isIntegral()) emplaceExp!(IntegerExp)(&ue, loc, v.toInteger(), type); else emplaceExp!(RealExp)(&ue, loc, v.toReal(), type); } - else if (e2.type.isfloating()) + else if (e2.type.isFloating()) { // x ^^ y for x < 0 and y not an integer is not defined; so set result as NaN if (e1.toReal() < CTFloat.zero) @@ -489,14 +489,14 @@ UnionExp Pow(const ref Loc loc, Type type, Expression e1, Expression e2) return ue; } -UnionExp Shl(const ref Loc loc, Type type, Expression e1, Expression e2) +UnionExp Shl(Loc loc, Type type, Expression e1, Expression e2) { UnionExp ue = void; emplaceExp!(IntegerExp)(&ue, loc, e1.toInteger() << e2.toInteger(), type); return ue; } -UnionExp Shr(const ref Loc loc, Type type, Expression e1, Expression e2) +UnionExp Shr(Loc loc, Type type, Expression e1, Expression e2) { UnionExp ue = void; dinteger_t value = e1.toInteger(); @@ -542,7 +542,7 @@ UnionExp Shr(const ref Loc loc, Type type, Expression e1, Expression e2) return ue; } -UnionExp Ushr(const ref Loc loc, Type type, Expression e1, Expression e2) +UnionExp Ushr(Loc loc, Type type, Expression e1, Expression e2) { UnionExp ue = void; dinteger_t value = e1.toInteger(); @@ -582,21 +582,21 @@ UnionExp Ushr(const ref Loc loc, Type type, Expression e1, Expression e2) return ue; } -UnionExp And(const ref Loc loc, Type type, Expression e1, Expression e2) +UnionExp And(Loc loc, Type type, Expression e1, Expression e2) { UnionExp ue = void; emplaceExp!(IntegerExp)(&ue, loc, e1.toInteger() & e2.toInteger(), type); return ue; } -UnionExp Or(const ref Loc loc, Type type, Expression e1, Expression e2) +UnionExp Or(Loc loc, Type type, Expression e1, Expression e2) { UnionExp ue = void; emplaceExp!(IntegerExp)(&ue, loc, e1.toInteger() | e2.toInteger(), type); return ue; } -UnionExp Xor(const ref Loc loc, Type type, Expression e1, Expression e2) +UnionExp Xor(Loc loc, Type type, Expression e1, Expression e2) { //printf("Xor(linnum = %d, e1 = %s, e2 = %s)\n", loc.linnum, e1.toChars(), e2.toChars()); UnionExp ue = void; @@ -606,7 +606,7 @@ UnionExp Xor(const ref Loc loc, Type type, Expression e1, Expression e2) /* Also returns EXP.cantExpression if cannot be computed. */ -UnionExp Equal(EXP op, const ref Loc loc, Type type, Expression e1, Expression e2) +UnionExp Equal(EXP op, Loc loc, Type type, Expression e1, Expression e2) { UnionExp ue = void; int cmp = 0; @@ -765,13 +765,13 @@ UnionExp Equal(EXP op, const ref Loc loc, Type type, Expression e1, Expression e cantExp(ue); return ue; } - else if (e1.type.isreal()) + else if (e1.type.isReal()) { r1 = e1.toReal(); r2 = e2.toReal(); goto L1; } - else if (e1.type.isimaginary()) + else if (e1.type.isImaginary()) { r1 = e1.toImaginary(); r2 = e2.toImaginary(); @@ -785,11 +785,11 @@ UnionExp Equal(EXP op, const ref Loc loc, Type type, Expression e1, Expression e cmp = (r1 == r2); } } - else if (e1.type.iscomplex()) + else if (e1.type.isComplex()) { cmp = e1.toComplex() == e2.toComplex(); } - else if (e1.type.isintegral() || e1.type.toBasetype().ty == Tpointer) + else if (e1.type.isIntegral() || e1.type.toBasetype().ty == Tpointer) { cmp = (e1.toInteger() == e2.toInteger()); } @@ -804,8 +804,9 @@ UnionExp Equal(EXP op, const ref Loc loc, Type type, Expression e1, Expression e return ue; } -UnionExp Identity(EXP op, const ref Loc loc, Type type, Expression e1, Expression e2) +UnionExp Identity(EXP op, Loc loc, Type type, Expression e1, Expression e2) { + //printf("Identity %s %s\n", e1.toChars(), e2.toChars()); UnionExp ue = void; int cmp; if (e1.op == EXP.null_) @@ -820,11 +821,21 @@ UnionExp Identity(EXP op, const ref Loc loc, Type type, Expression e1, Expressio { SymOffExp es1 = e1.isSymOffExp(); SymOffExp es2 = e2.isSymOffExp(); - cmp = (es1.var == es2.var && es1.offset == es2.offset); + cmp = es1.offset == es2.offset; + if (cmp) + { + cmp = es1.var == es2.var; + if (!cmp && (es1.var.isParameter() || es2.var.isParameter())) + { + // because of ref's, they may still be the same, we cannot tell + cantExp(ue); + return ue; + } + } } else { - if (e1.type.isfloating()) + if (e1.type.isFloating()) cmp = e1.isIdentical(e2); else { @@ -838,7 +849,7 @@ UnionExp Identity(EXP op, const ref Loc loc, Type type, Expression e1, Expressio return ue; } -UnionExp Cmp(EXP op, const ref Loc loc, Type type, Expression e1, Expression e2) +UnionExp Cmp(EXP op, Loc loc, Type type, Expression e1, Expression e2) { UnionExp ue = void; dinteger_t n; @@ -866,20 +877,20 @@ UnionExp Cmp(EXP op, const ref Loc loc, Type type, Expression e1, Expression e2) cantExp(ue); return ue; } - else if (e1.type.isreal()) + else if (e1.type.isReal()) { r1 = e1.toReal(); r2 = e2.toReal(); goto L1; } - else if (e1.type.isimaginary()) + else if (e1.type.isImaginary()) { r1 = e1.toImaginary(); r2 = e2.toImaginary(); L1: n = realCmp(op, r1, r2); } - else if (e1.type.iscomplex()) + else if (e1.type.isComplex()) { assert(0); } @@ -889,7 +900,7 @@ UnionExp Cmp(EXP op, const ref Loc loc, Type type, Expression e1, Expression e2) sinteger_t n2; n1 = e1.toInteger(); n2 = e2.toInteger(); - if (e1.type.isunsigned() || e2.type.isunsigned()) + if (e1.type.isUnsigned() || e2.type.isUnsigned()) n = intUnsignedCmp(op, n1, n2); else n = intSignedCmp(op, n1, n2); @@ -902,7 +913,7 @@ UnionExp Cmp(EXP op, const ref Loc loc, Type type, Expression e1, Expression e2) * to: type to cast to * type: type to paint the result */ -UnionExp Cast(const ref Loc loc, Type type, Type to, Expression e1) +UnionExp Cast(Loc loc, Type type, Type to, Expression e1) { UnionExp ue = void; Type tb = to.toBasetype(); @@ -968,9 +979,9 @@ UnionExp Cast(const ref Loc loc, Type type, Type to, Expression e1) emplaceExp!(IntegerExp)(&ue, loc, opt.get(), type); } - else if (type.isintegral()) + else if (type.isIntegral()) { - if (e1.type.isfloating()) + if (e1.type.isFloating()) { dinteger_t result; real_t r = e1.toReal(); @@ -1008,27 +1019,27 @@ UnionExp Cast(const ref Loc loc, Type type, Type to, Expression e1) } emplaceExp!(IntegerExp)(&ue, loc, result, type); } - else if (type.isunsigned()) + else if (type.isUnsigned()) emplaceExp!(IntegerExp)(&ue, loc, e1.toUInteger(), type); else emplaceExp!(IntegerExp)(&ue, loc, e1.toInteger(), type); } - else if (tb.isreal()) + else if (tb.isReal()) { real_t value = e1.toReal(); emplaceExp!(RealExp)(&ue, loc, value, type); } - else if (tb.isimaginary()) + else if (tb.isImaginary()) { real_t value = e1.toImaginary(); emplaceExp!(RealExp)(&ue, loc, value, type); } - else if (tb.iscomplex()) + else if (tb.isComplex()) { complex_t value = e1.toComplex(); emplaceExp!(ComplexExp)(&ue, loc, value, type); } - else if (tb.isscalar()) + else if (tb.isScalar()) { emplaceExp!(IntegerExp)(&ue, loc, e1.toInteger(), type); } @@ -1385,7 +1396,7 @@ private Expressions* copyElements(Expression e1, Expression e2 = null) /* Also return EXP.cantExpression if this fails */ -UnionExp Cat(const ref Loc loc, Type type, Expression e1, Expression e2) +UnionExp Cat(Loc loc, Type type, Expression e1, Expression e2) { UnionExp ue = void; Expression e = CTFEExp.cantexp; @@ -1494,7 +1505,7 @@ UnionExp Cat(const ref Loc loc, Type type, Expression e1, Expression e2) assert(ue.exp().type); return ue; } - else if (e2.op == EXP.string_ && e1.op == EXP.arrayLiteral && t1.nextOf().isintegral()) + else if (e2.op == EXP.string_ && e1.op == EXP.arrayLiteral && t1.nextOf().isIntegral()) { // [chars] ~ string --> [chars] StringExp es = e2.isStringExp(); @@ -1511,7 +1522,7 @@ UnionExp Cat(const ref Loc loc, Type type, Expression e1, Expression e2) assert(ue.exp().type); return ue; } - else if (e1.op == EXP.string_ && e2.op == EXP.arrayLiteral && t2.nextOf().isintegral()) + else if (e1.op == EXP.string_ && e2.op == EXP.arrayLiteral && t2.nextOf().isIntegral()) { // string ~ [chars] --> [chars] StringExp es = e1.isStringExp(); diff --git a/dmd/cparse.d b/dmd/cparse.d index e88d9cd4c8..7b27eff957 100644 --- a/dmd/cparse.d +++ b/dmd/cparse.d @@ -3,12 +3,12 @@ * * Specification: C11 * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/cparse.d, _cparse.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/cparse.d, _cparse.d) * Documentation: https://dlang.org/phobos/dmd_cparse.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/cparse.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/cparse.d */ module dmd.cparse; @@ -48,6 +48,9 @@ final class CParser(AST) : Parser!AST // #pragma pack stack Array!Identifier* records; // identifers (or null) Array!structalign_t* packs; // parallel alignment values + + STC defaultStorageClasses; + Array!STC* defaultStorageClassesStack; } /* C cannot be parsed without determining if an identifier is a type or a variable. @@ -1123,75 +1126,74 @@ final class CParser(AST) : Parser!AST */ private AST.Expression cparseCastExp() { - if (token.value == TOK.leftParenthesis) + if (token.value != TOK.leftParenthesis) + return cparseUnaryExp(); + + //printf("cparseCastExp()\n"); + auto tk = peek(&token); + bool iscast; + bool isexp; + if (tk.value == TOK.identifier) { - //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(); - } + iscast = isTypedef(tk.ident); + isexp = !iscast; + } + if (isexp) + { + // ( identifier ) is an expression + return cparseUnaryExp(); + } - // If ( type-name ) - auto pt = &token; + // If ( type-name ) + auto pt = &token; - if (isCastExpression(pt)) - { - // Expression may be either a cast or a compound literal, which - // requires checking whether the next token is leftCurly - const loc = token.loc; - nextToken(); - auto t = cparseTypeName(); - check(TOK.rightParenthesis); - pt = &token; + if (!isCastExpression(pt)) + return cparseUnaryExp(); - 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); - } + // 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 (iscast) - { - // ( type-name ) cast-expression - auto ce = cparseCastExp(); - return new AST.CastExp(loc, ce, t); - } + 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 (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); - } + if (iscast) + { + // ( type-name ) cast-expression + auto ce = cparseCastExp(); + return new AST.CastExp(loc, ce, t); + } - // ( 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); } - return cparseUnaryExp(); + + // ( type-name ) cast-expression + auto ce = cparseCastExp(); + return new AST.CastExp(loc, ce, t); } /************** @@ -1446,8 +1448,10 @@ final class CParser(AST) : Parser!AST auto e = cparseOrExp(); while (token.value == TOK.andAnd) { + e = new AST.CastExp(loc, e, AST.Type.tbool); nextToken(); auto e2 = cparseOrExp(); + e2 = new AST.CastExp(loc, e2, AST.Type.tbool); e = new AST.LogicalExp(loc, EXP.andAnd, e, e2); } return e; @@ -1466,8 +1470,10 @@ final class CParser(AST) : Parser!AST auto e = cparseAndAndExp(); while (token.value == TOK.orOr) { + e = new AST.CastExp(loc, e, AST.Type.tbool); nextToken(); auto e2 = cparseAndAndExp(); + e2 = new AST.CastExp(loc, e2, AST.Type.tbool); e = new AST.LogicalExp(loc, EXP.orOr, e, e2); } return e; @@ -1687,7 +1693,7 @@ final class CParser(AST) : Parser!AST private AST.Expression cparseStatementExpression() { AST.ParameterList parameterList; - StorageClass stc = 0; + STC stc = STC.none; const loc = token.loc; auto symbolsSave = symbols; symbols = new AST.Dsymbols(); @@ -1711,7 +1717,7 @@ final class CParser(AST) : Parser!AST } auto tf = new AST.TypeFunction(parameterList, null, LINK.d, stc); - auto fd = new AST.FuncLiteralDeclaration(loc, token.loc, tf, TOK.delegate_, null, null, 0); + auto fd = new AST.FuncLiteralDeclaration(loc, token.loc, tf, TOK.delegate_, null, null, STC.none); fd.fbody = fbody; auto fe = new AST.FuncExp(loc, fd); @@ -2007,7 +2013,7 @@ final class CParser(AST) : Parser!AST //printf("AliasDeclaration %s %s\n", id.toChars(), dt.toChars()); auto ad = new AST.AliasDeclaration(token.loc, id, dt); if (id == idt) - ad.adFlags |= ad.hidden; // do not print when generating .di files + ad.hidden = true; // do not print when generating .di files s = ad; } @@ -2047,7 +2053,7 @@ final class CParser(AST) : Parser!AST error("no initializer for function declaration"); if (specifier.scw & SCW.x_Thread_local) error("functions cannot be `_Thread_local`"); // C11 6.7.1-4 - StorageClass stc = specifiersToSTC(level, specifier); + 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); specifiersToFuncDeclaration(fd, specifier); @@ -2089,8 +2095,7 @@ final class CParser(AST) : Parser!AST { auto str = asmName.peekString(); p.mangleOverride = str; -// p.adFlags |= AST.VarDeclaration.nounderscore; - p.adFlags |= 4; // cannot get above line to compile on Ubuntu + p.noUnderscore = true; } } s = applySpecifier(s, specifier); @@ -2235,7 +2240,7 @@ final class CParser(AST) : Parser!AST auto body = cparseStatement(ParseStatementFlags.curly); // don't start a new scope; continue with parameter scope typedefTab.pop(); // end of function scope - StorageClass stc = specifiersToSTC(LVL.global, specifier); + STC stc = specifiersToSTC(LVL.global, specifier); stc &= ~STC.gshared; // no gshared functions auto fd = new AST.FuncDeclaration(locFunc, prevloc, id, stc, ft, specifier.noreturn); specifiersToFuncDeclaration(fd, specifier); @@ -2991,7 +2996,7 @@ final class CParser(AST) : Parser!AST if (isStatic || mod) error("static or type qualifier used outside of function prototype"); } - if (ts.isTypeSArray() || ts.isTypeDArray()) + if (ts.isStaticOrDynamicArray()) { /* C11 6.7.6.2-1: type qualifiers and 'static' shall only appear * in the outermost array type derivation. @@ -3020,9 +3025,10 @@ final class CParser(AST) : Parser!AST auto parameterList = cparseParameterList(); const lkg = specifier.mod & MOD.x__stdcall ? LINK.windows : linkage; - StorageClass stc = specifier._nothrow ? STC.nothrow_ : 0; + STC stc = specifier._nothrow ? STC.nothrow_ : STC.none; if (specifier._pure) stc |= STC.pure_; + stc |= defaultStorageClasses; AST.Type tf = new AST.TypeFunction(parameterList, t, lkg, stc); //tf = tf.addSTC(storageClass); // TODO insertTx(ts, tf, t); // ts -> ... -> tf -> t @@ -3175,7 +3181,7 @@ final class CParser(AST) : Parser!AST { auto parameters = new AST.Parameters(); AST.VarArg varargs = AST.VarArg.none; - StorageClass varargsStc; + STC varargsStc; check(TOK.leftParenthesis); if (token.value == TOK.void_ && peekNext() == TOK.rightParenthesis) // func(void) @@ -3538,7 +3544,7 @@ final class CParser(AST) : Parser!AST break; } nextToken(); - auto s = new AST.CompoundAsmStatement(loc, statements, 0); + auto s = new AST.CompoundAsmStatement(loc, statements, STC.none); return s; } @@ -3923,7 +3929,7 @@ final class CParser(AST) : Parser!AST cparseGnuAttributes(specifierx); } - auto em = new AST.EnumMember(mloc, ident, value, null, 0, null, null); + auto em = new AST.EnumMember(mloc, ident, value, null, STC.none, null, null); members.push(em); if (token.value == TOK.comma) @@ -5163,9 +5169,9 @@ final class CParser(AST) : Parser!AST * Returns: * corresponding D storage class */ - StorageClass specifiersToSTC(LVL level, const ref Specifier specifier) + STC specifiersToSTC(LVL level, const ref Specifier specifier) { - StorageClass stc; + STC stc; if (specifier.scw & SCW.x_Thread_local) { if (level == LVL.global) @@ -5530,6 +5536,12 @@ final class CParser(AST) : Parser!AST if (pt && *pt) t = *pt; } + if (t.mcache && t.mcache.typedefIdent) + { + t = t.copy(); + t.mcache = null; + } + t.getMcache().typedefIdent = id; static if (LDC_pre_2084) auto tab = *cast(void*[void*]*) &(typedefTab[$ - 1]); else @@ -5630,7 +5642,7 @@ final class CParser(AST) : Parser!AST * Params: * startloc = location to use for error messages */ - private void uupragmaDirective(const ref Loc startloc) + private void uupragmaDirective(Loc startloc) { const loc = startloc; nextToken(); // move past __pragma @@ -5678,12 +5690,14 @@ final class CParser(AST) : Parser!AST * the preprocessed output. Ignore them. * Upon return, p is at start of next line. */ - private void pragmaDirective(const ref Loc loc) + private void pragmaDirective(Loc loc) { Token n; scan(&n); if (n.value == TOK.identifier && n.ident == Id.pack) return pragmaPack(loc, true); + if (n.value == TOK.identifier && n.ident == Id.attribute) + return pragmaAttribute(loc); if (n.value != TOK.endOfLine) skipToNextLine(); } @@ -5697,7 +5711,7 @@ final class CParser(AST) : Parser!AST * startloc = location to use for error messages * useScan = use scan() to retrieve next token, instead of nextToken() */ - private void pragmaPack(const ref Loc startloc, bool useScan) + private void pragmaPack(Loc startloc, bool useScan) { const loc = startloc; @@ -5827,7 +5841,7 @@ final class CParser(AST) : Parser!AST if (len == 1) // stack is now empty packalign.setDefault(); else - packalign = (*this.packs)[len - 1]; + packalign = (*this.packs)[len - 2]; return closingParen(); } while (n.value == TOK.comma) // #pragma pack ( pop , @@ -5891,6 +5905,125 @@ final class CParser(AST) : Parser!AST skipToNextLine(); } + /********* + * # pragma attribute(...) + * Sets default storage classes + * Params: + * startloc = location to use for error messages + */ + private void pragmaAttribute(Loc startloc) + { + const loc = startloc; + + if (!defaultStorageClassesStack) + { + defaultStorageClassesStack = new Array!STC; + } + + Token n; + Lexer.scan(&n); + if (n.value != TOK.leftParenthesis) + { + error(loc, "left parenthesis expected to follow `#pragma attribute`"); + if (n.value != TOK.endOfLine) + skipToNextLine(); + return; + } + + void closingParen() + { + if (n.value != TOK.rightParenthesis) + { + error(loc, "right parenthesis expected to close `#pragma attribute(`"); + } + if (n.value != TOK.endOfLine) + skipToNextLine(); + } + + Lexer.scan(&n); + + /* # pragma attribute (push, ...) + */ + if (n.value == TOK.identifier && n.ident == Id.push) + { + Lexer.scan(&n); + if (n.value != TOK.comma) + { + error(loc, "comma expected to follow `#pragma attribute(push`"); + if (n.value != TOK.endOfLine) + skipToNextLine(); + return; + } + + while (1) + { + Lexer.scan(&n); + if (n.value == TOK.endOfLine) + { + error(loc, "right parenthesis expected to close `#pragma attribute(push, `"); + break; + } + + if (n.value == TOK.rightParenthesis) + break; + + if (n.value == TOK.identifier) + { + if (n.ident == Id._nothrow) + defaultStorageClasses |= STC.nothrow_; + else if (n.ident == Id.nogc) + defaultStorageClasses |= STC.nogc; + else if (n.ident == Id._pure) + defaultStorageClasses |= STC.pure_; + // Ignore unknown identifiers + } + else + { + error(loc, "unrecognized `#pragma attribute(push, %s)`", n.toChars()); + break; + } + + Lexer.scan(&n); + + if (n.value == TOK.rightParenthesis) + break; + + if (n.value != TOK.comma) + { + error(loc, "unrecognized `#pragma attribute(push, %s)`", n.toChars()); + break; + } + } + + this.defaultStorageClassesStack.push(defaultStorageClasses); + + return closingParen(); + } + + /* # pragma attribute(pop) + */ + if (n.value == TOK.identifier && n.ident == Id.pop) + { + scan(&n); + size_t len = this.defaultStorageClassesStack.length; + + if (len) + { + this.defaultStorageClassesStack.setDim(len - 1); + if (len == 1) // stack is now empty + defaultStorageClasses = STC.init; + else + defaultStorageClasses = (*this.defaultStorageClassesStack)[len - 2]; + } + + return closingParen(); + } + + error(loc, "unrecognized `#pragma attribute(%s)`", n.toChars()); + if (n.value != TOK.endOfLine) + skipToNextLine(); + } + //} /******************************************************************************/ @@ -5969,6 +6102,30 @@ final class CParser(AST) : Parser!AST AST.Type t; + bool hasMinus; + if (token.value == TOK.min) + { + nextToken(); + // Only allow integer and float literals after minus. + switch (token.value) + { + 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: + hasMinus = true; + break; + default: + continue; + } + } + Lswitch: switch (token.value) { @@ -5993,6 +6150,8 @@ final class CParser(AST) : Parser!AST * enum id = intvalue; */ AST.Expression e = new AST.IntegerExp(scanloc, intvalue, t); + if (hasMinus) + e = new AST.NegExp(scanloc, e); auto v = new AST.VarDeclaration(scanloc, t, id, new AST.ExpInitializer(scanloc, e), STC.manifest); addSym(v); ++p; @@ -6016,6 +6175,8 @@ final class CParser(AST) : Parser!AST * enum id = floatvalue; */ AST.Expression e = new AST.RealExp(scanloc, floatvalue, t); + if (hasMinus) + e = new AST.NegExp(scanloc, e); auto v = new AST.VarDeclaration(scanloc, t, id, new AST.ExpInitializer(scanloc, e), STC.manifest); addSym(v); ++p; @@ -6061,8 +6222,8 @@ final class CParser(AST) : Parser!AST if (token.value != TOK.endOfFile) break; auto ret = new AST.ReturnStatement(exp.loc, exp); - auto parameterList = AST.ParameterList(new AST.Parameters(), VarArg.none, 0); - StorageClass stc = STC.auto_; + auto parameterList = AST.ParameterList(new AST.Parameters(), VarArg.none, STC.none); + STC stc = STC.auto_; auto tf = new AST.TypeFunction(parameterList, null, LINK.d, stc); auto fd = new AST.FuncDeclaration(exp.loc, exp.loc, id, stc, tf, 0); fd.fbody = ret; @@ -6107,7 +6268,7 @@ final class CParser(AST) : Parser!AST if (token.value != TOK.identifier) break Lswitch; - auto param = new AST.Parameter(token.loc, 0, null, token.ident, null, null); + auto param = new AST.Parameter(token.loc, STC.none, null, token.ident, null, null); parameters.push(param); nextToken(); if (token.value == TOK.comma) @@ -6122,7 +6283,7 @@ final class CParser(AST) : Parser!AST //auto pstart = p; nextToken(); - auto parameterList = AST.ParameterList(parameters, varargs, 0); + auto parameterList = AST.ParameterList(parameters, varargs, STC.none); /* Create a type for each parameter. Add it to the template parameter list, * and the parameter list. */ @@ -6153,7 +6314,7 @@ final class CParser(AST) : Parser!AST // Generate function auto ret = new AST.ReturnStatement(exp.loc, exp); - StorageClass stc = STC.auto_; + STC stc = STC.auto_; auto tf = new AST.TypeFunction(parameterList, null, LINK.d, stc); auto fd = new AST.FuncDeclaration(exp.loc, exp.loc, id, stc, tf, 0); fd.fbody = ret; @@ -6185,7 +6346,6 @@ final class CParser(AST) : Parser!AST while (*p) ++p; ++p; // advance to start of next line - scanloc.linnum = scanloc.linnum + 1; } if (newSymbols.length) diff --git a/dmd/ctfe.h b/dmd/ctfe.h index 72d895c715..e022d7c2a2 100644 --- a/dmd/ctfe.h +++ b/dmd/ctfe.h @@ -1,6 +1,6 @@ /* Compiler implementation of the D programming language - * Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * written by Walter Bright * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. @@ -48,7 +48,6 @@ class ThrownExceptionExp final : public Expression { public: ClassReferenceExp *thrown; // the thing being tossed - const char *toChars() const override; void accept(Visitor *v) override { v->visit(this); } }; @@ -58,6 +57,4 @@ class ThrownExceptionExp final : public Expression class CTFEExp final : public Expression { -public: - const char *toChars() const override; }; diff --git a/dmd/ctfeexpr.d b/dmd/ctfeexpr.d index 8ed70c6639..2f577cea92 100644 --- a/dmd/ctfeexpr.d +++ b/dmd/ctfeexpr.d @@ -1,12 +1,12 @@ /** * CTFE for expressions involving pointers, slices, array concatenation etc. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/ctfeexpr.d, _ctfeexpr.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/ctfeexpr.d, _ctfeexpr.d) * Documentation: https://dlang.org/phobos/dmd_ctfeexpr.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/ctfeexpr.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/ctfeexpr.d */ module dmd.ctfeexpr; @@ -465,8 +465,7 @@ Expression resolveSlice(Expression e, UnionExp* pue = null) *pue = Slice(e.type, se.e1, se.lwr, se.upr); return pue.exp(); } - else - return Slice(e.type, se.e1, se.lwr, se.upr).copy(); + return Slice(e.type, se.e1, se.lwr, se.upr).copy(); } /* Determine the array length, without interpreting it. @@ -523,7 +522,7 @@ uinteger_t resolveArrayLength(Expression e) * Returns: * Constructed ArrayLiteralExp */ -ArrayLiteralExp createBlockDuplicatedArrayLiteral(UnionExp* pue, const ref Loc loc, Type type, Expression elem, size_t dim) +ArrayLiteralExp createBlockDuplicatedArrayLiteral(UnionExp* pue, Loc loc, Type type, Expression elem, size_t dim) { if (type.ty == Tsarray && type.nextOf().ty == Tsarray && elem.type.ty != Tsarray) { @@ -554,7 +553,7 @@ ArrayLiteralExp createBlockDuplicatedArrayLiteral(UnionExp* pue, const ref Loc l * Helper for NewExp * Create a string literal consisting of 'value' duplicated 'dim' times. */ -StringExp createBlockDuplicatedStringLiteral(UnionExp* pue, const ref Loc loc, Type type, dchar value, size_t dim, ubyte sz) +StringExp createBlockDuplicatedStringLiteral(UnionExp* pue, Loc loc, Type type, dchar value, size_t dim, ubyte sz) { auto s = cast(char*)mem.xcalloc(dim, sz); foreach (elemi; 0 .. dim) @@ -661,7 +660,7 @@ bool isSafePointerCast(Type srcPointee, Type destPointee) srcPointee = srcPointee.baseElemOf(); destPointee = destPointee.baseElemOf(); } - return srcPointee.isintegral() && destPointee.isintegral() && srcPointee.size() == destPointee.size(); + return srcPointee.isIntegral() && destPointee.isIntegral() && srcPointee.size() == destPointee.size(); } Expression getAggregateFromPointer(Expression e, dinteger_t* ofs) @@ -689,7 +688,7 @@ Expression getAggregateFromPointer(Expression e, dinteger_t* ofs) if (auto ie = e.isIndexExp()) { // Note that each AA element is part of its own memory block - if ((ie.e1.type.ty == Tarray || ie.e1.type.ty == Tsarray || ie.e1.op == EXP.string_ || ie.e1.op == EXP.arrayLiteral) && ie.e2.op == EXP.int64) + if ((ie.e1.type.isStaticOrDynamicArray() || ie.e1.op == EXP.string_ || ie.e1.op == EXP.arrayLiteral) && ie.e2.op == EXP.int64) { *ofs = ie.e2.toInteger(); return ie.e1; @@ -698,7 +697,7 @@ Expression getAggregateFromPointer(Expression e, dinteger_t* ofs) if (auto se = e.isSliceExp()) { if (se && e.type.toBasetype().ty == Tsarray && - (se.e1.type.ty == Tarray || se.e1.type.ty == Tsarray || se.e1.op == EXP.string_ || se.e1.op == EXP.arrayLiteral) && se.lwr.op == EXP.int64) + (se.e1.type.isStaticOrDynamicArray() || se.e1.op == EXP.string_ || se.e1.op == EXP.arrayLiteral) && se.lwr.op == EXP.int64) { *ofs = se.lwr.toInteger(); return se.e1; @@ -742,7 +741,7 @@ bool pointToSameMemoryBlock(Expression agg1, Expression agg2) } // return e1 - e2 as an integer, or error if not possible -Expression pointerDifference(UnionExp* pue, const ref Loc loc, Type type, Expression e1, Expression e2) +Expression pointerDifference(UnionExp* pue, Loc loc, Type type, Expression e1, Expression e2) { dinteger_t ofs1, ofs2; Expression agg1 = getAggregateFromPointer(e1, &ofs1); @@ -775,7 +774,7 @@ Expression pointerDifference(UnionExp* pue, const ref Loc loc, Type type, Expres // Return eptr op e2, where eptr is a pointer, e2 is an integer, // and op is EXP.add or EXP.min -Expression pointerArithmetic(UnionExp* pue, const ref Loc loc, EXP op, Type type, Expression eptr, Expression e2) +Expression pointerArithmetic(UnionExp* pue, Loc loc, EXP op, Type type, Expression eptr, Expression e2) { if (eptr.type.nextOf().ty == Tvoid) { @@ -951,7 +950,7 @@ int comparePointers(EXP op, Expression agg1, dinteger_t ofs1, Expression agg2, d // floating point -> integer or integer -> floating point bool isFloatIntPaint(Type to, Type from) { - return from.size() == to.size() && (from.isintegral() && to.isfloating() || from.isfloating() && to.isintegral()); + return from.size() == to.size() && (from.isIntegral() && to.isFloating() || from.isFloating() && to.isIntegral()); } // Reinterpret float/int value 'fromVal' as a float/integer of type 'to'. @@ -1053,7 +1052,7 @@ bool realCmp(EXP op, real_t r1, real_t r2) @safe nothrow * Returns: * -1,0,1 */ -private int ctfeCmpArrays(const ref Loc loc, Expression e1, Expression e2, uinteger_t len) +private int ctfeCmpArrays(Loc loc, Expression e1, Expression e2, uinteger_t len) { // Resolve slices, if necessary uinteger_t lo1 = 0; @@ -1088,7 +1087,7 @@ private int ctfeCmpArrays(const ref Loc loc, Expression e1, Expression e2, uinte // Comparing two array literals. This case is potentially recursive. // If they aren't strings, we just need an equality check rather than // a full cmp. - const bool needCmp = ae1.type.nextOf().isintegral(); + const bool needCmp = ae1.type.nextOf().isIntegral(); foreach (size_t i; 0 .. cast(size_t)len) { Expression ee1 = (*ae1.elements)[cast(size_t)(lo1 + i)]; @@ -1139,7 +1138,7 @@ private bool isArray(const Expression e) @safe nothrow * For strings, return <0 if e1 < e2, 0 if e1==e2, >0 if e1 > e2. * For all other types, return 0 if e1 == e2, !=0 if e1 != e2. */ -private int ctfeRawCmp(const ref Loc loc, Expression e1, Expression e2, bool identity = false) +private int ctfeRawCmp(Loc loc, Expression e1, Expression e2, bool identity = false) { if (e1.op == EXP.classReference || e2.op == EXP.classReference) { @@ -1213,26 +1212,23 @@ private int ctfeRawCmp(const ref Loc loc, Expression e1, Expression e2, bool ide } return cast(int)(len1 - len2); } - if (e1.type.isintegral()) + if (e1.type.isIntegral()) { return e1.toInteger() != e2.toInteger(); } - if (identity && e1.type.isfloating()) + if (identity && e1.type.isFloating()) return !e1.isIdentical(e2); - if (e1.type.isreal() || e1.type.isimaginary()) + if (e1.type.isReal() || e1.type.isImaginary()) { - real_t r1 = e1.type.isreal() ? e1.toReal() : e1.toImaginary(); - real_t r2 = e1.type.isreal() ? e2.toReal() : e2.toImaginary(); + real_t r1 = e1.type.isReal() ? e1.toReal() : e1.toImaginary(); + real_t r2 = e1.type.isReal() ? e2.toReal() : e2.toImaginary(); if (CTFloat.isNaN(r1) || CTFloat.isNaN(r2)) // if unordered { return 1; // they are not equal } - else - { - return (r1 != r2); - } + return (r1 != r2); } - else if (e1.type.iscomplex()) + else if (e1.type.isComplex()) { return e1.toComplex() != e2.toComplex(); } @@ -1243,33 +1239,30 @@ private int ctfeRawCmp(const ref Loc loc, Expression e1, Expression e2, bool ide // For structs, we only need to return 0 or 1 (< and > aren't legal). if (es1.sd != es2.sd) return 1; - else if ((!es1.elements || !es1.elements.length) && (!es2.elements || !es2.elements.length)) + if ((!es1.elements || !es1.elements.length) && (!es2.elements || !es2.elements.length)) return 0; // both arrays are empty - else if (!es1.elements || !es2.elements) + if (!es1.elements || !es2.elements) return 1; - else if (es1.elements.length != es2.elements.length) + if (es1.elements.length != es2.elements.length) return 1; - else + foreach (size_t i; 0 .. es1.elements.length) { - foreach (size_t i; 0 .. es1.elements.length) - { - Expression ee1 = (*es1.elements)[i]; - Expression ee2 = (*es2.elements)[i]; + Expression ee1 = (*es1.elements)[i]; + Expression ee2 = (*es2.elements)[i]; - // https://issues.dlang.org/show_bug.cgi?id=16284 - if (ee1.op == EXP.void_ && ee2.op == EXP.void_) // if both are VoidInitExp - continue; + // https://issues.dlang.org/show_bug.cgi?id=16284 + if (ee1.op == EXP.void_ && ee2.op == EXP.void_) // if both are VoidInitExp + continue; - if (ee1 == ee2) - continue; - if (!ee1 || !ee2) - return 1; - const int cmp = ctfeRawCmp(loc, ee1, ee2, identity); - if (cmp) - return 1; - } - return 0; // All elements are equal + if (ee1 == ee2) + continue; + if (!ee1 || !ee2) + return 1; + const int cmp = ctfeRawCmp(loc, ee1, ee2, identity); + if (cmp) + return 1; } + return 0; // All elements are equal } if (e1.op == EXP.assocArrayLiteral && e2.op == EXP.assocArrayLiteral) { @@ -1317,13 +1310,13 @@ private int ctfeRawCmp(const ref Loc loc, Expression e1, Expression e2, bool ide } /// Evaluate ==, !=. Resolves slices before comparing. Returns 0 or 1 -bool ctfeEqual(const ref Loc loc, EXP op, Expression e1, Expression e2) +bool ctfeEqual(Loc loc, EXP op, Expression e1, Expression e2) { return !ctfeRawCmp(loc, e1, e2) ^ (op == EXP.notEqual); } /// Evaluate is, !is. Resolves slices before comparing. Returns 0 or 1 -bool ctfeIdentity(const ref Loc loc, EXP op, Expression e1, Expression e2) +bool ctfeIdentity(Loc loc, EXP op, Expression e1, Expression e2) { //printf("ctfeIdentity %s %s\n", e1.toChars(), e2.toChars()); //printf("ctfeIdentity op = '%s', e1 = %s %s, e2 = %s %s\n", EXPtoString(op).ptr, @@ -1343,7 +1336,7 @@ bool ctfeIdentity(const ref Loc loc, EXP op, Expression e1, Expression e2) SymOffExp es2 = e2.isSymOffExp(); cmp = (es1.var == es2.var && es1.offset == es2.offset); } - else if (e1.type.isfloating()) + else if (e1.type.isFloating()) cmp = e1.isIdentical(e2); else { @@ -1355,29 +1348,29 @@ bool ctfeIdentity(const ref Loc loc, EXP op, Expression e1, Expression e2) } /// Evaluate >,<=, etc. Resolves slices before comparing. Returns 0 or 1 -bool ctfeCmp(const ref Loc loc, EXP op, Expression e1, Expression e2) +bool ctfeCmp(Loc loc, EXP op, Expression e1, Expression e2) { Type t1 = e1.type.toBasetype(); Type t2 = e2.type.toBasetype(); if (t1.isString() && t2.isString()) return specificCmp(op, ctfeRawCmp(loc, e1, e2)); - else if (t1.isreal()) + if (t1.isReal()) return realCmp(op, e1.toReal(), e2.toReal()); - else if (t1.isimaginary()) + if (t1.isImaginary()) return realCmp(op, e1.toImaginary(), e2.toImaginary()); - else if (t1.isunsigned() || t2.isunsigned()) + if (t1.isUnsigned() || t2.isUnsigned()) return intUnsignedCmp(op, e1.toInteger(), e2.toInteger()); else return intSignedCmp(op, e1.toInteger(), e2.toInteger()); } -UnionExp ctfeCat(const ref Loc loc, Type type, Expression e1, Expression e2) +UnionExp ctfeCat(Loc loc, Type type, Expression e1, Expression e2) { Type t1 = e1.type.toBasetype(); Type t2 = e2.type.toBasetype(); UnionExp ue; - if (e2.op == EXP.string_ && e1.op == EXP.arrayLiteral && t1.nextOf().isintegral()) + if (e2.op == EXP.string_ && e1.op == EXP.arrayLiteral && t1.nextOf().isIntegral()) { // [chars] ~ string => string (only valid for CTFE) StringExp es1 = e2.isStringExp(); @@ -1406,7 +1399,7 @@ UnionExp ctfeCat(const ref Loc loc, Type type, Expression e1, Expression e2) es.type = type; return ue; } - if (e1.op == EXP.string_ && e2.op == EXP.arrayLiteral && t2.nextOf().isintegral()) + if (e1.op == EXP.string_ && e2.op == EXP.arrayLiteral && t2.nextOf().isIntegral()) { // string ~ [chars] => string (only valid for CTFE) // Concatenate the strings @@ -1466,7 +1459,7 @@ UnionExp ctfeCat(const ref Loc loc, Type type, Expression e1, Expression e2) /* Given an AA literal 'ae', and a key 'e2': * Return ae[e2] if present, or NULL if not found. */ -Expression findKeyInAA(const ref Loc loc, AssocArrayLiteralExp ae, Expression e2) +Expression findKeyInAA(Loc loc, AssocArrayLiteralExp ae, Expression e2) { /* Search the keys backwards, in case there are duplicate keys */ @@ -1487,7 +1480,7 @@ Expression findKeyInAA(const ref Loc loc, AssocArrayLiteralExp ae, Expression e2 * dynamic arrays, and strings. We know that e1 is an * interpreted CTFE expression, so it cannot have side-effects. */ -Expression ctfeIndex(UnionExp* pue, const ref Loc loc, Type type, Expression e1, uinteger_t indx) +Expression ctfeIndex(UnionExp* pue, Loc loc, Type type, Expression e1, uinteger_t indx) { //printf("ctfeIndex(e1 = %s)\n", e1.toChars()); assert(e1.type); @@ -1516,7 +1509,7 @@ Expression ctfeIndex(UnionExp* pue, const ref Loc loc, Type type, Expression e1, assert(0); } -Expression ctfeCast(UnionExp* pue, const ref Loc loc, Type type, Type to, Expression e, bool explicitCast = false) +Expression ctfeCast(UnionExp* pue, Loc loc, Type type, Type to, Expression e, bool explicitCast = false) { Expression paint() { @@ -1536,11 +1529,9 @@ Expression ctfeCast(UnionExp* pue, const ref Loc loc, Type type, Type to, Expres : tclass.implicitConvTo(to.mutableOf()); if (match) return paint(); - else - { - emplaceExp!(NullExp)(pue, loc, to); - return pue.exp(); - } + + emplaceExp!(NullExp)(pue, loc, to); + return pue.exp(); } // Allow TypeInfo type painting @@ -1658,7 +1649,7 @@ void assignInPlace(Expression dest, Expression src) } // Given an AA literal aae, set aae[index] = newval and return newval. -Expression assignAssocArrayElement(const ref Loc loc, AssocArrayLiteralExp aae, Expression index, Expression newval) +Expression assignAssocArrayElement(Loc loc, AssocArrayLiteralExp aae, Expression index, Expression newval) { /* Create new associative array literal reflecting updated key/value */ @@ -1688,7 +1679,7 @@ Expression assignAssocArrayElement(const ref Loc loc, AssocArrayLiteralExp aae, /// Given array literal oldval of type ArrayLiteralExp or StringExp, of length /// oldlen, change its length to newlen. If the newlen is longer than oldlen, /// all new elements will be set to the default initializer for the element type. -Expression changeArrayLiteralLength(UnionExp* pue, const ref Loc loc, TypeArray arrayType, Expression oldval, size_t oldlen, size_t newlen) +Expression changeArrayLiteralLength(UnionExp* pue, Loc loc, TypeArray arrayType, Expression oldval, size_t oldlen, size_t newlen) { Type elemType = arrayType.next; assert(elemType); @@ -1775,7 +1766,7 @@ bool isCtfeValueValid(Expression newval) case EXP.int64: case EXP.float64: case EXP.complex80: - return tb.isscalar(); + return tb.isScalar(); case EXP.null_: return tb.ty == Tnull || @@ -1850,7 +1841,7 @@ bool isCtfeValueValid(Expression newval) const SliceExp se = newval.isSliceExp(); assert(se.lwr && se.lwr.op == EXP.int64); assert(se.upr && se.upr.op == EXP.int64); - return (tb.ty == Tarray || tb.ty == Tsarray) && (se.e1.op == EXP.string_ || se.e1.op == EXP.arrayLiteral); + return tb.isStaticOrDynamicArray() && (se.e1.op == EXP.string_ || se.e1.op == EXP.arrayLiteral); } case EXP.void_: diff --git a/dmd/ctorflow.d b/dmd/ctorflow.d index ba5240e5e1..e604700e1f 100644 --- a/dmd/ctorflow.d +++ b/dmd/ctorflow.d @@ -1,12 +1,12 @@ /** * Manage flow analysis for constructors. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/ctorflow.d, _ctorflow.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/ctorflow.d, _ctorflow.d) * Documentation: https://dlang.org/phobos/dmd_ctorflow.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/ctorflow.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/ctorflow.d */ module dmd.ctorflow; diff --git a/dmd/cxxfrontend.d b/dmd/cxxfrontend.d index 47a443e378..05f2d21e29 100644 --- a/dmd/cxxfrontend.d +++ b/dmd/cxxfrontend.d @@ -1,12 +1,12 @@ /** * Contains C++ interfaces for interacting with DMD as a library. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/cxxfrontend.d, _cxxfrontend.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/cxxfrontend.d, _cxxfrontend.d) * Documentation: https://dlang.org/phobos/dmd_cxxfrontend.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/cxxfrontend.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/cxxfrontend.d */ module dmd.cxxfrontend; @@ -15,6 +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.denum : EnumDeclaration; import dmd.dmodule /*: Module*/; import dmd.dscope : Scope; @@ -45,24 +47,24 @@ Expressions* getAttributes(UserAttributeDeclaration a) } /*********************************************************** - * cppmangle.d + * mangle/cpp.d */ const(char)* toCppMangleItanium(Dsymbol s) { - import dmd.cppmangle; - return dmd.cppmangle.toCppMangleItanium(s); + import dmd.mangle.cpp; + return dmd.mangle.cpp.toCppMangleItanium(s); } const(char)* cppTypeInfoMangleItanium(Dsymbol s) { - import dmd.cppmangle; - return dmd.cppmangle.cppTypeInfoMangleItanium(s); + import dmd.mangle.cpp; + return dmd.mangle.cpp.cppTypeInfoMangleItanium(s); } const(char)* cppThunkMangleItanium(FuncDeclaration fd, int offset) { - import dmd.cppmangle; - return dmd.cppmangle.cppThunkMangleItanium(fd, offset); + import dmd.mangle.cpp; + return dmd.mangle.cpp.cppThunkMangleItanium(fd, offset); } /*********************************************************** @@ -75,36 +77,36 @@ Expression ctfeInterpret(Expression e) } /*********************************************************** - * dmangle.d + * mangle/package.d */ const(char)* mangleExact(FuncDeclaration fd) { - import dmd.dmangle; - return dmd.dmangle.mangleExact(fd); + import dmd.mangle; + return dmd.mangle.mangleExact(fd); } void mangleToBuffer(Type t, ref OutBuffer buf) { - import dmd.dmangle; - return dmd.dmangle.mangleToBuffer(t, buf); + import dmd.mangle; + return dmd.mangle.mangleToBuffer(t, buf); } void mangleToBuffer(Expression e, ref OutBuffer buf) { - import dmd.dmangle; - return dmd.dmangle.mangleToBuffer(e, buf); + import dmd.mangle; + return dmd.mangle.mangleToBuffer(e, buf); } void mangleToBuffer(Dsymbol s, ref OutBuffer buf) { - import dmd.dmangle; - return dmd.dmangle.mangleToBuffer(s, buf); + import dmd.mangle; + return dmd.mangle.mangleToBuffer(s, buf); } void mangleToBuffer(TemplateInstance ti, ref OutBuffer buf) { - import dmd.dmangle; - return dmd.dmangle.mangleToBuffer(ti, buf); + import dmd.mangle; + return dmd.mangle.mangleToBuffer(ti, buf); } /*********************************************************** @@ -153,7 +155,7 @@ void addMember(Dsymbol dsym, Scope* sc, ScopeDsymbol sds) return dmd.dsymbolsem.addMember(dsym, sc, sds); } -Dsymbol search(Dsymbol d, const ref Loc loc, Identifier ident, SearchOptFlags +Dsymbol search(Dsymbol d, Loc loc, Identifier ident, SearchOptFlags flags = SearchOpt.all) { import dmd.dsymbolsem; @@ -172,6 +174,25 @@ void importAll(Dsymbol d, Scope* sc) return dmd.dsymbolsem.importAll(d, sc); } +Dsymbols* include(Dsymbol d, Scope* sc) +{ + import dmd.dsymbolsem; + return dmd.dsymbolsem.include(d, sc); +} + +bool isFuncHidden(ClassDeclaration cd, FuncDeclaration fd) +{ + import dmd.dsymbolsem; + return dmd.dsymbolsem.isFuncHidden(cd, fd); +} + +version (IN_LLVM) { /* not needed */ } else +Dsymbol vtblSymbol(ClassDeclaration cd) +{ + import dmd.dsymbolsem; + return dmd.dsymbolsem.vtblSymbol(cd); +} + /*********************************************************** * dtemplate.d */ @@ -227,7 +248,7 @@ void genCppHdrFiles(ref Modules ms) /*********************************************************** * enumsem.d */ -Expression getDefaultValue(EnumDeclaration ed, const ref Loc loc) +Expression getDefaultValue(EnumDeclaration ed, Loc loc) { import dmd.enumsem; return dmd.enumsem.getDefaultValue(ed, loc); @@ -250,6 +271,26 @@ Expression expressionSemantic(Expression e, Scope* sc) return dmd.expressionsem.expressionSemantic(e, sc); } +bool fill(StructDeclaration sd, Loc loc, + ref Expressions elements, bool ctorinit) +{ + import dmd.expressionsem; + return dmd.expressionsem.fill(sd, loc, elements, ctorinit); +} + +/*********************************************************** + * func.d + */ +FuncDeclaration genCfunc(Parameters* fparams, Type treturn, const(char)* name, StorageClass stc = STC.none) +{ + return FuncDeclaration.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); +} + /*********************************************************** * funcsem.d */ @@ -271,6 +312,12 @@ MATCH leastAsSpecialized(FuncDeclaration fd, FuncDeclaration g, Identifiers* nam return dmd.funcsem.leastAsSpecialized(fd, g, names); } +PURE isPure(FuncDeclaration fd) +{ + import dmd.funcsem; + return dmd.funcsem.isPure(fd); +} + /*********************************************************** * hdrgen.d */ @@ -321,13 +368,13 @@ version (IN_LLVM) {} else /*********************************************************** * iasm.d */ -Statement asmSemantic(AsmStatement s, Scope *sc) +Statement asmSemantic(AsmStatement s, Scope* sc) { import dmd.iasm; return dmd.iasm.asmSemantic(s, sc); } -void asmSemantic(CAsmDeclaration d, Scope *sc) +void asmSemantic(CAsmDeclaration d, Scope* sc) { import dmd.iasm; return dmd.iasm.asmSemantic(d, sc); @@ -337,13 +384,13 @@ void asmSemantic(CAsmDeclaration d, Scope *sc) /*********************************************************** * iasmgcc.d */ -Statement gccAsmSemantic(GccAsmStatement s, Scope *sc) +Statement gccAsmSemantic(GccAsmStatement s, Scope* sc) { import dmd.iasmgcc; return dmd.iasmgcc.gccAsmSemantic(s, sc); } -void gccAsmSemantic(CAsmDeclaration d, Scope *sc) +void gccAsmSemantic(CAsmDeclaration d, Scope* sc) { import dmd.iasmgcc; return dmd.iasmgcc.gccAsmSemantic(d, sc); @@ -422,6 +469,11 @@ void semanticTypeInfoMembers(StructDeclaration sd) return dmd.semantic3.semanticTypeInfoMembers(sd); } +bool checkClosure(FuncDeclaration fd) +{ + import dmd.semantic3; + return dmd.semantic3.checkClosure(fd); +} /*********************************************************** * statementsem.d */ @@ -449,13 +501,13 @@ bool hasPointers(Type t) return dmd.typesem.hasPointers(t); } -Type typeSemantic(Type type, const ref Loc loc, Scope* sc) +Type typeSemantic(Type type, Loc loc, Scope* sc) { import dmd.typesem; return dmd.typesem.typeSemantic(type, loc, sc); } -Type trySemantic(Type type, const ref Loc loc, Scope* sc) +Type trySemantic(Type type, Loc loc, Scope* sc) { import dmd.typesem; return dmd.typesem.trySemantic(type, loc, sc); @@ -473,7 +525,7 @@ Type merge2(Type type) return dmd.typesem.merge2(type); } -Expression defaultInit(Type mt, const ref Loc loc, const bool isCfile = false) +Expression defaultInit(Type mt, Loc loc, const bool isCfile = false) { import dmd.typesem; return dmd.typesem.defaultInit(mt, loc, isCfile); @@ -489,7 +541,13 @@ Covariant covariant(Type src, Type t, StorageClass* pstc = null, bool cppCovariant = false) { import dmd.typesem; - return dmd.typesem.covariant(src, t, pstc, cppCovariant); + return dmd.typesem.covariant(src, t, cast(STC*) pstc, cppCovariant); +} + +bool isZeroInit(Type t, Loc loc) +{ + import dmd.typesem; + return dmd.typesem.isZeroInit(t, loc); } bool isBaseOf(Type tthis, Type t, int* poffset) @@ -615,7 +673,7 @@ Type addMod(Type type, MOD mod) Type addStorageClass(Type type, StorageClass stc) { import dmd.typesem; - return dmd.typesem.addStorageClass(type, stc); + return dmd.typesem.addStorageClass(type, cast(STC) stc); } Type pointerTo(Type type) @@ -636,16 +694,28 @@ uinteger_t size(Type type) return dmd.typesem.size(type); } -uinteger_t size(Type type, const ref Loc loc) +uinteger_t size(Type type, Loc loc) { import dmd.typesem; return dmd.typesem.size(type, loc); } +MATCH implicitConvTo(Type from, Type to) +{ + import dmd.dcast; + return dmd.dcast.implicitConvTo(from, to); +} + +MATCH constConv(Type from, Type to) +{ + import dmd.typesem; + return dmd.typesem.constConv(from, to); +} + /*********************************************************** * typinf.d */ -bool genTypeInfo(Expression e, const ref Loc loc, Type torig, Scope* sc) +bool genTypeInfo(Expression e, Loc loc, Type torig, Scope* sc) { import dmd.typinf; return dmd.typinf.genTypeInfo(e, loc, torig, sc); @@ -664,6 +734,18 @@ bool builtinTypeInfo(Type t) return dmd.typinf.builtinTypeInfo(t); } +Type makeNakedAssociativeArray(TypeAArray t) +{ + import dmd.typinf; + return dmd.typinf.makeNakedAssociativeArray(t); +} + +TypeInfoDeclaration getTypeInfoAssocArrayDeclaration(TypeAArray t, Scope* sc) +{ + import dmd.typinf; + return dmd.typinf.getTypeInfoAssocArrayDeclaration(t, sc); +} + version (IN_LLVM) { /*********************************************************** diff --git a/dmd/dcast.d b/dmd/dcast.d index 29f5dd23b6..86d2f0f07c 100644 --- a/dmd/dcast.d +++ b/dmd/dcast.d @@ -1,12 +1,12 @@ /** * Semantic analysis for cast-expressions. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/dcast.d, _dcast.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/dcast.d, _dcast.d) * Documentation: https://dlang.org/phobos/dmd_dcast.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/dcast.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/dcast.d */ module dmd.dcast; @@ -46,6 +46,7 @@ import dmd.root.ctfloat; import dmd.common.outbuffer; import dmd.root.rmem; import dmd.root.utf; +import dmd.safe : setUnsafe; import dmd.tokens; import dmd.typesem; @@ -71,7 +72,7 @@ Expression implicitCastTo(Expression e, Scope* sc, Type t) Expression visit(Expression e) { //printf("Expression.implicitCastTo(%s of type %s) => %s\n", e.toChars(), e.type.toChars(), t.toChars()); - if (const match = (sc && sc.flags & SCOPE.Cfile) ? e.cimplicitConvTo(t) : e.implicitConvTo(t)) + if (const match = (sc && sc.inCfile) ? e.cimplicitConvTo(t) : e.implicitConvTo(t)) { // no need for an extra cast when matching is exact @@ -149,7 +150,7 @@ Expression implicitCastTo(Expression e, Scope* sc, Type t) //printf("type %s t %s\n", type.deco, t.deco); auto ts = toAutoQualChars(e.type, t); error(e.loc, "cannot implicitly convert expression `%s` of type `%s` to `%s`", - e.toChars(), ts[0], ts[1]); + e.toErrMsg(), ts[0], ts[1]); } } return ErrorExp.get(); @@ -272,7 +273,7 @@ MATCH implicitConvTo(Expression e, Type t) /* See if we can do integral narrowing conversions */ - if (e.type.isintegral() && t.isintegral() && e.type.isTypeBasic() && t.isTypeBasic()) + if (e.type.isIntegral() && t.isIntegral() && e.type.isTypeBasic() && t.isTypeBasic()) { IntRange src = getIntRange(e); IntRange target = IntRange.fromType(t); @@ -321,14 +322,14 @@ MATCH implicitConvTo(Expression e, Type t) Type t1b = e.e1.type.toBasetype(); Type t2b = e.e2.type.toBasetype(); - if (t1b.ty == Tpointer && t2b.isintegral() && t1b.equivalent(tb)) + if (t1b.ty == Tpointer && t2b.isIntegral() && t1b.equivalent(tb)) { // ptr + offset // ptr - offset MATCH m = e.e1.implicitConvTo(t); return (m > MATCH.constant) ? MATCH.constant : m; } - if (t2b.ty == Tpointer && t1b.isintegral() && t2b.equivalent(tb)) + if (t2b.ty == Tpointer && t1b.isIntegral() && t2b.equivalent(tb)) { // offset + ptr MATCH m = e.e2.implicitConvTo(t); @@ -453,7 +454,7 @@ MATCH implicitConvTo(Expression e, Type t) bool isLosslesslyConvertibleToFP(T)() { - if (e.type.isunsigned()) + if (e.type.isUnsigned()) { const f = cast(T) value; return cast(dinteger_t) f == value; @@ -473,7 +474,7 @@ MATCH implicitConvTo(Expression e, Type t) case Tint8: if (ty == Tuns64 && value & ~0x7FU) return MATCH.nomatch; - else if (cast(byte)value != value) + if (cast(byte)value != value) return MATCH.nomatch; break; @@ -490,7 +491,7 @@ MATCH implicitConvTo(Expression e, Type t) case Tint16: if (ty == Tuns64 && value & ~0x7FFFU) return MATCH.nomatch; - else if (cast(short)value != value) + if (cast(short)value != value) return MATCH.nomatch; break; @@ -625,7 +626,7 @@ MATCH implicitConvTo(Expression e, Type t) if (!e.committed && t.ty == Tpointer && t.nextOf().ty == Tvoid) return MATCH.nomatch; - if (!(e.type.ty == Tsarray || e.type.ty == Tarray || e.type.ty == Tpointer)) + if (!(e.type.isStaticOrDynamicArray() || e.type.ty == Tpointer)) return visit(e); TY tyn = e.type.nextOf().ty; @@ -710,7 +711,7 @@ MATCH implicitConvTo(Expression e, Type t) if (e.type.ty == t.ty && e.type.nextOf().ty == tn.ty) return m; } - if (e.type != t && e.hexString && tn.isintegral && (tn.size == e.sz || (!e.committed && (e.len % tn.size) == 0))) + if (e.type != t && e.hexString && tn.isIntegral && (tn.size == e.sz || (!e.committed && (e.len % tn.size) == 0))) { m = MATCH.convert; return m; @@ -763,8 +764,7 @@ MATCH implicitConvTo(Expression e, Type t) Type typeb = e.type.toBasetype(); auto result = MATCH.nomatch; - if ((tb.ty == Tarray || tb.ty == Tsarray) && - (typeb.ty == Tarray || typeb.ty == Tsarray)) + if (tb.isStaticOrDynamicArray() && typeb.isStaticOrDynamicArray()) { result = MATCH.exact; Type typen = typeb.nextOf().toBasetype(); @@ -807,7 +807,7 @@ MATCH implicitConvTo(Expression e, Type t) return result; } - else if (tb.ty == Tvector && (typeb.ty == Tarray || typeb.ty == Tsarray || typeb.ty == Tpointer)) + else if (tb.ty == Tvector && (typeb.isStaticOrDynamicArray() || typeb.ty == Tpointer)) { // Tpointer because ImportC eagerly converts Tsarray to Tpointer result = MATCH.exact; // Convert array literal to vector type @@ -937,7 +937,7 @@ MATCH implicitConvTo(Expression e, Type t) */ Type tb = t.toBasetype(); MOD mod = tb.mod; - if (tf.isref) + if (tf.isRef) { } else @@ -1169,7 +1169,7 @@ MATCH implicitConvTo(Expression e, Type t) if (result != MATCH.nomatch) return result; - if (t.isintegral() && e.e1.type.isintegral() && e.e1.implicitConvTo(t) != MATCH.nomatch) + if (t.isIntegral() && e.e1.type.isIntegral() && e.e1.implicitConvTo(t) != MATCH.nomatch) result = MATCH.convert; else result = visit(e); @@ -1504,12 +1504,11 @@ MATCH implicitConvTo(Type from, Type to) { if (from.mod == to.mod) return MATCH.exact; - else if (MODimplicitConv(from.mod, to.mod)) + if (MODimplicitConv(from.mod, to.mod)) return MATCH.constant; - else if (!((from.mod ^ to.mod) & MODFlags.shared_)) // for wild matching + if (!((from.mod ^ to.mod) & MODFlags.shared_)) // for wild matching return MATCH.constant; - else - return MATCH.convert; + return MATCH.convert; } if (from.ty == Tvoid || to.ty == Tvoid) @@ -1953,12 +1952,12 @@ MATCH cimplicitConvTo(Expression e, Type t) if (tb.isTypeVector() || typeb.isTypeVector()) return implicitConvTo(e, t); // permissive checking doesn't apply to vectors - if ((typeb.isintegral() || typeb.isfloating()) && - (tb.isintegral() || tb.isfloating())) + if ((typeb.isIntegral() || typeb.isFloating()) && + (tb.isIntegral() || tb.isFloating())) return MATCH.convert; - if (tb.ty == Tpointer && typeb.isintegral()) // C11 6.3.2.3-5 + if (tb.ty == Tpointer && typeb.isIntegral()) // C11 6.3.2.3-5 return MATCH.convert; - if (tb.isintegral() && typeb.ty == Tpointer) // C11 6.3.2.3-6 + if (tb.isIntegral() && typeb.ty == Tpointer) // C11 6.3.2.3-6 return MATCH.convert; if (tb.ty == Tpointer && typeb.ty == Tpointer) // C11 6.3.2.3-7 return MATCH.convert; @@ -2059,8 +2058,8 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null) const(bool) t1b_isR = (t1b_isFR || t1b.ty == Tpointer || t1b.ty == Taarray || t1b.ty == Tclass); // Arithmetic types (== valueable basic types) - const(bool) tob_isA = ((tob.isintegral() || tob.isfloating()) && tob.ty != Tvector); - const(bool) t1b_isA = ((t1b.isintegral() || t1b.isfloating()) && t1b.ty != Tvector); + const(bool) tob_isA = ((tob.isIntegral() || tob.isFloating()) && tob.ty != Tvector); + const(bool) t1b_isA = ((t1b.isIntegral() || t1b.isFloating()) && t1b.ty != Tvector); // Try casting the alias this member. // Return the expression if it succeeds, null otherwise. @@ -2146,8 +2145,7 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null) { if (hasAliasThis) { - auto result = tryAliasThisCast(); - if (result) + if (auto result = tryAliasThisCast()) return result; } @@ -2241,8 +2239,7 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null) */ if (hasAliasThis) { - auto result = tryAliasThisCast(); - if (result) + 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()); @@ -2265,7 +2262,7 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null) { if (!e.type.equals(t)) { - 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; @@ -2281,7 +2278,7 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null) { if (!e.type.equals(t)) { - if (e.type.iscomplex() && t.iscomplex()) + if (e.type.isComplex() && t.isComplex()) { auto result = e.copy(); result.type = t; @@ -2312,7 +2309,7 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null) //printf("StringExp::castTo(t = %s), '%s' committed = %d\n", t.toChars(), e.toChars(), e.committed); if (!e.committed && t.ty == Tpointer && t.nextOf().ty == Tvoid && - (!sc || !(sc.flags & SCOPE.Cfile))) + (!sc || !sc.inCfile)) { error(e.loc, "cannot convert string literal to `void*`"); return ErrorExp.get(); @@ -2342,7 +2339,7 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null) Type tb = t.toBasetype(); Type typeb = e.type.toBasetype(); - if (e.hexString && !e.committed && tb.nextOf().isintegral) + if (e.hexString && !e.committed && tb.nextOf().isIntegral) { const szx = cast(ubyte) tb.nextOf().size(); if (szx != se.sz && (e.len % szx) == 0) @@ -2443,9 +2440,9 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null) if (e.committed) goto Lcast; - auto X(T, U)(T tf, U tt) + static auto X(T, U)(T tf, U tt) { - return (cast(int)tf * 256 + cast(int)tt); + return cast(int)tf * 256 + cast(int)tt; } { @@ -2563,7 +2560,7 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null) // See if need to truncate or extend the literal if (auto tsa = tb.isTypeSArray()) { - size_t dim2 = cast(size_t)tsa.dim.toInteger(); + const dim2 = cast(size_t)tsa.dim.toInteger(); //printf("dim from = %d, to = %d\n", cast(int)se.len, cast(int)dim2); // Changing dimensions @@ -2643,8 +2640,7 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null) tb.ty == Tpointer && tb.nextOf().ty == Tfunction) { auto ve = e.e1.isVarExp(); - auto f = ve.var.isFuncDeclaration(); - if (f) + if (auto f = ve.var.isFuncDeclaration()) { assert(f.isImportedSymbol()); f = f.overloadExactMatch(tb.nextOf()); @@ -2733,8 +2729,7 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null) } Type typeb = e.type.toBasetype(); - if ((tb.ty == Tarray || tb.ty == Tsarray) && - (typeb.ty == Tarray || typeb.ty == Tsarray)) + if (tb.isStaticOrDynamicArray() && typeb.isStaticOrDynamicArray()) { if (tb.nextOf().toBasetype().ty == Tvoid && typeb.nextOf().toBasetype().ty != Tvoid) { @@ -2749,7 +2744,7 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null) if (auto tsa = tb.isTypeSArray()) { if (e.elements.length != tsa.dim.toInteger()) - goto L1; + return visit(ae); } ae = e.copy().isArrayLiteralExp(); @@ -2777,7 +2772,7 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null) ae.type = tp; } } - else if (tb.ty == Tvector && (typeb.ty == Tarray || typeb.ty == Tsarray || typeb.ty == Tpointer)) + else if (tb.ty == Tvector && (typeb.isStaticOrDynamicArray() || typeb.ty == Tpointer)) { // Convert array literal to vector type // The Tpointer case comes from C eagerly converting Tsarray to Tpointer @@ -2787,7 +2782,7 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null) const edim = e.elements.length; const tbasedim = tbase.dim.toInteger(); if (edim > tbasedim) - goto L1; + return visit(ae); ae = e.copy().isArrayLiteralExp(); ae.type = tbase; // https://issues.dlang.org/show_bug.cgi?id=12642 @@ -2811,7 +2806,6 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null) ev = ev.expressionSemantic(sc); return ev; } - L1: return visit(ae); } @@ -2844,6 +2838,7 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null) (*ae.keys)[i] = ex; } ae.type = t; + semanticTypeInfo(sc, ae.type); return ae; } return visit(e); @@ -2931,7 +2926,10 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null) { printf("DelegateExp::castTo(this=%s, type=%s, t=%s)\n", e.toChars(), e.type.toChars(), t.toChars()); } - static immutable msg = "cannot form delegate due to covariant return type"; + void errorCovariantReturnType() + { + error(e.loc, "cannot form delegate due to covariant return type"); + } Type tb = t.toBasetype(); Type typeb = e.type.toBasetype(); @@ -2941,7 +2939,7 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null) int offset; e.func.tookAddressOf++; if (e.func.tintro && e.func.tintro.nextOf().isBaseOf(e.func.type.nextOf(), &offset) && offset) - error(e.loc, "%s", msg.ptr); + errorCovariantReturnType(); auto result = e.copy(); result.type = t; return result; @@ -2952,12 +2950,11 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null) { if (e.func) { - auto f = e.func.overloadExactMatch(tb.nextOf()); - if (f) + if (auto f = e.func.overloadExactMatch(tb.nextOf())) { int offset; if (f.tintro && f.tintro.nextOf().isBaseOf(f.type.nextOf(), &offset) && offset) - error(e.loc, "%s", msg.ptr); + errorCovariantReturnType(); if (f != e.func) // if address not already marked as taken f.tookAddressOf++; auto result = new DelegateExp(e.loc, e.e1, f, false, e.vthis2); @@ -2965,7 +2962,7 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null) return result; } if (e.func.tintro) - error(e.loc, "%s", msg.ptr); + errorCovariantReturnType(); } } @@ -3147,7 +3144,7 @@ Expression inferType(Expression e, Type t, int flag = 0) Expression visitAle(ArrayLiteralExp ale) { Type tb = t.toBasetype(); - if (tb.ty == Tarray || tb.ty == Tsarray) + if (tb.isStaticOrDynamicArray()) { Type tn = tb.nextOf(); if (ale.basis) @@ -3229,7 +3226,7 @@ Expression scaleFactor(BinExp be, Scope* sc) Type t2b = be.e2.type.toBasetype(); Expression eoff; - if (t1b.ty == Tpointer && t2b.isintegral()) + if (t1b.ty == Tpointer && t2b.isIntegral()) { // Need to adjust operator by the stride // Replace (ptr + int) with (ptr + (int * stride)) @@ -3243,7 +3240,7 @@ Expression scaleFactor(BinExp be, Scope* sc) be.e2.type = t; be.type = be.e1.type; } - else if (t2b.ty == Tpointer && t1b.isintegral()) + else if (t2b.ty == Tpointer && t1b.isIntegral()) { // Need to adjust operator by the stride // Replace (int + ptr) with (ptr + (int * stride)) @@ -3270,7 +3267,7 @@ Expression scaleFactor(BinExp be, Scope* sc) if (eoff.op == EXP.int64 && eoff.toInteger() == 0) { } - else if (sc.setUnsafe(false, be.loc, "pointer arithmetic not allowed in @safe functions")) + else if (sc.setUnsafe(false, be.loc, "pointer arithmetic")) { return ErrorExp.get(); } @@ -3291,7 +3288,7 @@ private bool isVoidArrayLiteral(Expression e, Type other) { auto ale = e.isArrayLiteralExp(); e = ale[0]; - if (other.ty == Tsarray || other.ty == Tarray) + if (other.isStaticOrDynamicArray()) other = other.nextOf(); else return false; @@ -3366,16 +3363,16 @@ Type typeMerge(Scope* sc, EXP op, ref Expression pe1, ref Expression pe2) Type t1b = e1.type.toBasetype(); Type t2b = e2.type.toBasetype(); - if (sc && sc.flags & SCOPE.Cfile) + if (sc && sc.inCfile) { // Integral types can be implicitly converted to pointers if ((t1b.ty == Tpointer) != (t2b.ty == Tpointer)) { - if (t1b.isintegral()) + if (t1b.isIntegral()) { return convert(e1, t2b); } - else if (t2b.isintegral()) + else if (t2b.isIntegral()) { return convert(e2, t1b); } @@ -3497,8 +3494,8 @@ Lagain: d.purity = PURE.impure; assert(d.purity != PURE.fwdref); - d.isnothrow = (tf1.isnothrow && tf2.isnothrow); - d.isnogc = (tf1.isnogc && tf2.isnogc); + d.isNothrow = (tf1.isNothrow && tf2.isNothrow); + d.isNogc = (tf1.isNogc && tf2.isNogc); if (tf1.trust == tf2.trust) d.trust = tf1.trust; @@ -3557,7 +3554,7 @@ Lagain: return null; } - if ((t1.ty == Tsarray || t1.ty == Tarray) && (e2.op == EXP.null_ && t2.ty == Tpointer && t2.nextOf().ty == Tvoid || e2.op == EXP.arrayLiteral && t2.ty == Tsarray && t2.nextOf().ty == Tvoid && t2.isTypeSArray().dim.toInteger() == 0 || isVoidArrayLiteral(e2, t1))) + if (t1.isStaticOrDynamicArray() && (e2.op == EXP.null_ && t2.ty == Tpointer && t2.nextOf().ty == Tvoid || e2.op == EXP.arrayLiteral && t2.ty == Tsarray && t2.nextOf().ty == Tvoid && t2.isTypeSArray().dim.toInteger() == 0 || isVoidArrayLiteral(e2, t1))) { /* (T[n] op void*) => T[] * (T[] op void*) => T[] @@ -3569,7 +3566,9 @@ Lagain: return coerce(t1.nextOf().arrayOf()); } - if ((t2.ty == Tsarray || t2.ty == Tarray) && (e1.op == EXP.null_ && t1.ty == Tpointer && t1.nextOf().ty == Tvoid || e1.op == EXP.arrayLiteral && t1.ty == Tsarray && t1.nextOf().ty == Tvoid && t1.isTypeSArray().dim.toInteger() == 0 || isVoidArrayLiteral(e1, t2))) + if (t2.isStaticOrDynamicArray() && + (e1.op == EXP.null_ && t1.ty == Tpointer && t1.nextOf().ty == Tvoid || e1.op == EXP.arrayLiteral + && t1.ty == Tsarray && t1.nextOf().ty == Tvoid && t1.isTypeSArray().dim.toInteger() == 0 || isVoidArrayLiteral(e1, t2))) { /* (void* op T[n]) => T[] * (void* op T[]) => T[] @@ -3581,7 +3580,7 @@ Lagain: return coerce(t2.nextOf().arrayOf()); } - if ((t1.ty == Tsarray || t1.ty == Tarray) && (m = t1.implicitConvTo(t2)) != MATCH.nomatch) + if (t1.isStaticOrDynamicArray() && (m = t1.implicitConvTo(t2)) != MATCH.nomatch) { // https://issues.dlang.org/show_bug.cgi?id=7285 // Tsarray op [x, y, ...] should to be Tsarray @@ -3597,7 +3596,7 @@ Lagain: return convert(e1, t2); } - if ((t2.ty == Tsarray || t2.ty == Tarray) && t2.implicitConvTo(t1)) + if (t2.isStaticOrDynamicArray() && t2.implicitConvTo(t1)) { // https://issues.dlang.org/show_bug.cgi?id=7285 // https://issues.dlang.org/show_bug.cgi?id=14737 @@ -3606,7 +3605,8 @@ Lagain: return convert(e2, t1); } - if ((t1.ty == Tsarray || t1.ty == Tarray || t1.ty == Tpointer) && (t2.ty == Tsarray || t2.ty == Tarray || t2.ty == Tpointer) && t1.nextOf().mod != t2.nextOf().mod) + if ((t1.isStaticOrDynamicArray() || t1.ty == Tpointer) && (t2.isStaticOrDynamicArray() || t2.ty == Tpointer) + && t1.nextOf().mod != t2.nextOf().mod) { /* If one is mutable and the other immutable, then retry * with both of them as const @@ -3888,7 +3888,7 @@ LmergeClassTypes: goto Lagain; } - if (t1.isintegral() && t2.isintegral()) + if (t1.isIntegral() && t2.isIntegral()) { if (t1.ty != t2.ty) { @@ -3926,7 +3926,7 @@ LmodCompare: return convert(e1, t2); /// Covers array operations for user-defined types - Type checkArrayOpType(Expression e1, Expression e2, EXP op, Scope *sc) + Type checkArrayOpType(Expression e1, Expression e2, EXP op, Scope* sc) { // scalar op scalar - we shouldn't be here if (e1.type.ty != Tarray && e1.type.ty != Tsarray && e2.type.ty != Tarray && e2.type.ty != Tsarray) @@ -4145,26 +4145,12 @@ Expression typeCombine(BinExp be, Scope* sc) { Expression errorReturn() { - Expression ex = be.incompatibleTypes(); + Expression ex = be.incompatibleTypes(sc); if (ex.op == EXP.error) return ex; return ErrorExp.get(); } - Type t1 = be.e1.type.toBasetype(); - Type t2 = be.e2.type.toBasetype(); - - if (be.op == EXP.min || be.op == EXP.add) - { - // struct+struct, and class+class are errors - if (t1.ty == Tstruct && t2.ty == Tstruct) - return errorReturn(); - else if (t1.ty == Tclass && t2.ty == Tclass) - return errorReturn(); - else if (t1.ty == Taarray && t2.ty == Taarray) - return errorReturn(); - } - if (auto result = typeMerge(sc, be.op, be.e1, be.e2)) { if (be.type is null) @@ -4226,7 +4212,7 @@ Expression integralPromotions(Expression e, Scope* sc) void fix16997(Scope* sc, UnaExp ue) { - if (global.params.fix16997 || sc.flags & SCOPE.Cfile) + if (global.params.fix16997 || sc.inCfile) ue.e1 = integralPromotions(ue.e1, sc); // desired C-like behavor else { @@ -4261,7 +4247,7 @@ extern (D) bool arrayTypeCompatibleWithoutCasting(Type t1, Type t2) t1 = t1.toBasetype(); t2 = t2.toBasetype(); - if ((t1.ty == Tarray || t1.ty == Tsarray || t1.ty == Tpointer) && t2.ty == t1.ty) + if ((t1.isStaticOrDynamicArray() || t1.ty == Tpointer) && t2.ty == t1.ty) { if (t1.nextOf().implicitConvTo(t2.nextOf()) >= MATCH.constant || t2.nextOf().implicitConvTo(t1.nextOf()) >= MATCH.constant) return true; @@ -4408,10 +4394,9 @@ IntRange getIntRange(Expression e) VarDeclaration vd = e.var.isVarDeclaration(); if (vd && vd.range) return vd.range._cast(e.type); - else if (vd && vd._init && !vd.type.isMutable() && (ie = vd.getConstInitializer()) !is null) + if (vd && vd._init && !vd.type.isMutable() && (ie = vd.getConstInitializer()) !is null) return getIntRange(ie); - else - return visit(e); + return visit(e); } IntRange visitComma(CommaExp e) diff --git a/dmd/dclass.d b/dmd/dclass.d index c596b7933b..f96095144f 100644 --- a/dmd/dclass.d +++ b/dmd/dclass.d @@ -3,12 +3,12 @@ * * Specification: $(LINK2 https://dlang.org/spec/class.html, Classes) * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/dclass.d, _dclass.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/dclass.d, _dclass.d) * Documentation: https://dlang.org/phobos/dmd_dclass.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/dclass.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/dclass.d */ module dmd.dclass; @@ -23,10 +23,9 @@ import dmd.gluelayer; import dmd.declaration; import dmd.dscope; import dmd.dsymbol; -import dmd.dsymbolsem; +import dmd.dsymbolsem : dsymbolSemantic, addMember, setFieldOffset; import dmd.errors; import dmd.func; -import dmd.funcsem; import dmd.id; import dmd.identifier; import dmd.location; @@ -34,7 +33,7 @@ import dmd.mtype; import dmd.objc; import dmd.root.rmem; import dmd.target; -import dmd.typesem; +import dmd.typesem : covariant, immutableOf, sarrayOf; import dmd.visitor; /*********************************************************** @@ -202,7 +201,7 @@ version (IN_LLVM) {} else Symbol* cpp_type_info_ptr_sym; // cached instance of class Id.cpp_type_info_ptr } - final extern (D) this(const ref Loc loc, Identifier id, BaseClasses* baseclasses, Dsymbols* members, bool inObject) + final extern (D) this(Loc loc, Identifier id, BaseClasses* baseclasses, Dsymbols* members, bool inObject) { objc = ObjcClassDeclaration(this); @@ -210,6 +209,7 @@ version (IN_LLVM) {} else id = Identifier.generateAnonymousId("class"); super(loc, id); + this.dsym = DSYM.classDeclaration; static immutable msg = "only object.d can define this reserved class name"; @@ -379,7 +379,7 @@ version (IN_LLVM) {} else .error(loc, fmt, kind, toPrettyChars, arg); } - static ClassDeclaration create(const ref Loc loc, Identifier id, BaseClasses* baseclasses, Dsymbols* members, bool inObject) + static ClassDeclaration create(Loc loc, Identifier id, BaseClasses* baseclasses, Dsymbols* members, bool inObject) { return new ClassDeclaration(loc, id, baseclasses, members, inObject); } @@ -493,8 +493,7 @@ version (IN_LLVM) {} else return null; if (cdb.ident.equals(ident)) return cdb; - auto result = cdb.searchBase(ident); - if (result) + if (auto result = cdb.searchBase(ident)) return result; } return null; @@ -618,39 +617,6 @@ version (IN_LLVM) {} else return classKind == ClassKind.d; } - final bool isFuncHidden(FuncDeclaration fd) - { - //printf("ClassDeclaration.isFuncHidden(class = %s, fd = %s)\n", toChars(), fd.toPrettyChars()); - Dsymbol s = this.search(Loc.initial, fd.ident, SearchOpt.ignoreAmbiguous | SearchOpt.ignoreErrors); - if (!s) - { - //printf("not found\n"); - /* Because, due to a hack, if there are multiple definitions - * of fd.ident, NULL is returned. - */ - return false; - } - s = s.toAlias(); - if (auto os = s.isOverloadSet()) - { - foreach (sm; os.a) - { - auto fm = sm.isFuncDeclaration(); - if (overloadApply(fm, s => fd == s.isFuncDeclaration())) - return false; - } - return true; - } - else - { - auto f = s.isFuncDeclaration(); - //printf("%s fdstart = %p\n", s.kind(), fdstart); - if (overloadApply(f, s => fd == s.isFuncDeclaration())) - return false; - return !fd.parent.isTemplateMixin(); - } - } - /**************** * Find virtual function matching identifier and type. * Used to build virtual function tables for interface implementations. @@ -926,21 +892,6 @@ version (IN_LLVM) { /* not needed */ } else { // Back end Dsymbol vtblsym; - - final Dsymbol vtblSymbol() - { - if (!vtblsym) - { - auto vtype = Type.tvoidptr.immutableOf().sarrayOf(vtbl.length); - auto var = new VarDeclaration(loc, vtype, Identifier.idPool("__vtbl"), null, STC.immutable_ | STC.static_); - var.addMember(null, this); - var.isdataseg = 1; - var._linkage = LINK.d; - var.semanticRun = PASS.semanticdone; // no more semantic wanted - vtblsym = var; - } - return vtblsym; - } } extern (D) final bool isErrorException() @@ -948,11 +899,6 @@ version (IN_LLVM) { /* not needed */ } else return errorException && (this == errorException || errorException.isBaseOf(this, null)); } - override final inout(ClassDeclaration) isClassDeclaration() inout @nogc nothrow pure @safe - { - return this; - } - override void accept(Visitor v) { v.visit(this); @@ -963,9 +909,10 @@ version (IN_LLVM) { /* not needed */ } else */ extern (C++) final class InterfaceDeclaration : ClassDeclaration { - extern (D) this(const ref Loc loc, Identifier id, BaseClasses* baseclasses) + extern (D) this(Loc loc, Identifier id, BaseClasses* baseclasses) { super(loc, id, baseclasses, null, false); + this.dsym = DSYM.interfaceDeclaration; if (id == Id.IUnknown) // IUnknown is the root of all COM interfaces { com = true; @@ -1065,11 +1012,6 @@ extern (C++) final class InterfaceDeclaration : ClassDeclaration return com; } - override inout(InterfaceDeclaration) isInterfaceDeclaration() inout - { - return this; - } - override void accept(Visitor v) { v.visit(this); diff --git a/dmd/declaration.d b/dmd/declaration.d index c88bc48f90..77f3a0027f 100644 --- a/dmd/declaration.d +++ b/dmd/declaration.d @@ -2,12 +2,12 @@ * Miscellaneous declarations, including typedef, alias, variable declarations including the * implicit this declaration, type tuples, ClassInfo, ModuleInfo and various TypeInfos. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/declaration.d, _declaration.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/declaration.d, _declaration.d) * Documentation: https://dlang.org/phobos/dmd_declaration.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/declaration.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/declaration.d */ module dmd.declaration; @@ -22,18 +22,19 @@ import dmd.delegatize; import dmd.dscope; import dmd.dstruct; import dmd.dsymbol; -import dmd.dsymbolsem; +import dmd.dsymbolsem : dsymbolSemantic, aliasSemantic; import dmd.dtemplate; import dmd.errors; import dmd.expression; import dmd.func; -import dmd.funcsem; +import dmd.funcsem : overloadApply, getLevelAndCheck; import dmd.globals; import dmd.gluelayer; +import dmd.hdrgen; import dmd.id; import dmd.identifier; import dmd.init; -import dmd.initsem; +import dmd.initsem : initializerToExpression, initializerSemantic; import dmd.intrange; import dmd.location; import dmd.mtype; @@ -42,181 +43,18 @@ import dmd.rootobject; import dmd.root.filename; import dmd.target; import dmd.tokens; -import dmd.typesem; +import dmd.typesem : toDsymbol, typeSemantic, size, hasPointers; import dmd.visitor; version (IN_GCC) {} else version (IN_LLVM) {} else version = MARS; -/************************************ - * Check to see the aggregate type is nested and its context pointer is - * accessible from the current scope. - * Returns true if error occurs. - */ -bool checkFrameAccess(Loc loc, Scope* sc, AggregateDeclaration ad, size_t iStart = 0) -{ - Dsymbol sparent = ad.toParentLocal(); - Dsymbol sparent2 = ad.toParent2(); - Dsymbol s = sc.func; - if (ad.isNested() && s) - { - //printf("ad = %p %s [%s], parent:%p\n", ad, ad.toChars(), ad.loc.toChars(), ad.parent); - //printf("sparent = %p %s [%s], parent: %s\n", sparent, sparent.toChars(), sparent.loc.toChars(), sparent.parent,toChars()); - //printf("sparent2 = %p %s [%s], parent: %s\n", sparent2, sparent2.toChars(), sparent2.loc.toChars(), sparent2.parent,toChars()); - if (!ensureStaticLinkTo(s, sparent) || sparent != sparent2 && !ensureStaticLinkTo(s, sparent2)) - { - error(loc, "cannot access frame pointer of `%s`", ad.toPrettyChars()); - return true; - } - } - - bool result = false; - for (size_t i = iStart; i < ad.fields.length; i++) - { - VarDeclaration vd = ad.fields[i]; - Type tb = vd.type.baseElemOf(); - if (tb.ty == Tstruct) - { - result |= checkFrameAccess(loc, sc, (cast(TypeStruct)tb).sym); - } - } - return result; -} - -/*********************************************** - * Mark variable v as modified if it is inside a constructor that var - * is a field in. - * Also used to allow immutable globals to be initialized inside a static constructor. - * Returns: - * true if it's an initialization of v - */ -bool modifyFieldVar(Loc loc, Scope* sc, VarDeclaration var, Expression e1) -{ - //printf("modifyFieldVar(var = %s)\n", var.toChars()); - Dsymbol s = sc.func; - while (1) - { - FuncDeclaration fd = null; - if (s) - fd = s.isFuncDeclaration(); - if (fd && - ((fd.isCtorDeclaration() && var.isField()) || - ((fd.isStaticCtorDeclaration() || fd.isCrtCtor) && !var.isField())) && - fd.toParentDecl() == var.toParent2() && - (!e1 || e1.op == EXP.this_)) - { - bool result = true; - - var.ctorinit = true; - //printf("setting ctorinit\n"); - - if (var.isField() && sc.ctorflow.fieldinit.length && !sc.intypeof) - { - assert(e1); - auto mustInit = ((var.storage_class & STC.nodefaultctor) != 0 || - var.type.needsNested()); - - const dim = sc.ctorflow.fieldinit.length; - auto ad = fd.isMemberDecl(); - assert(ad); - size_t i; - for (i = 0; i < dim; i++) // same as findFieldIndexByName in ctfeexp.c ? - { - if (ad.fields[i] == var) - break; - } - assert(i < dim); - auto fieldInit = &sc.ctorflow.fieldinit[i]; - const fi = fieldInit.csx; - - if (fi & CSX.this_ctor) - { - if (var.type.isMutable() && e1.type.isMutable()) - result = false; - else - { - const(char)* modStr = !var.type.isMutable() ? MODtoChars(var.type.mod) : MODtoChars(e1.type.mod); - .error(loc, "%s field `%s` initialized multiple times", modStr, var.toChars()); - .errorSupplemental(fieldInit.loc, "Previous initialization is here."); - } - } - else if (sc.inLoop || (fi & CSX.label)) - { - if (!mustInit && var.type.isMutable() && e1.type.isMutable()) - result = false; - else - { - const(char)* modStr = !var.type.isMutable() ? MODtoChars(var.type.mod) : MODtoChars(e1.type.mod); - .error(loc, "%s field `%s` initialization is not allowed in loops or after labels", modStr, var.toChars()); - } - } - - fieldInit.csx |= CSX.this_ctor; - fieldInit.loc = e1.loc; - if (var.overlapped) // https://issues.dlang.org/show_bug.cgi?id=15258 - { - foreach (j, v; ad.fields) - { - if (v is var || !var.isOverlappedWith(v)) - continue; - v.ctorinit = true; - sc.ctorflow.fieldinit[j].csx = CSX.this_ctor; - } - } - } - else if (fd != sc.func) - { - if (var.type.isMutable()) - result = false; - else if (sc.func.fes) - { - const(char)* p = var.isField() ? "field" : var.kind(); - .error(loc, "%s %s `%s` initialization is not allowed in foreach loop", - MODtoChars(var.type.mod), p, var.toChars()); - } - else - { - const(char)* p = var.isField() ? "field" : var.kind(); - .error(loc, "%s %s `%s` initialization is not allowed in nested function `%s`", - MODtoChars(var.type.mod), p, var.toChars(), sc.func.toChars()); - } - } - else if (fd.isStaticCtorDeclaration() && !fd.isSharedStaticCtorDeclaration() && - var.type.isImmutable()) - { - .error(loc, "%s %s `%s` initialization is not allowed in `static this`", - MODtoChars(var.type.mod), var.kind(), var.toChars()); - errorSupplemental(loc, "Use `shared static this` instead."); - } - else if (fd.isStaticCtorDeclaration() && !fd.isSharedStaticCtorDeclaration() && - var.type.isConst()) - { - // @@@DEPRECATED_2.116@@@ - // Turn this into an error, merging with the branch above - .deprecation(loc, "%s %s `%s` initialization is not allowed in `static this`", - MODtoChars(var.type.mod), var.kind(), var.toChars()); - deprecationSupplemental(loc, "Use `shared static this` instead."); - } - return result; - } - else - { - if (s) - { - s = s.toParentP(var.toParent2()); - continue; - } - } - break; - } - return false; -} - /****************************************** */ 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) { @@ -249,29 +87,34 @@ extern (C++) abstract class Declaration : Dsymbol { Type type; Type originalType; // before semantic analysis - StorageClass storage_class = STC.undefined_; + STC storage_class = STC.none; + // overridden symbol with pragma(mangle, "...") + const(char)[] mangleOverride; Visibility visibility; - LINK _linkage = LINK.default_; // may be `LINK.system`; use `resolvedLinkage()` to resolve it short inuse; // used to detect cycles - ubyte adFlags; // control re-assignment of AliasDeclaration (put here for packing reasons) - enum wasRead = 1; // set if AliasDeclaration was read - enum ignoreRead = 2; // ignore any reads of AliasDeclaration - enum nounderscore = 4; // don't prepend _ to mangled name - enum hidden = 8; // don't print this in .di files + private extern (D) static struct BitFields + { + LINK _linkage = LINK.default_; // may be `LINK.system`; use `resolvedLinkage()` to resolve it + bool wasRead; // set if AliasDeclaration was read + bool ignoreRead; // ignore any reads of AliasDeclaration + bool noUnderscore; // don't prepend _ to mangled name + bool hidden; // don't print this in .di files + bool nrvo; /// forward to fd.nrvo_var when generating code + } - // overridden symbol with pragma(mangle, "...") - const(char)[] mangleOverride; + import dmd.common.bitfields; + mixin(generateBitFields!(BitFields, ubyte)); - final extern (D) this(Identifier ident) @safe + final extern (D) this(DSYM tag, Identifier ident) @safe { - super(ident); + super(tag, ident); visibility = Visibility(Visibility.Kind.undefined); } - final extern (D) this(const ref Loc loc, Identifier ident) @safe + final extern (D) this(DSYM tag, Loc loc, Identifier ident) @safe { - super(loc, ident); + super(tag, loc, ident); visibility = Visibility(Visibility.Kind.undefined); } @@ -280,7 +123,7 @@ extern (C++) abstract class Declaration : Dsymbol return "declaration"; } - override final uinteger_t size(const ref Loc loc) + override final uinteger_t size(Loc loc) { assert(type); const sz = type.size(); @@ -289,150 +132,6 @@ extern (C++) abstract class Declaration : Dsymbol return sz; } - /** - * Issue an error if an attempt to call a disabled method is made - * - * If the declaration is disabled but inside a disabled function, - * returns `true` but do not issue an error message. - * - * Params: - * loc = Location information of the call - * sc = Scope in which the call occurs - * isAliasedDeclaration = if `true` searches overload set - * - * Returns: - * `true` if this `Declaration` is `@disable`d, `false` otherwise. - */ - extern (D) final bool checkDisabled(Loc loc, Scope* sc, bool isAliasedDeclaration = false) - { - if (!(storage_class & STC.disable)) - return false; - - if (sc.func && sc.func.storage_class & STC.disable) - return true; - - if (auto p = toParent()) - { - if (auto postblit = isPostBlitDeclaration()) - { - /* https://issues.dlang.org/show_bug.cgi?id=21885 - * - * If the generated postblit is disabled, it - * means that one of the fields has a disabled - * postblit. Print the first field that has - * a disabled postblit. - */ - if (postblit.isGenerated()) - { - auto sd = p.isStructDeclaration(); - assert(sd); - for (size_t i = 0; i < sd.fields.length; i++) - { - auto structField = sd.fields[i]; - if (structField.overlapped) - continue; - Type tv = structField.type.baseElemOf(); - if (tv.ty != Tstruct) - continue; - auto sdv = (cast(TypeStruct)tv).sym; - if (!sdv.postblit) - continue; - if (sdv.postblit.isDisabled()) - { - .error(loc, "%s `%s` is not copyable because field `%s` is not copyable", p.kind, p.toPrettyChars, structField.toChars()); - return true; - } - } - } - .error(loc, "%s `%s` is not copyable because it has a disabled postblit", p.kind, p.toPrettyChars); - return true; - } - } - - // if the function is @disabled, maybe there - // is an overload in the overload set that isn't - if (isAliasedDeclaration) - { - FuncDeclaration fd = isFuncDeclaration(); - if (fd) - { - for (FuncDeclaration ovl = fd; ovl; ovl = cast(FuncDeclaration)ovl.overnext) - if (!(ovl.storage_class & STC.disable)) - return false; - } - } - - if (auto ctor = isCtorDeclaration()) - { - if (ctor.isCpCtor && ctor.isGenerated()) - { - .error(loc, "generating an `inout` copy constructor for `struct %s` failed, therefore instances of it are uncopyable", parent.toPrettyChars()); - return true; - } - } - .error(loc, "%s `%s` cannot be used because it is annotated with `@disable`", kind, toPrettyChars); - return true; - } - - /************************************* - * Check to see if declaration can be modified in this context (sc). - * Issue error if not. - * Params: - * loc = location for error messages - * e1 = `null` or `this` expression when this declaration is a field - * sc = context - * flag = if the first bit is set it means do not issue error message for - * invalid modification; if the second bit is set, it means that - this declaration is a field and a subfield of it is modified. - * Returns: - * Modifiable.yes or Modifiable.initialization - */ - extern (D) final Modifiable checkModify(Loc loc, Scope* sc, Expression e1, ModifyFlags flag) - { - VarDeclaration v = isVarDeclaration(); - if (v && v.canassign) - return Modifiable.initialization; - - if (isParameter() || isResult()) - { - for (Scope* scx = sc; scx; scx = scx.enclosing) - { - if (scx.func == parent && (scx.flags & SCOPE.contract)) - { - const(char)* s = isParameter() && parent.ident != Id.ensure ? "parameter" : "result"; - if (!(flag & ModifyFlags.noError)) - error(loc, "%s `%s` cannot modify %s `%s` in contract", kind, toPrettyChars, s, toChars()); - return Modifiable.initialization; // do not report type related errors - } - } - } - - if (e1 && e1.op == EXP.this_ && isField()) - { - VarDeclaration vthis = e1.isThisExp().var; - for (Scope* scx = sc; scx; scx = scx.enclosing) - { - if (scx.func == vthis.parent && (scx.flags & SCOPE.contract)) - { - if (!(flag & ModifyFlags.noError)) - error(loc, "%s `%s` cannot modify parameter `this` in contract", kind, toPrettyChars); - return Modifiable.initialization; // do not report type related errors - } - } - } - - if (v && (v.isCtorinit() || isField())) - { - // It's only modifiable if inside the right constructor - if ((storage_class & (STC.foreach_ | STC.ref_)) == (STC.foreach_ | STC.ref_)) - return Modifiable.initialization; - if (flag & ModifyFlags.fieldAssign) - return Modifiable.yes; - return modifyFieldVar(loc, sc, v, e1) ? Modifiable.initialization : Modifiable.yes; - } - return Modifiable.yes; - } - final bool isStatic() const pure nothrow @nogc @safe { return (storage_class & STC.static_) != 0; @@ -575,11 +274,6 @@ extern (C++) abstract class Declaration : Dsymbol return visibility; } - override final inout(Declaration) isDeclaration() inout pure nothrow @nogc @safe - { - return this; - } - override void accept(Visitor v) { v.visit(this); @@ -595,9 +289,9 @@ extern (C++) final class TupleDeclaration : Declaration bool isexp; // true: expression tuple bool building; // it's growing in AliasAssign semantic - extern (D) this(const ref Loc loc, Identifier ident, Objects* objects) @safe + extern (D) this(Loc loc, Identifier ident, Objects* objects) @safe { - super(loc, ident); + super(DSYM.tupleDeclaration, loc, ident); this.objects = objects; } @@ -651,7 +345,7 @@ extern (C++) final class TupleDeclaration : Declaration } else { - auto arg = new Parameter(Loc.initial, 0, t, null, null, null); + auto arg = new Parameter(Loc.initial, STC.none, t, null, null, null); } (*args)[i] = arg; if (!t.deco) @@ -731,11 +425,6 @@ extern (C++) final class TupleDeclaration : Declaration return 0; } - override inout(TupleDeclaration) isTupleDeclaration() inout - { - return this; - } - override void accept(Visitor v) { v.visit(this); @@ -752,25 +441,24 @@ extern (C++) final class AliasDeclaration : Declaration Dsymbol overnext; // next in overload list Dsymbol _import; // !=null if unresolved internal alias for selective import - extern (D) this(const ref Loc loc, Identifier ident, Type type) @safe + extern (D) this(Loc loc, Identifier ident, Type type) @safe { - super(loc, ident); - //printf("AliasDeclaration(id = '%s', type = %p)\n", ident.toChars(), type); - //printf("type = '%s'\n", type.toChars()); + super(DSYM.aliasDeclaration, loc, ident); + //debug printf("AliasDeclaration(id = '%s', type = `%s`, %p)\n", ident.toChars(), dmd.hdrgen.toChars(type), type.isTypeIdentifier()); this.type = type; assert(type); } - extern (D) this(const ref Loc loc, Identifier ident, Dsymbol s) @safe + extern (D) this(Loc loc, Identifier ident, Dsymbol s) @safe { - super(loc, ident); - //printf("AliasDeclaration(id = '%s', s = %p)\n", ident.toChars(), s); + super(DSYM.aliasDeclaration, loc, ident); + //debug printf("AliasDeclaration(id = '%s', s = `%s`)\n", ident.toChars(), s.toChars()); assert(s != this); this.aliassym = s; assert(s); } - static AliasDeclaration create(const ref Loc loc, Identifier id, Type type) @safe + static AliasDeclaration create(Loc loc, Identifier id, Type type) @safe { return new AliasDeclaration(loc, id, type); } @@ -925,28 +613,38 @@ extern (C++) final class AliasDeclaration : Declaration override Dsymbol toAlias() { - //printf("[%s] AliasDeclaration::toAlias('%s', this = %p, aliassym = %p, kind = '%s', inuse = %d)\n", - // loc.toChars(), toChars(), this, aliassym, aliassym ? aliassym.kind() : "", inuse); + static if (0) + printf("[%s] AliasDeclaration::toAlias('%s', this = %p, aliassym: %s, kind: '%s', inuse = %d)\n", + loc.toChars(), toChars(), this, aliassym ? aliassym.toChars() : "", aliassym ? aliassym.kind() : "", inuse); assert(this != aliassym); //static int count; if (++count == 10) *(char*)0=0; + Dsymbol err() + { + // Avoid breaking "recursive alias" state during errors gagged + if (global.gag) + return this; + aliassym = new AliasDeclaration(loc, ident, Type.terror); + type = Type.terror; + return aliassym; + } // Reading the AliasDeclaration - if (!(adFlags & ignoreRead)) - adFlags |= wasRead; // can never assign to this AliasDeclaration again + if (!this.ignoreRead) + this.wasRead = true; // can never assign to this AliasDeclaration again if (inuse == 1 && type && _scope) { inuse = 2; - uint olderrors = global.errors; + const olderrors = global.errors; Dsymbol s = type.toDsymbol(_scope); //printf("[%s] type = %s, s = %p, this = %p\n", loc.toChars(), type.toChars(), s, this); if (global.errors != olderrors) - goto Lerr; + return err(); if (s) { s = s.toAlias(); if (global.errors != olderrors) - goto Lerr; + return err(); aliassym = s; inuse = 0; } @@ -954,9 +652,9 @@ extern (C++) final class AliasDeclaration : Declaration { Type t = type.typeSemantic(loc, _scope); if (t.ty == Terror) - goto Lerr; + return err(); if (global.errors != olderrors) - goto Lerr; + return err(); //printf("t = %s\n", t.toChars()); inuse = 0; } @@ -964,14 +662,7 @@ extern (C++) final class AliasDeclaration : Declaration if (inuse) { .error(loc, "%s `%s` recursive alias declaration", kind, toPrettyChars); - - Lerr: - // Avoid breaking "recursive alias" state during errors gagged - if (global.gag) - return this; - aliassym = new AliasDeclaration(loc, ident, Type.terror); - type = Type.terror; - return aliassym; + return err(); } if (semanticRun >= PASS.semanticdone) @@ -1037,11 +728,6 @@ extern (C++) final class AliasDeclaration : Declaration aliassym && aliassym.isOverloadable(); } - override inout(AliasDeclaration) isAliasDeclaration() inout - { - return this; - } - /** Returns: `true` if this instance was created to make a template parameter visible in the scope of a template body, `false` otherwise */ extern (D) bool isAliasedTemplateParameter() const @@ -1064,7 +750,7 @@ extern (C++) final class OverDeclaration : Declaration extern (D) this(Identifier ident, Dsymbol s) @safe { - super(ident); + super(DSYM.overDeclaration, ident); this.aliassym = s; } @@ -1122,11 +808,6 @@ extern (C++) final class OverDeclaration : Declaration return result; } - override inout(OverDeclaration) isOverDeclaration() inout - { - return this; - } - override void accept(Visitor v) { v.visit(this); @@ -1143,7 +824,6 @@ extern (C++) class VarDeclaration : Declaration VarDeclaration lastVar; // Linked list of variables for goto-skips-init detection Expression edtor; // if !=null, does the destruction of the variable IntRange* range; // if !=null, the variable is known to be within the range - VarDeclarations* maybes; // maybeScope variables that are assigned to this maybeScope variable uint endlinnum; // line number of end of scope that this var lives in uint offset; @@ -1197,7 +877,7 @@ version (IN_LLVM) byte canassign; // it can be assigned to ubyte isdataseg; // private data for isDataseg 0 unset, 1 true, 2 false - final extern (D) this(const ref Loc loc, Type type, Identifier ident, Initializer _init, StorageClass storage_class = STC.undefined_) + final extern (D) this(Loc loc, Type type, Identifier ident, Initializer _init, STC storage_class = STC.none) in { assert(ident); @@ -1205,7 +885,7 @@ version (IN_LLVM) do { //printf("VarDeclaration('%s')\n", ident.toChars()); - super(loc, ident); + super(DSYM.varDeclaration, loc, ident); debug { if (!type && !_init) @@ -1222,9 +902,9 @@ version (IN_LLVM) this.storage_class = storage_class; } - static VarDeclaration create(const ref Loc loc, Type type, Identifier ident, Initializer _init, StorageClass storage_class = STC.undefined_) + static VarDeclaration create(Loc loc, Type type, Identifier ident, Initializer _init, StorageClass storage_class = STC.none) { - return new VarDeclaration(loc, type, ident, _init, storage_class); + return new VarDeclaration(loc, type, ident, _init, cast(STC) storage_class); } override VarDeclaration syntaxCopy(Dsymbol s) @@ -1249,8 +929,7 @@ version (IN_LLVM) */ for (auto s = cast(Dsymbol)this; s; s = s.parent) { - auto ad = (cast(inout)s).isMember(); - if (ad) + if (auto ad = (cast(inout)s).isMember()) return ad; if (!s.parent || !s.parent.isTemplateMixin()) break; @@ -1363,7 +1042,7 @@ version (IN_LLVM) auto vbitoffset = v.offset * 8; // Bitsize of types are overridden by any bit-field widths. - ulong tbitsize = void; + ulong tbitsize; if (auto bf = isBitFieldDeclaration()) { bitoffset += bf.bitOffset; @@ -1372,7 +1051,7 @@ version (IN_LLVM) else tbitsize = tsz * 8; - ulong vbitsize = void; + ulong vbitsize; if (auto vbf = v.isBitFieldDeclaration()) { vbitoffset += vbf.bitOffset; @@ -1408,118 +1087,6 @@ version (IN_LLVM) return edtor && !(storage_class & STC.nodtor); } - /****************************************** - * If a variable has a scope destructor call, return call for it. - * Otherwise, return NULL. - */ - extern (D) final Expression callScopeDtor(Scope* sc) - { - //printf("VarDeclaration::callScopeDtor() %s\n", toChars()); - - // Destruction of STC.field's is handled by buildDtor() - if (storage_class & (STC.nodtor | STC.ref_ | STC.out_ | STC.field)) - { - return null; - } - - if (iscatchvar) - return null; // destructor is built by `void semantic(Catch c, Scope* sc)`, not here - - Expression e = null; - // Destructors for structs and arrays of structs - Type tv = type.baseElemOf(); - if (tv.ty == Tstruct) - { - StructDeclaration sd = (cast(TypeStruct)tv).sym; - if (!sd.dtor || sd.errors) - return null; - - const sz = type.size(); - assert(sz != SIZE_INVALID); - if (!sz) - return null; - - if (type.toBasetype().ty == Tstruct) - { - // v.__xdtor() - e = new VarExp(loc, this); - - /* This is a hack so we can call destructors on const/immutable objects. - * Need to add things like "const ~this()" and "immutable ~this()" to - * fix properly. - */ - e.type = e.type.mutableOf(); - - // Enable calling destructors on shared objects. - // The destructor is always a single, non-overloaded function, - // and must serve both shared and non-shared objects. - e.type = e.type.unSharedOf; - - e = new DotVarExp(loc, e, sd.dtor, false); - e = new CallExp(loc, e); - } - else - { - // __ArrayDtor(v[0 .. n]) - e = new VarExp(loc, this); - - const sdsz = sd.type.size(); - assert(sdsz != SIZE_INVALID && sdsz != 0); - const n = sz / sdsz; - SliceExp se = new SliceExp(loc, e, new IntegerExp(loc, 0, Type.tsize_t), - new IntegerExp(loc, n, Type.tsize_t)); - - // Prevent redundant bounds check - se.upperIsInBounds = true; - se.lowerIsLessThanUpper = true; - - // This is a hack so we can call destructors on const/immutable objects. - se.type = sd.type.arrayOf(); - - e = new CallExp(loc, new IdentifierExp(loc, Id.__ArrayDtor), se); - } - return e; - } - // Destructors for classes - if (storage_class & (STC.auto_ | STC.scope_) && !(storage_class & STC.parameter)) - { - for (ClassDeclaration cd = type.isClassHandle(); cd; cd = cd.baseClass) - { - /* We can do better if there's a way with onstack - * classes to determine if there's no way the monitor - * could be set. - */ - //if (cd.isInterfaceDeclaration()) - // error("interface `%s` cannot be scope", cd.toChars()); - - if (onstack) // if any destructors - { - // delete'ing C++ classes crashes (and delete is deprecated anyway) - if (cd.classKind == ClassKind.cpp) - { - // Don't call non-existant dtor - if (!cd.dtor) - break; - - e = new VarExp(loc, this); - e.type = e.type.mutableOf().unSharedOf(); // Hack for mutable ctor on immutable instances - e = new DotVarExp(loc, e, cd.dtor, false); - e = new CallExp(loc, e); - break; - } - - // delete this; - Expression ec; - ec = new VarExp(loc, this); - e = new DeleteExp(loc, ec, true); - e.type = Type.tvoid; - break; - } - } - } - return e; - } - /******************************************* * If variable has a constant expression initializer, get it. * Otherwise, return null. @@ -1529,7 +1096,7 @@ version (IN_LLVM) assert(type && _init); // Ungag errors when not speculative - uint oldgag = global.gag; + const oldgag = global.gag; if (global.gag) { Dsymbol sym = isMember(); @@ -1552,35 +1119,6 @@ version (IN_LLVM) return e; } - /******************************************* - * Helper function for the expansion of manifest constant. - */ - extern (D) final Expression expandInitializer(Loc loc) - { - assert((storage_class & STC.manifest) && _init); - - auto e = getConstInitializer(); - if (!e) - { - .error(loc, "cannot make expression out of initializer for `%s`", toChars()); - return ErrorExp.get(); - } - - e = e.copy(); - e.loc = loc; // for better error message - return e; - } - - override final void checkCtorConstInit() - { - version (none) - { - /* doesn't work if more than one static ctor */ - if (ctorinit == 0 && isCtorinit() && !isField()) - error("missing initializer in static constructor for const variable"); - } - } - /************************************ * Check to see if this variable is actually in an enclosing function * rather than the current one. @@ -1590,7 +1128,7 @@ version (IN_LLVM) extern (D) final bool checkNestedReference(Scope* sc, Loc loc) { //printf("VarDeclaration::checkNestedReference() %s\n", toChars()); - if (sc.intypeof == 1 || (sc.flags & SCOPE.ctfe)) + if (sc.intypeof == 1 || sc.ctfe) return false; if (!parent || parent == sc.parent) return false; @@ -1625,7 +1163,7 @@ version (IN_LLVM) } // Add this VarDeclaration to fdv.closureVars[] if not already there - if (!sc.intypeof && !(sc.flags & SCOPE.compile) && + if (!sc.intypeof && !sc.traitsCompiles && // https://issues.dlang.org/show_bug.cgi?id=17605 (fdv.skipCodegen || !fdthis.skipCodegen)) { @@ -1669,12 +1207,6 @@ version (IN_LLVM) return s; } - // Eliminate need for dynamic_cast - override final inout(VarDeclaration) isVarDeclaration() inout - { - return this; - } - override void accept(Visitor v) { v.visit(this); @@ -1691,10 +1223,10 @@ extern (C++) class BitFieldDeclaration : VarDeclaration uint fieldWidth; uint bitOffset; - final extern (D) this(const ref Loc loc, Type type, Identifier ident, Expression width) + final extern (D) this(Loc loc, Type type, Identifier ident, Expression width) { super(loc, type, ident, null); - + this.dsym = DSYM.bitFieldDeclaration; this.width = width; this.storage_class |= STC.field; } @@ -1708,11 +1240,6 @@ extern (C++) class BitFieldDeclaration : VarDeclaration return bf; } - override final inout(BitFieldDeclaration) isBitFieldDeclaration() inout - { - return this; - } - override void accept(Visitor v) { v.visit(this); @@ -1729,7 +1256,7 @@ extern (C++) class BitFieldDeclaration : VarDeclaration final ulong getMinMax(Identifier id) { const width = fieldWidth; - const uns = type.isunsigned(); + const uns = type.isUnsigned(); const min = id == Id.min; ulong v; assert(width != 0); // should have been rejected in semantic pass @@ -1752,19 +1279,13 @@ extern (C++) final class SymbolDeclaration : Declaration { AggregateDeclaration dsym; - extern (D) this(const ref Loc loc, AggregateDeclaration dsym) @safe + extern (D) this(Loc loc, AggregateDeclaration dsym) @safe { - super(loc, dsym.ident); + super(DSYM.symbolDeclaration, loc, dsym.ident); this.dsym = dsym; storage_class |= STC.const_; } - // Eliminate need for dynamic_cast - override inout(SymbolDeclaration) isSymbolDeclaration() inout - { - return this; - } - override void accept(Visitor v) { v.visit(this); @@ -1775,7 +1296,7 @@ extern (C++) final class SymbolDeclaration : Declaration */ private Identifier getTypeInfoIdent(Type t) { - import dmd.dmangle; + import dmd.mangle; import core.stdc.stdlib; import dmd.root.rmem; // _init_10TypeInfo_%s @@ -1829,6 +1350,7 @@ extern (C++) class TypeInfoDeclaration : VarDeclaration final extern (D) this(Type tinfo) { super(Loc.initial, Type.dtypeinfo.type, tinfo.getTypeInfoIdent(), null); + this.dsym = DSYM.typeInfoDeclaration; this.tinfo = tinfo; storage_class = STC.static_ | STC.gshared; visibility = Visibility(Visibility.Kind.public_); @@ -1846,21 +1368,6 @@ extern (C++) class TypeInfoDeclaration : VarDeclaration assert(0); // should never be produced by syntax } - override final const(char)* toChars() const - { - //printf("TypeInfoDeclaration::toChars() tinfo = %s\n", tinfo.toChars()); - OutBuffer buf; - buf.writestring("typeid("); - buf.writestring(tinfo.toChars()); - buf.writeByte(')'); - return buf.extractChars(); - } - - override final inout(TypeInfoDeclaration) isTypeInfoDeclaration() inout @nogc nothrow pure @safe - { - return this; - } - override void accept(Visitor v) { v.visit(this); @@ -2021,6 +1528,8 @@ extern (C++) final class TypeInfoStaticArrayDeclaration : TypeInfoDeclaration */ extern (C++) final class TypeInfoAssociativeArrayDeclaration : TypeInfoDeclaration { + Type entry; // type of TypeInfo_AssociativeArray.Entry!(t.index, t.next) + extern (D) this(Type tinfo) { super(tinfo); @@ -2272,9 +1781,10 @@ extern (C++) final class TypeInfoVectorDeclaration : TypeInfoDeclaration */ extern (C++) final class ThisDeclaration : VarDeclaration { - extern (D) this(const ref Loc loc, Type t) + extern (D) this(Loc loc, Type t) { super(loc, t, Id.This, null); + this.dsym = DSYM.thisDeclaration; storage_class |= STC.nodtor; } @@ -2283,11 +1793,6 @@ extern (C++) final class ThisDeclaration : VarDeclaration assert(0); // should never be produced by syntax } - override inout(ThisDeclaration) isThisDeclaration() inout - { - return this; - } - override void accept(Visitor v) { v.visit(this); diff --git a/dmd/declaration.h b/dmd/declaration.h index 1e21c17bc1..2f91fb6b50 100644 --- a/dmd/declaration.h +++ b/dmd/declaration.h @@ -1,6 +1,6 @@ /* Compiler implementation of the D programming language - * Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * written by Walter Bright * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. @@ -34,8 +34,11 @@ namespace dmd { bool functionSemantic(FuncDeclaration* fd); bool functionSemantic3(FuncDeclaration* fd); + bool checkClosure(FuncDeclaration* fd); MATCH leastAsSpecialized(FuncDeclaration *f, FuncDeclaration *g, Identifiers *names); PURE isPure(FuncDeclaration *f); + FuncDeclaration *genCfunc(Parameters *args, Type *treturn, const char *name, StorageClass stc=0); + FuncDeclaration *genCfunc(Parameters *args, Type *treturn, Identifier *id, StorageClass stc=0); } //enum STC : ulong from astenums.d: @@ -120,14 +123,17 @@ class Declaration : public Dsymbol Type *type; Type *originalType; // before semantic analysis StorageClass storage_class; + DString mangleOverride; // overridden symbol with pragma(mangle, "...") Visibility visibility; - LINK _linkage; // may be `LINK::system`; use `resolvedLinkage()` to resolve it short inuse; // used to detect cycles - uint8_t adFlags; - DString mangleOverride; // overridden symbol with pragma(mangle, "...") + uint8_t bitFields; + + LINK _linkage() const; + LINK _linkage(LINK v); + bool noUnderscore() const; const char *kind() const override; - uinteger_t size(const Loc &loc) override final; + uinteger_t size(Loc loc) override final; bool isStatic() const { return (storage_class & STCstatic) != 0; } @@ -160,7 +166,6 @@ class Declaration : public Dsymbol Visibility visible() override final; - Declaration *isDeclaration() override final { return this; } void accept(Visitor *v) override { v->visit(this); } }; @@ -180,7 +185,6 @@ class TupleDeclaration final : public Declaration Dsymbol *toAlias2() override; bool needThis() override; - TupleDeclaration *isTupleDeclaration() override { return this; } void accept(Visitor *v) override { v->visit(this); } #if IN_LLVM @@ -197,7 +201,7 @@ class AliasDeclaration final : public Declaration Dsymbol *overnext; // next in overload list Dsymbol *_import; // !=NULL if unresolved internal alias for selective import - static AliasDeclaration *create(const Loc &loc, Identifier *id, Type *type); + static AliasDeclaration *create(Loc loc, Identifier *id, Type *type); AliasDeclaration *syntaxCopy(Dsymbol *) override; bool overloadInsert(Dsymbol *s) override; const char *kind() const override; @@ -206,7 +210,6 @@ class AliasDeclaration final : public Declaration Dsymbol *toAlias2() override; bool isOverloadable() const override; - AliasDeclaration *isAliasDeclaration() override { return this; } void accept(Visitor *v) override { v->visit(this); } }; @@ -226,7 +229,6 @@ class OverDeclaration final : public Declaration Dsymbol *isUnique(); bool isOverloadable() const override; - OverDeclaration *isOverDeclaration() override { return this; } void accept(Visitor *v) override { v->visit(this); } }; @@ -241,7 +243,6 @@ class VarDeclaration : public Declaration VarDeclaration *lastVar; // Linked list of variables for goto-skips-init detection Expression *edtor; // if !=NULL, does the destruction of the variable IntRange *range; // if !NULL, the variable is known to be within the range - VarDeclarations *maybes; // STCmaybescope variables that are assigned to this STCmaybescope variable unsigned endlinnum; // line number of end of scope that this var lives in unsigned offset; @@ -292,7 +293,7 @@ class VarDeclaration : public Declaration #endif bool systemInferred() const; bool systemInferred(bool v); - static VarDeclaration *create(const Loc &loc, Type *t, Identifier *id, Initializer *init, StorageClass storage_class = STCundefined); + static VarDeclaration *create(Loc loc, Type *t, Identifier *id, Initializer *init, StorageClass storage_class = STCundefined); VarDeclaration *syntaxCopy(Dsymbol *) override; const char *kind() const override; AggregateDeclaration *isThis() override final; @@ -307,10 +308,8 @@ class VarDeclaration : public Declaration bool hasPointers() override final; bool canTakeAddressOf(); bool needsScopeDtor(); - void checkCtorConstInit() override final; Dsymbol *toAlias() override final; // Eliminate need for dynamic_cast - VarDeclaration *isVarDeclaration() override final { return (VarDeclaration *)this; } void accept(Visitor *v) override { v->visit(this); } }; @@ -325,7 +324,6 @@ class BitFieldDeclaration : public VarDeclaration unsigned bitOffset; BitFieldDeclaration *syntaxCopy(Dsymbol *) override; - BitFieldDeclaration *isBitFieldDeclaration() override final { return this; } void accept(Visitor *v) override { v->visit(this); } }; @@ -339,7 +337,6 @@ class SymbolDeclaration final : public Declaration AggregateDeclaration *dsym; // Eliminate need for dynamic_cast - SymbolDeclaration *isSymbolDeclaration() override { return (SymbolDeclaration *)this; } void accept(Visitor *v) override { v->visit(this); } }; @@ -350,9 +347,7 @@ class TypeInfoDeclaration : public VarDeclaration static TypeInfoDeclaration *create(Type *tinfo); TypeInfoDeclaration *syntaxCopy(Dsymbol *) override final; - const char *toChars() const override final; - TypeInfoDeclaration *isTypeInfoDeclaration() override final { return this; } void accept(Visitor *v) override { v->visit(this); } }; @@ -407,6 +402,8 @@ class TypeInfoStaticArrayDeclaration final : public TypeInfoDeclaration class TypeInfoAssociativeArrayDeclaration final : public TypeInfoDeclaration { public: + Type* entry; + static TypeInfoAssociativeArrayDeclaration *create(Type *tinfo); void accept(Visitor *v) override { v->visit(this); } @@ -490,7 +487,6 @@ class ThisDeclaration final : public VarDeclaration { public: ThisDeclaration *syntaxCopy(Dsymbol *) override; - ThisDeclaration *isThisDeclaration() override { return this; } void accept(Visitor *v) override { v->visit(this); } }; @@ -546,7 +542,7 @@ enum class BUILTIN : unsigned char // IN_LLVM: more for LDC... }; -Expression *eval_builtin(const Loc &loc, FuncDeclaration *fd, Expressions *arguments); +Expression *eval_builtin(Loc loc, FuncDeclaration *fd, Expressions *arguments); BUILTIN isBuiltin(FuncDeclaration *fd); struct ContractInfo; @@ -614,14 +610,6 @@ class FuncDeclaration : public Declaration // Things that should really go into Scope - // 1 if there's a return exp; statement - // 2 if there's a throw statement - // 4 if there's an assert(0) - // 8 if there's inline asm - // 16 if there are multiple return statements - // IN_LLVM: 32 if there's DMD-style inline asm - int hasReturnExp; - VarDeclaration *nrvo_var; // variable to replace with shidden #if !IN_LLVM Symbol *shidden; // hidden pointer passed to function @@ -666,12 +654,12 @@ class FuncDeclaration : public Declaration bool nothrowInprocess(bool v); bool nogcInprocess() const; bool nogcInprocess(bool v); - bool returnInprocess() const; - bool returnInprocess(bool v); + bool saferD() const; + bool saferD(bool v); + bool scopeInprocess() const; + bool scopeInprocess(bool v); bool inlineScanned() const; bool inlineScanned(bool v); - bool inferScope() const; - bool inferScope(bool v); bool hasCatches() const; bool hasCatches(bool v); bool skipCodegen() const; @@ -708,12 +696,18 @@ class FuncDeclaration : public Declaration bool dllImport(bool v); bool dllExport() const; bool dllExport(bool v); + bool hasReturnExp() const; + bool hasReturnExp(bool v); + bool hasInlineAsm() const; + bool hasInlineAsm(bool v); + bool hasMultipleReturnExp() const; + bool hasMultipleReturnExp(bool v); // Data for a function declaration that is needed for the Objective-C // integration. ObjcFuncDeclaration objc; - static FuncDeclaration *create(const Loc &loc, const Loc &endloc, Identifier *id, StorageClass storage_class, Type *type, bool noreturn = false); + static FuncDeclaration *create(Loc loc, Loc endloc, Identifier *id, StorageClass storage_class, Type *type, bool noreturn = false); FuncDeclaration *syntaxCopy(Dsymbol *) override; Statements *frequires(); Ensures *fensures(); @@ -735,7 +729,7 @@ class FuncDeclaration : public Declaration bool overloadInsert(Dsymbol *s) override; bool inUnittest(); - LabelDsymbol *searchLabel(Identifier *ident, const Loc &loc); + LabelDsymbol *searchLabel(Identifier *ident, Loc loc); const char *toPrettyChars(bool QualifyTypes = false) override; const char *toFullSignature(); // for diagnostics, e.g. 'int foo(int x, int y) pure' bool isMain() const; @@ -749,7 +743,6 @@ class FuncDeclaration : public Declaration bool isAbstract() override final; bool isSafe(); bool isTrusted(); - bool isNogc(); virtual bool isNested() const; AggregateDeclaration *isThis() override; @@ -762,15 +755,9 @@ class FuncDeclaration : public Declaration const char *kind() const override; bool isUnique(); bool needsClosure(); - bool checkClosure(); bool hasNestedFrameRefs(); ParameterList getParameterList(); - static FuncDeclaration *genCfunc(Parameters *args, Type *treturn, const char *name, StorageClass stc=0); - static FuncDeclaration *genCfunc(Parameters *args, Type *treturn, Identifier *id, StorageClass stc=0); - - FuncDeclaration *isFuncDeclaration() override final { return this; } - virtual FuncDeclaration *toAliasFunc() { return this; } void accept(Visitor *v) override { v->visit(this); } }; @@ -781,7 +768,6 @@ class FuncAliasDeclaration final : public FuncDeclaration FuncDeclaration *funcalias; d_bool hasOverloads; - FuncAliasDeclaration *isFuncAliasDeclaration() override { return this; } const char *kind() const override; FuncDeclaration *toAliasFunc() override; @@ -804,7 +790,6 @@ class FuncLiteralDeclaration final : public FuncDeclaration bool addPreInvariant() override; bool addPostInvariant() override; - FuncLiteralDeclaration *isFuncLiteralDeclaration() override { return this; } const char *kind() const override; const char *toPrettyChars(bool QualifyTypes = false) override; void accept(Visitor *v) override { v->visit(this); } @@ -814,14 +799,13 @@ class CtorDeclaration final : public FuncDeclaration { public: d_bool isCpCtor; + d_bool isMoveCtor; CtorDeclaration *syntaxCopy(Dsymbol *) override; const char *kind() const override; - const char *toChars() const override; bool isVirtual() const override; bool addPreInvariant() override; bool addPostInvariant() override; - CtorDeclaration *isCtorDeclaration() override { return this; } void accept(Visitor *v) override { v->visit(this); } }; @@ -834,7 +818,6 @@ class PostBlitDeclaration final : public FuncDeclaration bool addPostInvariant() override; bool overloadInsert(Dsymbol *s) override; - PostBlitDeclaration *isPostBlitDeclaration() override { return this; } void accept(Visitor *v) override { v->visit(this); } }; @@ -843,13 +826,11 @@ class DtorDeclaration final : public FuncDeclaration public: DtorDeclaration *syntaxCopy(Dsymbol *) override; const char *kind() const override; - const char *toChars() const override; bool isVirtual() const override; bool addPreInvariant() override; bool addPostInvariant() override; bool overloadInsert(Dsymbol *s) override; - DtorDeclaration *isDtorDeclaration() override { return this; } void accept(Visitor *v) override { v->visit(this); } }; @@ -861,9 +842,7 @@ class StaticCtorDeclaration : public FuncDeclaration bool isVirtual() const override final; bool addPreInvariant() override final; bool addPostInvariant() override final; - bool hasStaticCtorOrDtor() override final; - StaticCtorDeclaration *isStaticCtorDeclaration() override final { return this; } void accept(Visitor *v) override { v->visit(this); } }; @@ -873,7 +852,6 @@ class SharedStaticCtorDeclaration final : public StaticCtorDeclaration bool standalone; SharedStaticCtorDeclaration *syntaxCopy(Dsymbol *) override; - SharedStaticCtorDeclaration *isSharedStaticCtorDeclaration() override { return this; } void accept(Visitor *v) override { v->visit(this); } }; @@ -885,11 +863,9 @@ class StaticDtorDeclaration : public FuncDeclaration StaticDtorDeclaration *syntaxCopy(Dsymbol *) override; AggregateDeclaration *isThis() override final; bool isVirtual() const override final; - bool hasStaticCtorOrDtor() override final; bool addPreInvariant() override final; bool addPostInvariant() override final; - StaticDtorDeclaration *isStaticDtorDeclaration() override final { return this; } void accept(Visitor *v) override { v->visit(this); } }; @@ -898,7 +874,6 @@ class SharedStaticDtorDeclaration final : public StaticDtorDeclaration public: SharedStaticDtorDeclaration *syntaxCopy(Dsymbol *) override; - SharedStaticDtorDeclaration *isSharedStaticDtorDeclaration() override { return this; } void accept(Visitor *v) override { v->visit(this); } }; @@ -910,7 +885,6 @@ class InvariantDeclaration final : public FuncDeclaration bool addPreInvariant() override; bool addPostInvariant() override; - InvariantDeclaration *isInvariantDeclaration() override { return this; } void accept(Visitor *v) override { v->visit(this); } }; @@ -928,7 +902,6 @@ class UnitTestDeclaration final : public FuncDeclaration bool addPreInvariant() override; bool addPostInvariant() override; - UnitTestDeclaration *isUnitTestDeclaration() override { return this; } void accept(Visitor *v) override { v->visit(this); } }; @@ -941,6 +914,5 @@ class NewDeclaration final : public FuncDeclaration bool addPreInvariant() override; bool addPostInvariant() override; - NewDeclaration *isNewDeclaration() override { return this; } void accept(Visitor *v) override { v->visit(this); } }; diff --git a/dmd/delegatize.d b/dmd/delegatize.d index 62800d3fdb..344130b5e3 100644 --- a/dmd/delegatize.d +++ b/dmd/delegatize.d @@ -3,12 +3,12 @@ * * Specification: $(LINK2 https://dlang.org/spec/function.html#lazy-params, Lazy Parameters) * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/delegatize.d, _delegatize.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/delegatize.d, _delegatize.d) * Documentation: https://dlang.org/phobos/dmd_delegatize.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/delegatize.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/delegatize.d */ module dmd.delegatize; @@ -25,10 +25,10 @@ import dmd.init; import dmd.initsem; import dmd.location; import dmd.mtype; -import dmd.postordervisitor; import dmd.statement; import dmd.tokens; import dmd.visitor; +import dmd.visitor.postorder; /********************************* @@ -215,15 +215,13 @@ bool lambdaCheckForNestedRef(Expression e, Scope* sc) override void visit(SymOffExp e) { - VarDeclaration v = e.var.isVarDeclaration(); - if (v) + if (VarDeclaration v = e.var.isVarDeclaration()) result = v.checkNestedReference(sc, Loc.initial); } override void visit(VarExp e) { - VarDeclaration v = e.var.isVarDeclaration(); - if (v) + if (VarDeclaration v = e.var.isVarDeclaration()) result = v.checkNestedReference(sc, Loc.initial); } @@ -235,8 +233,7 @@ bool lambdaCheckForNestedRef(Expression e, Scope* sc) override void visit(DeclarationExp e) { - VarDeclaration v = e.declaration.isVarDeclaration(); - if (v) + if (VarDeclaration v = e.declaration.isVarDeclaration()) { result = v.checkNestedReference(sc, Loc.initial); if (result) diff --git a/dmd/denum.d b/dmd/denum.d index 4086f9641f..d0571a7635 100644 --- a/dmd/denum.d +++ b/dmd/denum.d @@ -3,12 +3,12 @@ * * Specification: $(LINK2 https://dlang.org/spec/enum.html, Enums) * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/denum.d, _denum.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/denum.d, _denum.d) * Documentation: https://dlang.org/phobos/dmd_denum.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/denum.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/denum.d * References: https://dlang.org/spec/enum.html */ @@ -67,12 +67,13 @@ version (IN_LLVM) {} else Symbol* sinit; } - extern (D) this(const ref Loc loc, Identifier ident, Type memtype) + extern (D) this(Loc loc, Identifier ident, Type memtype) { super(loc, ident); //printf("EnumDeclaration() %p %s : %s\n", this, toChars(), memtype.toChars()); type = new TypeEnum(this); this.memtype = memtype; + this.dsym = DSYM.enumDeclaration; visibility = Visibility(Visibility.Kind.undefined); } @@ -84,13 +85,6 @@ version (IN_LLVM) {} else return ed; } - override bool oneMember(out Dsymbol ps, Identifier ident) - { - if (isAnonymous()) - return Dsymbol.oneMembers(members, ps, ident); - return Dsymbol.oneMember(ps, ident); - } - override Type getType() { return type; @@ -123,11 +117,6 @@ version (IN_LLVM) {} else return isSpecialEnumIdent(ident) && memtype; } - override inout(EnumDeclaration) isEnumDeclaration() inout - { - return this; - } - override void accept(Visitor v) { v.visit(this); @@ -157,15 +146,16 @@ extern (C++) final class EnumMember : VarDeclaration EnumDeclaration ed; - extern (D) this(const ref Loc loc, Identifier id, Expression value, Type origType) + extern (D) this(Loc loc, Identifier id, Expression value, Type origType) { super(loc, null, id ? id : Id.empty, new ExpInitializer(loc, value)); this.origValue = value; this.origType = origType; + this.dsym = DSYM.enumMember; } extern(D) this(Loc loc, Identifier id, Expression value, Type memtype, - StorageClass stc, UserAttributeDeclaration uad, DeprecatedDeclaration dd) + STC stc, UserAttributeDeclaration uad, DeprecatedDeclaration dd) { this(loc, id, value, memtype); storage_class = stc; @@ -190,11 +180,6 @@ extern (C++) final class EnumMember : VarDeclaration return "enum member"; } - override inout(EnumMember) isEnumMember() inout - { - return this; - } - override void accept(Visitor v) { v.visit(this); diff --git a/dmd/deps.d b/dmd/deps.d new file mode 100644 index 0000000000..4330ff2d93 --- /dev/null +++ b/dmd/deps.d @@ -0,0 +1,296 @@ +/** + * Implement the `-deps` and `-makedeps` switches, which output dependencies of modules for build tools. + * + * The grammar of the `-deps` output is: + * --- + * ImportDeclaration + * ::= BasicImportDeclaration [ " : " ImportBindList ] [ " -> " + * ModuleAliasIdentifier ] "\n" + * + * BasicImportDeclaration + * ::= ModuleFullyQualifiedName " (" FilePath ") : " Protection|"string" + * " [ " static" ] : " ModuleFullyQualifiedName " (" FilePath ")" + * + * FilePath + * - any string with '(', ')' and '\' escaped with the '\' character + * --- + * + * Make dependencies as generated by `-makedeps` look like this: + * --- + * source/app.d: + * source/importa.d \ + * source/importb.d + * --- + * + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved + * 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/deps.d, makedeps.d) + * Documentation: https://dlang.org/phobos/dmd_deps.html + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/deps.d + */ +module dmd.deps; + +import core.stdc.stdio : printf; +import core.stdc.string : strcmp; +import dmd.common.outbuffer; +import dmd.dimport : Import; +import dmd.dmodule : Module; +import dmd.globals : Param, Output; +import dmd.hdrgen : visibilityToBuffer; +import dmd.id : Id; +import dmd.location : Loc; +import dmd.root.filename; +import dmd.root.string : toDString; +import dmd.utils : escapePath; + +version (IN_LLVM) +{ + import dmd.globals : IN_LLVM; + + extern (C++): + + // in driver/archiver.cpp + const(char)* getPathToProducedStaticLibrary() pure; + // in driver/linker.cpp + const(char)* getPathToProducedBinary() pure; +} + +/** + * Output the makefile dependencies for the -makedeps switch + * + * Params: + * buf = outbuffer to write into + * params = dmd params + * link = an executable is being generated + * lib = a library is being generated + * libExt = file extension of libraries for current target + */ +void writeMakeDeps(ref OutBuffer buf, const ref Param params, bool link, bool lib, const(char)[] libExt) pure +{ + // start by resolving and writing the target (which is sometimes resolved during link phase) + if (link && (IN_LLVM || params.exefile)) + { +version (IN_LLVM) +{ + buf.writeEscapedMakePath(getPathToProducedBinary()); +} +else +{ + buf.writeEscapedMakePath(¶ms.exefile[0]); +} + } + else if (lib) + { +version (IN_LLVM) +{ + buf.writeEscapedMakePath(getPathToProducedStaticLibrary()); +} +else +{ + const(char)[] libname = params.libname ? params.libname : FileName.name(params.objfiles[0].toDString); + libname = FileName.forceExt(libname, libExt); + + buf.writeEscapedMakePath(&libname[0]); +} + } + else if (params.objname) + { + buf.writeEscapedMakePath(¶ms.objname[0]); + } + else if (params.objfiles.length) + { + buf.writeEscapedMakePath(params.objfiles[0]); + foreach (of; params.objfiles[1 .. $]) + { + buf.writestring(" "); + buf.writeEscapedMakePath(of); + } + } + else + { + assert(false, "cannot resolve makedeps target"); + } + + buf.writestring(":"); + + // then output every dependency + foreach (dep; params.makeDeps.files) + { + buf.writestringln(" \\"); + buf.writestring(" "); + buf.writeEscapedMakePath(dep); + } + buf.writenl(); +} + +/** + * Add an import expression to module dependencies + * Params: + * moduleDeps = output settings for `-deps` + * makeDeps = output settings for `-makedeps` + * fileNameZ = 0-termminated string containing the import expression's resolved filename + * importString = raw string passed to import exp + * imod = module import exp is in + */ +void addImportExpDep(ref Output moduleDeps, ref Output makeDeps, const(char)[] fileNameZ, const(char)[] importString, Module imod) +{ + if (moduleDeps.buffer !is null) + { + OutBuffer* ob = moduleDeps.buffer; + + if (!moduleDeps.name) + ob.writestring("depsFile "); + ob.writestring(imod.toPrettyChars()); + ob.writestring(" ("); + escapePath(ob, imod.srcfile.toChars()); + ob.writestring(") : "); + if (moduleDeps.name) + ob.writestring("string : "); + ob.write(importString); + ob.writestring(" ("); + escapePath(ob, fileNameZ.ptr); + ob.writestring(")"); + ob.writenl(); + } + if (makeDeps.doOutput) + { + makeDeps.files.push(fileNameZ.ptr); + } +} + +/** + * Add an import statement to module dependencies + * Params: + * moduleDeps = output settings + * imp = import to add + * imod = module that the import is in + */ +void addImportDep(ref Output moduleDeps, Import imp, Module imod) +{ + // object self-imports itself, so skip that + // https://issues.dlang.org/show_bug.cgi?id=7547 + // don't list pseudo modules __entrypoint.d, __main.d + // https://issues.dlang.org/show_bug.cgi?id=11117 + // https://issues.dlang.org/show_bug.cgi?id=11164 + if (moduleDeps.buffer is null || (imp.id == Id.object && imod.ident == Id.object) || + strcmp(imod.ident.toChars(), "__main") == 0) + return; + + OutBuffer* ob = moduleDeps.buffer; + if (!moduleDeps.name) + ob.writestring("depsImport "); + ob.writestring(imod.toPrettyChars()); + ob.writestring(" ("); + escapePath(ob, imod.srcfile.toChars()); + ob.writestring(") : "); + // use visibility instead of sc.visibility because it couldn't be + // resolved yet, see the comment above + visibilityToBuffer(*ob, imp.visibility); + ob.writeByte(' '); + if (imp.isstatic) + { + ob.writestring("static "); + } + ob.writestring(": "); + foreach (pid; imp.packages) + { + ob.printf("%s.", pid.toChars()); + } + ob.writestring(imp.id.toString()); + ob.writestring(" ("); + if (imp.mod) + escapePath(ob, imp.mod.srcfile.toChars()); + else + ob.writestring("???"); + ob.writeByte(')'); + foreach (i, name; imp.names) + { + if (i == 0) + ob.writeByte(':'); + else + ob.writeByte(','); + auto _alias = imp.aliases[i]; + if (!_alias) + { + ob.printf("%s", name.toChars()); + _alias = name; + } + else + ob.printf("%s=%s", _alias.toChars(), name.toChars()); + } + if (imp.aliasId) + ob.printf(" -> %s", imp.aliasId.toChars()); + ob.writenl(); +} + +/** + * Takes a path, and make it compatible with GNU Makefile format. + * + * GNU make uses a weird quoting scheme for white space. + * A space or tab preceded by 2N+1 backslashes represents N backslashes followed by space; + * a space or tab preceded by 2N backslashes represents N backslashes at the end of a file name; + * and backslashes in other contexts should not be doubled. + * + * Params: + * buf = Buffer to write the escaped path to + * fname = Path to escape + */ +void writeEscapedMakePath(ref OutBuffer buf, const(char)* fname) pure +{ + uint slashes; + + while (*fname) + { + switch (*fname) + { + case '\\': + slashes++; + break; + case '$': + buf.writeByte('$'); + goto default; + case ' ': + case '\t': + while (slashes--) + buf.writeByte('\\'); + goto case; + case '#': + buf.writeByte('\\'); + goto default; + case ':': + // ':' not escaped on Windows because it can + // create problems with absolute paths (e.g. C:\Project) + version (Windows) {} + else + { + buf.writeByte('\\'); + } + goto default; + default: + slashes = 0; + break; + } + + buf.writeByte(*fname); + fname++; + } +} + +/// +unittest +{ + version (Windows) + { + enum input = `C:\My Project\file#4$.ext`; + enum expected = `C:\My\ Project\file\#4$$.ext`; + } + else + { + enum input = `/foo\bar/weird$.:name#\ with spaces.ext`; + enum expected = `/foo\bar/weird$$.\:name\#\\\ with\ spaces.ext`; + } + + OutBuffer buf; + buf.writeEscapedMakePath(input); + assert(buf[] == expected); +} diff --git a/dmd/dimport.d b/dmd/dimport.d index 4c4c063cbf..bbd74a4da2 100644 --- a/dmd/dimport.d +++ b/dmd/dimport.d @@ -1,12 +1,12 @@ /** * A `Dsymbol` representing a renamed import. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/dimport.d, _dimport.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/dimport.d, _dimport.d) * Documentation: https://dlang.org/phobos/dmd_dimport.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/dimport.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/dimport.d */ module dmd.dimport; @@ -14,7 +14,6 @@ module dmd.dimport; import dmd.arraytypes; import dmd.dmodule; import dmd.dsymbol; -import dmd.errors; import dmd.identifier; import dmd.location; import dmd.visitor; @@ -41,7 +40,7 @@ extern (C++) final class Import : Dsymbol // corresponding AliasDeclarations for alias=name pairs AliasDeclarations aliasdecls; - extern (D) this(const ref Loc loc, Identifier[] packages, Identifier id, Identifier aliasId, int isstatic) + extern (D) this(Loc loc, Identifier[] packages, Identifier id, Identifier aliasId, int isstatic) { Identifier selectIdent() { @@ -51,19 +50,16 @@ extern (C++) final class Import : Dsymbol // import [aliasId] = std.stdio; return aliasId; } - else if (packages.length > 0) + if (packages.length > 0) { // import [std].stdio; return packages[0]; } - else - { - // import [id]; - return id; - } + // import [id]; + return id; } - super(loc, selectIdent()); + super(DSYM.import_, loc, selectIdent()); assert(id); version (none) @@ -84,16 +80,6 @@ extern (C++) final class Import : Dsymbol this.visibility = Visibility.Kind.private_; // default to private } - extern (D) void addAlias(Identifier name, Identifier _alias) - { - if (isstatic) - .error(loc, "%s `%s` cannot have an import bind list", kind, toPrettyChars); - if (!aliasId) - this.ident = null; // make it an anonymous import - names.push(name); - aliases.push(_alias); - } - override const(char)* kind() const { return isstatic ? "static import" : "import"; @@ -110,9 +96,13 @@ extern (C++) final class Import : Dsymbol assert(!s); auto si = new Import(loc, packages, id, aliasId, isstatic); si.comment = comment; + assert(!(isstatic && names.length)); + if (names.length && !si.aliasId) + si.ident = null; for (size_t i = 0; i < names.length; i++) { - si.addAlias(names[i], aliases[i]); + si.names.push(names[i]); + si.aliases.push(aliases[i]); } return si; } @@ -171,11 +161,6 @@ extern (C++) final class Import : Dsymbol return imp && !imp.aliasId; } - override inout(Import) isImport() inout - { - return this; - } - override void accept(Visitor v) { v.visit(this); diff --git a/dmd/dinterpret.d b/dmd/dinterpret.d index df01a14a4f..13051db378 100644 --- a/dmd/dinterpret.d +++ b/dmd/dinterpret.d @@ -3,12 +3,12 @@ * * Specification: ($LINK2 https://dlang.org/spec/function.html#interpretation, Compile Time Function Execution (CTFE)) * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/dinterpret.d, _dinterpret.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/dinterpret.d, _dinterpret.d) * Documentation: https://dlang.org/phobos/dmd_dinterpret.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/dinterpret.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/dinterpret.d */ module dmd.dinterpret; @@ -100,11 +100,9 @@ public Expression ctfeInterpret(Expression e) auto rgnpos = ctfeGlobals.region.savePos(); - version (IN_LLVM) - { - import driver.timetrace, std.format, std.conv; - auto timeScope = TimeTraceScope(text("CTFE start: ", e.toChars()), e.toChars().to!string, e.loc); - } + import dmd.timetrace; + timeTraceBeginEvent(TimeTraceEventType.ctfe); + scope (exit) timeTraceEndEvent(TimeTraceEventType.ctfe, e); Expression result = interpret(e, null); @@ -438,28 +436,26 @@ private Expression interpretFunction(UnionExp* pue, FuncDeclaration fd, InterSta printf("\n********\n%s FuncDeclaration::interpret(istate = %p) %s\n", fd.loc.toChars(), istate, fd.toChars()); } - version (IN_LLVM) - { - import driver.timetrace, std.format, std.conv; - scope dlg = () { - import dmd.common.outbuffer; - auto strbuf = OutBuffer(20); - strbuf.writestring(fd.toPrettyChars()); - strbuf.write("("); - if (arguments) - { - foreach (i, arg; *arguments) - { - if (i > 0) - strbuf.write(", "); - strbuf.writestring(arg.toChars()); - } - } - strbuf.write(")"); - return strbuf.extractSlice(); - }; - auto timeScope = TimeTraceScopeDelayedDetail(text("CTFE func: ", fd.toChars()), dlg, fd.loc); - } + scope dlg = () { + import dmd.common.outbuffer; + auto strbuf = OutBuffer(20); + strbuf.writestring(fd.toPrettyChars()); + strbuf.write("("); + if (arguments) + { + foreach (i, arg; *arguments) + { + if (i > 0) + strbuf.write(", "); + strbuf.writestring(arg.toChars()); + } + } + strbuf.write(")"); + return strbuf.extractSlice(); + }; + import dmd.timetrace; + timeTraceBeginEvent(TimeTraceEventType.ctfeCall); + scope (exit) timeTraceEndEvent(TimeTraceEventType.ctfeCall, fd, dlg); void fdError(const(char)* msg) { @@ -724,9 +720,9 @@ private Expression interpretFunction(UnionExp* pue, FuncDeclaration fd, InterSta } } - if (tf.isref && e.op == EXP.variable && e.isVarExp().var == fd.vthis) + if (tf.isRef && e.op == EXP.variable && e.isVarExp().var == fd.vthis) e = thisarg; - if (tf.isref && fd.hasDualContext() && e.op == EXP.index) + if (tf.isRef && fd.hasDualContext() && e.op == EXP.index) { auto ie = e.isIndexExp(); auto pe = ie.e1.isPtrExp(); @@ -762,7 +758,7 @@ private Expression interpretFunction(UnionExp* pue, FuncDeclaration fd, InterSta } /// used to collect coverage information in ctfe -void incUsageCtfe(InterState* istate, const ref Loc loc) +void incUsageCtfe(InterState* istate, Loc loc) { if (global.params.ctfe_cov && istate) { @@ -1003,7 +999,7 @@ Expression interpretStatement(UnionExp* pue, Statement s, InterState* istate) /* If the function returns a ref AND it's been called from an assignment, * we need to return an lvalue. Otherwise, just do an (rvalue) interpret. */ - if (tf.isref) + if (tf.isRef) { result = interpret(pue, s.exp, istate, CTFEGoal.LValue); return; @@ -1889,7 +1885,7 @@ public: // Check for taking an address of a shared variable. // If the shared variable is an array, the offset might not be zero. Type fromType = null; - if (e.var.type.ty == Tarray || e.var.type.ty == Tsarray) + if (e.var.type.isStaticOrDynamicArray()) { fromType = (cast(TypeArray)e.var.type).next; } @@ -1904,7 +1900,7 @@ public: Expression val = getVarExp(e.loc, istate, e.var, goal); if (exceptionOrCant(val)) return; - if (val.type.ty == Tarray || val.type.ty == Tsarray) + if (val.type.isStaticOrDynamicArray()) { // Check for unsupported type painting operations Type elemtype = (cast(TypeArray)val.type).next; @@ -1993,13 +1989,15 @@ public: Declaration decl = ve.var; // We cannot take the address of an imported symbol at compile time - if (decl.isImportedSymbol()) { + if (decl.isImportedSymbol()) + { error(e.loc, "cannot take address of imported symbol `%s` at compile time", decl.toChars()); result = CTFEExp.cantexp; return; } - if (decl.isDataseg()) { + if (decl.isDataseg()) + { // Normally this is already done by optimize() // Do it here in case optimize(WANTvalue) wasn't run before CTFE emplaceExp!(SymOffExp)(pue, e.loc, e.e1.isVarExp().var, 0); @@ -2056,7 +2054,7 @@ public: } } - static Expression getVarExp(const ref Loc loc, InterState* istate, Declaration d, CTFEGoal goal) + static Expression getVarExp(Loc loc, InterState* istate, Declaration d, CTFEGoal goal) { Expression e = CTFEExp.cantexp; if (VarDeclaration v = d.isVarDeclaration()) @@ -2756,7 +2754,7 @@ public: // Create an array literal of type 'newtype' with dimensions given by // 'arguments'[argnum..$] - static Expression recursivelyCreateArrayLiteral(UnionExp* pue, const ref Loc loc, Type newtype, InterState* istate, Expressions* arguments, int argnum) + static Expression recursivelyCreateArrayLiteral(UnionExp* pue, Loc loc, Type newtype, InterState* istate, Expressions* arguments, int argnum) { Expression lenExpr = interpret(pue, (*arguments)[argnum], istate); if (exceptionOrCantInterpret(lenExpr)) @@ -2798,6 +2796,13 @@ public: printf("%s NewExp::interpret() %s\n", e.loc.toChars(), e.toChars()); } + if (e.placement) + { + error(e.placement.loc, "`new ( %s )` PlacementExpression cannot be evaluated at compile time", e.placement.toChars()); + result = CTFEExp.cantexp; + return; + } + Expression epre = interpret(pue, e.argprefix, istate, CTFEGoal.Nothing); if (exceptionOrCant(epre)) return; @@ -2932,7 +2937,7 @@ public: result = eref; return; } - if (e.newtype.toBasetype().isscalar()) + if (e.newtype.toBasetype().isScalar()) { Expression newval; if (e.arguments && e.arguments.length) @@ -3009,8 +3014,8 @@ public: } } - private alias fp_t = extern (D) UnionExp function(const ref Loc loc, Type, Expression, Expression); - private alias fp2_t = extern (D) bool function(const ref Loc loc, EXP, Expression, Expression); + private alias fp_t = extern (D) UnionExp function(Loc loc, Type, Expression, Expression); + private alias fp2_t = extern (D) bool function(Loc loc, EXP, Expression, Expression); extern (D) private void interpretCommon(BinExp e, fp_t fp) { @@ -3031,7 +3036,7 @@ public: result = pointerDifference(pue, e.loc, e.type, e1, e2); return; } - if (e.e1.type.ty == Tpointer && e.e2.type.isintegral()) + if (e.e1.type.ty == Tpointer && e.e2.type.isIntegral()) { UnionExp ue1 = void; Expression e1 = interpret(&ue1, e.e1, istate); @@ -3044,7 +3049,7 @@ public: result = pointerArithmetic(pue, e.loc, e.op, e.type, e1, e2); return; } - if (e.e2.type.ty == Tpointer && e.e1.type.isintegral() && e.op == EXP.add) + if (e.e2.type.ty == Tpointer && e.e1.type.isIntegral() && e.op == EXP.add) { UnionExp ue1 = void; Expression e1 = interpret(&ue1, e.e1, istate); @@ -3358,7 +3363,7 @@ public: // a[] = e can have const e. So we compare the naked types. Type tdst = e1.type.toBasetype(); Type tsrc = e.e2.type.toBasetype(); - while (tdst.ty == Tsarray || tdst.ty == Tarray) + while (tdst.isStaticOrDynamicArray()) { tdst = (cast(TypeArray)tdst).next.toBasetype(); if (tsrc.equivalent(tdst)) @@ -3640,7 +3645,7 @@ public: newval = (*fp)(e.loc, e.type, oldval, newval).copy(); } - else if (e.e2.type.isintegral() && + else if (e.e2.type.isIntegral() && (e.op == EXP.addAssign || e.op == EXP.minAssign || e.op == EXP.plusPlus || @@ -3873,7 +3878,7 @@ public: if (auto bf = v.isBitFieldDeclaration()) { sinteger_t value = ival.toInteger(); - if (bf.type.isunsigned()) + if (bf.type.isUnsigned()) value &= (1L << bf.fieldWidth) - 1; // zero extra bits else { // sign extend extra bits @@ -4306,7 +4311,7 @@ public: Expression assignTo(ArrayLiteralExp ae, size_t lwr, size_t upr) { Expressions* w = ae.elements; - assert(ae.type.ty == Tsarray || ae.type.ty == Tarray || ae.type.ty == Tpointer); + assert(ae.type.isStaticOrDynamicArray() || ae.type.ty == Tpointer); bool directblk = (cast(TypeNext)ae.type).next.equivalent(newval.type); for (size_t k = lwr; k < upr; k++) { @@ -4859,33 +4864,6 @@ public: return; } - else if (fd.ident == Id._d_arrayappendT || fd.ident == Id._d_arrayappendTTrace) - { - // In expressionsem.d `ea ~= eb` was lowered to `_d_arrayappendT{,Trace}({file, line, funcname}, ea, eb);`. - // The following code will rewrite it back to `ea ~= eb` and then interpret that expression. - Expression lhs, rhs; - - if (fd.ident == Id._d_arrayappendT) - { - assert(e.arguments.length == 2); - lhs = (*e.arguments)[0]; - rhs = (*e.arguments)[1]; - } - else - { - assert(e.arguments.length == 5); - lhs = (*e.arguments)[3]; - rhs = (*e.arguments)[4]; - } - - auto cae = new CatAssignExp(e.loc, lhs, rhs); - cae.type = e.type; - - result = interpretRegion(cae, istate, CTFEGoal.LValue); - return; - } - else if (fd.ident == Id._d_arrayappendcTX) - assert(0, "CTFE cannot interpret _d_arrayappendcTX!"); } else if (auto soe = ecall.isSymOffExp()) { @@ -5071,7 +5049,7 @@ public: auto ce = e.e2.isCallExp(); assert(ce); - auto ne = new NewExp(e.loc, null, e.type, ce.arguments); + auto ne = new NewExp(e.loc, null, null, e.type, ce.arguments); ne.type = e.e1.type; result = interpret(ne, istate); @@ -5835,8 +5813,7 @@ public: auto expTb = exp.type.toBasetype(); if (exp.type.implicitConvTo(tbNext) >= MATCH.convert && - (tb.ty == Tarray || tb.ty == Tsarray) && - (expTb.ty == Tarray || expTb.ty == Tsarray)) + tb.isStaticOrDynamicArray() && expTb.isStaticOrDynamicArray()) return new ArrayLiteralExp(exp.loc, e.type, exp); return exp; } @@ -5952,7 +5929,7 @@ public: bool castToSarrayPointer = false; bool castBackFromVoid = false; - if (e1.type.ty == Tarray || e1.type.ty == Tsarray || e1.type.ty == Tpointer) + if (e1.type.isStaticOrDynamicArray() || e1.type.ty == Tpointer) { // Check for unsupported type painting operations // For slices, we need the type being sliced, @@ -6132,11 +6109,11 @@ public: // Disallow array type painting, except for conversions between built-in // types of identical size. - if ((e.to.ty == Tsarray || e.to.ty == Tarray) && (e1.type.ty == Tsarray || e1.type.ty == Tarray) && !isSafePointerCast(e1.type.nextOf(), e.to.nextOf())) + if (e.to.isStaticOrDynamicArray() && e1.type.isStaticOrDynamicArray() && !isSafePointerCast(e1.type.nextOf(), e.to.nextOf())) { auto se = e1.isStringExp(); // Allow casting a hex string literal to short[], int[] or long[] - if (se && se.hexString && se.postfix == StringExp.NoPostfix && e.to.nextOf().isintegral) + if (se && se.hexString && se.postfix == StringExp.NoPostfix && e.to.nextOf().isIntegral) { const sz = cast(size_t) e.to.nextOf().size; if ((se.len % sz) != 0) @@ -6155,8 +6132,7 @@ public: } error(e.loc, "array cast from `%s` to `%s` is not supported at compile time", e1.type.toChars(), e.to.toChars()); if (se && se.hexString && se.postfix != StringExp.NoPostfix) - errorSupplemental(e.loc, "perhaps remove postfix `%s` from hex string", - (cast(char) se.postfix ~ "\0").ptr); + errorSupplemental(e.loc, "perhaps remove postfix `%.*s` from hex string", 1, &se.postfix); result = CTFEExp.cantexp; return; @@ -6173,9 +6149,9 @@ public: } else if (tobt.isTypeBasic() && e1.op == EXP.null_) { - if (tobt.isintegral()) + if (tobt.isIntegral()) emplaceExp!(IntegerExp)(pue, e.loc, 0, e.to); - else if (tobt.isreal()) + else if (tobt.isReal()) emplaceExp!(RealExp)(pue, e.loc, CTFloat.zero, e.to); result = pue.exp(); return; @@ -6563,7 +6539,7 @@ public: /// Interpret `throw ` found at the specified location `loc` private -void interpretThrow(ref Expression result, Expression exp, const ref Loc loc, InterState* istate) +void interpretThrow(ref Expression result, Expression exp, Loc loc, InterState* istate) { incUsageCtfe(istate, loc); @@ -6704,7 +6680,7 @@ Expressions* copyArrayOnWrite(Expressions* exps, Expressions* original) true if it is safe to return, false if an error was generated. */ private -bool stopPointersEscaping(const ref Loc loc, Expression e) +bool stopPointersEscaping(Loc loc, Expression e) { import dmd.typesem : hasPointers; if (!e.type.hasPointers()) @@ -6754,7 +6730,7 @@ bool stopPointersEscaping(const ref Loc loc, Expression e) // Check all elements of an array for escaping local variables. Return false if error private -bool stopPointersEscapingFromArray(const ref Loc loc, Expressions* elems) +bool stopPointersEscapingFromArray(Loc loc, Expressions* elems) { foreach (e; *elems) { @@ -6818,7 +6794,7 @@ ThrownExceptionExp chainExceptions(ThrownExceptionExp oldest, ThrownExceptionExp * 1. all slices must be resolved. * 2. all .ownedByCtfe set to OwnedBy.code */ -private Expression scrubReturnValue(const ref Loc loc, Expression e) +private Expression scrubReturnValue(Loc loc, Expression e) { /* Returns: true if e is void, * or is an array literal or struct literal of void elements. @@ -6884,10 +6860,10 @@ private Expression scrubReturnValue(const ref Loc loc, Expression e) Expression scrubSE(StructLiteralExp sle) { sle.ownedByCtfe = OwnedBy.code; - if (!(sle.stageflags & stageScrub)) + if (!(sle.stageflags & StructLiteralExp.StageFlags.scrub)) { const old = sle.stageflags; - sle.stageflags |= stageScrub; // prevent infinite recursion + sle.stageflags |= StructLiteralExp.StageFlags.scrub; // prevent infinite recursion if (auto ex = scrubArray(sle.elements, true)) return ex; sle.stageflags = old; @@ -6964,10 +6940,10 @@ private Expression scrubCacheValue(Expression e) Expression scrubSE(StructLiteralExp sle) { sle.ownedByCtfe = OwnedBy.cache; - if (!(sle.stageflags & stageScrub)) + if (!(sle.stageflags & StructLiteralExp.StageFlags.scrub)) { const old = sle.stageflags; - sle.stageflags |= stageScrub; // prevent infinite recursion + sle.stageflags |= StructLiteralExp.StageFlags.scrub; // prevent infinite recursion if (auto ex = scrubArrayCache(sle.elements)) return ex; sle.stageflags = old; @@ -7041,10 +7017,10 @@ private Expression copyRegionExp(Expression e) static void copySE(StructLiteralExp sle) { - if (1 || !(sle.stageflags & stageScrub)) + if (1 || !(sle.stageflags & StructLiteralExp.StageFlags.scrub)) { const old = sle.stageflags; - sle.stageflags |= stageScrub; // prevent infinite recursion + sle.stageflags |= StructLiteralExp.StageFlags.scrub; // prevent infinite recursion copyArray(sle.elements); sle.stageflags = old; } @@ -7538,7 +7514,7 @@ private Expression foreachApplyUtf(UnionExp* pue, InterState* istate, Expression /* If this is a built-in function, return the interpreted result, * Otherwise, return NULL. */ -private Expression evaluateIfBuiltin(UnionExp* pue, InterState* istate, const ref Loc loc, FuncDeclaration fd, Expressions* arguments, Expression pthis) +private Expression evaluateIfBuiltin(UnionExp* pue, InterState* istate, Loc loc, FuncDeclaration fd, Expressions* arguments, Expression pthis) { Expression e = null; size_t nargs = arguments ? arguments.length : 0; diff --git a/dmd/dmacro.d b/dmd/dmacro.d index c04fbec731..307b43fb63 100644 --- a/dmd/dmacro.d +++ b/dmd/dmacro.d @@ -1,12 +1,12 @@ /** * Text macro processor for Ddoc. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/dmacro.d, _dmacro.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/dmacro.d, _dmacro.d) * Documentation: https://dlang.org/phobos/dmd_dmacro.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/dmacro.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/dmacro.d */ module dmd.dmacro; @@ -16,8 +16,6 @@ import core.stdc.string; import dmd.common.outbuffer; import dmd.root.rmem; -@trusted: - struct MacroTable { /********************************** @@ -303,7 +301,7 @@ struct Macro * copy allocated with mem.xmalloc() */ -char[] memdup(const(char)[] p) nothrow pure +char[] memdup(const(char)[] p) nothrow pure @trusted { size_t len = p.length; return (cast(char*)memcpy(mem.xmalloc(len), p.ptr, len))[0 .. len]; @@ -318,7 +316,7 @@ char[] memdup(const(char)[] p) nothrow pure * 1..9: get nth argument * -1: get 2nd through end */ -size_t extractArgN(const(char)[] buf, out const(char)[] marg, int n) @nogc nothrow pure +size_t extractArgN(const(char)[] buf, out const(char)[] marg, int n) @nogc nothrow pure @safe { /* Scan forward for matching right parenthesis. * Nest parentheses. @@ -334,7 +332,7 @@ size_t extractArgN(const(char)[] buf, out const(char)[] marg, int n) @nogc nothr uint inexp = 0; uint argn = 0; size_t v = 0; - const p = buf.ptr; + const p = buf; const end = buf.length; Largstart: // Skip first space, if any, to find the start of the macro argument diff --git a/dmd/dmodule.d b/dmd/dmodule.d index 8c660ee386..f4b52c4bf9 100644 --- a/dmd/dmodule.d +++ b/dmd/dmodule.d @@ -3,12 +3,12 @@ * * Specification: $(LINK2 https://dlang.org/spec/module.html, Modules) * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/dmodule.d, _dmodule.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/dmodule.d, _dmodule.d) * Documentation: https://dlang.org/phobos/dmd_dmodule.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/dmodule.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/dmodule.d */ module dmd.dmodule; @@ -30,7 +30,7 @@ import dmd.dmacro; import dmd.doc; import dmd.dscope; import dmd.dsymbol; -import dmd.dsymbolsem; +import dmd.dsymbolsem : dsymbolSemantic, importAll, load, include; import dmd.errors; import dmd.errorsink; import dmd.expression; @@ -58,12 +58,10 @@ import dmd.visitor; version (Windows) { - import core.sys.windows.winbase : getpid = GetCurrentProcessId; enum PathSeparator = '\\'; } else version (Posix) { - import core.sys.posix.unistd : getpid; enum PathSeparator = '/'; } else @@ -181,11 +179,12 @@ extern (C++) class Package : ScopeDsymbol uint tag; // auto incremented tag, used to mask package tree in scopes Module mod; // !=null if isPkgMod == PKG.module_ - final extern (D) this(const ref Loc loc, Identifier ident) nothrow + final extern (D) this(Loc loc, Identifier ident) nothrow { super(loc, ident); __gshared uint packageTag; this.tag = packageTag++; + this.dsym = DSYM.package_; } override const(char)* kind() const nothrow @@ -259,11 +258,6 @@ extern (C++) class Package : ScopeDsymbol return dst; } - override final inout(Package) isPackage() inout - { - return this; - } - /** * Checks if pkg is a sub-package of this * @@ -316,8 +310,9 @@ extern (C++) class Package : ScopeDsymbol packages ~= s.ident; reverse(packages); - if (Module.find(getFilename(packages, ident))) - Module.load(Loc.initial, packages, this.ident); + ImportPathInfo pathThatFoundThis; + if (Module.find(getFilename(packages, ident), pathThatFoundThis)) + Module.load(Loc.initial, packages, this.ident, pathThatFoundThis); else isPkgMod = PKG.package_; } @@ -355,9 +350,8 @@ extern (C++) final class Module : Package const(char)[] arg; // original argument name ModuleDeclaration* md; // if !=null, the contents of the ModuleDeclaration declaration const FileName srcfile; // input source file - // IN_LLVM: keep both following file names mutable (for -oq) - /*const*/ FileName objfile; // output .obj file - /*const*/ FileName hdrfile; // 'header' file + FileName objfile; // output .obj file + FileName hdrfile; // 'header' file FileName docfile; // output documentation file const(ubyte)[] src; /// Raw content of the file uint errors; // if any errors in file @@ -436,11 +430,9 @@ extern (C++) final class Module : Package Modules aimports; // all imported modules - uint debuglevel; // debug level Identifiers* debugids; // debug identifiers Identifiers* debugidsNot; // forward referenced debug identifiers - uint versionlevel; // version level Identifiers* versionids; // version identifiers Identifiers* versionidsNot; // forward referenced version identifiers @@ -450,9 +442,10 @@ extern (C++) final class Module : Package size_t nameoffset; // offset of module name from start of ModuleInfo size_t namelen; // length of module name in characters - extern (D) this(const ref Loc loc, const(char)[] filename, Identifier ident, int doDocComment, int doHdrGen) + extern (D) this(Loc loc, const(char)[] filename, Identifier ident, int doDocComment, int doHdrGen) { super(loc, ident); + this.dsym = DSYM.module_; const(char)[] srcfilename; //printf("Module::Module(filename = '%.*s', ident = '%s')\n", cast(int)filename.length, filename.ptr, ident.toChars()); this.arg = filename; @@ -526,20 +519,26 @@ else static const(char)* find(const(char)* filename) { - return find(filename.toDString).ptr; + ImportPathInfo pathThatFoundThis; // is this needed? In fact is this function needed still??? + return find(filename.toDString, pathThatFoundThis).ptr; } - extern (D) static const(char)[] find(const(char)[] filename) + extern (D) static const(char)[] find(const(char)[] filename, out ImportPathInfo pathThatFoundThis) { - return global.fileManager.lookForSourceFile(filename, global.path[]); + ptrdiff_t whichPathFoundThis; + const(char)[] ret = global.fileManager.lookForSourceFile(filename, global.path[], whichPathFoundThis); + + if (whichPathFoundThis >= 0) + pathThatFoundThis = global.path[whichPathFoundThis]; + return ret; } - extern (C++) static Module load(const ref Loc loc, Identifiers* packages, Identifier ident) + extern (C++) static Module load(Loc loc, Identifiers* packages, Identifier ident) { return load(loc, packages ? (*packages)[] : null, ident); } - extern (D) static Module load(const ref Loc loc, Identifier[] packages, Identifier ident) + extern (D) static Module load(Loc loc, Identifier[] packages, Identifier ident, ImportPathInfo pathInfo = ImportPathInfo.init) { //printf("Module::load(ident = '%s')\n", ident.toChars()); // Build module filename by turning: @@ -548,11 +547,17 @@ else // foo\bar\baz const(char)[] filename = getFilename(packages, ident); // Look for the source file - if (const result = find(filename)) + ImportPathInfo importPathThatFindUs; + if (const result = find(filename, importPathThatFindUs)) + { filename = result; // leaks + pathInfo = importPathThatFindUs; + } auto m = new Module(loc, filename, ident, 0, 0); + // TODO: apply import path information (pathInfo) on to module + if (!m.read(loc)) return null; if (global.params.v.verbose) @@ -596,17 +601,12 @@ else else { const(char)[] argdoc; - OutBuffer buf; - if (arg == "__stdin.d") - { - buf.printf("__stdin_%d.d", getpid()); - arg = buf[]; - } if (global.params.preservePaths) argdoc = arg; else argdoc = FileName.name(arg); - if (IN_LLVM && global.params.fullyQualifiedObjectFiles) + + if (global.params.fullyQualifiedObjectFiles) { const fqn = md ? md.toString() : toString(); argdoc = FileName.replaceName(argdoc, fqn); @@ -663,12 +663,12 @@ version (IN_LLVM) * Params: * loc = The location at which the file read originated (e.g. import) */ - private void onFileReadError(const ref Loc loc) + private void onFileReadError(Loc loc) { const name = srcfile.toString(); if (FileName.equals(name, "object.d")) { - ObjectNotFound(loc, ident); + ObjectNotFound(Loc.initial, ident); } else if (FileName.ext(this.arg) || !loc.isValid()) { @@ -698,7 +698,7 @@ version (IN_LLVM) if (global.path.length) { foreach (i, p; global.path[]) - fprintf(stderr, "import path[%llu] = %s\n", cast(ulong)i, p); + fprintf(stderr, "import path[%llu] = %s\n", cast(ulong)i, p.path); } else { @@ -720,7 +720,7 @@ version (IN_LLVM) * * Returns: `true` if successful */ - bool read(const ref Loc loc) + bool read(Loc loc) { if (this.src) return true; // already read @@ -779,6 +779,11 @@ else { const(char)* srcname = srcfile.toChars(); //printf("Module::parse(srcname = '%s')\n", srcname); + + import dmd.timetrace; + timeTraceBeginEvent(TimeTraceEventType.parse); + scope (exit) timeTraceEndEvent(TimeTraceEventType.parse, this); + isPackageFile = isPackageFileName(srcfile); const(char)[] buf = processSource(src, this); // an error happened on UTF conversion @@ -837,13 +842,12 @@ else checkCompiledImport(); members = p.parseModule(); assert(!p.md); // C doesn't have module declarations - numlines = p.scanloc.linnum; + numlines = p.linnum; } else { const bool doUnittests = global.params.parsingUnittestsRequired(); scope p = new Parser!AST(this, buf, cast(bool) docfile, global.errorSink, &global.compileEnv, doUnittests); - p.transitionIn = global.params.v.vin; p.nextToken(); p.parseModuleDeclaration(); md = p.md; @@ -862,7 +866,7 @@ else checkCompiledImport(); members = p.parseModuleContent(); - numlines = p.scanloc.linnum; + numlines = p.linnum; } /* The symbol table into which the module is to be inserted. @@ -1002,7 +1006,7 @@ else * sc = the scope into which we are imported * loc = the location of the import statement */ - void checkImportDeprecation(const ref Loc loc, Scope* sc) + void checkImportDeprecation(Loc loc, Scope* sc) { if (md && md.isdeprecated && !sc.isDeprecated) { @@ -1235,11 +1239,6 @@ else uint[uint] ctfe_cov; /// coverage information from ctfe execution_count[line] - override inout(Module) isModule() inout nothrow - { - return this; - } - override void accept(Visitor v) { v.visit(this); @@ -1351,7 +1350,7 @@ extern (C++) struct ModuleDeclaration bool isdeprecated; // if it is a deprecated module Expression msg; - extern (D) this(const ref Loc loc, Identifier[] packages, Identifier id, Expression msg, bool isdeprecated) @safe + extern (D) this(Loc loc, Identifier[] packages, Identifier id, Expression msg, bool isdeprecated) @safe { this.loc = loc; this.packages = packages; @@ -1475,7 +1474,7 @@ private const(char)[] processSource (const(ubyte)[] src, Module mod) { enum SourceEncoding { utf16, utf32} enum Endian { little, big} - immutable loc = mod.getLoc(); + immutable loc = mod.loc; /* * Convert a buffer from UTF32 to UTF8 diff --git a/dmd/doc.d b/dmd/doc.d index fecf6e0c23..79e06ea235 100644 --- a/dmd/doc.d +++ b/dmd/doc.d @@ -3,12 +3,12 @@ * * Specification: $(LINK2 https://dlang.org/spec/ddoc.html, Documentation Generator) * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/doc.d, _doc.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/doc.d, _doc.d) * Documentation: https://dlang.org/phobos/dmd_doc.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/doc.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/doc.d */ module dmd.doc; @@ -438,10 +438,7 @@ version (IN_LLVM) OutBuffer buf; if (m.filetype == FileType.ddoc) { - const ploc = m.md ? &m.md.loc : &m.loc; - Loc loc = *ploc; - if (!loc.filename) - loc.filename = srcfilename.ptr; + Loc loc = m.md ? m.md.loc : m.loc; size_t commentlen = m.comment ? strlen(cast(char*)m.comment) : 0; Dsymbols a; @@ -482,7 +479,7 @@ version (IN_LLVM) auto p = slice.ptr; for (size_t j = 0; j < slice.length; j++) { - char c = p[j]; + const c = p[j]; if (c == 0xFF && j + 1 < slice.length) { j++; @@ -515,7 +512,7 @@ void escapeDdocString(ref OutBuffer buf, size_t start) { for (size_t u = start; u < buf.length; u++) { - char c = buf[u]; + const c = buf[u]; switch (c) { case '$': @@ -642,7 +639,7 @@ private void escapeStrayParenthesis(Loc loc, ref OutBuffer buf, size_t start, bo for (size_t u = buf.length; u > start;) { u--; - char c = buf[u]; + const c = buf[u]; switch (c) { case ')': @@ -699,9 +696,7 @@ bool emitAnchorName(ref OutBuffer buf, Dsymbol s, Scope* sc, bool includeParent) } else { - /* We just want the identifier, not overloads like TemplateDeclaration::toChars. - * We don't want the template parameter list and constraints. */ - buf.writestring(s.Dsymbol.toChars()); + buf.writestring(s.ident ? s.ident.toString : "__anonymous"); } return true; } @@ -804,7 +799,11 @@ void emitAnchor(ref OutBuffer buf, Dsymbol s, Scope* sc, bool forHeader = false) } else { - auto symbolName = ident.toString(); + // 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); @@ -2067,9 +2066,9 @@ string toLowercase(string s) pure @safe // TODO: maybe unicode lowercase, somehow if (c >= 'A' && c <= 'Z') { - if (!lower.length) { + if (!lower.length) lower.reserve(s.length); - } + lower ~= s[lower.length..i]; c += 'a' - 'A'; lower ~= c; @@ -2279,10 +2278,9 @@ void removeBlankLineMacro(ref OutBuffer buf, ref size_t iAt, ref size_t i) * thematic break. If the replacement is made `i` changes to * point to the closing parenthesis of the `$(HR)` macro. * iLineStart = the index within `buf` that the thematic break's line starts at - * loc = the current location within the file * Returns: whether a thematic break was replaced */ -bool replaceMarkdownThematicBreak(ref OutBuffer buf, ref size_t i, size_t iLineStart, const ref Loc loc) +bool replaceMarkdownThematicBreak(ref OutBuffer buf, ref size_t i, size_t iLineStart) { const slice = buf[]; @@ -2383,11 +2381,10 @@ void removeAnyAtxHeadingSuffix(ref OutBuffer buf, size_t i) * iEnd = the index within `buf` of the character after the last * heading character. Is incremented by the length of the * inserted heading macro when this function ends. - * loc = the location of the Ddoc within the file * headingLevel = the level (1-6) of heading to end. Is set to `0` when this * function ends. */ -void endMarkdownHeading(ref OutBuffer buf, size_t iStart, ref size_t iEnd, const ref Loc loc, ref int headingLevel) +void endMarkdownHeading(ref OutBuffer buf, size_t iStart, ref size_t iEnd, ref int headingLevel) { char[5] heading = "$(H0 "; heading[3] = cast(char) ('0' + headingLevel); @@ -2444,12 +2441,11 @@ size_t endAllListsAndQuotes(ref OutBuffer buf, ref size_t i, ref MarkdownList[] * e.g. `*very* **nice**` becomes `$(EM very) $(STRONG nice)`. * Params: * buf = an OutBuffer containing the DDoc - * loc = the current location within the file * inlineDelimiters = the collection of delimiters found within a paragraph. When this function returns its length will be reduced to `downToLevel`. * downToLevel = the length within `inlineDelimiters`` to reduce emphasis to * Returns: the number of characters added to the buffer by the replacements */ -size_t replaceMarkdownEmphasis(ref OutBuffer buf, const ref Loc loc, ref MarkdownDelimiter[] inlineDelimiters, int downToLevel = 0) +size_t replaceMarkdownEmphasis(ref OutBuffer buf, ref MarkdownDelimiter[] inlineDelimiters, int downToLevel = 0) { size_t replaceEmphasisPair(ref MarkdownDelimiter start, ref MarkdownDelimiter end) { @@ -2632,7 +2628,7 @@ Parameter isFunctionParameter(Dsymbols* a, const(char)[] p) @safe /**************************************************** */ -Parameter isEponymousFunctionParameter(Dsymbols *a, const(char)[] p) @safe +Parameter isEponymousFunctionParameter(Dsymbols* a, const(char)[] p) @safe { foreach (Dsymbol dsym; *a) { @@ -2828,10 +2824,9 @@ struct MarkdownList * i = the index within `buf` of the list item. If this function succeeds `i` will be adjusted to fit the inserted macro. * iPrecedingBlankLine = the index within `buf` of the preceeding blank line. If non-zero and a new list was started, the preceeding blank line is removed and this value is set to `0`. * nestedLists = a set of nested lists. If this function succeeds it may contain a new nested list. - * loc = the location of the Ddoc within the file * Returns: `true` if a list was created */ - bool startItem(ref OutBuffer buf, ref size_t iLineStart, ref size_t i, ref size_t iPrecedingBlankLine, ref MarkdownList[] nestedLists, const ref Loc loc) + bool startItem(ref OutBuffer buf, ref size_t iLineStart, ref size_t i, ref size_t iPrecedingBlankLine, ref MarkdownList[] nestedLists) { buf.remove(iStart, iContentStart - iStart); @@ -3006,14 +3001,13 @@ struct MarkdownLink * buf = an OutBuffer containing the DDoc * i = the index within `buf` that points to the `]` character of the potential link. * If this function succeeds it will be adjusted to fit the inserted link macro. - * loc = the current location within the file * inlineDelimiters = previously parsed Markdown delimiters, including emphasis and link/image starts * delimiterIndex = the index within `inlineDelimiters` of the nearest link/image starting delimiter * linkReferences = previously parsed link references. When this function returns it may contain * additional previously unparsed references. * Returns: whether a reference link was found and replaced at `i` */ - static bool replaceLink(ref OutBuffer buf, ref size_t i, const ref Loc loc, ref MarkdownDelimiter[] inlineDelimiters, int delimiterIndex, ref MarkdownLinkReferences linkReferences) + static bool replaceLink(ref OutBuffer buf, ref size_t i, ref MarkdownDelimiter[] inlineDelimiters, int delimiterIndex, ref MarkdownLinkReferences linkReferences) { const delimiter = inlineDelimiters[delimiterIndex]; MarkdownLink link; @@ -3022,7 +3016,7 @@ struct MarkdownLink if (iEnd > i) { i = delimiter.iStart; - link.storeAndReplaceDefinition(buf, i, iEnd, linkReferences, loc); + link.storeAndReplaceDefinition(buf, i, iEnd, linkReferences); inlineDelimiters.length = delimiterIndex; return true; } @@ -3034,7 +3028,7 @@ struct MarkdownLink if (iEnd > i) { const label = link.label; - link = linkReferences.lookupReference(label, buf, i, loc); + link = linkReferences.lookupReference(label, buf, i); // check rightFlanking to avoid replacing things like int[string] if (!link.href.length && !delimiter.rightFlanking) link = linkReferences.lookupSymbol(label); @@ -3046,7 +3040,7 @@ struct MarkdownLink if (iEnd == i) return false; - immutable delta = replaceMarkdownEmphasis(buf, loc, inlineDelimiters, delimiterIndex); + immutable delta = replaceMarkdownEmphasis(buf, inlineDelimiters, delimiterIndex); iEnd += delta; i += delta; link.replaceLink(buf, i, iEnd, delimiter); @@ -3063,10 +3057,9 @@ struct MarkdownLink * delimiterIndex = the index within `inlineDelimiters` of the nearest link/image starting delimiter * linkReferences = previously parsed link references. When this function returns it may contain * additional previously unparsed references. - * loc = the current location in the file * Returns: whether a reference link was found and replaced at `i` */ - static bool replaceReferenceDefinition(ref OutBuffer buf, ref size_t i, ref MarkdownDelimiter[] inlineDelimiters, int delimiterIndex, ref MarkdownLinkReferences linkReferences, const ref Loc loc) + static bool replaceReferenceDefinition(ref OutBuffer buf, ref size_t i, ref MarkdownDelimiter[] inlineDelimiters, int delimiterIndex, ref MarkdownLinkReferences linkReferences) { const delimiter = inlineDelimiters[delimiterIndex]; MarkdownLink link; @@ -3075,7 +3068,7 @@ struct MarkdownLink return false; i = delimiter.iStart; - link.storeAndReplaceDefinition(buf, i, iEnd, linkReferences, loc); + link.storeAndReplaceDefinition(buf, i, iEnd, linkReferences); inlineDelimiters.length = delimiterIndex; return true; } @@ -3448,9 +3441,8 @@ struct MarkdownLink * iEnd = the index within `buf` that points just after the end of the definition * linkReferences = previously parsed link references. When this function returns it may contain * an additional reference. - * loc = the current location in the file */ - private void storeAndReplaceDefinition(ref OutBuffer buf, ref size_t i, size_t iEnd, ref MarkdownLinkReferences linkReferences, const ref Loc loc) + private void storeAndReplaceDefinition(ref OutBuffer buf, ref size_t i, size_t iEnd, ref MarkdownLinkReferences linkReferences) { // Remove the definition and trailing whitespace iEnd = skipChars(buf, iEnd, " \t\r\n"); @@ -3583,14 +3575,13 @@ struct MarkdownLinkReferences * label = the label to find the reference for * buf = an OutBuffer containing the DDoc * i = the index within `buf` to start searching for references at - * loc = the current location in the file * Returns: a link. If the `href` member has a value then the reference is valid. */ - MarkdownLink lookupReference(string label, ref OutBuffer buf, size_t i, const ref Loc loc) + MarkdownLink lookupReference(string label, ref OutBuffer buf, size_t i) { const lowercaseLabel = label.toLowercase(); if (lowercaseLabel !in references) - extractReferences(buf, i, loc); + extractReferences(buf, i); if (lowercaseLabel in references) return references[lowercaseLabel]; @@ -3616,7 +3607,7 @@ struct MarkdownLinkReferences auto id = Identifier.lookup(ids[0].ptr, ids[0].length); if (id) { - auto loc = Loc(); + auto loc = Loc.initial; Dsymbol pscopesym; auto symbol = _scope.search(loc, id, pscopesym, SearchOpt.ignoreErrors); for (size_t i = 1; symbol && i < ids.length; ++i) @@ -3638,10 +3629,9 @@ struct MarkdownLinkReferences * Params: * buf = an OutBuffer containing the DDoc * i = the index within `buf` to start looking at - * loc = the current location in the file * Returns: whether a reference was extracted */ - private void extractReferences(ref OutBuffer buf, size_t i, const ref Loc loc) + private void extractReferences(ref OutBuffer buf, size_t i) { static bool isFollowedBySpace(ref OutBuffer buf, size_t i) { @@ -3729,7 +3719,7 @@ struct MarkdownLinkReferences break; case ']': if (delimiters.length && !inCode && - MarkdownLink.replaceReferenceDefinition(buf, i, delimiters, cast(int) delimiters.length - 1, this, loc)) + MarkdownLink.replaceReferenceDefinition(buf, i, delimiters, cast(int) delimiters.length - 1, this)) --i; break; default: @@ -3902,19 +3892,18 @@ size_t parseTableDelimiterRow(ref OutBuffer buf, const size_t iStart, bool inQuo * buf = an OutBuffer containing the DDoc * iStart = the index within `buf` that the table header row starts at, inclusive * iEnd = the index within `buf` that the table header row ends at, exclusive - * loc = the current location in the file * inQuote = whether the table is inside a quote * inlineDelimiters = delimiters containing columns separators and any inline emphasis * columnAlignments = the parsed alignments for each column * Returns: the number of characters added by starting the table, or `0` if unchanged */ -size_t startTable(ref OutBuffer buf, size_t iStart, size_t iEnd, const ref Loc loc, bool inQuote, ref MarkdownDelimiter[] inlineDelimiters, out TableColumnAlignment[] columnAlignments) +size_t startTable(ref OutBuffer buf, size_t iStart, size_t iEnd, bool inQuote, ref MarkdownDelimiter[] inlineDelimiters, out TableColumnAlignment[] columnAlignments) { const iDelimiterRowEnd = parseTableDelimiterRow(buf, iEnd + 1, inQuote, columnAlignments); if (iDelimiterRowEnd) { size_t delta; - if (replaceTableRow(buf, iStart, iEnd, loc, inlineDelimiters, columnAlignments, true, delta)) + if (replaceTableRow(buf, iStart, iEnd, inlineDelimiters, columnAlignments, true, delta)) { buf.remove(iEnd + delta, iDelimiterRowEnd - iEnd); buf.insert(iEnd + delta, "$(TBODY "); @@ -3935,7 +3924,6 @@ size_t startTable(ref OutBuffer buf, size_t iStart, size_t iEnd, const ref Loc l * buf = an OutBuffer containing the DDoc * iStart = the index within `buf` that the table row starts at, inclusive * iEnd = the index within `buf` that the table row ends at, exclusive - * loc = the current location in the file * inlineDelimiters = delimiters containing columns separators and any inline emphasis * columnAlignments = alignments for each column * headerRow = if `true` then the number of columns will be enforced to match @@ -3944,7 +3932,7 @@ size_t startTable(ref OutBuffer buf, size_t iStart, size_t iEnd, const ref Loc l * delta = the number of characters added by replacing the row, or `0` if unchanged * Returns: `true` if a table row was found and replaced */ -bool replaceTableRow(ref OutBuffer buf, size_t iStart, size_t iEnd, const ref Loc loc, ref MarkdownDelimiter[] inlineDelimiters, TableColumnAlignment[] columnAlignments, bool headerRow, out size_t delta) +bool replaceTableRow(ref OutBuffer buf, size_t iStart, size_t iEnd, ref MarkdownDelimiter[] inlineDelimiters, TableColumnAlignment[] columnAlignments, bool headerRow, out size_t delta) { delta = 0; @@ -3970,7 +3958,7 @@ bool replaceTableRow(ref OutBuffer buf, size_t iStart, size_t iEnd, const ref Lo void replaceTableCell(size_t iCellStart, size_t iCellEnd, int cellIndex, int di) { - const eDelta = replaceMarkdownEmphasis(buf, loc, inlineDelimiters, di); + const eDelta = replaceMarkdownEmphasis(buf, inlineDelimiters, di); delta += eDelta; iCellEnd += eDelta; @@ -4088,15 +4076,14 @@ size_t endTable(ref OutBuffer buf, size_t i, ref TableColumnAlignment[] columnAl * buf = an OutBuffer containing the DDoc * iStart = the index within `buf` that the table row starts at, inclusive * iEnd = the index within `buf` that the table row ends at, exclusive - * loc = the current location in the file * inlineDelimiters = delimiters containing columns separators and any inline emphasis * columnAlignments = alignments for each column; upon return is set to length `0` * Returns: the number of characters added by replacing the row, or `0` if unchanged */ -size_t endRowAndTable(ref OutBuffer buf, size_t iStart, size_t iEnd, const ref Loc loc, ref MarkdownDelimiter[] inlineDelimiters, ref TableColumnAlignment[] columnAlignments) +size_t endRowAndTable(ref OutBuffer buf, size_t iStart, size_t iEnd, ref MarkdownDelimiter[] inlineDelimiters, ref TableColumnAlignment[] columnAlignments) { size_t delta; - replaceTableRow(buf, iStart, iEnd, loc, inlineDelimiters, columnAlignments, false, delta); + replaceTableRow(buf, iStart, iEnd, inlineDelimiters, columnAlignments, false, delta); delta += endTable(buf, iEnd + delta, columnAlignments); return delta; } @@ -4113,9 +4100,8 @@ size_t endRowAndTable(ref OutBuffer buf, size_t iStart, size_t iEnd, const ref L */ void highlightText(Scope* sc, Dsymbols* a, Loc loc, ref OutBuffer buf, size_t offset) { - const incrementLoc = loc.linnum == 0 ? 1 : 0; - loc.linnum = loc.linnum + incrementLoc; - loc.charnum = 0; + loc.nextLine(); + //printf("highlightText()\n"); bool leadingBlank = true; size_t iParagraphStart = offset; @@ -4166,19 +4152,19 @@ void highlightText(Scope* sc, Dsymbols* a, Loc loc, ref OutBuffer buf, size_t of } if (headingLevel) { - i += replaceMarkdownEmphasis(buf, loc, inlineDelimiters); - endMarkdownHeading(buf, iParagraphStart, i, loc, headingLevel); + i += replaceMarkdownEmphasis(buf, inlineDelimiters); + endMarkdownHeading(buf, iParagraphStart, i, headingLevel); removeBlankLineMacro(buf, iPrecedingBlankLine, i); ++i; iParagraphStart = skipChars(buf, i, " \t\r\n"); } if (tableRowDetected && !columnAlignments.length) - i += startTable(buf, iLineStart, i, loc, lineQuoted, inlineDelimiters, columnAlignments); + i += startTable(buf, iLineStart, i, lineQuoted, inlineDelimiters, columnAlignments); else if (columnAlignments.length) { size_t delta; - if (replaceTableRow(buf, iLineStart, i, loc, inlineDelimiters, columnAlignments, false, delta)) + if (replaceTableRow(buf, iLineStart, i, inlineDelimiters, columnAlignments, false, delta)) i += delta; else i += endTable(buf, i, columnAlignments); @@ -4193,7 +4179,7 @@ void highlightText(Scope* sc, Dsymbols* a, Loc loc, ref OutBuffer buf, size_t of i += endTable(buf, i, columnAlignments); if (!lineQuoted && quoteLevel) endAllListsAndQuotes(buf, i, nestedLists, quoteLevel, quoteMacroLevel); - i += replaceMarkdownEmphasis(buf, loc, inlineDelimiters); + i += replaceMarkdownEmphasis(buf, inlineDelimiters); // if we don't already know about this paragraph break then // insert a blank line and record the paragraph break @@ -4219,7 +4205,7 @@ void highlightText(Scope* sc, Dsymbols* a, Loc loc, ref OutBuffer buf, size_t of lineQuoted = false; tableRowDetected = false; iLineStart = i + 1; - loc.linnum = loc.linnum + incrementLoc; + loc.nextLine(); // update the paragraph start if we just entered a macro if (previousMacroLevel < macroLevel && iParagraphStart < iLineStart) @@ -4309,7 +4295,7 @@ void highlightText(Scope* sc, Dsymbols* a, Loc loc, ref OutBuffer buf, size_t of if (quoteLevel < lineQuoteLevel) { - i += endRowAndTable(buf, iLineStart, i, loc, inlineDelimiters, columnAlignments); + i += endRowAndTable(buf, iLineStart, i, inlineDelimiters, columnAlignments); if (nestedLists.length) { const indent = getMarkdownIndent(buf, iLineStart, i); @@ -4432,7 +4418,7 @@ void highlightText(Scope* sc, Dsymbols* a, Loc loc, ref OutBuffer buf, size_t of if (!headingLevel) break; - i += endRowAndTable(buf, iLineStart, i, loc, inlineDelimiters, columnAlignments); + i += endRowAndTable(buf, iLineStart, i, inlineDelimiters, columnAlignments); if (!lineQuoted && quoteLevel) i += endAllListsAndQuotes(buf, iLineStart, nestedLists, quoteLevel, quoteMacroLevel); @@ -4473,7 +4459,7 @@ void highlightText(Scope* sc, Dsymbols* a, Loc loc, ref OutBuffer buf, size_t of const list = MarkdownList.parseItem(buf, iLineStart, i); if (list.isValid) { - if (replaceMarkdownThematicBreak(buf, i, iLineStart, loc)) + if (replaceMarkdownThematicBreak(buf, i, iLineStart)) { removeBlankLineMacro(buf, iPrecedingBlankLine, i); iParagraphStart = skipChars(buf, i+1, " \t\r\n"); @@ -4597,7 +4583,7 @@ void highlightText(Scope* sc, Dsymbols* a, Loc loc, ref OutBuffer buf, size_t of } else { - i += endRowAndTable(buf, iLineStart, i, loc, inlineDelimiters, columnAlignments); + i += endRowAndTable(buf, iLineStart, i, inlineDelimiters, columnAlignments); if (!lineQuoted && quoteLevel) { const delta = endAllListsAndQuotes(buf, iLineStart, nestedLists, quoteLevel, quoteMacroLevel); @@ -4629,9 +4615,9 @@ void highlightText(Scope* sc, Dsymbols* a, Loc loc, ref OutBuffer buf, size_t of case '_': { - if (leadingBlank && !inCode && replaceMarkdownThematicBreak(buf, i, iLineStart, loc)) + if (leadingBlank && !inCode && replaceMarkdownThematicBreak(buf, i, iLineStart)) { - i += endRowAndTable(buf, iLineStart, i, loc, inlineDelimiters, columnAlignments); + i += endRowAndTable(buf, iLineStart, i, inlineDelimiters, columnAlignments); if (!lineQuoted && quoteLevel) i += endAllListsAndQuotes(buf, iLineStart, nestedLists, quoteLevel, quoteMacroLevel); removeBlankLineMacro(buf, iPrecedingBlankLine, i); @@ -4659,7 +4645,7 @@ void highlightText(Scope* sc, Dsymbols* a, Loc loc, ref OutBuffer buf, size_t of break; } - i += endRowAndTable(buf, iLineStart, i, loc, inlineDelimiters, columnAlignments); + i += endRowAndTable(buf, iLineStart, i, inlineDelimiters, columnAlignments); if (!lineQuoted && quoteLevel) { const delta = endAllListsAndQuotes(buf, iLineStart, nestedLists, quoteLevel, quoteMacroLevel); @@ -4669,7 +4655,7 @@ void highlightText(Scope* sc, Dsymbols* a, Loc loc, ref OutBuffer buf, size_t of } list.macroLevel = macroLevel; - list.startItem(buf, iLineStart, i, iPrecedingBlankLine, nestedLists, loc); + list.startItem(buf, iLineStart, i, iPrecedingBlankLine, nestedLists); break; } } @@ -4688,9 +4674,9 @@ void highlightText(Scope* sc, Dsymbols* a, Loc loc, ref OutBuffer buf, size_t of if (leadingBlank) { // Check for a thematic break - if (replaceMarkdownThematicBreak(buf, i, iLineStart, loc)) + if (replaceMarkdownThematicBreak(buf, i, iLineStart)) { - i += endRowAndTable(buf, iLineStart, i, loc, inlineDelimiters, columnAlignments); + i += endRowAndTable(buf, iLineStart, i, inlineDelimiters, columnAlignments); if (!lineQuoted && quoteLevel) i += endAllListsAndQuotes(buf, iLineStart, nestedLists, quoteLevel, quoteMacroLevel); removeBlankLineMacro(buf, iPrecedingBlankLine, i); @@ -4769,7 +4755,7 @@ void highlightText(Scope* sc, Dsymbols* a, Loc loc, ref OutBuffer buf, size_t of if (delimiter.type == '[' || delimiter.type == '!') { if (delimiter.isValid && - MarkdownLink.replaceLink(buf, i, loc, inlineDelimiters, d, linkReferences)) + MarkdownLink.replaceLink(buf, i, inlineDelimiters, d, linkReferences)) { // if we removed a reference link then we're at line start if (i <= delimiter.iStart) @@ -4867,10 +4853,10 @@ void highlightText(Scope* sc, Dsymbols* a, Loc loc, ref OutBuffer buf, size_t of --downToLevel; if (headingLevel && headingMacroLevel >= macroLevel) { - endMarkdownHeading(buf, iParagraphStart, i, loc, headingLevel); + endMarkdownHeading(buf, iParagraphStart, i, headingLevel); removeBlankLineMacro(buf, iPrecedingBlankLine, i); } - i += endRowAndTable(buf, iLineStart, i, loc, inlineDelimiters, columnAlignments); + i += endRowAndTable(buf, iLineStart, i, inlineDelimiters, columnAlignments); while (nestedLists.length && nestedLists[$-1].macroLevel >= macroLevel) { i = buf.insert(i, ")\n)"); @@ -4878,7 +4864,7 @@ void highlightText(Scope* sc, Dsymbols* a, Loc loc, ref OutBuffer buf, size_t of } if (quoteLevel && quoteMacroLevel >= macroLevel) i += endAllMarkdownQuotes(buf, i, quoteLevel); - i += replaceMarkdownEmphasis(buf, loc, inlineDelimiters, downToLevel); + i += replaceMarkdownEmphasis(buf, inlineDelimiters, downToLevel); --macroLevel; quoteMacroLevel = 0; @@ -4954,11 +4940,11 @@ void highlightText(Scope* sc, Dsymbols* a, Loc loc, ref OutBuffer buf, size_t of size_t i = buf.length; if (headingLevel) { - endMarkdownHeading(buf, iParagraphStart, i, loc, headingLevel); + endMarkdownHeading(buf, iParagraphStart, i, headingLevel); removeBlankLineMacro(buf, iPrecedingBlankLine, i); } - i += endRowAndTable(buf, iLineStart, i, loc, inlineDelimiters, columnAlignments); - i += replaceMarkdownEmphasis(buf, loc, inlineDelimiters); + i += endRowAndTable(buf, iLineStart, i, inlineDelimiters, columnAlignments); + i += replaceMarkdownEmphasis(buf, inlineDelimiters); endAllListsAndQuotes(buf, i, nestedLists, quoteLevel, quoteMacroLevel); } diff --git a/dmd/doc.h b/dmd/doc.h index 1775e354e0..61a51a0b82 100644 --- a/dmd/doc.h +++ b/dmd/doc.h @@ -1,6 +1,6 @@ /* Compiler implementation of the D programming language - * Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * written by Walter Bright * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. diff --git a/dmd/dscope.d b/dmd/dscope.d index 0703bfa5fb..00d2fab829 100644 --- a/dmd/dscope.d +++ b/dmd/dscope.d @@ -3,12 +3,12 @@ * * Not to be confused with the `scope` storage class. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/dscope.d, _dscope.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/dscope.d, _dscope.d) * Documentation: https://dlang.org/phobos/dmd_dscope.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/dscope.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/dscope.d */ module dmd.dscope; @@ -24,6 +24,7 @@ import dmd.dclass; import dmd.declaration; import dmd.dmodule; import dmd.doc; +import dmd.dstruct; import dmd.dsymbol; import dmd.dsymbolsem; import dmd.dtemplate; @@ -45,37 +46,80 @@ import dmd.tokens; //version=LOGSEARCH; +/// What kind of contract function we're in, if any +enum Contract : ubyte +{ + none = 0, + invariant_ = 1, + require = 2, // in contract + ensure = 3, // out contract +} -// List of flags that can be applied to this `Scope` -enum SCOPE +private extern (D) struct BitFields { - ctor = 0x0001, /// constructor type - noaccesscheck = 0x0002, /// don't do access checks - condition = 0x0004, /// inside static if/assert condition - debug_ = 0x0008, /// inside debug conditional - constraint = 0x0010, /// inside template constraint - invariant_ = 0x0020, /// inside invariant code - require = 0x0040, /// inside in contract code - ensure = 0x0060, /// inside out contract code - contract = 0x0060, /// [mask] we're inside contract code - ctfe = 0x0080, /// inside a ctfe-only expression - compile = 0x0100, /// inside __traits(compile) - ignoresymbolvisibility = 0x0200, /// ignore symbol visibility - /// https://issues.dlang.org/show_bug.cgi?id=15907 - Cfile = 0x0800, /// C semantics apply - free = 0x8000, /// is on free list - - fullinst = 0x10000, /// fully instantiate templates - ctfeBlock = 0x20000, /// inside a `if (__ctfe)` block - dip1000 = 0x40000, /// dip1000 errors enabled for this scope - dip25 = 0x80000, /// dip25 errors enabled for this scope + bool ctor; /// constructor type + bool noAccessCheck; /// don't do access checks + bool condition; /// inside static if/assert condition + bool debug_; /// inside debug conditional + bool inTemplateConstraint; /// inside template constraint + Contract contract; + bool ctfe; /// inside a ctfe-only expression + bool traitsCompiles; /// inside __traits(compile) + /// ignore symbol visibility + /// https://issues.dlang.org/show_bug.cgi?id=15907 + bool ignoresymbolvisibility; + bool inCfile; /// C semantics apply + bool canFree; /// is on free list + bool fullinst; /// fully instantiate templates + bool ctfeBlock; /// inside a `if (__ctfe)` block } -/// Flags that are carried along with a scope push() -private enum PersistentFlags = - SCOPE.contract | SCOPE.debug_ | SCOPE.ctfe | SCOPE.compile | SCOPE.constraint | - SCOPE.noaccesscheck | SCOPE.ignoresymbolvisibility | - SCOPE.Cfile | SCOPE.ctfeBlock | SCOPE.dip1000 | SCOPE.dip25; +/// State of -preview switches +/// +/// By making them part of a Scope, we reduce reliance on dmd.globals, +/// and can enable/disable them per module / edition. +private struct Previews +{ + // Run `dmd -preview=h` for the meaning of these switches + private extern (D) static struct BitFields + { + bool bitfields; + bool dip1000; + bool dip1008; + bool dip1021; + bool dip25; + bool fieldwise; + bool fixAliasThis; + bool fixImmutableConv; + bool in_; + bool inclusiveInContracts; + bool noSharedAccess; + bool rvalueRefParam; + bool safer; + FeatureState systemVariables; + } + + import dmd.common.bitfields : generateBitFields; + mixin(generateBitFields!(BitFields, ushort)); + + void setFromParams(ref Param params) @nogc nothrow pure @safe + { + this.bitfields = params.bitfields; + this.dip1000 = params.useDIP1000 == FeatureState.enabled; + this.dip1008 = params.ehnogc; + this.dip1021 = params.useDIP1021; // == FeatureState.enabled; + this.dip25 = params.useDIP25 == FeatureState.enabled; + this.fixAliasThis = params.fixAliasThis; + this.fixImmutableConv = params.fixImmutableConv; + this.in_ = params.previewIn; + this.inclusiveInContracts = params.inclusiveInContracts; + this.noSharedAccess = params.noSharedAccess == FeatureState.enabled; + this.rvalueRefParam = params.rvalueRefParam == FeatureState.enabled; + this.safer = params.safer == FeatureState.enabled; + this.systemVariables = params.systemVariables; + this.fieldwise = params.fieldwise == FeatureState.enabled; + } +} extern (C++) struct Scope { @@ -87,10 +131,10 @@ extern (C++) struct Scope VarDeclaration varDecl; /// variable we are in during semantic2 Dsymbol parent; /// parent to use LabelStatement slabel; /// enclosing labelled statement - SwitchStatement sw; /// enclosing switch statement + SwitchStatement switchStatement;/// enclosing switch statement Statement tryBody; /// enclosing _body of TryCatchStatement or TryFinallyStatement - TryFinallyStatement tf; /// enclosing try finally statement - ScopeGuardStatement os; /// enclosing scope(xxx) statement + TryFinallyStatement tryFinally; /// enclosing try finally statement + ScopeGuardStatement scopeGuard; /// enclosing scope(xxx) statement Statement sbreak; /// enclosing statement that supports "break" Statement scontinue; /// enclosing statement that supports "continue" ForeachStatement fes; /// if nested function for ForeachStatement, this is it @@ -137,11 +181,14 @@ version (IN_LLVM) Visibility visibility = Visibility(Visibility.Kind.public_); int explicitVisibility; /// set if in an explicit visibility attribute - StorageClass stc; /// storage class + STC stc; /// storage class DeprecatedDeclaration depdecl; /// customized deprecation message - uint flags; + import dmd.common.bitfields : generateBitFields; + mixin(generateBitFields!(BitFields, ushort)); + + Previews previews; // user defined attributes UserAttributeDeclaration userAttribDecl; @@ -152,6 +199,7 @@ version (IN_LLVM) AliasDeclaration aliasAsg; /// if set, then aliasAsg is being assigned a new value, /// do not set wasRead for it + StructDeclaration argStruct; /// elimiate recursion when looking for rvalue construction extern (D) __gshared Scope* freelist; @@ -162,8 +210,8 @@ version (IN_LLVM) Scope* s = freelist; freelist = s.enclosing; //printf("freelist %p\n", s); - assert(s.flags & SCOPE.free); - s.flags &= ~SCOPE.free; + assert(s.canFree); + s.canFree = false; return s; } return new Scope(); @@ -185,12 +233,10 @@ version (IN_LLVM) m = m.parent; m.addMember(null, sc.scopesym); m.parent = null; // got changed by addMember() - if (global.params.useDIP1000 == FeatureState.enabled) - sc.flags |= SCOPE.dip1000; - if (global.params.useDIP25 == FeatureState.enabled) - sc.flags |= SCOPE.dip25; + sc.previews.setFromParams(global.params); + if (_module.filetype == FileType.c) - sc.flags |= SCOPE.Cfile; + sc.inCfile = true; // Create the module scope underneath the global scope sc = sc.push(_module); sc.parent = _module; @@ -212,13 +258,13 @@ version (IN_LLVM) { Scope* s = copy(); //printf("Scope::push(this = %p) new = %p\n", this, s); - assert(!(flags & SCOPE.free)); + assert(!this.canFree); s.scopesym = null; s.enclosing = &this; debug { if (enclosing) - assert(!(enclosing.flags & SCOPE.free)); + assert(!enclosing.canFree); if (s == enclosing) { printf("this = %p, enclosing = %p, enclosing.enclosing = %p\n", s, &this, enclosing); @@ -228,12 +274,36 @@ version (IN_LLVM) s.slabel = null; s.nofree = false; s.ctorflow.fieldinit = ctorflow.fieldinit.arraydup; - s.flags = (flags & PersistentFlags); + + // Only keep persistent flags + s.resetAllFlags(); + s.contract = this.contract; + s.debug_ = this.debug_; + s.ctfe = this.ctfe; + s.traitsCompiles = this.traitsCompiles; + s.inTemplateConstraint = this.inTemplateConstraint; + s.noAccessCheck = this.noAccessCheck; + s.ignoresymbolvisibility = this.ignoresymbolvisibility; + s.inCfile = this.inCfile; + s.ctfeBlock = this.ctfeBlock; + s.previews = this.previews; s.lastdc = null; assert(&this != s); return s; } + /// Copy flags from scope `other` + extern(D) void copyFlagsFrom(Scope* other) @safe + { + this.bitFields = other.bitFields; + } + + /// Set all scope flags to their initial value + extern(D) void resetAllFlags() @safe + { + this.bitFields = 0; + } + extern (D) Scope* push(ScopeDsymbol ss) { //printf("Scope::push(%s)\n", ss.toChars()); @@ -256,7 +326,7 @@ version (IN_LLVM) this = this.init; enclosing = freelist; freelist = &this; - flags |= SCOPE.free; + this.canFree = true; } return enc; } @@ -275,7 +345,8 @@ version (IN_LLVM) extern (D) Scope* startCTFE() { Scope* sc = this.push(); - sc.flags = this.flags | SCOPE.ctfe; + sc.copyFlagsFrom(&this); + sc.ctfe = true; version (none) { /* TODO: Currently this is not possible, because we need to @@ -305,7 +376,7 @@ version (IN_LLVM) extern (D) Scope* endCTFE() { - assert(flags & SCOPE.ctfe); + assert(this.ctfe); return pop(); } @@ -316,30 +387,29 @@ version (IN_LLVM) * loc = for error messages * ctorflow = flow results to merge in */ - extern (D) void merge(const ref Loc loc, const ref CtorFlow ctorflow) + 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) + 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) { - 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) { - 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()); - } + error(loc, "one path skips field `%s`", v.toChars()); } } } @@ -357,7 +427,7 @@ version (IN_LLVM) * Returns: * symbol if found, null if not */ - extern (C++) Dsymbol search(const ref Loc loc, Identifier ident, out Dsymbol pscopesym, SearchOptFlags flags = SearchOpt.all) + extern (C++) Dsymbol search(Loc loc, Identifier ident, out Dsymbol pscopesym, SearchOptFlags flags = SearchOpt.all) { version (LOGSEARCH) { @@ -475,7 +545,7 @@ version (IN_LLVM) if (sc.scopesym.isModule()) flags |= SearchOpt.unqualifiedModule; // tell Module.search() that SearchOpt.localsOnly is to be obeyed - else if (sc.flags & SCOPE.Cfile && sc.scopesym.isStructDeclaration()) + else if (sc.inCfile && sc.scopesym.isStructDeclaration()) continue; // C doesn't have struct scope if (Dsymbol s = sc.scopesym.search(loc, ident, flags)) @@ -497,11 +567,10 @@ version (IN_LLVM) } NotFound: - if (global.params.fixAliasThis) + if (sc.previews.fixAliasThis) { Expression exp = new ThisExp(loc); - Dsymbol aliasSym = checkAliasThis(sc.scopesym.isAggregateDeclaration(), ident, flags, &exp); - if (aliasSym) + if (Dsymbol aliasSym = checkAliasThis(sc.scopesym.isAggregateDeclaration(), ident, flags, &exp)) { //printf("found aliassym: %s\n", aliasSym.toChars()); pscopesym = new ExpressionDsymbol(exp); @@ -516,7 +585,7 @@ version (IN_LLVM) return null; } - if (this.flags & SCOPE.ignoresymbolvisibility) + if (this.ignoresymbolvisibility) flags |= SearchOpt.ignoreVisibility; // First look in local scopes @@ -663,7 +732,7 @@ version (IN_LLVM) //printf("\t\tscopesym = %p\n", scopesym); if (!scopesym.symtab) scopesym.symtab = new DsymbolTable(); - if (!(flags & SCOPE.Cfile)) + if (!this.inCfile) return scopesym.symtabInsert(s); // ImportC insert @@ -758,7 +827,7 @@ version (IN_LLVM) { //printf("\tsc = %p\n", sc); sc.nofree = true; - assert(!(flags & SCOPE.free)); + assert(!this.canFree); //assert(sc != sc.enclosing); //assert(!sc.enclosing || sc != sc.enclosing.enclosing); //if (++i == 10) @@ -819,7 +888,7 @@ version (IN_LLVM) */ extern (D) bool isFromSpeculativeSemanticContext() scope { - return this.intypeof || this.flags & SCOPE.compile; + return this.intypeof || this.traitsCompiles; } @@ -829,19 +898,19 @@ version (IN_LLVM) */ extern (D) bool needsCodegen() { - return (flags & (SCOPE.ctfe | SCOPE.ctfeBlock | SCOPE.compile)) == 0; + return !this.ctfe && !this.ctfeBlock && !this.traitsCompiles; } /// Returns: whether to raise DIP1000 warnings (FeatureStabe.default) or errors (FeatureState.enabled) extern (D) FeatureState useDIP1000() { - return (flags & SCOPE.dip1000 || hasEdition(Edition.v2024)) ? FeatureState.enabled : FeatureState.disabled; + return (this.previews.dip1000 || hasEdition(Edition.v2024)) ? FeatureState.enabled : FeatureState.disabled; } /// Returns: whether to raise DIP25 warnings (FeatureStabe.default) or errors (FeatureState.enabled) extern (D) FeatureState useDIP25() { - return (flags & SCOPE.dip25 || hasEdition(Edition.v2024)) ? FeatureState.enabled : FeatureState.disabled; + return (this.previews.dip25 || hasEdition(Edition.v2024)) ? FeatureState.enabled : FeatureState.disabled; } /// Returns: whether this scope compiles with `edition` or later diff --git a/dmd/dstruct.d b/dmd/dstruct.d index 32ebdf40e1..70743831c7 100644 --- a/dmd/dstruct.d +++ b/dmd/dstruct.d @@ -3,12 +3,12 @@ * * Specification: $(LINK2 https://dlang.org/spec/struct.html, Structs, Unions) * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/dstruct.d, _dstruct.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/dstruct.d, _dstruct.d) * Documentation: https://dlang.org/phobos/dmd_dstruct.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/dstruct.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/dstruct.d */ module dmd.dstruct; @@ -23,7 +23,7 @@ import dmd.declaration; import dmd.dmodule; import dmd.dscope; import dmd.dsymbol; -import dmd.dsymbolsem; +import dmd.dsymbolsem : search, setFieldOffset; import dmd.dtemplate; import dmd.errors; import dmd.expression; @@ -37,7 +37,7 @@ import dmd.mtype; import dmd.opover; import dmd.target; import dmd.tokens; -import dmd.typesem; +import dmd.typesem : isZeroInit, merge, size, hasPointers; import dmd.typinf; import dmd.visitor; @@ -66,132 +66,6 @@ FuncDeclaration search_toString(StructDeclaration sd) return fd; } -/*************************************** - * Request additional semantic analysis for TypeInfo generation. - * Params: - * sc = context - * t = type that TypeInfo is being generated for - */ -extern (D) void semanticTypeInfo(Scope* sc, Type t) -{ - if (sc) - { - if (sc.intypeof) - return; - if (!sc.needsCodegen()) - return; - } - - if (!t) - return; - - void visitVector(TypeVector t) - { - semanticTypeInfo(sc, t.basetype); - } - - void visitAArray(TypeAArray t) - { - semanticTypeInfo(sc, t.index); - semanticTypeInfo(sc, t.next); - } - - void visitStruct(TypeStruct t) - { - //printf("semanticTypeInfo.visit(TypeStruct = %s)\n", t.toChars()); - StructDeclaration sd = t.sym; - - /* Step 1: create TypeInfoDeclaration - */ - if (!sc) // inline may request TypeInfo. - { - Scope scx; - scx.eSink = global.errorSink; - scx._module = sd.getModule(); - getTypeInfoType(sd.loc, t, &scx); -version (IN_LLVM) {} else -{ - sd.requestTypeInfo = true; -} - } - else if (!sc.minst) - { - // don't yet have to generate TypeInfo instance if - // the typeid(T) expression exists in speculative scope. - } - else - { - getTypeInfoType(sd.loc, t, sc); -version (IN_LLVM) {} else -{ - sd.requestTypeInfo = true; -} - - // https://issues.dlang.org/show_bug.cgi?id=15149 - // if the typeid operand type comes from a - // result of auto function, it may be yet speculative. - // unSpeculative(sc, sd); - } - - /* Step 2: If the TypeInfo generation requires sd.semantic3, run it later. - * This should be done even if typeid(T) exists in speculative scope. - * Because it may appear later in non-speculative scope. - */ - if (!sd.members) - return; // opaque struct - if (!sd.xeq && !sd.xcmp && !sd.postblit && !sd.tidtor && !sd.xhash && !search_toString(sd)) - return; // none of TypeInfo-specific members - - // If the struct is in a non-root module, run semantic3 to get - // correct symbols for the member function. - if (sd.semanticRun >= PASS.semantic3) - { - // semantic3 is already done - } - else if (TemplateInstance ti = sd.isInstantiated()) - { - if (ti.minst && !ti.minst.isRoot()) - Module.addDeferredSemantic3(sd); - } - else - { - if (sd.inNonRoot()) - { - //printf("deferred sem3 for TypeInfo - sd = %s, inNonRoot = %d\n", sd.toChars(), sd.inNonRoot()); - Module.addDeferredSemantic3(sd); - } - } - } - - void visitTuple(TypeTuple t) - { - if (t.arguments) - { - foreach (arg; *t.arguments) - { - semanticTypeInfo(sc, arg.type); - } - } - } - - /* Note structural similarity of this Type walker to that in isSpeculativeType() - */ - - Type tb = t.toBasetype(); - switch (tb.ty) - { - case Tvector: visitVector(tb.isTypeVector()); break; - case Taarray: visitAArray(tb.isTypeAArray()); break; - case Tstruct: visitStruct(tb.isTypeStruct()); break; - case Ttuple: visitTuple (tb.isTypeTuple()); break; - - case Tclass: - case Tenum: break; - - default: semanticTypeInfo(sc, tb.nextOf()); break; - } -} - enum StructFlags : int { none = 0x0, @@ -227,6 +101,7 @@ extern (C++) class StructDeclaration : AggregateDeclaration bool hasIdentityEquals; // true if has identity opEquals bool hasNoFields; // has no fields bool hasCopyCtor; // copy constructor + bool hasMoveCtor; // move constructor bool hasPointerField; // members with indirections bool hasVoidInitPointers; // void-initialized unsafe fields bool hasUnsafeBitpatterns; // @system members, pointers, bool @@ -244,9 +119,10 @@ version (IN_LLVM) {} else import dmd.common.bitfields : generateBitFields; mixin(generateBitFields!(BitFields, ushort)); - extern (D) this(const ref Loc loc, Identifier id, bool inObject) + extern (D) this(Loc loc, Identifier id, bool inObject) { super(loc, id); + this.dsym = DSYM.structDeclaration; zeroInit = false; // assume false until we do semantic processing ispod = ThreeState.none; // For forward references @@ -259,7 +135,7 @@ version (IN_LLVM) {} else } } - static StructDeclaration create(const ref Loc loc, Identifier id, bool inObject) + static StructDeclaration create(Loc loc, Identifier id, bool inObject) { return new StructDeclaration(loc, id, inObject); } @@ -300,11 +176,12 @@ version (IN_LLVM) {} else { Dsymbol s = (*members)[i]; s.setFieldOffset(this, &fieldState, isunion); - } - if (type.ty == Terror) - { - errors = true; - return; + if (type.ty == Terror) + { + errorSupplemental(s.loc, "error on member `%s`", s.toPrettyChars); + errors = true; + return; + } } if (structsize == 0) @@ -359,8 +236,18 @@ version (IN_LLVM) {} else // Determine if struct is all zeros or not zeroInit = true; + auto lastOffset = -1; foreach (vd; fields) { + // First skip zero sized fields + if (vd.type.size(vd.loc) == 0) + continue; + + // only consider first sized member of an (anonymous) union + if (vd.overlapped && vd.offset == lastOffset) + continue; + lastOffset = vd.offset; + if (vd._init) { if (vd._init.isVoidInitializer()) @@ -369,10 +256,6 @@ version (IN_LLVM) {} else */ continue; - // Zero size fields are zero initialized - if (vd.type.size(vd.loc) == 0) - continue; - // Examine init to see if it is all 0s. auto exp = vd.getConstInitializer(); if (!exp || !_isZeroInit(exp)) @@ -440,13 +323,22 @@ version (IN_LLVM) {} else if (ispod != ThreeState.none) return (ispod == ThreeState.yes); - ispod = ThreeState.yes; - import dmd.clone; - bool hasCpCtorLocal; - needCopyCtor(this, hasCpCtorLocal); - if (enclosing || search(this, loc, Id.postblit) || search(this, loc, Id.dtor) || hasCpCtorLocal) + bool hasCpCtorLocal; + bool hasMoveCtorLocal; + bool needCopyCtor; + bool needMoveCtor; + needCopyOrMoveCtor(this, hasCpCtorLocal, hasMoveCtorLocal, needCopyCtor, needMoveCtor); + + if (enclosing || // is nested + search(this, loc, Id.postblit) || // has postblit + search(this, 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 { ispod = ThreeState.no; return false; @@ -462,12 +354,9 @@ version (IN_LLVM) {} else return false; } - Type tv = v.type.baseElemOf(); - if (tv.ty == Tstruct) + if (auto ts = v.type.baseElemOf().isTypeStruct()) { - auto ts = cast(TypeStruct)tv; - StructDeclaration sd = ts.sym; - if (!sd.isPOD()) + if (!ts.sym.isPOD()) { ispod = ThreeState.no; return false; @@ -475,7 +364,8 @@ version (IN_LLVM) {} else } } - return (ispod == ThreeState.yes); + ispod = ThreeState.yes; + return true; } /*************************************** @@ -488,11 +378,6 @@ version (IN_LLVM) {} else return postblit || hasCopyCtor; } - override final inout(StructDeclaration) isStructDeclaration() inout @nogc nothrow pure @safe - { - return this; - } - override void accept(Visitor v) { v.visit(this); @@ -654,9 +539,10 @@ bool _isZeroInit(Expression exp) */ extern (C++) final class UnionDeclaration : StructDeclaration { - extern (D) this(const ref Loc loc, Identifier id) + extern (D) this(Loc loc, Identifier id) { super(loc, id, false); + this.dsym = DSYM.unionDeclaration; } override UnionDeclaration syntaxCopy(Dsymbol s) @@ -672,11 +558,6 @@ extern (C++) final class UnionDeclaration : StructDeclaration return "union"; } - override inout(UnionDeclaration) isUnionDeclaration() inout - { - return this; - } - override void accept(Visitor v) { v.visit(this); diff --git a/dmd/dsymbol.d b/dmd/dsymbol.d index 6cdb1ab80d..1efe773d5e 100644 --- a/dmd/dsymbol.d +++ b/dmd/dsymbol.d @@ -1,12 +1,12 @@ /** * The base class for a D symbol, which can be a module, variable, function, enum, etc. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/dsymbol.d, _dsymbol.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/dsymbol.d, _dsymbol.d) * Documentation: https://dlang.org/phobos/dmd_dsymbol.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/dsymbol.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/dsymbol.d */ module dmd.dsymbol; @@ -51,6 +51,7 @@ import dmd.statement; import dmd.staticassert; import dmd.tokens; import dmd.visitor; +import dmd.dsymbolsem; import dmd.common.outbuffer; @@ -62,7 +63,7 @@ version (IN_LLVM) } /*************************************** - * Calls dg(Dsymbol *sym) for each Dsymbol. + * Calls dg(Dsymbol* sym) for each Dsymbol. * If dg returns !=0, stops and returns that value else returns 0. * Params: * symbols = Dsymbols @@ -91,7 +92,7 @@ int foreachDsymbol(Dsymbols* symbols, scope int delegate(Dsymbol) dg) } /*************************************** - * Calls dg(Dsymbol *sym) for each Dsymbol. + * Calls dg(Dsymbol* sym) for each Dsymbol. * Params: * symbols = Dsymbols * dg = delegate to call for each Dsymbol @@ -265,6 +266,76 @@ private struct DsymbolAttributes UserAttributeDeclaration userAttribDecl; } +enum DSYM : ubyte +{ + none, + dsymbol, + linkDeclaration, + cppMangleDeclaration, + alignDeclaration, + pragmaDeclaration, + conditionalDeclaration, + staticForeachDeclaration, + userAttributeDeclaration, + labelDsymbol, + aliasThis, + package_, + module_, + enumMember, + templateDeclaration, + templateInstance, + templateMixin, + forwardingAttribDeclaration, + nspace, + declaration, + storageClassDeclaration, + expressionDsymbol, + aliasAssign, + thisDeclaration, + bitFieldDeclaration, + typeInfoDeclaration, + tupleDeclaration, + aliasDeclaration, + aggregateDeclaration, + funcDeclaration, + funcAliasDeclaration, + overDeclaration, + funcLiteralDeclaration, + ctorDeclaration, + postBlitDeclaration, + dtorDeclaration, + staticCtorDeclaration, + staticDtorDeclaration, + sharedStaticCtorDeclaration, + sharedStaticDtorDeclaration, + invariantDeclaration, + unitTestDeclaration, + newDeclaration, + varDeclaration, + versionSymbol, + debugSymbol, + classDeclaration, + structDeclaration, + unionDeclaration, + interfaceDeclaration, + scopeDsymbol, + forwardingScopeDsymbol, + withScopeSymbol, + arrayScopeSymbol, + import_, + enumDeclaration, + symbolDeclaration, + attribDeclaration, + anonDeclaration, + cppNamespaceDeclaration, + visibilityDeclaration, + overloadSet, + mixinDeclaration, + staticAssert, + staticIfDeclaration, + cAsmDeclaration +} + /*********************************************************** */ extern (C++) class Dsymbol : ASTNode @@ -280,28 +351,35 @@ else { Symbol* csym; // symbol for code generator } - const Loc loc; // where defined Scope* _scope; // !=null means context to use for semantic() - const(char)* prettystring; // cached value of toPrettyChars() private DsymbolAttributes* atts; /// attached attribute declarations - bool errors; // this symbol failed to pass semantic() - PASS semanticRun = PASS.initial; + const Loc loc; // where defined ushort localNum; /// perturb mangled name to avoid collisions with those in FuncDeclaration.localsymtab + static struct BitFields + { + bool errors; // this symbol failed to pass semantic() + PASS semanticRun = PASS.initial; + } + import dmd.common.bitfields; + mixin(generateBitFields!(BitFields, ubyte)); + DSYM dsym; - final extern (D) this() nothrow @safe + final extern (D) this(DSYM tag) nothrow @safe { //printf("Dsymbol::Dsymbol(%p)\n", this); - loc = Loc(null, 0, 0); + this.dsym = tag; + loc = Loc.initial; version (IN_LLVM) { this.ir = newIrDsymbol(); } } - final extern (D) this(Identifier ident) nothrow @safe + final extern (D) this(DSYM tag, Identifier ident) nothrow @safe { //printf("Dsymbol::Dsymbol(%p, ident)\n", this); - this.loc = Loc(null, 0, 0); + this.dsym = tag; + this.loc = Loc.initial; this.ident = ident; version (IN_LLVM) { @@ -309,9 +387,10 @@ version (IN_LLVM) } } - final extern (D) this(const ref Loc loc, Identifier ident) nothrow @safe + final extern (D) this(DSYM tag, Loc loc, Identifier ident) nothrow @safe { //printf("Dsymbol::Dsymbol(%p, ident)\n", this); + this.dsym = tag; this.loc = loc; this.ident = ident; version (IN_LLVM) @@ -331,12 +410,13 @@ version (IN_LLVM) static Dsymbol create(Identifier ident) nothrow @safe { - return new Dsymbol(ident); + return new Dsymbol(DSYM.dsymbol, ident); } - override const(char)* toChars() const + final override const(char)* toChars() const { - return ident ? ident.toChars() : "__anonymous"; + import dmd.hdrgen : toChars; + return toChars(this); } // Getters / setters for fields stored in `DsymbolAttributes` @@ -381,19 +461,6 @@ version (IN_LLVM) return toChars(); } - final const(Loc) getLoc() - { - if (!loc.isValid()) // avoid bug 5861. - if (const m = getModule()) - return Loc(m.srcfile.toChars(), 0, 0); - return loc; - } - - final const(char)* locToChars() - { - return getLoc().toChars(); - } - override bool equals(const RootObject o) const { if (this == o) @@ -432,8 +499,7 @@ version (IN_LLVM) while (s) { //printf("\ts = %s '%s'\n", s.kind(), s.toPrettyChars()); - Module m = s.isModule(); - if (m) + if (Module m = s.isModule()) return m; s = s.parent; } @@ -464,8 +530,7 @@ version (IN_LLVM) while (s) { //printf("\ts = %s '%s'\n", s.kind(), s.toPrettyChars()); - Module m = s.isModule(); - if (m) + if (Module m = s.isModule()) return m; TemplateInstance ti = s.isTemplateInstance(); if (ti && ti.enclosing) @@ -629,7 +694,7 @@ version (IN_LLVM) continue; if (sa == p1) return true; - else if (p2 && sa == p2) + if (p2 && sa == p2) return true; } outer = ti.tempdecl.toParent(); @@ -657,7 +722,7 @@ version (IN_LLVM) final Ungag ungagSpeculative() const { - uint oldgag = global.gag; + const oldgag = global.gag; if (global.gag && !isSpeculative() && !toParent2().isFuncDeclaration()) global.gag = 0; return Ungag(oldgag); @@ -693,15 +758,10 @@ version (IN_LLVM) const(char)* toPrettyChars(bool QualifyTypes = false) { - if (prettystring && !QualifyTypes) - return prettystring; // value cached for speed - //printf("Dsymbol::toPrettyChars() '%s'\n", toChars()); if (!parent) { auto s = toChars(); - if (!QualifyTypes) - prettystring = s; return s; } @@ -721,8 +781,6 @@ version (IN_LLVM) addQualifiers(this); auto s = buf.extractSlice(true).ptr; - if (!QualifyTypes) - prettystring = s; return s; } @@ -759,7 +817,7 @@ version (IN_LLVM) * Returns: * SIZE_INVALID when the size cannot be determined */ - uinteger_t size(const ref Loc loc) + uinteger_t size(Loc loc) { .error(loc, "%s `%s` symbol `%s` has no size", kind, toPrettyChars, toChars()); return SIZE_INVALID; @@ -878,20 +936,6 @@ version (IN_LLVM) assert(0); } - /************************************** - * Determine if this symbol is only one. - * Returns: - * false, ps = null: There are 2 or more symbols - * true, ps = null: There are zero symbols - * true, ps = symbol: The one and only one symbol - */ - bool oneMember(out Dsymbol ps, Identifier ident) - { - //printf("Dsymbol::oneMember()\n"); - ps = this; - return true; - } - /***************************************** * Same as Dsymbol::oneMember(), but look at an array of Dsymbols. */ @@ -908,7 +952,7 @@ version (IN_LLVM) for (size_t i = 0; i < members.length; i++) { Dsymbol sx = (*members)[i]; - bool x = sx.oneMember(ps, ident); + bool x = sx.oneMember(ps, ident); //MYTODO: this temporarily creates a new dependency to dsymbolsem, will need to extract oneMembers() later //printf("\t[%d] kind %s = %d, s = %p\n", i, sx.kind(), x, *ps); if (!x) { @@ -964,42 +1008,18 @@ version (IN_LLVM) return false; } - bool hasStaticCtorOrDtor() - { - //printf("Dsymbol::hasStaticCtorOrDtor() %s\n", toChars()); - return false; - } - void addObjcSymbols(ClassDeclarations* classes, ClassDeclarations* categories) { } - void checkCtorConstInit() - { - } - /**************************************** * Add documentation comment to Dsymbol. * Ignore NULL comments. */ void addComment(const(char)* comment) { - if (!comment || !*comment) - return; - - //printf("addComment '%s' to Dsymbol %p '%s'\n", comment, this, toChars()); - void* h = cast(void*)this; // just the pointer is the key - auto p = h in commentHashTable; - if (!p) - { - commentHashTable[h] = comment; - return; - } - if (strcmp(*p, comment) != 0) - { - // Concatenate the two - *p = Lexer.combineComments((*p).toDString(), comment.toDString(), true); - } + import dmd.dsymbolsem; + dmd.dsymbolsem.addComment(this, comment); } /// get documentation comment for this Dsymbol @@ -1017,7 +1037,7 @@ version (IN_LLVM) /* Shell around addComment() to avoid disruption for the moment */ final void comment(const(char)* comment) { addComment(comment); } - private extern (D) __gshared const(char)*[void*] commentHashTable; + extern (D) __gshared const(char)*[void*] commentHashTable; /********************************** @@ -1084,64 +1104,181 @@ version (IN_LLVM) v.visit(this); } - pure nothrow @safe @nogc: - - // Eliminate need for dynamic_cast - inout(Package) isPackage() inout { return null; } - inout(Module) isModule() inout { return null; } - inout(EnumMember) isEnumMember() inout { return null; } - inout(TemplateDeclaration) isTemplateDeclaration() inout { return null; } - inout(TemplateInstance) isTemplateInstance() inout { return null; } - inout(TemplateMixin) isTemplateMixin() inout { return null; } - inout(ForwardingAttribDeclaration) isForwardingAttribDeclaration() inout { return null; } - inout(Nspace) isNspace() inout { return null; } - inout(Declaration) isDeclaration() inout { return null; } - inout(StorageClassDeclaration) isStorageClassDeclaration() inout { return null; } - inout(ExpressionDsymbol) isExpressionDsymbol() inout { return null; } - inout(AliasAssign) isAliasAssign() inout { return null; } - inout(ThisDeclaration) isThisDeclaration() inout { return null; } - inout(BitFieldDeclaration) isBitFieldDeclaration() inout { return null; } - inout(TypeInfoDeclaration) isTypeInfoDeclaration() inout { return null; } - inout(TupleDeclaration) isTupleDeclaration() inout { return null; } - inout(AliasDeclaration) isAliasDeclaration() inout { return null; } - inout(AggregateDeclaration) isAggregateDeclaration() inout { return null; } - inout(FuncDeclaration) isFuncDeclaration() inout { return null; } - inout(FuncAliasDeclaration) isFuncAliasDeclaration() inout { return null; } - inout(OverDeclaration) isOverDeclaration() inout { return null; } - inout(FuncLiteralDeclaration) isFuncLiteralDeclaration() inout { return null; } - inout(CtorDeclaration) isCtorDeclaration() inout { return null; } - inout(PostBlitDeclaration) isPostBlitDeclaration() inout { return null; } - inout(DtorDeclaration) isDtorDeclaration() inout { return null; } - inout(StaticCtorDeclaration) isStaticCtorDeclaration() inout { return null; } - inout(StaticDtorDeclaration) isStaticDtorDeclaration() inout { return null; } - inout(SharedStaticCtorDeclaration) isSharedStaticCtorDeclaration() inout { return null; } - inout(SharedStaticDtorDeclaration) isSharedStaticDtorDeclaration() inout { return null; } - inout(InvariantDeclaration) isInvariantDeclaration() inout { return null; } - inout(UnitTestDeclaration) isUnitTestDeclaration() inout { return null; } - inout(NewDeclaration) isNewDeclaration() inout { return null; } - inout(VarDeclaration) isVarDeclaration() inout { return null; } - inout(VersionSymbol) isVersionSymbol() inout { return null; } - inout(DebugSymbol) isDebugSymbol() inout { return null; } - inout(ClassDeclaration) isClassDeclaration() inout { return null; } - inout(StructDeclaration) isStructDeclaration() inout { return null; } - inout(UnionDeclaration) isUnionDeclaration() inout { return null; } - inout(InterfaceDeclaration) isInterfaceDeclaration() inout { return null; } - inout(ScopeDsymbol) isScopeDsymbol() inout { return null; } - inout(ForwardingScopeDsymbol) isForwardingScopeDsymbol() inout { return null; } - inout(WithScopeSymbol) isWithScopeSymbol() inout { return null; } - inout(ArrayScopeSymbol) isArrayScopeSymbol() inout { return null; } - inout(Import) isImport() inout { return null; } - inout(EnumDeclaration) isEnumDeclaration() inout { return null; } - inout(SymbolDeclaration) isSymbolDeclaration() inout { return null; } - inout(AttribDeclaration) isAttribDeclaration() inout { return null; } - inout(AnonDeclaration) isAnonDeclaration() inout { return null; } - inout(CPPNamespaceDeclaration) isCPPNamespaceDeclaration() inout { return null; } - inout(VisibilityDeclaration) isVisibilityDeclaration() inout { return null; } - inout(OverloadSet) isOverloadSet() inout { return null; } - inout(MixinDeclaration) isMixinDeclaration() inout { return null; } - inout(StaticAssert) isStaticAssert() inout { return null; } - inout(StaticIfDeclaration) isStaticIfDeclaration() inout { return null; } - inout(CAsmDeclaration) isCAsmDeclaration() inout { return null; } + pure nothrow @trusted @nogc final: + + inout(Package) isPackage() inout { return (dsym == DSYM.package_ || dsym == DSYM.module_) ? cast(inout(Package)) cast(void*) this : null; } + inout(Module) isModule() inout { return dsym == DSYM.module_ ? cast(inout(Module)) cast(void*) this : null; } + inout(EnumMember) isEnumMember() inout { return dsym == DSYM.enumMember ? cast(inout(EnumMember)) cast(void*) this : null; } + inout(TemplateDeclaration) isTemplateDeclaration() inout { return dsym == DSYM.templateDeclaration ? cast(inout(TemplateDeclaration)) cast(void*) this : null; } + inout(TemplateInstance) isTemplateInstance() inout { return (dsym == DSYM.templateInstance || dsym == DSYM.templateMixin) ? cast(inout(TemplateInstance)) cast(void*) this : null; } + inout(TemplateMixin) isTemplateMixin() inout { return dsym == DSYM.templateMixin ? cast(inout(TemplateMixin)) cast(void*) this : null; } + inout(ForwardingAttribDeclaration) isForwardingAttribDeclaration() inout { return dsym == DSYM.forwardingAttribDeclaration ? cast(inout(ForwardingAttribDeclaration)) cast(void*) this : null; } + inout(Nspace) isNspace() inout { return dsym == DSYM.nspace ? cast(inout(Nspace)) cast(void*) this : null; } + inout(Declaration) isDeclaration() inout { + switch (dsym) + { + case DSYM.tupleDeclaration: + case DSYM.aliasDeclaration: + case DSYM.overDeclaration: + case DSYM.varDeclaration: + case DSYM.bitFieldDeclaration: + case DSYM.typeInfoDeclaration: + case DSYM.thisDeclaration: + case DSYM.enumMember: + case DSYM.symbolDeclaration: + case DSYM.funcDeclaration: + case DSYM.funcAliasDeclaration: + case DSYM.funcLiteralDeclaration: + case DSYM.ctorDeclaration: + case DSYM.postBlitDeclaration: + case DSYM.dtorDeclaration: + case DSYM.staticCtorDeclaration: + case DSYM.sharedStaticCtorDeclaration: + case DSYM.staticDtorDeclaration: + case DSYM.sharedStaticDtorDeclaration: + case DSYM.invariantDeclaration: + case DSYM.unitTestDeclaration: + case DSYM.newDeclaration: + return cast(inout(Declaration)) cast(void*) this; + default: + return null; + } + } + inout(StorageClassDeclaration) isStorageClassDeclaration() inout { return dsym == DSYM.storageClassDeclaration ? cast(inout(StorageClassDeclaration)) cast(void*) this : null; } + inout(ExpressionDsymbol) isExpressionDsymbol() inout { return dsym == DSYM.expressionDsymbol ? cast(inout(ExpressionDsymbol)) cast(void*) this : null; } + inout(AliasAssign) isAliasAssign() inout { return dsym == DSYM.aliasAssign ? cast(inout(AliasAssign)) cast(void*) this : null; } + inout(ThisDeclaration) isThisDeclaration() inout { return dsym == DSYM.thisDeclaration ? cast(inout(ThisDeclaration)) cast(void*) this : null; } + inout(BitFieldDeclaration) isBitFieldDeclaration() inout { return dsym == DSYM.bitFieldDeclaration ? cast(inout(BitFieldDeclaration)) cast(void*) this : null; } + inout(TypeInfoDeclaration) isTypeInfoDeclaration() inout { return dsym == DSYM.typeInfoDeclaration ? cast(inout(TypeInfoDeclaration)) cast(void*) this : null; } + inout(TupleDeclaration) isTupleDeclaration() inout { return dsym == DSYM.tupleDeclaration ? cast(inout(TupleDeclaration)) cast(void*) this : null; } + inout(AliasDeclaration) isAliasDeclaration() inout { return dsym == DSYM.aliasDeclaration ? cast(inout(AliasDeclaration)) cast(void*) this : null; } + inout(AggregateDeclaration) isAggregateDeclaration() inout { + switch (dsym) + { + case DSYM.aggregateDeclaration: + case DSYM.structDeclaration: + case DSYM.unionDeclaration: + case DSYM.classDeclaration: + case DSYM.interfaceDeclaration: + return cast(inout(AggregateDeclaration)) cast(void*) this; + default: + return null; + } + } + inout(FuncDeclaration) isFuncDeclaration() inout { + switch (dsym) + { + case DSYM.funcDeclaration: + case DSYM.funcAliasDeclaration: + case DSYM.funcLiteralDeclaration: + case DSYM.ctorDeclaration: + case DSYM.postBlitDeclaration: + case DSYM.dtorDeclaration: + case DSYM.staticCtorDeclaration: + case DSYM.sharedStaticCtorDeclaration: + case DSYM.staticDtorDeclaration: + case DSYM.sharedStaticDtorDeclaration: + case DSYM.invariantDeclaration: + case DSYM.unitTestDeclaration: + case DSYM.newDeclaration: + return cast(inout(FuncDeclaration)) cast(void*) this; + default: + return null; + } + } + inout(FuncAliasDeclaration) isFuncAliasDeclaration() inout { return dsym == DSYM.funcAliasDeclaration ? cast(inout(FuncAliasDeclaration)) cast(void*) this : null; } + inout(OverDeclaration) isOverDeclaration() inout { return dsym == DSYM.overDeclaration ? cast(inout(OverDeclaration)) cast(void*) this : null; } + inout(FuncLiteralDeclaration) isFuncLiteralDeclaration() inout { return dsym == DSYM.funcLiteralDeclaration ? cast(inout(FuncLiteralDeclaration)) cast(void*) this : null; } + inout(CtorDeclaration) isCtorDeclaration() inout { return dsym == DSYM.ctorDeclaration ? cast(inout(CtorDeclaration)) cast(void*) this : null; } + inout(PostBlitDeclaration) isPostBlitDeclaration() inout { return dsym == DSYM.postBlitDeclaration ? cast(inout(PostBlitDeclaration)) cast(void*) this : null; } + inout(DtorDeclaration) isDtorDeclaration() inout { return dsym == DSYM.dtorDeclaration ? cast(inout(DtorDeclaration)) cast(void*) this : null; } + inout(StaticCtorDeclaration) isStaticCtorDeclaration() inout { return (dsym == DSYM.staticCtorDeclaration || dsym == DSYM.sharedStaticCtorDeclaration) ? cast(inout(StaticCtorDeclaration)) cast(void*) this : null; } + inout(StaticDtorDeclaration) isStaticDtorDeclaration() inout { return (dsym == DSYM.staticDtorDeclaration || dsym == DSYM.sharedStaticDtorDeclaration) ? cast(inout(StaticDtorDeclaration)) cast(void*) this : null; } + inout(SharedStaticCtorDeclaration) isSharedStaticCtorDeclaration() inout { return dsym == DSYM.sharedStaticCtorDeclaration ? cast(inout(SharedStaticCtorDeclaration)) cast(void*) this : null; } + inout(SharedStaticDtorDeclaration) isSharedStaticDtorDeclaration() inout { return dsym == DSYM.sharedStaticDtorDeclaration ? cast(inout(SharedStaticDtorDeclaration)) cast(void*) this : null; } + inout(InvariantDeclaration) isInvariantDeclaration() inout { return dsym == DSYM.invariantDeclaration ? cast(inout(InvariantDeclaration)) cast(void*) this : null; } + inout(UnitTestDeclaration) isUnitTestDeclaration() inout { return dsym == DSYM.unitTestDeclaration ? cast(inout(UnitTestDeclaration)) cast(void*) this : null; } + inout(NewDeclaration) isNewDeclaration() inout { return dsym == DSYM.newDeclaration ? cast(inout(NewDeclaration)) cast(void*) this : null; } + inout(VarDeclaration) isVarDeclaration() inout { + switch (dsym) + { + case DSYM.varDeclaration: + case DSYM.bitFieldDeclaration: + case DSYM.typeInfoDeclaration: + case DSYM.thisDeclaration: + case DSYM.enumMember: + return cast(inout(VarDeclaration)) cast(void*) this; + default: + return null; + } + } + inout(VersionSymbol) isVersionSymbol() inout { return dsym == DSYM.versionSymbol ? cast(inout(VersionSymbol)) cast(void*) this : null; } + inout(DebugSymbol) isDebugSymbol() inout { return dsym == DSYM.debugSymbol ? cast(inout(DebugSymbol)) cast(void*) this : null; } + inout(ClassDeclaration) isClassDeclaration() inout { return (dsym == DSYM.classDeclaration || dsym == DSYM.interfaceDeclaration) ? cast(inout(ClassDeclaration)) cast(void*) this : null; } + inout(StructDeclaration) isStructDeclaration() inout { return (dsym == DSYM.structDeclaration || dsym == DSYM.unionDeclaration) ? cast(inout(StructDeclaration)) cast(void*) this : null; } + inout(UnionDeclaration) isUnionDeclaration() inout { return dsym == DSYM.unionDeclaration ? cast(inout(UnionDeclaration)) cast(void*) this : null; } + inout(InterfaceDeclaration) isInterfaceDeclaration() inout { return dsym == DSYM.interfaceDeclaration ? cast(inout(InterfaceDeclaration)) cast(void*) this : null; } + inout(ScopeDsymbol) isScopeDsymbol() inout { + switch (dsym) + { + case DSYM.enumDeclaration: + case DSYM.scopeDsymbol: + case DSYM.package_: + case DSYM.module_: + case DSYM.nspace: + case DSYM.templateInstance: + case DSYM.templateMixin: + case DSYM.templateDeclaration: + case DSYM.aggregateDeclaration: + case DSYM.structDeclaration: + case DSYM.unionDeclaration: + case DSYM.classDeclaration: + case DSYM.interfaceDeclaration: + case DSYM.withScopeSymbol: + case DSYM.arrayScopeSymbol: + case DSYM.forwardingScopeDsymbol: + return cast(inout(ScopeDsymbol)) cast(void*) this; + default: + return null; + } + } + inout(ForwardingScopeDsymbol) isForwardingScopeDsymbol() inout { return dsym == DSYM.forwardingScopeDsymbol ? cast(inout(ForwardingScopeDsymbol)) cast(void*) this : null; } + inout(WithScopeSymbol) isWithScopeSymbol() inout { return dsym == DSYM.withScopeSymbol ? cast(inout(WithScopeSymbol)) cast(void*) this : null; } + inout(ArrayScopeSymbol) isArrayScopeSymbol() inout { return dsym == DSYM.arrayScopeSymbol ? cast(inout(ArrayScopeSymbol)) cast(void*) this : null; } + inout(Import) isImport() inout { return dsym == DSYM.import_ ? cast(inout(Import)) cast(void*) this : null; } + inout(EnumDeclaration) isEnumDeclaration() inout { return dsym == DSYM.enumDeclaration ? cast(inout(EnumDeclaration)) cast(void*) this : null; } + inout(SymbolDeclaration) isSymbolDeclaration() inout { return dsym == DSYM.symbolDeclaration ? cast(inout(SymbolDeclaration)) cast(void*) this : null; } + inout(AttribDeclaration) isAttribDeclaration() inout { + switch (dsym) + { + case DSYM.attribDeclaration: + case DSYM.storageClassDeclaration: + case DSYM.linkDeclaration: + case DSYM.cppMangleDeclaration: + case DSYM.cppNamespaceDeclaration: + case DSYM.visibilityDeclaration: + case DSYM.alignDeclaration: + case DSYM.anonDeclaration: + case DSYM.pragmaDeclaration: + case DSYM.conditionalDeclaration: + case DSYM.staticIfDeclaration: + case DSYM.staticForeachDeclaration: + case DSYM.forwardingAttribDeclaration: + case DSYM.mixinDeclaration: + case DSYM.userAttributeDeclaration: + return cast(inout(AttribDeclaration)) cast(void*) this; + default: + return null; + } + } + inout(AnonDeclaration) isAnonDeclaration() inout { return dsym == DSYM.anonDeclaration ? cast(inout(AnonDeclaration)) cast(void*) this : null; } + inout(CPPNamespaceDeclaration) isCPPNamespaceDeclaration() inout { return dsym == DSYM.cppNamespaceDeclaration ? cast(inout(CPPNamespaceDeclaration)) cast(void*) this : null; } + inout(VisibilityDeclaration) isVisibilityDeclaration() inout { return dsym == DSYM.visibilityDeclaration ? cast(inout(VisibilityDeclaration)) cast(void*) this : null; } + inout(OverloadSet) isOverloadSet() inout { return dsym == DSYM.overloadSet ? cast(inout(OverloadSet)) cast(void*) this : null; } + inout(MixinDeclaration) isMixinDeclaration() inout { return dsym == DSYM.mixinDeclaration ? cast(inout(MixinDeclaration)) cast(void*) this : null; } + inout(StaticAssert) isStaticAssert() inout { return dsym == DSYM.staticAssert ? cast(inout(StaticAssert)) cast(void*) this : null; } + inout(StaticIfDeclaration) isStaticIfDeclaration() inout { return dsym == DSYM.staticIfDeclaration ? cast(inout(StaticIfDeclaration)) cast(void*) this : null; } + inout(CAsmDeclaration) isCAsmDeclaration() inout { return dsym == DSYM.cAsmDeclaration ? cast(inout(CAsmDeclaration)) cast(void*) this : null; } } /*********************************************************** @@ -1164,16 +1301,17 @@ private: public: final extern (D) this() nothrow @safe { + super(DSYM.scopeDsymbol); } final extern (D) this(Identifier ident) nothrow @safe { - super(ident); + super(DSYM.scopeDsymbol, ident); } - final extern (D) this(const ref Loc loc, Identifier ident) nothrow @safe + final extern (D) this(Loc loc, Identifier ident) nothrow @safe { - super(loc, ident); + super(DSYM.scopeDsymbol, loc, ident); } override ScopeDsymbol syntaxCopy(Dsymbol s) @@ -1313,7 +1451,7 @@ public: return (members is null); } - static void multiplyDefined(const ref Loc loc, Dsymbol s1, Dsymbol s2) + static void multiplyDefined(Loc loc, Dsymbol s1, Dsymbol s2) { version (none) { @@ -1349,7 +1487,7 @@ public: } else { - .error(s1.loc, "%s `%s` conflicts with %s `%s` at %s", s1.kind, s1.toPrettyChars, s2.kind(), s2.toPrettyChars(), s2.locToChars()); + .error(s1.loc, "%s `%s` conflicts with %s `%s` at %s", s1.kind, s1.toPrettyChars, s2.kind(), s2.toPrettyChars(), s2.loc.toChars()); } } @@ -1383,29 +1521,6 @@ public: return symtab.lookup(id); } - /**************************************** - * Return true if any of the members are static ctors or static dtors, or if - * any members have members that are. - */ - override bool hasStaticCtorOrDtor() - { - if (members) - { - for (size_t i = 0; i < members.length; i++) - { - Dsymbol member = (*members)[i]; - if (member.hasStaticCtorOrDtor()) - return true; - } - } - return false; - } - - override final inout(ScopeDsymbol) isScopeDsymbol() inout - { - return this; - } - override void accept(Visitor v) { v.visit(this); @@ -1422,11 +1537,7 @@ extern (C++) final class WithScopeSymbol : ScopeDsymbol extern (D) this(WithStatement withstate) nothrow @safe { this.withstate = withstate; - } - - override inout(WithScopeSymbol) isWithScopeSymbol() inout - { - return this; + this.dsym = DSYM.withScopeSymbol; } override void accept(Visitor v) @@ -1450,6 +1561,7 @@ extern (C++) final class ArrayScopeSymbol : ScopeDsymbol assert(exp.op == EXP.index || exp.op == EXP.slice || exp.op == EXP.array); this._scope = sc; this.arrayContent = exp; + this.dsym = DSYM.arrayScopeSymbol; } extern (D) this(Scope* sc, TypeTuple type) nothrow @safe @@ -1464,11 +1576,6 @@ extern (C++) final class ArrayScopeSymbol : ScopeDsymbol this.arrayContent = td; } - override inout(ArrayScopeSymbol) isArrayScopeSymbol() inout - { - return this; - } - override void accept(Visitor v) { v.visit(this); @@ -1484,7 +1591,7 @@ extern (C++) final class OverloadSet : Dsymbol extern (D) this(Identifier ident, OverloadSet os = null) nothrow { - super(ident); + super(DSYM.overloadSet, ident); if (os) { a.pushSlice(os.a[]); @@ -1496,11 +1603,6 @@ extern (C++) final class OverloadSet : Dsymbol a.push(s); } - override inout(OverloadSet) isOverloadSet() inout - { - return this; - } - override const(char)* kind() const { return "overloadset"; @@ -1523,6 +1625,7 @@ extern (C++) final class ForwardingScopeDsymbol : ScopeDsymbol extern (D) this() nothrow @safe { super(); + this.dsym = DSYM.forwardingScopeDsymbol; } override Dsymbol symtabInsert(Dsymbol s) nothrow @@ -1591,11 +1694,6 @@ extern (C++) final class ForwardingScopeDsymbol : ScopeDsymbol override const(char)* kind()const{ return "local scope"; } - override inout(ForwardingScopeDsymbol) isForwardingScopeDsymbol() inout nothrow - { - return this; - } - } /** @@ -1608,14 +1706,9 @@ extern (C++) final class ExpressionDsymbol : Dsymbol Expression exp; this(Expression exp) nothrow @safe { - super(); + super(DSYM.expressionDsymbol); this.exp = exp; } - - override inout(ExpressionDsymbol) isExpressionDsymbol() inout nothrow - { - return this; - } } /********************************************** @@ -1631,9 +1724,9 @@ extern (C++) final class AliasAssign : Dsymbol Dsymbol aliassym; /// replace previous RHS of AliasDeclaration with `aliassym` /// only one of type and aliassym can be != null - extern (D) this(const ref Loc loc, Identifier ident, Type type, Dsymbol aliassym) nothrow @safe + extern (D) this(Loc loc, Identifier ident, Type type, Dsymbol aliassym) nothrow @safe { - super(loc, null); + super(DSYM.aliasAssign, loc, null); this.ident = ident; this.type = type; this.aliassym = aliassym; @@ -1648,11 +1741,6 @@ extern (C++) final class AliasAssign : Dsymbol return aa; } - override inout(AliasAssign) isAliasAssign() inout - { - return this; - } - override const(char)* kind() const { return "alias assignment"; @@ -1745,15 +1833,10 @@ extern (C++) final class CAsmDeclaration : Dsymbol Expression code; extern (D) this(Expression e) nothrow @safe { - super(); + super(DSYM.cAsmDeclaration); this.code = e; } - override inout(CAsmDeclaration) isCAsmDeclaration() inout nothrow - { - return this; - } - override void accept(Visitor v) { v.visit(this); diff --git a/dmd/dsymbol.h b/dmd/dsymbol.h index 05024a0811..51511c1449 100644 --- a/dmd/dsymbol.h +++ b/dmd/dsymbol.h @@ -1,6 +1,6 @@ /* Compiler implementation of the D programming language - * Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * written by Walter Bright * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. @@ -141,20 +141,6 @@ enum class PASS : uint8_t obj // toObjFile() run }; -enum -{ - PASSinit, // initial state - PASSsemantic, // semantic() started - PASSsemanticdone, // semantic() done - PASSsemantic2, // semantic2() started - PASSsemantic2done, // semantic2() done - PASSsemantic3, // semantic3() started - PASSsemantic3done, // semantic3() done - PASSinline, // inline started - PASSinlinedone, // inline done - PASSobj // toObjFile() run -}; - /* Flags for symbol search */ typedef unsigned SearchOptFlags; @@ -199,17 +185,22 @@ class Dsymbol : public ASTNode #else Symbol *csym; // symbol for code generator #endif - Loc loc; // where defined Scope *_scope; // !=NULL means context to use for semantic() - const utf8_t *prettystring; private: DsymbolAttributes* atts; public: - d_bool errors; // this symbol failed to pass semantic() - PASS semanticRun; + Loc loc; // where defined unsigned short localNum; // perturb mangled name to avoid collisions with those in FuncDeclaration.localsymtab + + bool errors() const; + PASS semanticRun() const; + PASS semanticRun(PASS v); +private: + unsigned char bitfields; + unsigned char dsym; +public: static Dsymbol *create(Identifier *); - const char *toChars() const override; + const char *toChars() const final override; DeprecatedDeclaration* depdecl(); CPPNamespaceDeclaration* cppnamespace(); UserAttributeDeclaration* userAttribDecl(); @@ -217,8 +208,6 @@ class Dsymbol : public ASTNode CPPNamespaceDeclaration* cppnamespace(CPPNamespaceDeclaration* ns); UserAttributeDeclaration* userAttribDecl(UserAttributeDeclaration* uad); virtual const char *toPrettyCharsHelper(); // helper to print fully qualified (template) arguments - Loc getLoc(); - const char *locToChars(); bool equals(const RootObject * const o) const override; bool isAnonymous() const; Module *getModule(); @@ -244,7 +233,7 @@ class Dsymbol : public ASTNode virtual Dsymbol *toAlias(); // resolve real symbol virtual Dsymbol *toAlias2(); virtual bool overloadInsert(Dsymbol *s); - virtual uinteger_t size(const Loc &loc); + 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? @@ -261,11 +250,8 @@ class Dsymbol : public ASTNode virtual bool needThis(); // need a 'this' pointer? virtual Visibility visible(); virtual Dsymbol *syntaxCopy(Dsymbol *s); // copy only syntax trees - virtual bool oneMember(Dsymbol *&ps, Identifier *ident); virtual bool hasPointers(); - virtual bool hasStaticCtorOrDtor(); virtual void addObjcSymbols(ClassDeclarations *, ClassDeclarations *) { } - virtual void checkCtorConstInit() { } virtual void addComment(const utf8_t *comment); const utf8_t *comment(); // current value of comment @@ -276,61 +262,61 @@ class Dsymbol : public ASTNode bool inNonRoot(); // Eliminate need for dynamic_cast - virtual Package *isPackage() { return nullptr; } - virtual Module *isModule() { return nullptr; } - virtual EnumMember *isEnumMember() { return nullptr; } - virtual TemplateDeclaration *isTemplateDeclaration() { return nullptr; } - virtual TemplateInstance *isTemplateInstance() { return nullptr; } - virtual TemplateMixin *isTemplateMixin() { return nullptr; } - virtual ForwardingAttribDeclaration *isForwardingAttribDeclaration() { return nullptr; } - virtual Nspace *isNspace() { return nullptr; } - virtual Declaration *isDeclaration() { return nullptr; } - virtual StorageClassDeclaration *isStorageClassDeclaration(){ return nullptr; } - virtual ExpressionDsymbol *isExpressionDsymbol() { return nullptr; } - virtual AliasAssign *isAliasAssign() { return nullptr; } - virtual ThisDeclaration *isThisDeclaration() { return nullptr; } - virtual BitFieldDeclaration *isBitFieldDeclaration() { return nullptr; } - virtual TypeInfoDeclaration *isTypeInfoDeclaration() { return nullptr; } - virtual TupleDeclaration *isTupleDeclaration() { return nullptr; } - virtual AliasDeclaration *isAliasDeclaration() { return nullptr; } - virtual AggregateDeclaration *isAggregateDeclaration() { return nullptr; } - virtual FuncDeclaration *isFuncDeclaration() { return nullptr; } - virtual FuncAliasDeclaration *isFuncAliasDeclaration() { return nullptr; } - virtual OverDeclaration *isOverDeclaration() { return nullptr; } - virtual FuncLiteralDeclaration *isFuncLiteralDeclaration() { return nullptr; } - virtual CtorDeclaration *isCtorDeclaration() { return nullptr; } - virtual PostBlitDeclaration *isPostBlitDeclaration() { return nullptr; } - virtual DtorDeclaration *isDtorDeclaration() { return nullptr; } - virtual StaticCtorDeclaration *isStaticCtorDeclaration() { return nullptr; } - virtual StaticDtorDeclaration *isStaticDtorDeclaration() { return nullptr; } - virtual SharedStaticCtorDeclaration *isSharedStaticCtorDeclaration() { return nullptr; } - virtual SharedStaticDtorDeclaration *isSharedStaticDtorDeclaration() { return nullptr; } - virtual InvariantDeclaration *isInvariantDeclaration() { return nullptr; } - virtual UnitTestDeclaration *isUnitTestDeclaration() { return nullptr; } - virtual NewDeclaration *isNewDeclaration() { return nullptr; } - virtual VarDeclaration *isVarDeclaration() { return nullptr; } - virtual VersionSymbol *isVersionSymbol() { return nullptr; } - virtual DebugSymbol *isDebugSymbol() { return nullptr; } - virtual ClassDeclaration *isClassDeclaration() { return nullptr; } - virtual StructDeclaration *isStructDeclaration() { return nullptr; } - virtual UnionDeclaration *isUnionDeclaration() { return nullptr; } - virtual InterfaceDeclaration *isInterfaceDeclaration() { return nullptr; } - virtual ScopeDsymbol *isScopeDsymbol() { return nullptr; } - virtual ForwardingScopeDsymbol *isForwardingScopeDsymbol() { return nullptr; } - virtual WithScopeSymbol *isWithScopeSymbol() { return nullptr; } - virtual ArrayScopeSymbol *isArrayScopeSymbol() { return nullptr; } - virtual Import *isImport() { return nullptr; } - virtual EnumDeclaration *isEnumDeclaration() { return nullptr; } - virtual SymbolDeclaration *isSymbolDeclaration() { return nullptr; } - virtual AttribDeclaration *isAttribDeclaration() { return nullptr; } - virtual AnonDeclaration *isAnonDeclaration() { return nullptr; } - virtual CPPNamespaceDeclaration *isCPPNamespaceDeclaration() { return nullptr; } - virtual VisibilityDeclaration *isVisibilityDeclaration() { return nullptr; } - virtual OverloadSet *isOverloadSet() { return nullptr; } - virtual MixinDeclaration *isMixinDeclaration() { return nullptr; } - virtual StaticAssert *isStaticAssert() { return nullptr; } - virtual StaticIfDeclaration *isStaticIfDeclaration() { return nullptr; } - virtual CAsmDeclaration *isCAsmDeclaration() { return nullptr; } + Package *isPackage(); + Module *isModule(); + EnumMember *isEnumMember(); + TemplateDeclaration *isTemplateDeclaration(); + TemplateInstance *isTemplateInstance(); + TemplateMixin *isTemplateMixin(); + ForwardingAttribDeclaration *isForwardingAttribDeclaration(); + Nspace *isNspace(); + Declaration *isDeclaration(); + StorageClassDeclaration *isStorageClassDeclaration(); + ExpressionDsymbol *isExpressionDsymbol(); + AliasAssign *isAliasAssign(); + ThisDeclaration *isThisDeclaration(); + BitFieldDeclaration *isBitFieldDeclaration(); + TypeInfoDeclaration *isTypeInfoDeclaration(); + TupleDeclaration *isTupleDeclaration(); + AliasDeclaration *isAliasDeclaration(); + AggregateDeclaration *isAggregateDeclaration(); + FuncDeclaration *isFuncDeclaration(); + FuncAliasDeclaration *isFuncAliasDeclaration(); + OverDeclaration *isOverDeclaration(); + FuncLiteralDeclaration *isFuncLiteralDeclaration(); + CtorDeclaration *isCtorDeclaration(); + PostBlitDeclaration *isPostBlitDeclaration(); + DtorDeclaration *isDtorDeclaration(); + StaticCtorDeclaration *isStaticCtorDeclaration(); + StaticDtorDeclaration *isStaticDtorDeclaration(); + SharedStaticCtorDeclaration *isSharedStaticCtorDeclaration(); + SharedStaticDtorDeclaration *isSharedStaticDtorDeclaration(); + InvariantDeclaration *isInvariantDeclaration(); + UnitTestDeclaration *isUnitTestDeclaration(); + NewDeclaration *isNewDeclaration(); + VarDeclaration *isVarDeclaration(); + VersionSymbol *isVersionSymbol(); + DebugSymbol *isDebugSymbol(); + ClassDeclaration *isClassDeclaration(); + StructDeclaration *isStructDeclaration(); + UnionDeclaration *isUnionDeclaration(); + InterfaceDeclaration *isInterfaceDeclaration(); + ScopeDsymbol *isScopeDsymbol(); + ForwardingScopeDsymbol *isForwardingScopeDsymbol(); + WithScopeSymbol *isWithScopeSymbol(); + ArrayScopeSymbol *isArrayScopeSymbol(); + Import *isImport(); + EnumDeclaration *isEnumDeclaration(); + SymbolDeclaration *isSymbolDeclaration(); + AttribDeclaration *isAttribDeclaration(); + AnonDeclaration *isAnonDeclaration(); + CPPNamespaceDeclaration *isCPPNamespaceDeclaration(); + VisibilityDeclaration *isVisibilityDeclaration(); + OverloadSet *isOverloadSet(); + MixinDeclaration *isMixinDeclaration(); + StaticAssert *isStaticAssert(); + StaticIfDeclaration *isStaticIfDeclaration(); + CAsmDeclaration *isCAsmDeclaration(); void accept(Visitor *v) override { v->visit(this); } }; @@ -353,13 +339,11 @@ class ScopeDsymbol : public Dsymbol virtual void importScope(Dsymbol *s, Visibility visibility); virtual bool isPackageAccessible(Package *p, Visibility visibility, SearchOptFlags flags = (SearchOptFlags)SearchOpt::all); bool isforwardRef() override final; - static void multiplyDefined(const Loc &loc, Dsymbol *s1, Dsymbol *s2); + static void multiplyDefined(Loc loc, Dsymbol *s1, Dsymbol *s2); const char *kind() const override; virtual Dsymbol *symtabInsert(Dsymbol *s); virtual Dsymbol *symtabLookup(Dsymbol *s, Identifier *id); - bool hasStaticCtorOrDtor() override; - ScopeDsymbol *isScopeDsymbol() override final { return this; } void accept(Visitor *v) override { v->visit(this); } }; @@ -371,7 +355,6 @@ class WithScopeSymbol final : public ScopeDsymbol WithStatement *withstate; - WithScopeSymbol *isWithScopeSymbol() override { return this; } void accept(Visitor *v) override { v->visit(this); } }; @@ -382,7 +365,6 @@ class ArrayScopeSymbol final : public ScopeDsymbol public: RootObject *arrayContent; - ArrayScopeSymbol *isArrayScopeSymbol() override { return this; } void accept(Visitor *v) override { v->visit(this); } }; @@ -394,7 +376,6 @@ class OverloadSet final : public Dsymbol Dsymbols a; // array of Dsymbols void push(Dsymbol *s); - OverloadSet *isOverloadSet() override { return this; } const char *kind() const override; void accept(Visitor *v) override { v->visit(this); } }; @@ -409,7 +390,6 @@ class ForwardingScopeDsymbol final : public ScopeDsymbol void importScope(Dsymbol *s, Visibility visibility) override; const char *kind() const override; - ForwardingScopeDsymbol *isForwardingScopeDsymbol() override { return this; } }; class ExpressionDsymbol final : public Dsymbol @@ -417,7 +397,6 @@ class ExpressionDsymbol final : public Dsymbol public: Expression *exp; - ExpressionDsymbol *isExpressionDsymbol() override { return this; } }; class CAsmDeclaration final : public Dsymbol @@ -425,7 +404,6 @@ class CAsmDeclaration final : public Dsymbol public: Expression *code; // string expression - CAsmDeclaration *isCAsmDeclaration() override { return this; } void accept(Visitor *v) override { v->visit(this); } }; @@ -453,7 +431,11 @@ class DsymbolTable final : public RootObject namespace dmd { void addMember(Dsymbol *dsym, Scope *sc, ScopeDsymbol *sds); - Dsymbol *search(Dsymbol *d, const Loc &loc, Identifier *ident, SearchOptFlags flags = (SearchOptFlags)SearchOpt::localsOnly); + Dsymbol *search(Dsymbol *d, Loc loc, Identifier *ident, SearchOptFlags flags = (SearchOptFlags)SearchOpt::localsOnly); + Dsymbols *include(Dsymbol *d, Scope *sc); void setScope(Dsymbol *d, Scope *sc); void importAll(Dsymbol *d, Scope *sc); + void addComment(Dsymbol *d, const char *comment); + bool oneMember(Dsymbol *d, Dsymbol *&ps, Identifier *ident); + bool hasStaticCtorOrDtor(Dsymbol *d); } diff --git a/dmd/dsymbolsem.d b/dmd/dsymbolsem.d index 45b8f43c6b..439232c0dd 100644 --- a/dmd/dsymbolsem.d +++ b/dmd/dsymbolsem.d @@ -2,12 +2,12 @@ * Does the semantic 1 pass on the AST, which looks at symbol declarations but not initializers * or function bodies. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/dsymbolsem.d, _dsymbolsem.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/dsymbolsem.d, _dsymbolsem.d) * Documentation: https://dlang.org/phobos/dmd_dsymbolsem.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/dsymbolsem.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/dsymbolsem.d */ module dmd.dsymbolsem; @@ -24,10 +24,12 @@ import dmd.attrib; import dmd.attribsem; import dmd.clone; import dmd.cond; +import dmd.timetrace; import dmd.dcast; import dmd.dclass; import dmd.declaration; import dmd.denum; +import dmd.deps; import dmd.dimport; import dmd.dinterpret; import dmd.dmodule; @@ -51,6 +53,7 @@ import dmd.init; import dmd.initsem; import dmd.intrange; import dmd.hdrgen; +import dmd.lexer; import dmd.location; import dmd.mtype; import dmd.mustuse; @@ -59,11 +62,14 @@ 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.rootobject; +import dmd.safe; import dmd.semantic2; import dmd.semantic3; import dmd.sideeffect; @@ -87,20 +93,10 @@ enum LOG = false; * Does semantic analysis on the public face of declarations. */ void dsymbolSemantic(Dsymbol dsym, Scope* sc) -{ -version (IN_LLVM) -{ - import driver.timetrace_sema; - scope v = new DsymbolSemanticVisitor(sc); - scope vtimetrace = new SemanticTimeTraceVisitor!DsymbolSemanticVisitor(v); - dsym.accept(vtimetrace); -} -else { scope v = new DsymbolSemanticVisitor(sc); dsym.accept(v); } -} /*************************************************** * Determine the numerical value of the AlignmentDeclaration @@ -137,10 +133,10 @@ AlignDeclaration getAlignment(AlignDeclaration ad, Scope* sc) else { auto n = e.toInteger(); - if (sc.flags & SCOPE.Cfile && n == 0) // C11 6.7.5-6 allows 0 for alignment + 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()) + 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; @@ -178,7 +174,7 @@ const(char)* getMessage(DeprecatedDeclaration dd) return dd.msgstr; } -bool checkDeprecated(Dsymbol d, const ref Loc loc, Scope* sc) +bool checkDeprecated(Dsymbol d, Loc loc, Scope* sc) { if (global.params.useDeprecated == DiagnosticReporting.off) return false; @@ -189,7 +185,7 @@ bool checkDeprecated(Dsymbol d, const ref Loc loc, Scope* sc) return false; // Don't complain if we're inside a template constraint // https://issues.dlang.org/show_bug.cgi?id=21831 - if (sc.flags & SCOPE.constraint) + if (sc.inTemplateConstraint) return false; const(char)* message = null; @@ -215,7 +211,7 @@ bool checkDeprecated(Dsymbol d, const ref Loc loc, Scope* sc) /********************************* * Check type to see if it is based on a deprecated symbol. */ -private void checkDeprecated(Type type, const ref Loc loc, Scope* sc) +private void checkDeprecated(Type type, Loc loc, Scope* sc) { if (Dsymbol s = type.toDsymbol(sc)) { @@ -249,38 +245,58 @@ package bool allowsContractWithoutBody(FuncDeclaration funcdecl) } /* -Tests whether the `ctor` that is part of `ti` is an rvalue constructor -(i.e. a constructor that receives a single parameter of the same type as -`Unqual!typeof(this)`). If that is the case and `sd` contains a copy -constructor, than an error is issued. +If sd has a copy constructor and ctor is an rvalue constructor, +issue an error. Params: - sd = struct declaration that may contin both an rvalue and copy constructor - ctor = constructor that will be checked if it is an evalue constructor + 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 Return: - `false` if ctor is not an rvalue constructor or if `sd` does not contain a - copy constructor. `true` otherwise + `true` if sd has a copy constructor and ctor is an rvalue constructor */ bool checkHasBothRvalueAndCpCtor(StructDeclaration sd, CtorDeclaration ctor, TemplateInstance ti) { - auto loc = ctor.loc; - auto tf = cast(TypeFunction)ctor.type; - auto dim = tf.parameterList.length; - if (sd && sd.hasCopyCtor && (dim == 1 || (dim > 1 && tf.parameterList[1].defaultArg))) + //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()); + + return true; + } + + return false; +} + +/************************************************ + * 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()) { - .error(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()); - return true; } } - return false; } @@ -297,6 +313,7 @@ bool checkHasBothRvalueAndCpCtor(StructDeclaration sd, CtorDeclaration ctor, Tem */ 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;) { @@ -305,7 +322,7 @@ Expression resolveAliasThis(Scope* sc, Expression e, bool gag = false, bool find Loc loc = e.loc; Type tthis = (e.op == EXP.type ? e.type : null); const flags = cast(DotExpFlag) (DotExpFlag.noAliasThis | (gag * DotExpFlag.gag)); - uint olderrors = gag ? global.startGagging() : 0; + 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; @@ -379,7 +396,7 @@ Expression resolveAliasThis(Scope* sc, Expression e, bool gag = false, bool find * Returns: * Whether the alias this was reported as deprecated. */ -private bool checkDeprecatedAliasThis(AliasThis at, const ref Loc loc, Scope* sc) +private bool checkDeprecatedAliasThis(AliasThis at, Loc loc, Scope* sc) { if (global.params.useDeprecated != DiagnosticReporting.off && at.isDeprecated() && !sc.isDeprecated()) @@ -407,7 +424,7 @@ private bool checkDeprecatedAliasThis(AliasThis at, const ref Loc loc, Scope* sc } // Save the scope and defer semantic analysis on the Dsymbol. -void deferDsymbolSemantic(Scope* sc, Dsymbol s, Scope *scx) +void deferDsymbolSemantic(Scope* sc, Dsymbol s, Scope* scx) { s._scope = scx ? scx : sc.copy(); s._scope.setNoFree(); @@ -606,12 +623,31 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor } if (dsym.storage_class & STC.extern_ && dsym._init) - .error(dsym.loc, "%s `%s` extern symbols cannot have initializers", dsym.kind, dsym.toPrettyChars); + { + 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); + + } 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)) + { + .error(dsym.loc, "%s `%s` - `auto ref` variable must have `auto` and `ref` adjacent", dsym.kind, dsym.toChars()); + dsym.storage_class |= STC.autoref; + } + } + /* If auto type inference, do the inference */ int inferred = 0; @@ -624,12 +660,12 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor bool needctfe = (dsym.storage_class & (STC.manifest | STC.static_)) != 0 || !sc.func; if (needctfe) { - sc.flags |= SCOPE.condition; + 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.flags & SCOPE.Cfile) != 0).type; + dsym.type = dsym._init.initializerToExpression(null, sc.inCfile).type; if (needctfe) sc = sc.endCTFE(); @@ -681,7 +717,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor // Calculate type size + safety checks if (dsym.storage_class & STC.gshared && !dsym.isMember()) { - sc.setUnsafe(false, dsym.loc, "__gshared not allowed in safe functions; use shared"); + sc.setUnsafe(false, dsym.loc, "using `__gshared` instead of `shared`"); } Dsymbol parent = dsym.toParent(); @@ -729,7 +765,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor dsym.type = Type.terror; } } - if ((dsym.storage_class & STC.auto_) && !inferred) + 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 (auto tt = tb.isTypeTuple()) @@ -738,7 +774,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor * and add those. */ size_t nelems = Parameter.dim(tt.arguments); - Expression ie = (dsym._init && !dsym._init.isVoidInitializer()) ? dsym._init.initializerToExpression(null, (sc.flags & SCOPE.Cfile) != 0) : null; + Expression ie = (dsym._init && !dsym._init.isVoidInitializer()) ? dsym._init.initializerToExpression(null, sc.inCfile) : null; if (ie) ie = ie.expressionSemantic(sc); if (nelems > 0 && ie) @@ -776,7 +812,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor } else if (isAliasThisTuple(e)) { - auto v = copyToTemp(0, "__tup", e); + auto v = copyToTemp(STC.none, "__tup", e); v.dsymbolSemantic(sc); auto ve = new VarExp(dsym.loc, v); ve.type = e.type; @@ -860,7 +896,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor else ti = dsym._init ? dsym._init.syntaxCopy() : null; - StorageClass storage_class = STC.temp | dsym.storage_class; + 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); @@ -899,7 +935,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor else if (dsym.type.isWild()) dsym.storage_class |= STC.wild; - if (StorageClass stc = dsym.storage_class & (STC.synchronized_ | STC.override_ | STC.abstract_ | STC.final_)) + if (STC stc = dsym.storage_class & (STC.synchronized_ | STC.override_ | STC.abstract_ | STC.final_)) { if (stc == STC.final_) .error(dsym.loc, "%s `%s` cannot be `final`, perhaps you meant `const`?", dsym.kind, dsym.toPrettyChars); @@ -919,7 +955,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor if (dsym.storage_class & STC.scope_) { - StorageClass stc = dsym.storage_class & (STC.static_ | STC.extern_ | STC.manifest | STC.gshared); + STC stc = dsym.storage_class & (STC.static_ | STC.extern_ | STC.manifest | STC.gshared); if (stc) { OutBuffer buf; @@ -988,7 +1024,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor } // If it's a member template AggregateDeclaration ad2 = ti.tempdecl.isMember(); - if (ad2 && dsym.storage_class != STC.undefined_) + 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()); } @@ -1011,9 +1047,9 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor } } - if ((dsym.storage_class & (STC.ref_ | STC.parameter | STC.foreach_ | STC.temp | STC.result)) == STC.ref_ && dsym.ident != Id.This) + if ((dsym.storage_class & (STC.ref_ | STC.field)) == (STC.ref_ | STC.field) && dsym.ident != Id.This) { - .error(dsym.loc, "%s `%s` - only parameters, functions and `foreach` declarations can be `ref`", dsym.kind, dsym.toPrettyChars); + .error(dsym.loc, "%s `%s` - field declarations cannot be `ref`", dsym.kind, dsym.toPrettyChars); } if (dsym.type.hasWild()) @@ -1062,8 +1098,27 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor } } + 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) + { + 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); + } + } + FuncDeclaration fd = parent.isFuncDeclaration(); - if (dsym.type.isscope() && !(dsym.storage_class & STC.nodtor)) + 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) { @@ -1091,22 +1146,22 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor if (dsym.type.hasPointers()) // also computes type size sc.setUnsafe(false, dsym.loc, - "`void` initializers for pointers not allowed in safe functions"); + "`void` initializing a pointer"); else if (dsym.type.hasInvariant()) sc.setUnsafe(false, dsym.loc, - "`void` initializers for structs with invariants are not allowed in safe functions"); + "`void` initializing a struct with an invariant"); else if (dsym.type.toBasetype().ty == Tbool) sc.setUnsafePreview(global.params.systemVariables, false, dsym.loc, - "a `bool` must be 0 or 1, so void intializing it is not allowed in safe functions"); + "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` initializers for types with unsafe bit patterns are not allowed in safe functions"); + "`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 not allowed in safe functions"); + sc.setUnsafe(false, dsym.loc, "`void` initializers for pointers"); } } @@ -1132,7 +1187,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor bool isBlit = false; uinteger_t sz; - if (sc.flags & SCOPE.Cfile && !dsym._init) + if (sc.inCfile && !dsym._init) { addDefaultCInitializer(dsym); } @@ -1197,7 +1252,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor sc = sc.push(); sc.stc &= ~(STC.TYPECTOR | STC.pure_ | STC.nothrow_ | STC.nogc | STC.ref_ | STC.disable); - if (sc.flags & SCOPE.Cfile && + if (sc.inCfile && dsym.type.isTypeSArray() && dsym.type.isTypeSArray().isIncomplete() && dsym._init.isVoidInitializer() && @@ -1227,12 +1282,12 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor if (ai && tb.ty == Taarray) e = ai.toAssocArrayLiteral(); else - e = dsym._init.initializerToExpression(null, (sc.flags & SCOPE.Cfile) != 0); + 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.flags & SCOPE.Cfile) != 0); + 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); @@ -1242,7 +1297,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor ei = new ExpInitializer(dsym._init.loc, e); dsym._init = ei; } - else if (sc.flags & SCOPE.Cfile && dsym.type.isTypeSArray() && + else if (sc.inCfile && dsym.type.isTypeSArray() && dsym.type.isTypeSArray().isIncomplete()) { // C11 6.7.9-22 determine the size of the incomplete array, @@ -1257,9 +1312,12 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor 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. */ - if (dsym.type.toBasetype().ty == Tclass) + 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 @@ -1268,7 +1326,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor { 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` requires that constructor be annotated with `scope`", dsym)) + 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; @@ -1307,21 +1365,67 @@ version (IN_LLVM) Expression exp = ei.exp; Expression e1 = new VarExp(dsym.loc, dsym); - if (isBlit) - exp = new BlitExp(dsym.loc, e1, exp); + + 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--; + } + + 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 + { + constructInit(false); + } + } else - exp = new ConstructExp(dsym.loc, e1, exp); - dsym.canassign++; - exp = exp.expressionSemantic(sc); - dsym.canassign--; - exp = exp.optimize(WANTvalue); + { + constructInit(isBlit); + } + if (exp.op == EXP.error) { dsym._init = new ErrorInitializer(); ei = null; } else - ei.exp = exp; + ei.exp = exp.optimize(WANTvalue); } else { @@ -1345,7 +1449,7 @@ version (IN_LLVM) } else if (dsym.storage_class & (STC.const_ | STC.immutable_ | STC.manifest) || dsym.type.isConst() || dsym.type.isImmutable() || - sc.flags & SCOPE.Cfile) + sc.inCfile) { /* Because we may need the results of a const declaration in a * subsequent type, such as an array dimension, before semantic2() @@ -1356,7 +1460,7 @@ version (IN_LLVM) */ if (!inferred) { - uint errors = global.errors; + 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 @@ -1498,7 +1602,7 @@ else if (dsym.errors) return; - if (!(global.params.bitfields || sc.flags & SCOPE.Cfile)) + if (!(sc.previews.bitfields || sc.inCfile)) { version (IN_GCC) .error(dsym.loc, "%s `%s` use `-fpreview=bitfields` for bitfield support", dsym.kind, dsym.toPrettyChars); @@ -1515,7 +1619,7 @@ else auto width = dsym.width.expressionSemantic(sc); sc = sc.endCTFE(); width = width.ctfeInterpret(); - if (!dsym.type.isintegral()) + if (!dsym.type.isIntegral()) { // C11 6.7.2.1-5 error(width.loc, "bit-field type `%s` is not an integer type", dsym.type.toChars()); @@ -1546,6 +1650,8 @@ else override void visit(Import imp) { + timeTraceBeginEvent(TimeTraceEventType.sema1Import); + scope (exit) timeTraceEndEvent(TimeTraceEventType.sema1Import, imp); static if (LOG) { printf("Import::semantic('%s') %s\n", imp.toPrettyChars(), imp.id.toChars()); @@ -1586,156 +1692,91 @@ else imp.mod.checkImportDeprecation(imp.loc, sc); } } - if (imp.mod) + if (!imp.mod) { - // Modules need a list of each imported module - - // if inside a template instantiation, the instantianting - // module gets the import. - // https://issues.dlang.org/show_bug.cgi?id=17181 - Module importer = sc._module; - if (sc.minst && sc.tinst) - { - importer = sc.minst; - if (!sc.tinst.importedModules.contains(imp.mod)) - sc.tinst.importedModules.push(imp.mod); - } - //printf("%s imports %s\n", importer.toChars(), imp.mod.toChars()); - if (!importer.aimports.contains(imp.mod)) - importer.aimports.push(imp.mod); - - if (sc.explicitVisibility) - imp.visibility = sc.visibility; - - if (!imp.aliasId && !imp.names.length) // neither a selective nor a renamed import - { - ScopeDsymbol scopesym = sc.getScopesym(); + imp.semanticRun = PASS.semanticdone; + addImportDep(global.params.moduleDeps, imp, sc._module); + } - if (!imp.isstatic) - { - scopesym.importScope(imp.mod, imp.visibility); - } + // Modules need a list of each imported module + // if inside a template instantiation, the instantianting + // module gets the import. + // https://issues.dlang.org/show_bug.cgi?id=17181 + Module importer = sc._module; + if (sc.minst && sc.tinst) + { + importer = sc.minst; + if (!sc.tinst.importedModules.contains(imp.mod)) + sc.tinst.importedModules.push(imp.mod); + } + //printf("%s imports %s\n", importer.toChars(), imp.mod.toChars()); + if (!importer.aimports.contains(imp.mod)) + importer.aimports.push(imp.mod); - imp.addPackageAccess(scopesym); - } + if (sc.explicitVisibility) + imp.visibility = sc.visibility; - // if a module has errors it means that parsing has failed. - if (!imp.mod.errors) - imp.mod.dsymbolSemantic(null); + if (!imp.aliasId && !imp.names.length) // neither a selective nor a renamed import + { + ScopeDsymbol scopesym = sc.getScopesym(); - if (imp.mod.needmoduleinfo) + if (!imp.isstatic) { - //printf("module4 %s because of %s\n", importer.toChars(), imp.mod.toChars()); - importer.needmoduleinfo = 1; + scopesym.importScope(imp.mod, imp.visibility); } - 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) - { - 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 - { - 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(); + + imp.addPackageAccess(scopesym); } - imp.semanticRun = PASS.semanticdone; + // if a module has errors it means that parsing has failed. + if (!imp.mod.errors) + imp.mod.dsymbolSemantic(null); - // object self-imports itself, so skip that - // https://issues.dlang.org/show_bug.cgi?id=7547 - // don't list pseudo modules __entrypoint.d, __main.d - // https://issues.dlang.org/show_bug.cgi?id=11117 - // https://issues.dlang.org/show_bug.cgi?id=11164 - if (global.params.moduleDeps.buffer is null || (imp.id == Id.object && sc._module.ident == Id.object) || - strcmp(sc._module.ident.toChars(), "__main") == 0) - return; + if (imp.mod.needmoduleinfo) + { + //printf("module4 %s because of %s\n", importer.toChars(), imp.mod.toChars()); + importer.needmoduleinfo = 1; + } - /* The grammar of the file is: - * ImportDeclaration - * ::= BasicImportDeclaration [ " : " ImportBindList ] [ " -> " - * ModuleAliasIdentifier ] "\n" - * - * BasicImportDeclaration - * ::= ModuleFullyQualifiedName " (" FilePath ") : " Protection|"string" - * " [ " static" ] : " ModuleFullyQualifiedName " (" FilePath ")" - * - * FilePath - * - any string with '(', ')' and '\' escaped with the '\' character - */ - OutBuffer* ob = global.params.moduleDeps.buffer; - Module imod = sc._module; - if (!global.params.moduleDeps.name) - ob.writestring("depsImport "); - ob.writestring(imod.toPrettyChars()); - ob.writestring(" ("); - escapePath(ob, imod.srcfile.toChars()); - ob.writestring(") : "); - // use visibility instead of sc.visibility because it couldn't be - // resolved yet, see the comment above - visibilityToBuffer(*ob, imp.visibility); - ob.writeByte(' '); - if (imp.isstatic) - { - stcToBuffer(*ob, STC.static_); - ob.writeByte(' '); - } - ob.writestring(": "); - foreach (pid; imp.packages) - { - ob.printf("%s.", pid.toChars()); - } - ob.writestring(imp.id.toString()); - ob.writestring(" ("); - if (imp.mod) - escapePath(ob, imp.mod.srcfile.toChars()); - else - ob.writestring("???"); - ob.writeByte(')'); - foreach (i, name; imp.names) + sc = sc.push(imp.mod); + sc.visibility = imp.visibility; + for (size_t i = 0; i < imp.aliasdecls.length; i++) { - if (i == 0) - ob.writeByte(':'); - else - ob.writeByte(','); - Identifier _alias = imp.aliases[i]; - if (!_alias) + 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) { - ob.printf("%s", name.toChars()); - _alias = name; + 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 - ob.printf("%s=%s", _alias.toChars(), name.toChars()); + { + 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; + } } - if (imp.aliasId) - ob.printf(" -> %s", imp.aliasId.toChars()); - ob.writenl(); + sc = sc.pop(); + + imp.semanticRun = PASS.semanticdone; + addImportDep(global.params.moduleDeps, imp, sc._module); } void attribSemantic(AttribDeclaration ad) @@ -1745,20 +1786,25 @@ else ad.semanticRun = PASS.semantic; Dsymbols* d = ad.include(sc); //printf("\tAttribDeclaration::semantic '%s', d = %p\n",toChars(), d); - if (d) + if (!d) { - Scope* sc2 = ad.newScope(sc); - bool errors; - for (size_t i = 0; i < d.length; i++) - { - Dsymbol s = (*d)[i]; - s.dsymbolSemantic(sc2); - errors |= s.errors; - } - ad.errors |= errors; - if (sc2 != sc) - sc2.pop(); + ad.semanticRun = PASS.semanticdone; + return; + } + + Scope* sc2 = ad.newScope(sc); + bool errors; + for (size_t i = 0; i < d.length; i++) + { + Dsymbol s = (*d)[i]; + s.dsymbolSemantic(sc2); + errors |= s.errors; } + if (errors) + ad.errors = true; + if (sc2 != sc) + sc2.pop(); + ad.semanticRun = PASS.semanticdone; } @@ -1786,7 +1832,7 @@ else sc = sc.push(); sc.stc &= ~(STC.auto_ | STC.scope_ | STC.static_ | STC.gshared); sc.inunion = scd.isunion ? scd : null; - sc.flags = 0; + sc.resetAllFlags(); for (size_t i = 0; i < scd.decl.length; i++) { Dsymbol s = (*scd.decl)[i]; @@ -1828,9 +1874,9 @@ else buf.writeByte(0); const str = buf.extractSlice()[0 .. len]; const bool doUnittests = global.params.parsingUnittestsRequired(); - auto loc = adjustLocForMixin(str, cd.loc, global.params.mixinOut); - scope p = new Parser!ASTCodegen(loc, sc._module, str, false, global.errorSink, &global.compileEnv, doUnittests); - p.transitionIn = global.params.v.vin; + 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(); auto d = p.parseDeclDefs(0); @@ -1876,8 +1922,7 @@ else const sident = se.toStringz(); if (!sident.length || !Identifier.isValidIdentifier(sident)) { - error(ns.exp.loc, "expected valid identifier for C++ namespace but got `%.*s`", - cast(int)sident.length, sident.ptr); + error(ns.exp.loc, "expected valid identifier for C++ namespace but got `%s`", se.toErrMsg()); return null; } else @@ -1943,6 +1988,25 @@ else { 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) @@ -1968,6 +2032,10 @@ else { if (m.semanticRun != PASS.initial) 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. @@ -2180,7 +2248,7 @@ else tm.argsym.parent = scy.parent; Scope* argscope = scy.push(tm.argsym); - uint errorsave = global.errors; + const errorsave = global.errors; // Declare each template parameter as an alias for the argument type tm.declareParameters(argscope); @@ -2325,7 +2393,7 @@ else ns.semanticRun = PASS.semantic; ns.parent = sc.parent; // Link does not matter here, if the UDA is present it will error - UserAttributeDeclaration.checkGNUABITag(ns, LINK.cpp); + checkGNUABITag(ns, LINK.cpp); if (!ns.members) { @@ -2365,7 +2433,7 @@ else override void visit(CtorDeclaration ctd) { - //printf("CtorDeclaration::semantic() %s\n", toChars()); + //printf("CtorDeclaration::semantic() %p %s\n", ctd, ctd.toChars()); if (ctd.semanticRun >= PASS.semanticdone) return; if (ctd._scope) @@ -2398,6 +2466,24 @@ else sc.stc &= ~STC.static_; // not a static constructor 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())) + { + 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"); + } + } + } + } sc.pop(); @@ -2448,12 +2534,17 @@ else } else if ((dim == 1 || (dim > 1 && tf.parameterList[1].defaultArg))) { - //printf("tf: %s\n", tf.toChars()); + //printf("tf: %s\n", toChars(tf)); auto param = tf.parameterList[0]; - if (param.storageClass & STC.ref_ && param.type.mutableOf().unSharedOf() == sd.type.mutableOf().unSharedOf()) + if (param.type.mutableOf().unSharedOf() == sd.type.mutableOf().unSharedOf()) { - //printf("copy constructor\n"); - ctd.isCpCtor = true; + //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)); } } } @@ -2567,50 +2658,53 @@ else sc.pop(); } - override void visit(StaticCtorDeclaration scd) + void visitStaticCDtorDeclaration(FuncDeclaration sd, bool isDestructor) { - //printf("StaticCtorDeclaration::semantic()\n"); - if (scd.semanticRun >= PASS.semanticdone) + if (sd.semanticRun >= PASS.semanticdone) return; - if (scd._scope) + if (sd._scope) { - sc = scd._scope; - scd._scope = null; + sc = sd._scope; + sd._scope = null; } - - scd.parent = sc.parent; - Dsymbol p = scd.parent.pastMixin(); + 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 = (scd.isSharedStaticCtorDeclaration() ? "shared " : ""); - error(scd.loc, "`%sstatic` constructor can only be member of module/aggregate/template, not %s `%s`", s, p.kind(), p.toChars()); - scd.type = Type.terror; - scd.errors = true; + 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 (!scd.type) - scd.type = new TypeFunction(ParameterList(), Type.tvoid, LINK.d, scd.storage_class); - /* If the static ctor appears within a template instantiation, + 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 (scd.isInstantiated() && scd.semanticRun < PASS.semantic) + if (sd.isInstantiated() && sd.semanticRun < PASS.semantic) { /* Add this prefix to the constructor: * ``` * static int gate; - * if (++gate != 1) return; + * if ([--|++]gate != [0|1]) return; // dependant on ctor/dtor * ``` * or, for shared constructor: * ``` * shared int gate; - * if (core.atomic.atomicOp!"+="(gate, 1) != 1) return; + * enum op = isDestructor ? "-=" : "+="; + * enum cmp = isDestructor ? 0 : 1; + * if (core.atomic.atomicOp!op(gate, 1) != cmp) return; * ``` */ - const bool isShared = !!scd.isSharedStaticCtorDeclaration(); + auto v = new VarDeclaration(Loc.initial, Type.tint32, Id.gate, null); - v.storage_class = STC.temp | STC.static_ | (isShared ? STC.shared_ : 0); + v.storage_class = STC.temp | STC.static_ | (isShared ? STC.shared_ : STC.none); auto sa = new Statements(); Statement s = new ExpStatement(Loc.initial, v); @@ -2619,49 +2713,56 @@ else Expression e; if (isShared) { - e = doAtomicOp("+=", v.ident, IntegerExp.literal!(1)); + e = doAtomicOp(isDestructor ? "-=" : "+=", v.ident, IntegerExp.literal!(1)); if (e is null) { - .error(scd.loc, "%s `%s` shared static constructor within a template require `core.atomic : atomicOp` to be present", scd.kind, scd.toPrettyChars); + .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 { + IntegerExp one = isDestructor ? IntegerExp.literal!(-1) : IntegerExp.literal!(1); e = new AddAssignExp( - Loc.initial, new IdentifierExp(Loc.initial, v.ident), IntegerExp.literal!1); + Loc.initial, new IdentifierExp(Loc.initial, v.ident), one); } - - e = new EqualExp(EXP.notEqual, Loc.initial, e, IntegerExp.literal!1); + 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 (scd.fbody) - sa.push(scd.fbody); + if (sd.fbody) + sa.push(sd.fbody); - scd.fbody = new CompoundStatement(Loc.initial, sa); + 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 = (scd.isSharedStaticCtorDeclaration() ? "shared " : ""); - deprecation(scd.loc, "`%sstatic` constructor can only be of D linkage", s); + 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, scd); + funcDeclarationSemantic(sc, sd); sc.linkage = save; // We're going to need ModuleInfo - Module m = scd.getModule(); + Module m = sd.getModule(); if (!m) m = sc._module; if (m) { m.needmoduleinfo = 1; - //printf("module1 %s needs moduleinfo\n", m.toChars()); + //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; @@ -2684,100 +2785,7 @@ else override void visit(StaticDtorDeclaration sdd) { - if (sdd.semanticRun >= PASS.semanticdone) - return; - if (sdd._scope) - { - sc = sdd._scope; - sdd._scope = null; - } - - sdd.parent = sc.parent; - Dsymbol p = sdd.parent.pastMixin(); - if (!p.isScopeDsymbol()) - { - const(char)* s = (sdd.isSharedStaticDtorDeclaration() ? "shared " : ""); - error(sdd.loc, "`%sstatic` destructor can only be member of module/aggregate/template, not %s `%s`", s, p.kind(), p.toChars()); - sdd.type = Type.terror; - sdd.errors = true; - return; - } - if (!sdd.type) - sdd.type = new TypeFunction(ParameterList(), Type.tvoid, LINK.d, sdd.storage_class); - - /* If the static ctor appears within a template instantiation, - * it could get called multiple times by the module constructors - * for different modules. Thus, protect it with a gate. - */ - if (sdd.isInstantiated() && sdd.semanticRun < PASS.semantic) - { - /* Add this prefix to the constructor: - * ``` - * static int gate; - * if (--gate != 0) return; - * ``` - * or, for shared constructor: - * ``` - * shared int gate; - * if (core.atomic.atomicOp!"-="(gate, 1) != 0) return; - * ``` - */ - const bool isShared = !!sdd.isSharedStaticDtorDeclaration(); - auto v = new VarDeclaration(Loc.initial, Type.tint32, Id.gate, null); - v.storage_class = STC.temp | STC.static_ | (isShared ? STC.shared_ : 0); - - auto sa = new Statements(); - Statement s = new ExpStatement(Loc.initial, v); - sa.push(s); - - Expression e; - if (isShared) - { - e = doAtomicOp("-=", v.ident, IntegerExp.literal!(1)); - if (e is null) - { - .error(sdd.loc, "%s `%s` shared static destructo within a template require `core.atomic : atomicOp` to be present", sdd.kind, sdd.toPrettyChars); - return; - } - } - else - { - e = new AddAssignExp( - Loc.initial, new IdentifierExp(Loc.initial, v.ident), IntegerExp.literal!(-1)); - } - - e = new EqualExp(EXP.notEqual, Loc.initial, e, IntegerExp.literal!0); - s = new IfStatement(Loc.initial, null, e, new ReturnStatement(Loc.initial, null), null, Loc.initial); - - sa.push(s); - if (sdd.fbody) - sa.push(sdd.fbody); - - sdd.fbody = new CompoundStatement(Loc.initial, sa); - - sdd.vgate = v; - } - - const LINK save = sc.linkage; - if (save != LINK.d) - { - const(char)* s = (sdd.isSharedStaticDtorDeclaration() ? "shared " : ""); - deprecation(sdd.loc, "`%sstatic` destructor can only be of D linkage", s); - // Just correct it - sc.linkage = LINK.d; - } - funcDeclarationSemantic(sc, sdd); - sc.linkage = save; - - // We're going to need ModuleInfo - Module m = sdd.getModule(); - if (!m) - m = sc._module; - if (m) - { - m.needmoduleinfo = 1; - //printf("module2 %s needs moduleinfo\n", m.toChars()); - } + visitStaticCDtorDeclaration(sdd, true); } override void visit(InvariantDeclaration invd) @@ -2814,7 +2822,7 @@ else sc = sc.push(); sc.stc &= ~STC.static_; // not a static invariant sc.stc |= STC.const_; // invariant() is always const - sc.flags = (sc.flags & ~SCOPE.contract) | SCOPE.invariant_; + sc.contract = Contract.invariant_; sc.linkage = LINK.d; funcDeclarationSemantic(sc, invd); @@ -2891,7 +2899,7 @@ else if (sd.semanticRun >= PASS.semanticdone) return; - int errors = global.errors; + const errors = global.errors; //printf("+StructDeclaration::semantic(this=%p, '%s', sizeok = %d)\n", sd, sd.toPrettyChars(), sd.sizeok); Scope* scx = null; @@ -2958,7 +2966,7 @@ else return; sd.semanticRun = PASS.semantic; - UserAttributeDeclaration.checkGNUABITag(sd, sc.linkage); + checkGNUABITag(sd, sc.linkage); if (!sd.members) // if opaque declaration { @@ -2980,7 +2988,7 @@ else */ sd.members.foreachDsymbol( s => s.setScope(sc2) ); sd.members.foreachDsymbol( s => s.importAll(sc2) ); - sd.members.foreachDsymbol( (s) { s.dsymbolSemantic(sc2); sd.errors |= s.errors; } ); + sd.members.foreachDsymbol( (s) { s.dsymbolSemantic(sc2); if (sd.errors) s.errors = true; } ); if (sd.errors) sd.type = Type.terror; @@ -3027,13 +3035,42 @@ else buildDtors(sd, sc2); - sd.hasCopyCtor = buildCopyCtor(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) + { + 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.flags & SCOPE.Cfile) && + if (!sc2.inCfile && global.params.useTypeInfo && Type.dtypeinfo) // these functions are used for TypeInfo { sd.xeq = buildXopEquals(sd, sc2); @@ -3050,10 +3087,10 @@ else if (sd.ctor) { - Dsymbol scall = sd.search(Loc.initial, Id.call); + Dsymbol scall = sd.search(Loc.initial, Id.opCall); if (scall) { - uint xerrors = global.startGagging(); + const xerrors = global.startGagging(); sc = sc.push(); sc.tinst = null; sc.minst = null; @@ -3138,7 +3175,7 @@ else if (cldec.semanticRun >= PASS.semanticdone) return; - int errors = global.errors; + const errors = global.errors; //printf("+ClassDeclaration.dsymbolSemantic(%s), type = %p, sizeok = %d, this = %p\n", toChars(), type, sizeok, this); @@ -3198,7 +3235,7 @@ else return; } cldec.semanticRun = PASS.semantic; - UserAttributeDeclaration.checkGNUABITag(cldec, sc.linkage); + checkGNUABITag(cldec, sc.linkage); checkMustUseReserved(cldec); if (cldec.baseok < Baseok.done) @@ -3672,7 +3709,7 @@ else // This is required if other lowerings add code to the generated constructor which // is less strict (e.g. `preview=dtorfields` might introduce a call to a less qualified dtor) - auto ctor = new CtorDeclaration(cldec.loc, Loc.initial, 0, tf); + 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()); @@ -3801,7 +3838,7 @@ else //printf("InterfaceDeclaration.dsymbolSemantic(%s), type = %p\n", toChars(), type); if (idec.semanticRun >= PASS.semanticdone) return; - int errors = global.errors; + const errors = global.errors; //printf("+InterfaceDeclaration.dsymbolSemantic(%s), type = %p\n", toChars(), type); @@ -3910,7 +3947,7 @@ else if (!idec.baseclasses.length && sc.linkage == LINK.cpp) idec.classKind = ClassKind.cpp; idec.cppnamespace = sc.namespace; - UserAttributeDeclaration.checkGNUABITag(idec, sc.linkage); + checkGNUABITag(idec, sc.linkage); checkMustUseReserved(idec); if (sc.linkage == LINK.objc) @@ -4131,7 +4168,7 @@ private extern(C++) class AddMemberVisitor : Visitor Scope* sc; ScopeDsymbol sds; - this(Scope* sc, ScopeDsymbol sds) + this(Scope* sc, ScopeDsymbol sds) @safe { this.sc = sc; this.sds = sds; @@ -4169,7 +4206,7 @@ private extern(C++) class AddMemberVisitor : Visitor } // If using C tag/prototype/forward declaration rules - if (sc && sc.flags & SCOPE.Cfile && !dsym.isImport()) + if (sc && sc.inCfile && !dsym.isImport()) // When merging master, replace with: if (sc && sc.inCfile && !dsym.isImport()) { if (handleTagSymbols(*sc, dsym, s2, sds)) @@ -4191,7 +4228,7 @@ private extern(C++) class AddMemberVisitor : Visitor if (sds.isAggregateDeclaration() || sds.isEnumDeclaration()) { if (dsym.ident == Id.__sizeof || - !(sc && sc.flags & SCOPE.Cfile) && (dsym.ident == Id.__xalignof || dsym.ident == Id._mangleof)) + !(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; @@ -4342,34 +4379,21 @@ private extern(C++) class AddMemberVisitor : Visitor Module m = sds.isModule(); // Do not add the member to the symbol table, // just make sure subsequent debug declarations work. - if (ds.ident) + if (!m) { - if (!m) - { - .error(ds.loc, "%s `%s` declaration must be at module level", ds.kind, ds.toPrettyChars); - ds.errors = true; - } - else - { - 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); - } + .error(ds.loc, "%s `%s` declaration must be at module level", ds.kind, ds.toPrettyChars); + ds.errors = true; } else { - if (!m) + if (m.debugidsNot && findCondition(*m.debugidsNot, ds.ident)) { - .error(ds.loc, "%s `%s` level declaration must be at module level", ds.kind, ds.toPrettyChars); + .error(ds.loc, "%s `%s` defined after use", ds.kind, ds.toPrettyChars); ds.errors = true; } - else - m.debuglevel = ds.level; + if (!m.debugids) + m.debugids = new Identifiers(); + m.debugids.push(ds.ident); } } @@ -4379,36 +4403,24 @@ private extern(C++) class AddMemberVisitor : Visitor Module m = sds.isModule(); // Do not add the member to the symbol table, // just make sure subsequent debug declarations work. - if (vs.ident) + VersionCondition.checkReserved(vs.loc, vs.ident.toString()); + if (!m) { - VersionCondition.checkReserved(vs.loc, vs.ident.toString()); - if (!m) - { - .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); - } + .error(vs.loc, "%s `%s` declaration must be at module level", vs.kind, vs.toPrettyChars); + vs.errors = true; } else { - if (!m) + if (m.versionidsNot && findCondition(*m.versionidsNot, vs.ident)) { - .error(vs.loc, "%s `%s` level declaration must be at module level", vs.kind, vs.toPrettyChars); + .error(vs.loc, "%s `%s` defined after use", vs.kind, vs.toPrettyChars); vs.errors = true; } - else - m.versionlevel = vs.level; + if (!m.versionids) + m.versionids = new Identifiers(); + m.versionids.push(vs.ident); } + } override void visit(Nspace ns) @@ -4467,7 +4479,7 @@ private extern(C++) class AddMemberVisitor : Visitor */ void addEnumMembersToSymtab(EnumDeclaration ed, Scope* sc, ScopeDsymbol sds) { - const bool isCEnum = (sc.flags & SCOPE.Cfile) != 0; // it's an ImportC enum + 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; @@ -4498,6 +4510,8 @@ void addEnumMembersToSymtab(EnumDeclaration ed, Scope* sc, ScopeDsymbol sds) * 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 } @@ -4768,6 +4782,15 @@ void templateInstanceSemantic(TemplateInstance tempinst, Scope* sc, ArgumentList 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) ); @@ -4822,7 +4845,7 @@ void templateInstanceSemantic(TemplateInstance tempinst, Scope* sc, ArgumentList printf("\timplement template instance %s '%s'\n", tempdecl.parent.toChars(), tempinst.toChars()); printf("\ttempdecl %s\n", tempdecl.toChars()); } - uint errorsave = global.errors; + const errorsave = global.errors; tempinst.inst = tempinst; tempinst.parent = tempinst.enclosing ? tempinst.enclosing : tempdecl.parent; @@ -4850,7 +4873,7 @@ void templateInstanceSemantic(TemplateInstance tempinst, Scope* sc, ArgumentList continue; Type t = isType((*tempinst.tiargs)[i]); assert(t); - if (StorageClass stc = ModToStc(t.mod)) + if (STC stc = ModToStc(t.mod)) { //printf("t = %s, stc = x%llx\n", t.toChars(), stc); auto s = new Dsymbols(); @@ -4878,11 +4901,11 @@ void templateInstanceSemantic(TemplateInstance tempinst, Scope* sc, ArgumentList _scope = _scope.push(tempinst.argsym); _scope.tinst = tempinst; _scope.minst = tempinst.minst; - //scope.stc = 0; + //scope.stc = STC.none; // Declare each template parameter as an alias for the argument type Scope* paramscope = _scope.push(); - paramscope.stc = 0; + 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); @@ -5025,7 +5048,7 @@ version (IN_LLVM) if (global.errors != errorsave) goto Laftersemantic; - if ((sc.func || (sc.flags & SCOPE.fullinst)) && !tempinst.tinst) + 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 @@ -5225,7 +5248,7 @@ void aliasSeqInstanceSemantic(TemplateInstance tempinst, Scope* sc, TemplateDecl { //printf("[%s] aliasSeqInstance.dsymbolSemantic('%s')\n", tempinst.loc.toChars(), tempinst.toChars()); Scope* paramscope = sc.push(); - paramscope.stc = 0; + paramscope.stc = STC.none; paramscope.visibility = Visibility(Visibility.Kind.public_); TemplateTupleParameter ttp = (*tempdecl.parameters)[0].isTemplateTupleParameter(); @@ -5250,7 +5273,7 @@ void aliasInstanceSemantic(TemplateInstance tempinst, Scope* sc, TemplateDeclara { //printf("[%s] aliasInstance.dsymbolSemantic('%s')\n", tempinst.loc.toChars(), tempinst.toChars()); Scope* paramscope = sc.push(); - paramscope.stc = 0; + paramscope.stc = STC.none; paramscope.visibility = Visibility(Visibility.Kind.public_); TemplateTypeParameter ttp = (*tempdecl.parameters)[0].isTemplateTypeParameter(); @@ -5272,7 +5295,7 @@ void aliasInstanceSemantic(TemplateInstance tempinst, Scope* sc, TemplateDeclara // function used to perform semantic on AliasDeclaration void aliasSemantic(AliasDeclaration ds, Scope* sc) { - //printf("AliasDeclaration::semantic() %s\n", ds.toChars()); + //printf("AliasDeclaration::semantic() %s %p\n", ds.toChars(), ds.aliassym); // as DsymbolSemanticVisitor::visit(AliasDeclaration), in case we're called first. // see https://issues.dlang.org/show_bug.cgi?id=21001 @@ -5367,9 +5390,12 @@ void aliasSemantic(AliasDeclaration ds, Scope* sc) mt.ident != Id.This && mt.ident != Id._super) { s = tident.toDsymbol(sc); - if (s && s.isVarDeclaration()) { - error(mt.loc, "cannot alias member of variable `%s`", mt.ident.toChars()); - errorSupplemental(mt.loc, "Use `typeof(%s)` instead to preserve behaviour", + // don't error for `var1.static_symbol` + if (s && s.needThis()) + { + error(ds.loc, "cannot alias %s member `%s` of variable `%s`", + s.kind(), s.toChars(), mt.ident.toChars()); + errorSupplemental(ds.loc, "Use `typeof(%s)` instead to preserve behaviour", mt.ident.toChars()); } } @@ -5523,7 +5549,7 @@ private void aliasAssignSemantic(AliasAssign ds, Scope* sc) if (!aliassym) return errorRet(); - if (aliassym.adFlags & Declaration.wasRead) + if (aliassym.wasRead) { if (!aliassym.errors) error(ds.loc, "%s was read, so cannot reassign", aliassym.toChars()); @@ -5531,7 +5557,7 @@ private void aliasAssignSemantic(AliasAssign ds, Scope* sc) return errorRet(); } - aliassym.adFlags |= Declaration.ignoreRead; // temporarilly allow reads of aliassym + aliassym.ignoreRead = true; // temporarilly allow reads of aliassym const storage_class = sc.stc & (STC.deprecated_ | STC.ref_ | STC.nothrow_ | STC.nogc | STC.pure_ | STC.shared_ | STC.disable); @@ -5655,8 +5681,7 @@ private void aliasAssignSemantic(AliasAssign ds, Scope* sc) aliassym.aliassym = null; } - - aliassym.adFlags &= ~Declaration.ignoreRead; + aliassym.ignoreRead = false; if (aliassym.type && aliassym.type.ty == Terror || global.gag && errors != global.errors) @@ -5919,34 +5944,40 @@ private CallExp doAtomicOp (string op, Identifier var, Expression arg) * Set up loc for a parse of a mixin. Append the input text to the mixin. * Params: * input = mixin text - * loc = location to adjust + * loc = location of expansion + * baseLoc = location to adjust * mixinOut = sink for mixin text data * Returns: * adjusted loc suitable for Parser */ -Loc adjustLocForMixin(const(char)[] input, ref const Loc loc, ref Output mixinOut) +void adjustLocForMixin(const(char)[] input, Loc loc, ref BaseLoc baseLoc, ref Output mixinOut) { - Loc result; if (mixinOut.doOutput) { const lines = mixinOut.bufferLines; writeMixin(input, loc, mixinOut.bufferLines, *mixinOut.buffer); - result = Loc(mixinOut.name.ptr, lines + 2, loc.charnum); + baseLoc.startLine = lines + 2; + baseLoc.filename = mixinOut.name; + return; } - else if (loc.filename) + + SourceLoc sl = SourceLoc(loc); + if (sl.filename.length == 0) { - /* Create a pseudo-filename for the mixin string, as it may not even exist - * in the source file. - */ - auto len = strlen(loc.filename) + 7 + (loc.linnum).sizeof * 3 + 1; - char* filename = cast(char*)mem.xmalloc(len); - snprintf(filename, len, "%s-mixin-%d", loc.filename, cast(int)loc.linnum); - result = Loc(filename, loc.linnum, loc.charnum); + // Rare case of compiler-generated mixin exp, e.g. __xtoHash + baseLoc.filename = ""; + return; } - else - result = loc; - return result; + + /* Create a pseudo-filename for the mixin string, as it may not even exist + * in the source file. + */ + auto len = sl.filename.length + 7 + (sl.linnum).sizeof * 3 + 1; + char* filename = cast(char*) mem.xmalloc(len); + snprintf(filename, len, "%.*s-mixin-%d", cast(int) sl.filename.length, sl.filename.ptr, cast(int) sl.linnum); + baseLoc.startLine = sl.line; + baseLoc.filename = filename.toDString; } /************************************** @@ -5958,7 +5989,7 @@ Loc adjustLocForMixin(const(char)[] input, ref const Loc loc, ref Output mixinOu * lines = line count to update * output = sink for output */ -private void writeMixin(const(char)[] s, ref const Loc loc, ref int lines, ref OutBuffer buf) +private void writeMixin(const(char)[] s, Loc loc, ref int lines, ref OutBuffer buf) { buf.writestring("// expansion at "); buf.writestring(loc.toChars()); @@ -5995,67 +6026,6 @@ private void writeMixin(const(char)[] s, ref const Loc loc, ref int lines, ref O ++lines; } -/** - * Check signature of `pragma(printf)` function, print error if invalid. - * - * printf/scanf-like functions must be of the form: - * extern (C/C++) T printf([parameters...], const(char)* format, ...); - * or: - * extern (C/C++) T vprintf([parameters...], const(char)* format, va_list); - * - * Params: - * funcdecl = function to check - * f = function type - * sc = scope - */ -void checkPrintfScanfSignature(FuncDeclaration funcdecl, TypeFunction f, Scope* sc) -{ - static bool isPointerToChar(Parameter p) - { - if (auto tptr = p.type.isTypePointer()) - { - return tptr.next.ty == Tchar; - } - return false; - } - - bool isVa_list(Parameter p) - { - return p.type.equals(target.va_listType(funcdecl.loc, sc)); - } - - const nparams = f.parameterList.length; - const p = (funcdecl.printf ? Id.printf : Id.scanf).toChars(); - if (!(f.linkage == LINK.c || f.linkage == LINK.cpp)) - { - .error(funcdecl.loc, "`pragma(%s)` function `%s` must have `extern(C)` or `extern(C++)` linkage," - ~" not `extern(%s)`", - p, funcdecl.toChars(), f.linkage.linkageToChars()); - } - if (f.parameterList.varargs == VarArg.variadic) - { - if (!(nparams >= 1 && isPointerToChar(f.parameterList[nparams - 1]))) - { - .error(funcdecl.loc, "`pragma(%s)` function `%s` must have" - ~ " signature `%s %s([parameters...], const(char)*, ...)` not `%s`", - p, funcdecl.toChars(), f.next.toChars(), funcdecl.toChars(), funcdecl.type.toChars()); - } - } - else if (f.parameterList.varargs == VarArg.none) - { - if(!(nparams >= 2 && isPointerToChar(f.parameterList[nparams - 2]) && - isVa_list(f.parameterList[nparams - 1]))) - .error(funcdecl.loc, "`pragma(%s)` function `%s` must have"~ - " signature `%s %s([parameters...], const(char)*, va_list)`", - p, funcdecl.toChars(), f.next.toChars(), funcdecl.toChars()); - } - else - { - .error(funcdecl.loc, "`pragma(%s)` function `%s` must have C-style variadic `...` or `va_list` parameter", - p, funcdecl.toChars()); - } -} - /********************************************* * Search for ident as member of d. * Params: @@ -6066,7 +6036,7 @@ void checkPrintfScanfSignature(FuncDeclaration funcdecl, TypeFunction f, Scope* * Returns: * null if not found */ -Dsymbol search(Dsymbol d, const ref Loc loc, Identifier ident, SearchOptFlags flags = SearchOpt.all) +Dsymbol search(Dsymbol d, Loc loc, Identifier ident, SearchOptFlags flags = SearchOpt.all) { scope v = new SearchVisitor(loc, ident, flags); d.accept(v); @@ -6113,7 +6083,7 @@ private extern(C++) class SearchVisitor : Visitor SearchOptFlags flags; Dsymbol result; - this(const ref Loc loc, Identifier ident, SearchOptFlags flags) + this(Loc loc, Identifier ident, SearchOptFlags flags) @safe { this.loc = loc; this.ident = ident; @@ -6329,7 +6299,7 @@ private extern(C++) class SearchVisitor : Visitor VarDeclaration* pvar; Expression ce; - static Dsymbol dollarFromTypeTuple(const ref Loc loc, TypeTuple tt, Scope* sc) + static Dsymbol dollarFromTypeTuple(Loc loc, TypeTuple tt, Scope* sc) { /* $ gives the number of type entries in the type tuple @@ -6590,7 +6560,7 @@ private extern(C++) class SearchVisitor : Visitor return setResult(m.searchCacheSymbol); } - uint errors = global.errors; + const errors = global.errors; m.insearch = true; visit(cast(ScopeDsymbol)m); @@ -6689,7 +6659,7 @@ private extern(C++) class SearchVisitor : Visitor s = b.sym.search(loc, ident, flags); if (!s) continue; - else if (s == cd) // happens if s is nested in this and derives from this + if (s == cd) // happens if s is nested in this and derives from this s = null; else if (!(flags & SearchOpt.ignoreVisibility) && !(s.visible().kind == Visibility.Kind.protected_) && !symbolIsVisible(cd, s)) s = null; @@ -6719,7 +6689,7 @@ private extern(C++) class SetScopeVisitor : Visitor alias visit = typeof(super).visit; Scope* sc; - this(Scope* sc) + this(Scope* sc) @safe { this.sc = sc; } @@ -6862,7 +6832,7 @@ extern(C++) class ImportAllVisitor : Visitor alias visit = typeof(super).visit; Scope* sc; - this(Scope* sc) + this(Scope* sc) @safe { this.sc = sc; } @@ -6941,7 +6911,7 @@ extern(C++) class ImportAllVisitor : Visitor (*m.members)[0].ident != Id.object || (*m.members)[0].isImport() is null)) { - auto im = new Import(Loc.initial, null, Id.object, null, 0); + auto im = new Import(m.loc, null, Id.object, null, 0); m.members.shift(im); } if (!m.symtab) @@ -7028,7 +6998,7 @@ extern (D) bool load(Import imp, Scope* sc) { if (p.isPkgMod == PKG.unknown) { - uint preverrors = global.errors; + const preverrors = global.errors; imp.mod = Module.load(imp.loc, imp.packages, imp.id); if (!imp.mod) p.isPkgMod = PKG.package_; @@ -7105,7 +7075,7 @@ private extern(C++) class SetFieldOffsetVisitor : Visitor FieldState* fieldState; bool isunion; - this(AggregateDeclaration ad, FieldState* fieldState, bool isunion) + this(AggregateDeclaration ad, FieldState* fieldState, bool isunion) @safe { this.ad = ad; this.fieldState = fieldState; @@ -7194,7 +7164,7 @@ private extern(C++) class SetFieldOffsetVisitor : Visitor assert(sz != SIZE_INVALID && sz < uint.max); uint memsize = cast(uint)sz; // size of member uint memalignsize = target.fieldalign(t); // size of member for alignment purposes - vd.offset = placeField( + vd.offset = placeField(vd.loc, fieldState.offset, memsize, memalignsize, vd.alignment, ad.structsize, ad.alignsize, @@ -7266,7 +7236,7 @@ private extern(C++) class SetFieldOffsetVisitor : Visitor } uint dummy; - bfd.offset = placeField( + bfd.offset = placeField(bfd.loc, fieldState.offset, memsize, alignsize, bfd.alignment, ad.structsize, @@ -7312,9 +7282,11 @@ private extern(C++) class SetFieldOffsetVisitor : Visitor } else if (!isMicrosoftStyle) { - // If the bit-field spans more units of alignment than its type, - // start a new field at the next alignment boundary. - if (fieldState.bitOffset == fieldState.fieldSize * 8 && + // If the bit-field spans more units of alignment than its type + // and is at the alignment boundary, start a new field at the + // next alignment boundary. This affects when offsetof reports + // a higher number and bitoffsetof starts at zero again. + if (fieldState.bitOffset % (memalignsize * 8) == 0 && fieldState.bitOffset + bfd.fieldWidth > memsize * 8) { if (log) printf("more units of alignment than its type\n"); @@ -7351,7 +7323,7 @@ private extern(C++) class SetFieldOffsetVisitor : Visitor fieldState.fieldSize = memsize; else { - auto size = (pastField + 7) / 8; + const size = (pastField + 7) / 8; fieldState.fieldSize = size; //printf(" offset: %d, size: %d\n", offset, size); if (isunion) @@ -7449,7 +7421,7 @@ private extern(C++) class SetFieldOffsetVisitor : Visitor /* Given the anon 'member's size and alignment, * go ahead and place it. */ - anond.anonoffset = placeField( + anond.anonoffset = placeField(anond.loc, fieldState.offset, anond.anonstructsize, anond.anonalignsize, alignment, ad.structsize, ad.alignsize, @@ -7466,3 +7438,841 @@ private extern(C++) class SetFieldOffsetVisitor : Visitor } } } + +extern(D) Scope* newScope(Dsymbol d, Scope* sc) +{ + scope nsv = new NewScopeVisitor(sc); + d.accept(nsv); + return nsv.sc; +} + +private extern(C++) class NewScopeVisitor : Visitor +{ + alias visit = typeof(super).visit; + Scope* sc; + this(Scope* sc) + { + this.sc = sc; + } + + /**************************************** + * A hook point to supply scope for members. + * addMember, setScope, importAll, semantic, semantic2 and semantic3 will use this. + */ + override void visit(AttribDeclaration dc){} + + override void visit(StorageClassDeclaration swt) + { + STC scstc = sc.stc; + /* These sets of storage classes are mutually exclusive, + * so choose the innermost or most recent one. + */ + if (swt.stc & (STC.auto_ | STC.scope_ | STC.static_ | STC.extern_ | STC.manifest)) + scstc &= ~(STC.auto_ | STC.scope_ | STC.static_ | STC.extern_ | STC.manifest); + if (swt.stc & (STC.auto_ | STC.scope_ | STC.static_ | STC.manifest | STC.gshared)) + scstc &= ~(STC.auto_ | STC.scope_ | STC.static_ | STC.manifest | STC.gshared); + if (swt.stc & (STC.const_ | STC.immutable_ | STC.manifest)) + scstc &= ~(STC.const_ | STC.immutable_ | STC.manifest); + if (swt.stc & (STC.gshared | STC.shared_)) + scstc &= ~(STC.gshared | STC.shared_); + if (swt.stc & (STC.safe | STC.trusted | STC.system)) + scstc &= ~(STC.safe | STC.trusted | STC.system); + scstc |= swt.stc; + //printf("scstc = x%llx\n", scstc); + sc = swt.createNewScope(sc, scstc, sc.linkage, sc.cppmangle, + sc.visibility, sc.explicitVisibility, sc.aligndecl, sc.inlining); + } + + /** + * Provides a new scope with `STC.deprecated_` and `Scope.depdecl` set + * + * Calls `StorageClassDeclaration.newScope` (as it must be called or copied + * in any function overriding `newScope`), then set the `Scope`'s depdecl. + * + * Returns: + * Always a new scope, to use for this `DeprecatedDeclaration`'s members. + */ + override void visit(DeprecatedDeclaration dpd) + { + auto oldsc = sc; + visit((cast(StorageClassDeclaration)dpd)); + auto scx = sc; + sc = oldsc; + // The enclosing scope is deprecated as well + if (scx == sc) + scx = sc.push(); + scx.depdecl = dpd; + sc = scx; + } + + override void visit(LinkDeclaration lid) + { + sc= lid.createNewScope(sc, sc.stc, lid.linkage, sc.cppmangle, sc.visibility, sc.explicitVisibility, + sc.aligndecl, sc.inlining); + } + + override void visit(CPPMangleDeclaration cpmd) + { + sc = cpmd.createNewScope(sc, sc.stc, LINK.cpp, cpmd.cppmangle, sc.visibility, sc.explicitVisibility, + sc.aligndecl, sc.inlining); + } + + /** + * Returns: + * A copy of the parent scope, with `this` as `namespace` and C++ linkage + *///override Scope* visit(Scope* sc) + override void visit(CPPNamespaceDeclaration scd) + { + auto scx = sc.copy(); + scx.linkage = LINK.cpp; + scx.namespace = scd; + sc = scx; + } + + override void visit(VisibilityDeclaration atbd) + { + if (atbd.pkg_identifiers) + dsymbolSemantic(atbd, sc); + + sc = atbd.createNewScope(sc, sc.stc, sc.linkage, sc.cppmangle, atbd.visibility, 1, sc.aligndecl, sc.inlining); + } + + override void visit(AlignDeclaration visd) + { + sc = visd.createNewScope(sc, sc.stc, sc.linkage, sc.cppmangle, sc.visibility, + sc.explicitVisibility, visd, sc.inlining); + } + + override void visit(PragmaDeclaration prd) + { + if (prd.ident == Id.Pinline) + { + // We keep track of this pragma inside scopes, + // then it's evaluated on demand in function semantic + sc = prd.createNewScope(sc, sc.stc, sc.linkage, sc.cppmangle, sc.visibility, sc.explicitVisibility, sc.aligndecl, prd); // @suppress(dscanner.style.long_line) + } + else if (IN_LLVM && prd.ident == Id.LDC_profile_instr) + { + import gen.dpragma : DtoCheckProfileInstrPragma; + + bool emitInstr = true; + if (!prd.args || prd.args.length != 1 || !DtoCheckProfileInstrPragma((*prd.args)[0], emitInstr)) + { + error(prd.loc, "pragma(LDC_profile_instr, true or false) expected"); + (*prd.args)[0] = ErrorExp.get(); + } + else + { + // Only create a new scope if the emitInstrumentation flag is changed + if (sc.emitInstrumentation != emitInstr) + { + auto scx = sc.copy(); + scx.emitInstrumentation = emitInstr; + sc = scx; + } + } + } + } + + /************************************** + * Use the ForwardingScopeDsymbol as the parent symbol for members. + */ + override void visit(ForwardingAttribDeclaration fad) + { + sc = sc.push(fad.sym); + } + + override void visit(UserAttributeDeclaration uac) + { + Scope* sc2 = sc; + if (uac.atts && uac.atts.length) + { + // create new one for changes + sc2 = sc.copy(); + sc2.userAttribDecl = uac; + } + sc = sc2; + } +} + + +extern(C++) Dsymbols* include(Dsymbol d, Scope* sc) +{ + scope icv = new IncludeVisitor(sc); + d.accept(icv); + return icv.symbols; +} + +extern(C++) class IncludeVisitor : Visitor +{ + alias visit = typeof(super).visit; + Scope* sc; + Dsymbols* symbols; + this(Scope* sc) + { + this.sc = sc; + } + + override void visit(AttribDeclaration ad) + { + if (ad.errors) + { + symbols = null; + return; + } + symbols = ad.decl; + return; + } + +// Decide if 'then' or 'else' code should be included + override void visit(ConditionalDeclaration cdc) + { + //printf("ConditionalDeclaration::include(sc = %p) scope = %p\n", sc, _scope); + + if (cdc.errors) + { + symbols = null; + return; + } + assert(cdc.condition); + symbols = cdc.condition.include(cdc._scope ? cdc._scope : sc) ? cdc.decl : cdc.elsedecl; + } + + override void visit(StaticIfDeclaration sif) + { + /**************************************** + * Different from other AttribDeclaration subclasses, include() call requires + * the completion of addMember and setScope phases. + */ + //printf("StaticIfDeclaration::include(sc = %p) scope = %p\n", sc, _scope); + if (sif.errors || sif.onStack) + { + symbols = null; + return; + } + sif.onStack = true; + scope(exit) sif.onStack = false; + + if (sc && sif.condition.inc == Include.notComputed) + { + assert(sif.scopesym); // addMember is already done + assert(sif._scope); // setScope is already done + + Scope* saved_scope = sc; + sc = sif._scope; + visit(cast(ConditionalDeclaration) sif); + Dsymbols* d = symbols; + sc = saved_scope; + + if (d && !sif.addisdone) + { + // Add members lazily. + d.foreachDsymbol( s => s.addMember(sif._scope, sif.scopesym) ); + + // Set the member scopes lazily. + d.foreachDsymbol( s => s.setScope(sif._scope) ); + + sif.addisdone = true; + } + symbols = d; + return; + } + else + { + visit(cast(ConditionalDeclaration)sif); + } + } + + override void visit(StaticForeachDeclaration sfd) + { + if (sfd.errors || sfd.onStack) + { + symbols = null; + return; + } + if (sfd.cached) + { + assert(!sfd.onStack); + symbols = sfd.cache; + return; + } + sfd.onStack = true; + scope(exit) sfd.onStack = false; + + if (sfd._scope) + { + sfd.sfe.prepare(sfd._scope); // lower static foreach aggregate + } + if (!sfd.sfe.ready()) + { + symbols = null;// TODO: ok? + return; + } + + // expand static foreach + import dmd.statementsem: makeTupleForeach; + Dsymbols* d = makeTupleForeach(sfd._scope, true, true, sfd.sfe.aggrfe, sfd.decl, sfd.sfe.needExpansion).decl; + if (d) // process generated declarations + { + // Add members lazily. + d.foreachDsymbol( s => s.addMember(sfd._scope, sfd.scopesym) ); + + // Set the member scopes lazily. + d.foreachDsymbol( s => s.setScope(sfd._scope) ); + } + sfd.cached = true; + sfd.cache = d; + symbols = d; + } +} + +/** + * Called from a symbol's semantic to check if `gnuAbiTag` UDA + * can be applied to them + * + * Directly emits an error if the UDA doesn't work with this symbol + * + * Params: + * sym = symbol to check for `gnuAbiTag` + * linkage = Linkage of the symbol (Declaration.link or sc.link) + */ +void checkGNUABITag(Dsymbol sym, LINK linkage) +{ + if (global.params.cplusplus < CppStdRevision.cpp11) + return; + + foreachUdaNoSemantic(sym, (exp) { + if (!isGNUABITag(exp)) + return 0; // continue + if (sym.isCPPNamespaceDeclaration() || sym.isNspace()) + { + .error(exp.loc, "`@%s` cannot be applied to namespaces", Id.udaGNUAbiTag.toChars()); + sym.errors = true; + } + else if (linkage != LINK.cpp) + { + .error(exp.loc, "`@%s` can only apply to C++ symbols", Id.udaGNUAbiTag.toChars()); + sym.errors = true; + } + // Only one `@gnuAbiTag` is allowed by semantic2 + return 1; // break + }); +} + +/** + * Check if the provided expression references `core.attribute.gnuAbiTag` + * + * This should be called after semantic has been run on the expression. + * Semantic on UDA happens in semantic2 (see `dmd.semantic2`). + * + * Params: + * e = Expression to check (usually from `UserAttributeDeclaration.atts`) + * + * Returns: + * `true` if the expression references the compiler-recognized `gnuAbiTag` + */ +bool isGNUABITag(Expression e) +{ + if (global.params.cplusplus < CppStdRevision.cpp11) + return false; + + auto ts = e.type ? e.type.isTypeStruct() : null; + if (!ts) + return false; + if (ts.sym.ident != Id.udaGNUAbiTag || !ts.sym.parent) + return false; + // Can only be defined in druntime + Module m = ts.sym.parent.isModule(); + if (!m || !m.isCoreModule(Id.attribute)) + return false; + return true; +} + +/****************************************** + * If a variable has a scope destructor call, return call for it. + * Otherwise, return NULL. + */ +private Expression callScopeDtor(VarDeclaration vd, Scope* sc) +{ + //printf("VarDeclaration::callScopeDtor() %s\n", toChars()); + + // Destruction of STC.field's is handled by buildDtor() + if (vd.storage_class & (STC.nodtor | STC.ref_ | STC.out_ | STC.field)) + { + return null; + } + + if (vd.iscatchvar) + return null; // destructor is built by `void semantic(Catch c, Scope* sc)`, not here + + Expression e = null; + // Destructors for structs and arrays of structs + Type tv = vd.type.baseElemOf(); + if (tv.ty == Tstruct) + { + StructDeclaration sd = (cast(TypeStruct)tv).sym; + if (!sd.dtor || sd.errors) + return null; + + const sz = vd.type.size(); + assert(sz != SIZE_INVALID); + if (!sz) + return null; + + if (vd.type.toBasetype().ty == Tstruct) + { + // v.__xdtor() + e = new VarExp(vd.loc, vd); + + /* This is a hack so we can call destructors on const/immutable objects. + * Need to add things like "const ~this()" and "immutable ~this()" to + * fix properly. + */ + e.type = e.type.mutableOf(); + + // Enable calling destructors on shared objects. + // The destructor is always a single, non-overloaded function, + // and must serve both shared and non-shared objects. + e.type = e.type.unSharedOf; + + e = new DotVarExp(vd.loc, e, sd.dtor, false); + e = new CallExp(vd.loc, e); + } + else + { + // __ArrayDtor(v[0 .. n]) + e = new VarExp(vd.loc, vd); + + const sdsz = sd.type.size(); + assert(sdsz != SIZE_INVALID && sdsz != 0); + const n = sz / sdsz; + SliceExp se = new SliceExp(vd.loc, e, new IntegerExp(vd.loc, 0, Type.tsize_t), + new IntegerExp(vd.loc, n, Type.tsize_t)); + + // Prevent redundant bounds check + se.upperIsInBounds = true; + se.lowerIsLessThanUpper = true; + + // This is a hack so we can call destructors on const/immutable objects. + se.type = sd.type.arrayOf(); + + e = new CallExp(vd.loc, new IdentifierExp(vd.loc, Id.__ArrayDtor), se); + } + return e; + } + // Destructors for classes + if (!(vd.storage_class & (STC.auto_ | STC.scope_) && !(vd.storage_class & STC.parameter))) + return null; + + for (ClassDeclaration cd = vd.type.isClassHandle(); cd; cd = cd.baseClass) + { + /* We can do better if there's a way with onstack + * classes to determine if there's no way the monitor + * could be set. + */ + //if (cd.isInterfaceDeclaration()) + // error("interface `%s` cannot be scope", cd.toChars()); + + if (!vd.onstack) // if any destructors + continue; + // delete'ing C++ classes crashes (and delete is deprecated anyway) + if (cd.classKind == ClassKind.cpp) + { + // Don't call non-existant dtor + if (!cd.dtor) + break; + + e = new VarExp(vd.loc, vd); + e.type = e.type.mutableOf().unSharedOf(); // Hack for mutable ctor on immutable instances + e = new DotVarExp(vd.loc, e, cd.dtor, false); + e = new CallExp(vd.loc, e); + break; + } + + // delete this; + Expression ec; + ec = new VarExp(vd.loc, vd); + e = new DeleteExp(vd.loc, ec, true); + break; + } + return e; +} + +/*************************************** + * Collect all instance fields, then determine instance size. + * Returns: + * false if failed to determine the size. + */ +bool determineSize(AggregateDeclaration ad, Loc loc) +{ + //printf("AggregateDeclaration::determineSize() %s, sizeok = %d\n", toChars(), sizeok); + + // The previous instance size finalizing had: + if (ad.type.ty == Terror || ad.errors) + return false; // failed already + if (ad.sizeok == Sizeok.done) + return true; // succeeded + + if (!ad.members) + { + .error(loc, "%s `%s` unknown size", ad.kind, ad.toPrettyChars); + return false; + } + + if (ad._scope) + dsymbolSemantic(ad, null); + + // Determine the instance size of base class first. + if (auto cd = ad.isClassDeclaration()) + { + cd = cd.baseClass; + if (cd && !cd.determineSize(loc)) + goto Lfail; + } + + // Determine instance fields when sizeok == Sizeok.none + if (!ad.determineFields()) + goto Lfail; + if (ad.sizeok != Sizeok.done) + ad.finalizeSize(); + + // this aggregate type has: + if (ad.type.ty == Terror) + return false; // marked as invalid during the finalizing. + if (ad.sizeok == Sizeok.done) + return true; // succeeded to calculate instance size. + +Lfail: + // There's unresolvable forward reference. + if (ad.type != Type.terror) + error(loc, "%s `%s` no size because of forward reference", ad.kind, ad.toPrettyChars); + // Don't cache errors from speculative semantic, might be resolvable later. + // https://issues.dlang.org/show_bug.cgi?id=16574 + if (!global.gag) + { + ad.type = Type.terror; + ad.errors = true; + } + return false; +} + +extern (C++) void addComment(Dsymbol d, const(char)* comment) +{ + scope v = new AddCommentVisitor(comment); + d.accept(v); +} + +extern (C++) class AddCommentVisitor: Visitor +{ + alias visit = Visitor.visit; + + const(char)* comment; + + this(const(char)* comment) + { + this.comment = comment; + } + + override void visit(Dsymbol d) + { + if (!comment || !*comment) + return; + + //printf("addComment '%s' to Dsymbol %p '%s'\n", comment, this, toChars()); + void* h = cast(void*)d; // just the pointer is the key + auto p = h in d.commentHashTable; + if (!p) + { + d.commentHashTable[h] = comment; + return; + } + if (strcmp(*p, comment) != 0) + { + // Concatenate the two + *p = Lexer.combineComments((*p).toDString(), comment.toDString(), true); + } + } + override void visit(AttribDeclaration atd) + { + if (comment) + { + atd.include(null).foreachDsymbol( s => s.addComment(comment) ); + } + } + override void visit(ConditionalDeclaration cd) + { + if (comment) + { + cd.decl .foreachDsymbol( s => s.addComment(comment) ); + cd.elsedecl.foreachDsymbol( s => s.addComment(comment) ); + } + } + override void visit(StaticForeachDeclaration sfd) {} +} + +void checkCtorConstInit(Dsymbol d) +{ + scope v = new CheckCtorConstInitVisitor(); + d.accept(v); +} + +private extern(C++) class CheckCtorConstInitVisitor : Visitor +{ + alias visit = Visitor.visit; + + override void visit(AttribDeclaration ad) + { + ad.include(null).foreachDsymbol( s => s.checkCtorConstInit() ); + } + + override void visit(VarDeclaration vd) + { + version (none) + { + /* doesn't work if more than one static ctor */ + if (vd.ctorinit == 0 && vd.isCtorinit() && !vd.isField()) + error("missing initializer in static constructor for const variable"); + } + } + + override void visit(Dsymbol d){} +} + +/************************************** +* Determine if this symbol is only one. +* Returns: +* false, ps = null: There are 2 or more symbols +* true, ps = null: There are zero symbols +* true, ps = symbol: The one and only one symbol +*/ +bool oneMember(Dsymbol d, out Dsymbol ps, Identifier ident) +{ + scope v = new OneMemberVisitor(ps, ident); + d.accept(v); + return v.result; +} + +private extern(C++) class OneMemberVisitor : Visitor +{ + alias visit = Visitor.visit; + + Dsymbol* ps; + Identifier ident; + bool result; + + this(out Dsymbol ps, Identifier ident) + { + this.ps = &ps; + this.ident = ident; + } + + override void visit(AttribDeclaration atb) + { + Dsymbols* d = atb.include(null); + result = Dsymbol.oneMembers(d, *ps, ident); + } + + override void visit(StaticForeachDeclaration sfd) + { + // Required to support IFTI on a template that contains a + // `static foreach` declaration. `super.oneMember` calls + // include with a `null` scope. As `static foreach` requires + // the scope for expansion, `oneMember` can only return a + // precise result once `static foreach` has been expanded. + if (sfd.cached) + { + this.visit(cast(AttribDeclaration) sfd); + } + else + { + *ps = null; // a `static foreach` declaration may in general expand to multiple symbols + result = false; + } + } + + override void visit(StorageClassDeclaration scd) + { + bool t = Dsymbol.oneMembers(scd.decl, *ps, ident); + if (t && *ps) + { + /* This is to deal with the following case: + * struct Tick { + * template to(T) { const T to() { ... } } + * } + * For eponymous function templates, the 'const' needs to get attached to 'to' + * before the semantic analysis of 'to', so that template overloading based on the + * 'this' pointer can be successful. + */ + if (FuncDeclaration fd = (*ps).isFuncDeclaration()) + { + /* Use storage_class2 instead of storage_class otherwise when we do .di generation + * we'll wind up with 'const const' rather than 'const'. + */ + /* Don't think we need to worry about mutually exclusive storage classes here + */ + fd.storage_class2 |= scd.stc; + } + } + result = t; + } + + override void visit(ConditionalDeclaration cd) + { + //printf("ConditionalDeclaration::oneMember(), inc = %d\n", condition.inc); + if (cd.condition.inc != Include.notComputed) + { + Dsymbols* d = cd.condition.include(null) ? cd.decl : cd.elsedecl; + result = Dsymbol.oneMembers(d, *ps, ident); + } + else + { + bool res = (Dsymbol.oneMembers(cd.decl, *ps, ident) && *ps is null && Dsymbol.oneMembers(cd.elsedecl, *ps, ident) && *ps is null); + *ps = null; + result = res; + } + } + + override void visit(ScopeDsymbol sd) + { + if (sd.isAnonymous()) + result = Dsymbol.oneMembers(sd.members, *ps, ident); + else { + // visit(Dsymbol dsym) + *ps = sd; + result = true; + } + } + + override void visit(StaticAssert sa) + { + //printf("StaticAssert::oneMember())\n"); + *ps = null; + result = true; + } + + override void visit(TemplateInstance ti) + { + *ps = null; + result = true; + } + + override void visit(TemplateMixin tm) + { + *ps = tm; + result = true; + } + + override void visit(Dsymbol dsym) + { + *ps = dsym; + result = true; + } +} + +/**************************************** +* Return true if any of the members are static ctors or static dtors, or if +* any members have members that are. +*/ +extern(C++) bool hasStaticCtorOrDtor(Dsymbol d) +{ + scope v = new HasStaticCtorOrDtor(); + d.accept(v); + return v.result; +} + +private extern(C++) class HasStaticCtorOrDtor : Visitor +{ + import dmd.mtype : Type; + + alias visit = Visitor.visit; + bool result; + + // attrib.d + override void visit(AttribDeclaration ad){ + result = ad.include(null).foreachDsymbol( (s) { return s.hasStaticCtorOrDtor(); } ) != 0; + } + + // dsymbol.d + override void visit(Dsymbol d){ + //printf("Dsymbol::hasStaticCtorOrDtor() %s\n", toChars()); + result = false; + } + + override void visit(ScopeDsymbol sd) { + if (sd.members) + { + for (size_t i = 0; i < sd.members.length; i++) + { + Dsymbol member = (*(sd.members))[i]; + if (member.hasStaticCtorOrDtor()) + result = true; + return; + } + } + result = false; + } + + // dtemplate.d + override void visit(TemplateDeclaration td) { + result = false; // don't scan uninstantiated templates + } + + // func.d + override void visit(StaticCtorDeclaration scd) { + result = true; + } + + override void visit(StaticDtorDeclaration sdd) @nogc nothrow pure @safe { + result = true; + } +} + +extern(C++) bool isFuncHidden(ClassDeclaration cd, FuncDeclaration fd) +{ + import dmd.funcsem : overloadApply; + //printf("ClassDeclaration.isFuncHidden(class = %s, fd = %s)\n", toChars(), fd.toPrettyChars()); + Dsymbol s = cd.search(Loc.initial, fd.ident, SearchOpt.ignoreAmbiguous | SearchOpt.ignoreErrors); + if (!s) + { + //printf("not found\n"); + /* Because, due to a hack, if there are multiple definitions + * of fd.ident, NULL is returned. + */ + return false; + } + s = s.toAlias(); + if (auto os = s.isOverloadSet()) + { + foreach (sm; os.a) + { + auto fm = sm.isFuncDeclaration(); + if (overloadApply(fm, s => fd == s.isFuncDeclaration())) + return false; + } + return true; + } + else + { + auto f = s.isFuncDeclaration(); + //printf("%s fdstart = %p\n", s.kind(), fdstart); + if (overloadApply(f, s => fd == s.isFuncDeclaration())) + return false; + return !fd.parent.isTemplateMixin(); + } +} + +version (IN_LLVM) { /* not needed */ } else +Dsymbol vtblSymbol(ClassDeclaration cd) +{ + if (!cd.vtblsym) + { + auto vtype = Type.tvoidptr.immutableOf().sarrayOf(cd.vtbl.length); + auto var = new VarDeclaration(cd.loc, vtype, Identifier.idPool("__vtbl"), null, STC.immutable_ | STC.static_); + var.addMember(null, cd); + var.isdataseg = 1; + var._linkage = LINK.d; + var.semanticRun = PASS.semanticdone; // no more semantic wanted + cd.vtblsym = var; + } + return cd.vtblsym; +} diff --git a/dmd/dtemplate.d b/dmd/dtemplate.d index 36e0060e5a..bc6ecfd7be 100644 --- a/dmd/dtemplate.d +++ b/dmd/dtemplate.d @@ -28,12 +28,12 @@ * arguments, and uses it if found. * - Otherwise, the rest of semantic is run on the `TemplateInstance`. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/dtemplate.d, _dtemplate.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/dtemplate.d, _dtemplate.d) * Documentation: https://dlang.org/phobos/dmd_dtemplate.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/dtemplate.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/dtemplate.d */ module dmd.dtemplate; @@ -50,36 +50,36 @@ import dmd.dcast; import dmd.dclass; import dmd.declaration; import dmd.dinterpret; -import dmd.dmangle; import dmd.dmodule; import dmd.dscope; import dmd.dsymbol; -import dmd.dsymbolsem; +import dmd.dsymbolsem : dsymbolSemantic, checkDeprecated, aliasSemantic, search, search_correct, setScope, importAll, include, hasStaticCtorOrDtor; import dmd.errors; import dmd.errorsink; import dmd.expression; -import dmd.expressionsem; +import dmd.expressionsem : resolveLoc, expressionSemantic, resolveProperties, checkValue; import dmd.func; -import dmd.funcsem; +import dmd.funcsem : functionSemantic, leastAsSpecialized, overloadApply; import dmd.globals; import dmd.hdrgen; import dmd.id; import dmd.identifier; import dmd.impcnvtab; import dmd.init; -import dmd.initsem; 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.semantic2; -import dmd.semantic3; -import dmd.templatesem; +import dmd.semantic3 : semantic3; +import dmd.templatesem : matchWithInstance, formatParamsWithTiargs, leastAsSpecialized, declareParameter; import dmd.tokens; -import dmd.typesem; +import dmd.typesem : hasPointers, typeSemantic, merge, merge2, resolve, toDsymbol, + addStorageClass, isBaseOf, equivalent, sarrayOf, constOf, mutableOf, unSharedOf, + unqualify, aliasthisOf, castMod, substWildTo, addMod; import dmd.visitor; import dmd.templateparamsem; @@ -136,6 +136,13 @@ inout(Parameter) isParameter(inout RootObject o) return cast(inout(Parameter))o; } +inout(Identifier) isIdentifier(inout RootObject o) +{ + if (!o || o.dyncast() != DYNCAST.identifier) + return null; + return cast(inout(Identifier))o; +} + inout(TemplateParameter) isTemplateParameter(inout RootObject o) { if (!o || o.dyncast() != DYNCAST.templateparameter) @@ -211,23 +218,18 @@ Dsymbol getDsymbol(RootObject oarg) // Try to convert Expression to symbol if (auto ve = ea.isVarExp()) return ve.var; - else if (auto fe = ea.isFuncExp()) + if (auto fe = ea.isFuncExp()) return fe.td ? fe.td : fe.fd; - else if (auto te = ea.isTemplateExp()) + if (auto te = ea.isTemplateExp()) return te.td; - else if (auto te = ea.isScopeExp()) + if (auto te = ea.isScopeExp()) return te.sds; - else - return null; - } - else - { - // Try to convert Type to symbol - if (auto ta = isType(oarg)) - return ta.toDsymbol(null); - else - return isDsymbol(oarg); // if already a symbol + return null; } + // Try to convert Type to symbol + if (auto ta = isType(oarg)) + return ta.toDsymbol(null); + return isDsymbol(oarg); // if already a symbol } @@ -423,8 +425,7 @@ private size_t arrayObjectHash(ref Objects oa1) hash = mixHash(hash, expressionHash(e1)); else if (auto s1 = isDsymbol(o1)) { - auto fa1 = s1.isFuncAliasDeclaration(); - if (fa1) + 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)); } @@ -606,9 +607,10 @@ version (IN_LLVM) Array!Expression lastConstraintNegs; /// its negative parts Objects* lastConstraintTiargs; /// template instance arguments for `lastConstraint` - extern (D) this(const ref Loc loc, Identifier ident, TemplateParameters* parameters, Expression constraint, Dsymbols* decldefs, bool ismixin = false, bool literal = false) + extern (D) this(Loc loc, Identifier ident, TemplateParameters* parameters, Expression constraint, Dsymbols* decldefs, bool ismixin = false, bool literal = false) { super(loc, ident); + this.dsym = DSYM.templateDeclaration; static if (LOG) { printf("TemplateDeclaration(this = %p, id = '%s')\n", this, ident.toChars()); @@ -758,24 +760,11 @@ else return true; } - override bool hasStaticCtorOrDtor() - { - return false; // don't scan uninstantiated templates - } - override const(char)* kind() const { return (onemember && onemember.isAggregateDeclaration()) ? onemember.kind() : "template"; } - override const(char)* toChars() const - { - HdrGenState hgs; - OutBuffer buf; - toCharsMaybeConstraints(this, buf, hgs); - return buf.extractChars(); - } - /**************************** * Similar to `toChars`, but does not print the template constraints */ @@ -913,11 +902,6 @@ else instances.remove(tibox); } - override inout(TemplateDeclaration) isTemplateDeclaration() inout - { - return this; - } - /** * Check if the last template parameter is a tuple one, * and returns it if so, else returns `null`. @@ -927,7 +911,7 @@ else */ extern (D) TemplateTupleParameter isVariadic() { - size_t dim = parameters.length; + const dim = parameters.length; if (dim == 0) return null; return (*parameters)[dim - 1].isTemplateTupleParameter(); @@ -1028,6 +1012,11 @@ size_t templateParameterLookup(Type tparam, TemplateParameters* parameters) return IDX_NOTFOUND; } +private auto X(T, U)(T m, U n) +{ + return (m << 4) | n; +} + ubyte deduceWildHelper(Type t, Type* at, Type tparam) { if ((tparam.mod & MODFlags.wild) == 0) @@ -1035,11 +1024,6 @@ ubyte deduceWildHelper(Type t, Type* at, Type tparam) *at = null; - auto X(T, U)(T U, U T) - { - return (U << 4) | T; - } - switch (X(tparam.mod, t.mod)) { case X(MODFlags.wild, 0): @@ -1114,12 +1098,6 @@ private Type rawTypeMerge(Type t1, Type t2) MATCH deduceTypeHelper(Type t, out Type at, Type tparam) { // 9*9 == 81 cases - - auto X(T, U)(T U, U T) - { - return (U << 4) | T; - } - switch (X(tparam.mod, t.mod)) { case X(0, 0): @@ -1666,62 +1644,66 @@ MATCH deduceType(RootObject o, Scope* sc, Type tparam, ref TemplateParameters pa override void visit(TypeSArray t) { // Extra check that array dimensions must match - if (tparam) + if (!tparam) { - if (tparam.ty == Tarray) - { - MATCH m = deduceType(t.next, sc, tparam.nextOf(), parameters, dedtypes, wm); - result = (m >= MATCH.constant) ? MATCH.convert : MATCH.nomatch; - return; - } + visit(cast(Type)t); + return; + } + + if (tparam.ty == Tarray) + { + MATCH m = deduceType(t.next, sc, tparam.nextOf(), parameters, dedtypes, wm); + result = (m >= MATCH.constant) ? MATCH.convert : MATCH.nomatch; + return; + } - TemplateParameter tp = null; - Expression edim = null; - size_t i; - if (auto tsa = tparam.isTypeSArray()) + TemplateParameter tp = null; + Expression edim = null; + size_t i; + if (auto tsa = tparam.isTypeSArray()) + { + if (tsa.dim.isVarExp() && tsa.dim.isVarExp().var.storage_class & STC.templateparameter) { - if (tsa.dim.isVarExp() && tsa.dim.isVarExp().var.storage_class & STC.templateparameter) - { - Identifier id = tsa.dim.isVarExp().var.ident; - i = templateIdentifierLookup(id, ¶meters); - assert(i != IDX_NOTFOUND); - tp = parameters[i]; - } - else - edim = tsa.dim; + Identifier id = tsa.dim.isVarExp().var.ident; + i = templateIdentifierLookup(id, ¶meters); + assert(i != IDX_NOTFOUND); + tp = parameters[i]; } - else if (auto taa = tparam.isTypeAArray()) + else + edim = tsa.dim; + } + else if (auto taa = tparam.isTypeAArray()) + { + i = templateParameterLookup(taa.index, ¶meters); + if (i != IDX_NOTFOUND) + tp = parameters[i]; + else { - i = templateParameterLookup(taa.index, ¶meters); - if (i != IDX_NOTFOUND) - tp = parameters[i]; - else + Loc loc; + // The "type" (it hasn't been resolved yet) of the function parameter + // does not have a location but the parameter it is related to does, + // so we use that for the resolution (better error message). + if (inferStart < parameters.length) { - Loc loc; - // The "type" (it hasn't been resolved yet) of the function parameter - // does not have a location but the parameter it is related to does, - // so we use that for the resolution (better error message). - if (inferStart < parameters.length) - { - TemplateParameter loctp = parameters[inferStart]; - loc = loctp.loc; - } - - Expression e; - Type tx; - Dsymbol s; - taa.index.resolve(loc, sc, e, tx, s); - edim = s ? getValue(s) : getValue(e); + TemplateParameter loctp = parameters[inferStart]; + loc = loctp.loc; } - } - if ((tp && tp.matchArg(sc, t.dim, i, ¶meters, dedtypes, null)) || - (edim && edim.isIntegerExp() && edim.toInteger() == t.dim.toInteger()) - ) - { - result = deduceType(t.next, sc, tparam.nextOf(), parameters, dedtypes, wm); - return; + + Expression e; + Type tx; + Dsymbol s; + taa.index.resolve(loc, sc, e, tx, s); + edim = s ? getValue(s) : getValue(e); } } + if ((tp && tp.matchArg(sc, t.dim, i, ¶meters, dedtypes, null)) || + (edim && edim.isIntegerExp() && edim.toInteger() == t.dim.toInteger()) + ) + { + result = deduceType(t.next, sc, tparam.nextOf(), parameters, dedtypes, wm); + return; + } + visit(cast(Type)t); } @@ -1746,132 +1728,137 @@ MATCH deduceType(RootObject o, Scope* sc, Type tparam, ref TemplateParameters pa if (!tparam) return visit(cast(Type)t); - if (auto tp = tparam.isTypeFunction()) + auto tp = tparam.isTypeFunction(); + if (!tp) { - if (t.parameterList.varargs != tp.parameterList.varargs || t.linkage != tp.linkage) - { - result = MATCH.nomatch; - return; - } + visit(cast(Type)t); + return; + } + + if (t.parameterList.varargs != tp.parameterList.varargs || t.linkage != tp.linkage) + { + result = MATCH.nomatch; + return; + } + + foreach (fparam; *tp.parameterList.parameters) + { + // https://issues.dlang.org/show_bug.cgi?id=2579 + // Apply function parameter storage classes to parameter types + fparam.type = fparam.type.addStorageClass(fparam.storageClass); + fparam.storageClass &= ~STC.TYPECTOR; - foreach (fparam; *tp.parameterList.parameters) + // https://issues.dlang.org/show_bug.cgi?id=15243 + // Resolve parameter type if it's not related with template parameters + if (!reliesOnTemplateParameters(fparam.type, parameters[inferStart .. parameters.length])) { - // https://issues.dlang.org/show_bug.cgi?id=2579 - // Apply function parameter storage classes to parameter types - fparam.type = fparam.type.addStorageClass(fparam.storageClass); - fparam.storageClass &= ~STC.TYPECTOR; - - // https://issues.dlang.org/show_bug.cgi?id=15243 - // Resolve parameter type if it's not related with template parameters - if (!reliesOnTemplateParameters(fparam.type, parameters[inferStart .. parameters.length])) + auto tx = fparam.type.typeSemantic(Loc.initial, sc); + if (tx.ty == Terror) { - auto tx = fparam.type.typeSemantic(Loc.initial, sc); - if (tx.ty == Terror) - { - result = MATCH.nomatch; - return; - } - fparam.type = tx; + result = MATCH.nomatch; + return; } + fparam.type = tx; } + } - const size_t nfargs = t.parameterList.length; - size_t nfparams = tp.parameterList.length; + const size_t nfargs = t.parameterList.length; + size_t nfparams = tp.parameterList.length; - /* See if tuple match + /* See if tuple match + */ + if (nfparams > 0 && nfargs >= nfparams - 1) + { + /* See if 'A' of the template parameter matches 'A' + * of the type of the last function parameter. */ - if (nfparams > 0 && nfargs >= nfparams - 1) + Parameter fparam = tp.parameterList[nfparams - 1]; + assert(fparam); + assert(fparam.type); + if (fparam.type.ty != Tident) + goto L1; + TypeIdentifier tid = fparam.type.isTypeIdentifier(); + if (tid.idents.length) + goto L1; + + /* Look through parameters to find tuple matching tid.ident + */ + size_t tupi = 0; + for (; 1; tupi++) { - /* See if 'A' of the template parameter matches 'A' - * of the type of the last function parameter. - */ - Parameter fparam = tp.parameterList[nfparams - 1]; - assert(fparam); - assert(fparam.type); - if (fparam.type.ty != Tident) - goto L1; - TypeIdentifier tid = fparam.type.isTypeIdentifier(); - if (tid.idents.length) + if (tupi == parameters.length) goto L1; + TemplateParameter tx = parameters[tupi]; + TemplateTupleParameter tup = tx.isTemplateTupleParameter(); + if (tup && tup.ident.equals(tid.ident)) + break; + } - /* Look through parameters to find tuple matching tid.ident - */ - size_t tupi = 0; - for (; 1; tupi++) + /* The types of the function arguments [nfparams - 1 .. nfargs] + * now form the tuple argument. + */ + size_t tuple_dim = nfargs - (nfparams - 1); + + /* See if existing tuple, and whether it matches or not + */ + RootObject o = dedtypes[tupi]; + if (o) + { + // Existing deduced argument must be a tuple, and must match + Tuple tup = isTuple(o); + if (!tup || tup.objects.length != tuple_dim) { - if (tupi == parameters.length) - goto L1; - TemplateParameter tx = parameters[tupi]; - TemplateTupleParameter tup = tx.isTemplateTupleParameter(); - if (tup && tup.ident.equals(tid.ident)) - break; + result = MATCH.nomatch; + return; } - - /* The types of the function arguments [nfparams - 1 .. nfargs] - * now form the tuple argument. - */ - size_t tuple_dim = nfargs - (nfparams - 1); - - /* See if existing tuple, and whether it matches or not - */ - RootObject o = dedtypes[tupi]; - if (o) + for (size_t i = 0; i < tuple_dim; i++) { - // Existing deduced argument must be a tuple, and must match - Tuple tup = isTuple(o); - if (!tup || tup.objects.length != tuple_dim) + Parameter arg = t.parameterList[nfparams - 1 + i]; + if (!arg.type.equals(tup.objects[i])) { result = MATCH.nomatch; return; } - for (size_t i = 0; i < tuple_dim; i++) - { - Parameter arg = t.parameterList[nfparams - 1 + i]; - if (!arg.type.equals(tup.objects[i])) - { - result = MATCH.nomatch; - return; - } - } } - else + } + else + { + // Create new tuple + auto tup = new Tuple(tuple_dim); + for (size_t i = 0; i < tuple_dim; i++) { - // Create new tuple - auto tup = new Tuple(tuple_dim); - for (size_t i = 0; i < tuple_dim; i++) - { - Parameter arg = t.parameterList[nfparams - 1 + i]; - tup.objects[i] = arg.type; - } - dedtypes[tupi] = tup; + Parameter arg = t.parameterList[nfparams - 1 + i]; + tup.objects[i] = arg.type; } - nfparams--; // don't consider the last parameter for type deduction - goto L2; + dedtypes[tupi] = tup; } + nfparams--; // don't consider the last parameter for type deduction + goto L2; + } - L1: - if (nfargs != nfparams) + L1: + if (nfargs != nfparams) + { + result = MATCH.nomatch; + return; + } + L2: + assert(nfparams <= tp.parameterList.length); + foreach (i, ap; tp.parameterList) + { + if (i == nfparams) + break; + + Parameter a = t.parameterList[i]; + + if (!a.isCovariant(t.isRef, ap) || + !deduceType(a.type, sc, ap.type, parameters, dedtypes)) { result = MATCH.nomatch; return; } - L2: - assert(nfparams <= tp.parameterList.length); - foreach (i, ap; tp.parameterList) - { - if (i == nfparams) - break; - - Parameter a = t.parameterList[i]; - - if (!a.isCovariant(t.isref, ap) || - !deduceType(a.type, sc, ap.type, parameters, dedtypes)) - { - result = MATCH.nomatch; - return; - } - } } + visit(cast(Type)t); } @@ -1898,78 +1885,82 @@ MATCH deduceType(RootObject o, Scope* sc, Type tparam, ref TemplateParameters pa override void visit(TypeInstance t) { // Extra check - if (tparam && tparam.ty == Tinstance && t.tempinst.tempdecl) + if (!tparam || tparam.ty != Tinstance || !t.tempinst.tempdecl) { - TemplateDeclaration tempdecl = t.tempinst.tempdecl.isTemplateDeclaration(); - assert(tempdecl); + visit(cast(Type)t); + return; + } - TypeInstance tp = tparam.isTypeInstance(); + TemplateDeclaration tempdecl = t.tempinst.tempdecl.isTemplateDeclaration(); + assert(tempdecl); - //printf("tempinst.tempdecl = %p\n", tempdecl); - //printf("tp.tempinst.tempdecl = %p\n", tp.tempinst.tempdecl); - if (!tp.tempinst.tempdecl) - { - //printf("tp.tempinst.name = '%s'\n", tp.tempinst.name.toChars()); + TypeInstance tp = tparam.isTypeInstance(); - /* Handle case of: - * template Foo(T : sa!(T), alias sa) + //printf("tempinst.tempdecl = %p\n", tempdecl); + //printf("tp.tempinst.tempdecl = %p\n", tp.tempinst.tempdecl); + if (!tp.tempinst.tempdecl) + { + //printf("tp.tempinst.name = '%s'\n", tp.tempinst.name.toChars()); + + /* Handle case of: + * template Foo(T : sa!(T), alias sa) + */ + size_t i = templateIdentifierLookup(tp.tempinst.name, ¶meters); + if (i == IDX_NOTFOUND) + { + /* Didn't find it as a parameter identifier. Try looking + * it up and seeing if is an alias. + * https://issues.dlang.org/show_bug.cgi?id=1454 */ - size_t i = templateIdentifierLookup(tp.tempinst.name, ¶meters); - if (i == IDX_NOTFOUND) + auto tid = new TypeIdentifier(tp.loc, tp.tempinst.name); + Type tx; + Expression e; + Dsymbol s; + tid.resolve(tp.loc, sc, e, tx, s); + if (tx) { - /* Didn't find it as a parameter identifier. Try looking - * it up and seeing if is an alias. - * https://issues.dlang.org/show_bug.cgi?id=1454 - */ - auto tid = new TypeIdentifier(tp.loc, tp.tempinst.name); - Type tx; - Expression e; - Dsymbol s; - tid.resolve(tp.loc, sc, e, tx, s); - if (tx) + s = tx.toDsymbol(sc); + if (TemplateInstance ti = s ? s.parent.isTemplateInstance() : null) { - s = tx.toDsymbol(sc); - if (TemplateInstance ti = s ? s.parent.isTemplateInstance() : null) - { - // https://issues.dlang.org/show_bug.cgi?id=14290 - // Try to match with ti.tempecl, - // only when ti is an enclosing instance. - Dsymbol p = sc.parent; - while (p && p != ti) - p = p.parent; - if (p) - s = ti.tempdecl; - } + // https://issues.dlang.org/show_bug.cgi?id=14290 + // Try to match with ti.tempecl, + // only when ti is an enclosing instance. + Dsymbol p = sc.parent; + while (p && p != ti) + p = p.parent; + if (p) + s = ti.tempdecl; } - if (s) + } + if (s) + { + s = s.toAlias(); + TemplateDeclaration td = s.isTemplateDeclaration(); + if (td) { - s = s.toAlias(); - TemplateDeclaration td = s.isTemplateDeclaration(); - if (td) + if (td.overroot) + td = td.overroot; + for (; td; td = td.overnext) { - if (td.overroot) - td = td.overroot; - for (; td; td = td.overnext) - { - if (td == tempdecl) - goto L2; - } + if (td == tempdecl) + goto L2; } } - goto Lnomatch; } - - TemplateParameter tpx = parameters[i]; - if (!tpx.matchArg(sc, tempdecl, i, ¶meters, dedtypes, null)) - goto Lnomatch; - } - else if (tempdecl != tp.tempinst.tempdecl) goto Lnomatch; + } - L2: - if (!resolveTemplateInstantiation(sc, ¶meters, t.tempinst.tiargs, &t.tempinst.tdtypes, tempdecl, tp, &dedtypes)) + TemplateParameter tpx = parameters[i]; + if (!tpx.matchArg(sc, tempdecl, i, ¶meters, dedtypes, null)) goto Lnomatch; } + else if (tempdecl != tp.tempinst.tempdecl) + goto Lnomatch; + + L2: + if (!resolveTemplateInstantiation(sc, ¶meters, t.tempinst.tiargs, &t.tempinst.tdtypes, tempdecl, tp, &dedtypes)) + goto Lnomatch; + visit(cast(Type)t); return; @@ -2575,8 +2566,7 @@ MATCH deduceType(RootObject o, Scope* sc, Type tparam, ref TemplateParameters pa */ private void deduceBaseClassParameters(ref BaseClass b, Scope* sc, Type tparam, ref TemplateParameters parameters, ref Objects dedtypes, ref Objects best, ref int numBaseClassMatches) { - TemplateInstance parti = b.sym ? b.sym.parent.isTemplateInstance() : null; - if (parti) + if (TemplateInstance parti = b.sym ? b.sym.parent.isTemplateInstance() : null) { // Make a temporary copy of dedtypes so we don't destroy it auto tmpdedtypes = new Objects(dedtypes.length); @@ -3019,6 +3009,8 @@ private bool reliesOnTemplateParameters(Expression e, TemplateParameter[] tparam 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); @@ -3195,7 +3187,7 @@ extern (C++) class TemplateParameter : ASTNode bool dependent; /* ======================== TemplateParameter =============================== */ - extern (D) this(const ref Loc loc, Identifier ident) @safe + extern (D) this(Loc loc, Identifier ident) @safe { this.loc = loc; this.ident = ident; @@ -3234,7 +3226,7 @@ extern (C++) class TemplateParameter : ASTNode abstract RootObject specialization(); - abstract RootObject defaultArg(const ref Loc instLoc, Scope* sc); + abstract RootObject defaultArg(Loc instLoc, Scope* sc); abstract bool hasDefaultArg(); @@ -3248,10 +3240,6 @@ extern (C++) class TemplateParameter : ASTNode return DYNCAST.templateparameter; } - /* Create dummy argument based on parameter. - */ - abstract RootObject dummyArg(); - override void accept(Visitor v) { v.visit(this); @@ -3270,7 +3258,7 @@ extern (C++) class TemplateTypeParameter : TemplateParameter extern (D) __gshared Type tdummy = null; - extern (D) this(const ref Loc loc, Identifier ident, Type specType, Type defaultType) @safe + extern (D) this(Loc loc, Identifier ident, Type specType, Type defaultType) @safe { super(loc, ident); this.specType = specType; @@ -3316,7 +3304,7 @@ extern (C++) class TemplateTypeParameter : TemplateParameter return specType; } - override final RootObject defaultArg(const ref Loc instLoc, Scope* sc) + override final RootObject defaultArg(Loc instLoc, Scope* sc) { Type t = defaultType; if (t) @@ -3332,19 +3320,6 @@ extern (C++) class TemplateTypeParameter : TemplateParameter return defaultType !is null; } - override final RootObject dummyArg() - { - Type t = specType; - if (!t) - { - // Use this for alias-parameter's too (?) - if (!tdummy) - tdummy = new TypeIdentifier(loc, ident); - t = tdummy; - } - return t; - } - override void accept(Visitor v) { v.visit(this); @@ -3358,7 +3333,7 @@ extern (C++) class TemplateTypeParameter : TemplateParameter */ extern (C++) final class TemplateThisParameter : TemplateTypeParameter { - extern (D) this(const ref Loc loc, Identifier ident, Type specType, Type defaultType) @safe + extern (D) this(Loc loc, Identifier ident, Type specType, Type defaultType) @safe { super(loc, ident, specType, defaultType); } @@ -3392,7 +3367,7 @@ extern (C++) final class TemplateValueParameter : TemplateParameter extern (D) __gshared Expression[void*] edummies; - extern (D) this(const ref Loc loc, Identifier ident, Type valType, + extern (D) this(Loc loc, Identifier ident, Type valType, Expression specValue, Expression defaultValue) @safe { super(loc, ident); @@ -3449,37 +3424,38 @@ extern (C++) final class TemplateValueParameter : TemplateParameter return specValue; } - override RootObject defaultArg(const ref Loc instLoc, Scope* sc) + override RootObject defaultArg(Loc instLoc, Scope* sc) { Expression e = defaultValue; - if (e) - { - e = e.syntaxCopy(); - Scope* sc2 = sc.push(); - sc2.inDefaultArg = true; - e = e.expressionSemantic(sc2); - sc2.pop(); - if (e is null) - return null; - if (auto te = e.isTemplateExp()) - { - assert(sc && sc.tinst); - if (te.td == sc.tinst.tempdecl) - { - // defaultValue is a reference to its template declaration - // i.e: `template T(int arg = T)` - // Raise error now before calling resolveProperties otherwise we'll - // start looping on the expansion of the template instance. - auto td = sc.tinst.tempdecl; - .error(td.loc, "%s `%s` recursive template expansion", td.kind, td.toPrettyChars); - return ErrorExp.get(); - } + if (!e) + return null; + + e = e.syntaxCopy(); + Scope* sc2 = sc.push(); + sc2.inDefaultArg = true; + e = e.expressionSemantic(sc2); + sc2.pop(); + if (e is null) + return null; + if (auto te = e.isTemplateExp()) + { + assert(sc && sc.tinst); + if (te.td == sc.tinst.tempdecl) + { + // defaultValue is a reference to its template declaration + // i.e: `template T(int arg = T)` + // Raise error now before calling resolveProperties otherwise we'll + // start looping on the expansion of the template instance. + auto td = sc.tinst.tempdecl; + .error(td.loc, "%s `%s` recursive template expansion", td.kind, td.toPrettyChars); + return ErrorExp.get(); } - if ((e = resolveProperties(sc, e)) is null) - return null; - e = e.resolveLoc(instLoc, sc); // use the instantiated loc - e = e.optimize(WANTvalue); } + if ((e = resolveProperties(sc, e)) is null) + return null; + e = e.resolveLoc(instLoc, sc); // use the instantiated loc + e = e.optimize(WANTvalue); + return e; } @@ -3488,24 +3464,6 @@ extern (C++) final class TemplateValueParameter : TemplateParameter return defaultValue !is null; } - override RootObject dummyArg() - { - Expression e = specValue; - if (!e) - { - // Create a dummy value - auto pe = cast(void*)valType in edummies; - if (!pe) - { - e = valType.defaultInit(Loc.initial); - edummies[cast(void*)valType] = e; - } - else - e = *pe; - } - return e; - } - override void accept(Visitor v) { v.visit(this); @@ -3525,7 +3483,7 @@ extern (C++) final class TemplateAliasParameter : TemplateParameter extern (D) __gshared Dsymbol sdummy = null; - extern (D) this(const ref Loc loc, Identifier ident, Type specType, RootObject specAlias, RootObject defaultAlias) @safe + extern (D) this(Loc loc, Identifier ident, Type specType, RootObject specAlias, RootObject defaultAlias) @safe { super(loc, ident); this.specType = specType; @@ -3563,7 +3521,7 @@ extern (C++) final class TemplateAliasParameter : TemplateParameter return specAlias; } - override RootObject defaultArg(const ref Loc instLoc, Scope* sc) + override RootObject defaultArg(Loc instLoc, Scope* sc) { RootObject da = defaultAlias; if (auto ta = isType(defaultAlias)) @@ -3591,18 +3549,6 @@ extern (C++) final class TemplateAliasParameter : TemplateParameter return defaultAlias !is null; } - override RootObject dummyArg() - { - RootObject s = specAlias; - if (!s) - { - if (!sdummy) - sdummy = new Dsymbol(); - s = sdummy; - } - return s; - } - override void accept(Visitor v) { v.visit(this); @@ -3616,7 +3562,7 @@ extern (C++) final class TemplateAliasParameter : TemplateParameter */ extern (C++) final class TemplateTupleParameter : TemplateParameter { - extern (D) this(const ref Loc loc, Identifier ident) @safe + extern (D) this(Loc loc, Identifier ident) @safe { super(loc, ident); } @@ -3670,7 +3616,7 @@ extern (C++) final class TemplateTupleParameter : TemplateParameter return null; } - override RootObject defaultArg(const ref Loc instLoc, Scope* sc) + override RootObject defaultArg(Loc instLoc, Scope* sc) { return null; } @@ -3680,11 +3626,6 @@ extern (C++) final class TemplateTupleParameter : TemplateParameter return false; } - override RootObject dummyArg() - { - return null; - } - override void accept(Visitor v) { v.visit(this); @@ -3799,13 +3740,14 @@ extern (C++) class TemplateInstance : ScopeDsymbol } } - extern (D) this(const ref Loc loc, Identifier ident, Objects* tiargs) scope + extern (D) this(Loc loc, Identifier ident, Objects* tiargs) scope { super(loc, null); static if (LOG) { printf("TemplateInstance(this = %p, ident = '%s')\n", this, ident ? ident.toChars() : "null"); } + this.dsym = DSYM.templateInstance; this.name = ident; this.tiargs = tiargs; } @@ -3814,13 +3756,14 @@ extern (C++) class TemplateInstance : ScopeDsymbol * This constructor is only called when we figured out which function * template to instantiate. */ - extern (D) this(const ref Loc loc, TemplateDeclaration td, Objects* tiargs) scope + extern (D) this(Loc loc, TemplateDeclaration td, Objects* tiargs) scope { super(loc, null); static if (LOG) { printf("TemplateInstance(this = %p, tempdecl = '%s')\n", this, td.toChars()); } + this.dsym = DSYM.templateInstance; this.name = td.ident; this.tiargs = tiargs; this.tempdecl = td; @@ -3891,19 +3834,6 @@ extern (C++) class TemplateInstance : ScopeDsymbol return "template instance"; } - override bool oneMember(out Dsymbol ps, Identifier ident) - { - ps = null; - return true; - } - - override const(char)* toChars() const - { - OutBuffer buf; - toCBufferInstance(this, buf); - return buf.extractChars(); - } - override final const(char)* toPrettyCharsHelper() { OutBuffer buf; @@ -3950,7 +3880,7 @@ extern (C++) class TemplateInstance : ScopeDsymbol // Set error here as we don't want it to depend on the number of // entries that are being printed. if (cl == Classification.error || - (cl == Classification.warning && global.params.warnings == DiagnosticReporting.error) || + (cl == Classification.warning && global.params.useWarnings == DiagnosticReporting.error) || (cl == Classification.deprecation && global.params.useDeprecated == DiagnosticReporting.error)) cur.errors = true; @@ -3966,7 +3896,7 @@ extern (C++) class TemplateInstance : ScopeDsymbol if (n_instantiations <= max_shown) { for (TemplateInstance cur = this; cur; cur = cur.tinst) - printFn(cur.loc, format, cur.toChars()); + printFn(cur.loc, format, cur.toErrMsg()); } else if (n_instantiations - n_totalrecursions <= max_shown) { @@ -4034,66 +3964,63 @@ extern (C++) class TemplateInstance : ScopeDsymbol if (enclosing != ti.enclosing) { //printf("test2 enclosing %s ti.enclosing %s\n", enclosing ? enclosing.toChars() : "", ti.enclosing ? ti.enclosing.toChars() : ""); - goto Lnotequals; + return false; } //printf("parent = %s, ti.parent = %s\n", parent.toPrettyChars(), ti.parent.toPrettyChars()); if (!arrayObjectMatch(tdtypes, ti.tdtypes)) - goto Lnotequals; + return false; /* Template functions may have different instantiations based on * "auto ref" parameters. */ - if (auto fd = ti.toAlias().isFuncDeclaration()) - { - if (!fd.errors) - { - auto resolvedArgs = fd.type.isTypeFunction().resolveNamedArgs( - ArgumentList(this.fargs, this.fnames), null); + auto fd = ti.toAlias().isFuncDeclaration(); + if (!fd) + return true; + if (fd.errors) + return true; - // 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; + auto resolvedArgs = fd.type.isTypeFunction().resolveNamedArgs( + ArgumentList(this.fargs, this.fnames), null); - Expression[] args = resolvedArgs ? (*resolvedArgs)[] : []; + // 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; - auto fparameters = fd.getParameterList(); - size_t nfparams = fparameters.length; // Num function parameters - for (size_t j = 0; j < nfparams; j++) - { - Parameter fparam = fparameters[j]; - if (fparam.storageClass & STC.autoref) // if "auto ref" - { - Expression farg = (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) - goto Lnotequals; - if (farg.isLvalue()) - { - if (!(fparam.storageClass & STC.ref_)) - goto Lnotequals; // auto ref's don't match - } - else - { - if (fparam.storageClass & STC.ref_) - goto Lnotequals; // auto ref's don't match - } - } - } + 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; - - Lnotequals: - return false; } extern (D) final size_t toHash() @@ -4250,77 +4177,75 @@ version (IN_LLVM) // Elide codegen because there's no instantiation from any root modules. return false; } - else - { - // Prefer instantiations from non-root modules, to minimize object code size. - /* If a TemplateInstance is ever instantiated from a non-root module, - * we do not have to generate code for it, - * because it will be generated when the non-root module is compiled. - * - * But, if the non-root 'minst' imports any root modules, it might still need codegen. - * - * The problem is if A imports B, and B imports A, and both A - * and B instantiate the same template, does the compilation of A - * or the compilation of B do the actual instantiation? - * - * See https://issues.dlang.org/show_bug.cgi?id=2500. - * - * => Elide codegen if there is at least one instantiation from a non-root module - * which doesn't import any root modules. - */ - static ThreeState needsCodegenRootOnly(TemplateInstance tithis, TemplateInstance tinst) + // Prefer instantiations from non-root modules, to minimize object code size. + + /* If a TemplateInstance is ever instantiated from a non-root module, + * we do not have to generate code for it, + * because it will be generated when the non-root module is compiled. + * + * But, if the non-root 'minst' imports any root modules, it might still need codegen. + * + * The problem is if A imports B, and B imports A, and both A + * and B instantiate the same template, does the compilation of A + * or the compilation of B do the actual instantiation? + * + * See https://issues.dlang.org/show_bug.cgi?id=2500. + * + * => Elide codegen if there is at least one instantiation from a non-root module + * which doesn't import any root modules. + */ + static ThreeState needsCodegenRootOnly(TemplateInstance tithis, TemplateInstance tinst) + { + // If the ancestor isn't speculative, + // 1. do codegen if the ancestor needs it + // 2. elide codegen if the ancestor doesn't need it (non-root instantiation of ancestor incl. subtree) + if (tinst && tinst.inst) { - // If the ancestor isn't speculative, - // 1. do codegen if the ancestor needs it - // 2. elide codegen if the ancestor doesn't need it (non-root instantiation of ancestor incl. subtree) - if (tinst && tinst.inst) + tinst = tinst.inst; + const needsCodegen = tinst.needsCodegen(); // sets tinst.minst + if (tinst.minst) // not speculative { - tinst = tinst.inst; - const needsCodegen = tinst.needsCodegen(); // sets tinst.minst - if (tinst.minst) // not speculative - { - tithis.minst = tinst.minst; // cache result - return needsCodegen ? ThreeState.yes : ThreeState.no; - } + tithis.minst = tinst.minst; // cache result + return needsCodegen ? ThreeState.yes : ThreeState.no; } + } - // Elide codegen if `this` doesn't need it. - if (tithis.minst && !tithis.minst.isRoot() && !tithis.minst.rootImports()) - return ThreeState.no; + // Elide codegen if `this` doesn't need it. + if (tithis.minst && !tithis.minst.isRoot() && !tithis.minst.rootImports()) + return ThreeState.no; - return ThreeState.none; - } + return ThreeState.none; + } - if (const needsCodegen = needsCodegenRootOnly(this, tinst)) - return needsCodegen == ThreeState.yes ? true : false; + if (const needsCodegen = needsCodegenRootOnly(this, tinst)) + return needsCodegen == ThreeState.yes ? true : false; - // Elide codegen if a (non-speculative) sibling doesn't need it. - for (; tnext; tnext = tnext.tnext) + // Elide codegen if a (non-speculative) sibling doesn't need it. + for (; tnext; tnext = tnext.tnext) + { + const needsCodegen = needsCodegenRootOnly(tnext, tnext.tinst); // sets tnext.minst + if (tnext.minst) // not speculative { - const needsCodegen = needsCodegenRootOnly(tnext, tnext.tinst); // sets tnext.minst - if (tnext.minst) // not speculative + if (needsCodegen == ThreeState.no) { - if (needsCodegen == ThreeState.no) - { - minst = tnext.minst; // cache result - assert(!minst.isRoot() && !minst.rootImports()); - return false; - } - else if (!minst) - { - minst = tnext.minst; // cache result from non-speculative sibling - // continue searching - } - else if (needsCodegen != ThreeState.none) - break; + minst = tnext.minst; // cache result + assert(!minst.isRoot() && !minst.rootImports()); + return false; + } + else if (!minst) + { + minst = tnext.minst; // cache result from non-speculative sibling + // continue searching } + else if (needsCodegen != ThreeState.none) + break; } - - // Unless `this` is still speculative (=> all further siblings speculative too), - // do codegen because we found no guaranteed-codegen'd non-root instantiation. - return minst !is null; } + + // Unless `this` is still speculative (=> all further siblings speculative too), + // do codegen because we found no guaranteed-codegen'd non-root instantiation. + return minst !is null; } /********************************************** @@ -4569,7 +4494,7 @@ version (IN_LLVM) * Returns: * false if one or more arguments have errors. */ - extern (D) static bool semanticTiargs(const ref Loc loc, Scope* sc, Objects* tiargs, int flags, TupleDeclaration atd = null) + extern (D) static bool semanticTiargs(Loc loc, Scope* sc, Objects* tiargs, int flags, TupleDeclaration atd = null) { // Run semantic on each argument, place results in tiargs[] //printf("+TemplateInstance.semanticTiargs()\n"); @@ -4579,14 +4504,12 @@ version (IN_LLVM) // The arguments are not treated as part of a default argument, // because they are evaluated at compile time. - const inCondition = !!(sc.flags & SCOPE.condition); - // For master branch: const inCondition = sc.condition; + const inCondition = sc.condition; sc = sc.push(); sc.inDefaultArg = false; // https://issues.dlang.org/show_bug.cgi?id=24699 - sc.flags |= SCOPE.condition * inCondition; - // For master branch: sc.condition = inCondition; + sc.condition = inCondition; for (size_t j = 0; j < tiargs.length; j++) { @@ -4681,7 +4604,7 @@ version (IN_LLVM) { if (ea.checkValue()) // check void expression ea = ErrorExp.get(); - uint olderrs = global.errors; + const olderrs = global.errors; ea = ea.ctfeInterpret(); if (global.errors != olderrs) ea = ErrorExp.get(); @@ -4893,7 +4816,7 @@ version (IN_LLVM) printf("TemplateInstance.findBestMatch()\n"); } - uint errs = global.errors; + const errs = global.errors; TemplateDeclaration td_last = null; Objects dedtypes; @@ -5108,135 +5031,6 @@ version (IN_LLVM) return (errs == global.errors); } - /***************************************************** - * Determine if template instance is really a template function, - * and that template function needs to infer types from the function - * arguments. - * - * Like findBestMatch, iterate possible template candidates, - * but just looks only the necessity of type inference. - */ - extern (D) final bool needsTypeInference(Scope* sc, int flag = 0) - { - //printf("TemplateInstance.needsTypeInference() %s\n", toChars()); - if (semanticRun != PASS.initial) - return false; - - uint olderrs = global.errors; - Objects dedtypes; - size_t count = 0; - - auto tovers = tempdecl.isOverloadSet(); - foreach (size_t oi; 0 .. tovers ? tovers.a.length : 1) - { - Dsymbol dstart = tovers ? tovers.a[oi] : tempdecl; - int r = overloadApply(dstart, (Dsymbol s) - { - auto td = s.isTemplateDeclaration(); - if (!td) - return 0; - - /* If any of the overloaded template declarations need inference, - * then return true - */ - if (!td.onemember) - return 0; - if (auto td2 = td.onemember.isTemplateDeclaration()) - { - if (!td2.onemember || !td2.onemember.isFuncDeclaration()) - return 0; - if (tiargs.length >= td.parameters.length - (td.isVariadic() ? 1 : 0)) - return 0; - return 1; - } - auto fd = td.onemember.isFuncDeclaration(); - if (!fd || fd.type.ty != Tfunction) - return 0; - - foreach (tp; *td.parameters) - { - if (tp.isTemplateThisParameter()) - return 1; - } - - /* Determine if the instance arguments, tiargs, are all that is necessary - * to instantiate the template. - */ - //printf("tp = %p, td.parameters.length = %d, tiargs.length = %d\n", tp, td.parameters.length, tiargs.length); - auto tf = fd.type.isTypeFunction(); - if (tf.parameterList.length) - { - auto tp = td.isVariadic(); - if (tp && td.parameters.length > 1) - return 1; - - if (!tp && tiargs.length < td.parameters.length) - { - // Can remain tiargs be filled by default arguments? - foreach (size_t i; tiargs.length .. td.parameters.length) - { - if (!(*td.parameters)[i].hasDefaultArg()) - return 1; - } - } - - foreach (i, fparam; tf.parameterList) - { - // 'auto ref' needs inference. - if (fparam.storageClass & STC.auto_) - return 1; - } - } - - if (!flag) - { - /* Calculate the need for overload resolution. - * When only one template can match with tiargs, inference is not necessary. - */ - dedtypes.setDim(td.parameters.length); - dedtypes.zero(); - if (td.semanticRun == PASS.initial) - { - if (td._scope) - { - // Try to fix forward reference. Ungag errors while doing so. - Ungag ungag = td.ungagSpeculative(); - td.dsymbolSemantic(td._scope); - } - if (td.semanticRun == PASS.initial) - { - .error(loc, "%s `%s` `%s` forward references template declaration `%s`", kind, toPrettyChars, toChars(), td.toChars()); - return 1; - } - } - MATCH m = matchWithInstance(sc, td, this, dedtypes, ArgumentList(), 0); - if (m == MATCH.nomatch) - return 0; - } - - /* If there is more than one function template which matches, we may - * need type inference (see https://issues.dlang.org/show_bug.cgi?id=4430) - */ - return ++count > 1 ? 1 : 0; - }); - if (r) - return true; - } - - if (olderrs != global.errors) - { - if (!global.gag) - { - errorSupplemental(loc, "while looking for match for `%s`", toChars()); - semanticRun = PASS.semanticdone; - inst = this; - } - errors = true; - } - //printf("false\n"); - return false; - } - /***************************************** * Determines if a TemplateInstance will need a nested * generation of the TemplateDeclaration. @@ -5577,11 +5371,6 @@ version (IN_LLVM) --nest; } - override final inout(TemplateInstance) isTemplateInstance() inout - { - return this; - } - override void accept(Visitor v) { v.visit(this); @@ -5719,12 +5508,13 @@ extern (C++) final class TemplateMixin : TemplateInstance { TypeQualified tqual; - extern (D) this(const ref Loc loc, Identifier ident, TypeQualified tqual, Objects* tiargs) + extern (D) this(Loc loc, Identifier ident, TypeQualified tqual, Objects* tiargs) { super(loc, tqual.idents.length ? cast(Identifier)tqual.idents[tqual.idents.length - 1] : (cast(TypeIdentifier)tqual).ident, tiargs ? tiargs : new Objects()); //printf("TemplateMixin(ident = '%s')\n", ident ? ident.toChars() : ""); + this.dsym = DSYM.templateMixin; this.ident = ident; this.tqual = tqual; } @@ -5740,24 +5530,12 @@ extern (C++) final class TemplateMixin : TemplateInstance return "mixin"; } - override bool oneMember(out Dsymbol ps, Identifier ident) - { - return Dsymbol.oneMember(ps, ident); - } - override bool hasPointers() { //printf("TemplateMixin.hasPointers() %s\n", toChars()); return members.foreachDsymbol( (s) { return s.hasPointers(); } ) != 0; } - override const(char)* toChars() const - { - OutBuffer buf; - toCBufferInstance(this, buf); - return buf.extractChars(); - } - extern (D) bool findTempDecl(Scope* sc) { // Follow qualifications to find the TemplateDeclaration @@ -5832,11 +5610,6 @@ extern (C++) final class TemplateMixin : TemplateInstance return true; } - override inout(TemplateMixin) isTemplateMixin() inout - { - return this; - } - override void accept(Visitor v) { v.visit(this); @@ -5974,8 +5747,8 @@ MATCH matchArg(TemplateParameter tp, Loc instLoc, Scope* sc, Objects* tiargs, si if (auto ttp = tp.isTemplateTupleParameter()) return matchArgTuple(ttp); - else - return matchArgParameter(); + + return matchArgParameter(); } MATCH matchArg(TemplateParameter tp, Scope* sc, RootObject oarg, size_t i, TemplateParameters* parameters, ref Objects dedtypes, Declaration* psparam) @@ -6078,7 +5851,7 @@ MATCH matchArg(TemplateParameter tp, Scope* sc, RootObject oarg, size_t i, Templ /* If a function is really property-like, and then * it's CTFEable, ei will be a literal expression. */ - uint olderrors = global.startGagging(); + const olderrors = global.startGagging(); ei = resolveProperties(sc, ei); ei = ei.ctfeInterpret(); if (global.endGagging(olderrors) || ei.op == EXP.error) @@ -6351,14 +6124,13 @@ MATCH matchArg(TemplateParameter tp, Scope* sc, RootObject oarg, size_t i, Templ if (auto ttp = tp.isTemplateTypeParameter()) return matchArgType(ttp); - else if (auto tvp = tp.isTemplateValueParameter()) + if (auto tvp = tp.isTemplateValueParameter()) return matchArgValue(tvp); - else if (auto tap = tp.isTemplateAliasParameter()) + if (auto tap = tp.isTemplateAliasParameter()) return matchArgAlias(tap); - else if (auto ttp = tp.isTemplateTupleParameter()) + if (auto ttp = tp.isTemplateTupleParameter()) return matchArgTuple(ttp); - else - assert(0); + assert(0); } @@ -6444,8 +6216,8 @@ void printTemplateStats(bool listInstances, ErrorSink eSink) auto diff = b.ts.uniqueInstantiations - a.ts.uniqueInstantiations; if (diff) return diff; - else - return b.ts.numInstantiations - a.ts.numInstantiations; + + return b.ts.numInstantiations - a.ts.numInstantiations; } } @@ -6515,6 +6287,9 @@ void write(ref OutBuffer buf, RootObject obj) { if (obj) { - buf.writestring(obj.toChars()); + if (auto e = isExpression(obj)) + buf.writestring(e.toErrMsg()); + else + buf.writestring(obj.toChars()); } } diff --git a/dmd/dtoh.d b/dmd/dtoh.d index 883091be9e..3946c25c9f 100644 --- a/dmd/dtoh.d +++ b/dmd/dtoh.d @@ -2,12 +2,12 @@ * This module contains the implementation of the C++ header generation available through * the command line switch -Hc. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/dtohd, _dtoh.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/dtoh.d, _dtoh.d) * Documentation: https://dlang.org/phobos/dmd_dtoh.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/dtoh.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/dtoh.d */ module dmd.dtoh; @@ -512,7 +512,7 @@ public: } } - if (global.params.warnings != DiagnosticReporting.off || canFix) + if (global.params.useWarnings != DiagnosticReporting.off || canFix) { // Warn about identifiers that are keywords in C++. if (auto kc = keywordClass(ident)) @@ -976,7 +976,8 @@ public: { EnumKind kind = getEnumKind(type); - if (vd.visibility.kind == AST.Visibility.Kind.none || vd.visibility.kind == AST.Visibility.Kind.private_) { + if (vd.visibility.kind == AST.Visibility.Kind.none || vd.visibility.kind == AST.Visibility.Kind.private_) + { ignored("enum `%s` because it is `%s`.", vd.toPrettyChars(), AST.visibilityToChars(vd.visibility.kind)); return; } @@ -2324,7 +2325,7 @@ public: assert(tf.next, fd.loc.toChars().toDString()); tf.next == AST.Type.tsize_t ? originalType.next.accept(this) : tf.next.accept(this); - if (tf.isref) + if (tf.isRef) buf.writeByte('&'); buf.writeByte(' '); @@ -2796,7 +2797,7 @@ public: { if (vd._init && !vd._init.isVoidInitializer()) return AST.initializerToExpression(vd._init); - else if (auto ts = vd.type.isTypeStruct()) + if (auto ts = vd.type.isTypeStruct()) { if (!ts.sym.noDefaultCtor && !ts.sym.isUnionDeclaration()) { diff --git a/dmd/dversion.d b/dmd/dversion.d index 2e3b35264d..26528e9f51 100644 --- a/dmd/dversion.d +++ b/dmd/dversion.d @@ -4,12 +4,12 @@ * Specification: $(LINK2 https://dlang.org/spec/version.html#version-specification, Version Specification), * $(LINK2 https://dlang.org/spec/version.html#debug_specification, Debug Specification). * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/dversion.d, _dversion.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/dversion.d, _dversion.d) * Documentation: https://dlang.org/phobos/dmd_dversion.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/dversion.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/dversion.d */ module dmd.dversion; @@ -20,7 +20,6 @@ import dmd.dmodule; import dmd.dscope; import dmd.dsymbol; import dmd.dsymbolsem; -import dmd.errors; import dmd.globals; import dmd.identifier; import dmd.location; @@ -30,21 +29,17 @@ import dmd.visitor; /*********************************************************** * DebugSymbol's happen for statements like: * debug = identifier; - * debug = integer; */ extern (C++) final class DebugSymbol : Dsymbol { - uint level; - - extern (D) this(const ref Loc loc, Identifier ident) @safe + extern (D) this(Loc loc, Identifier ident) @safe { - super(loc, ident); + super(DSYM.debugSymbol, loc, ident); } - extern (D) this(const ref Loc loc, uint level) @safe + extern (D) this(Loc loc) @safe { - super(loc, null); - this.level = level; + super(DSYM.aliasDeclaration, loc, null); } override DebugSymbol syntaxCopy(Dsymbol s) @@ -52,32 +47,14 @@ extern (C++) final class DebugSymbol : Dsymbol assert(!s); auto ds = new DebugSymbol(loc, ident); ds.comment = comment; - ds.level = level; return ds; } - override const(char)* toChars() const nothrow - { - if (ident) - return ident.toChars(); - else - { - OutBuffer buf; - buf.print(level); - return buf.extractChars(); - } - } - override const(char)* kind() const nothrow { return "debug"; } - override inout(DebugSymbol) isDebugSymbol() inout - { - return this; - } - override void accept(Visitor v) { v.visit(this); @@ -87,54 +64,33 @@ extern (C++) final class DebugSymbol : Dsymbol /*********************************************************** * VersionSymbol's happen for statements like: * version = identifier; - * version = integer; */ extern (C++) final class VersionSymbol : Dsymbol { - uint level; - extern (D) this(const ref Loc loc, Identifier ident) @safe + extern (D) this(Loc loc, Identifier ident) @safe { - super(loc, ident); + super(DSYM.versionSymbol, loc, ident); } - extern (D) this(const ref Loc loc, uint level) @safe + extern (D) this(Loc loc) @safe { - super(loc, null); - this.level = level; + super(DSYM.versionSymbol, loc, null); } override VersionSymbol syntaxCopy(Dsymbol s) { assert(!s); - auto ds = ident ? new VersionSymbol(loc, ident) - : new VersionSymbol(loc, level); + auto ds = new VersionSymbol(loc, ident); ds.comment = comment; return ds; } - override const(char)* toChars() const nothrow - { - if (ident) - return ident.toChars(); - else - { - OutBuffer buf; - buf.print(level); - return buf.extractChars(); - } - } - override const(char)* kind() const nothrow { return "version"; } - override inout(VersionSymbol) isVersionSymbol() inout - { - return this; - } - override void accept(Visitor v) { v.visit(this); diff --git a/dmd/entity.d b/dmd/entity.d index af74c3bb8d..a70029b374 100644 --- a/dmd/entity.d +++ b/dmd/entity.d @@ -3,12 +3,12 @@ * * Specification $(LINK2 https://dlang.org/spec/entity.html, Named Character Entities) * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/entity.d, _entity.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/entity.d, _entity.d) * Documentation: https://dlang.org/phobos/dmd_entity.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/entity.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/entity.d */ module dmd.entity; diff --git a/dmd/enum.h b/dmd/enum.h index 65b236c614..39984bcffd 100644 --- a/dmd/enum.h +++ b/dmd/enum.h @@ -1,6 +1,6 @@ /* Compiler implementation of the D programming language - * Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * written by Walter Bright * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. @@ -20,7 +20,7 @@ class Expression; namespace dmd { // in enumsem.d - Expression *getDefaultValue(EnumDeclaration *ed, const Loc &loc); + Expression *getDefaultValue(EnumDeclaration *ed, Loc loc); } class EnumDeclaration final : public ScopeDsymbol @@ -52,14 +52,12 @@ class EnumDeclaration final : public ScopeDsymbol bool inuse(bool v); EnumDeclaration *syntaxCopy(Dsymbol *s) override; - bool oneMember(Dsymbol *&ps, Identifier *ident) override; Type *getType() override; const char *kind() const override; bool isDeprecated() const override; // is Dsymbol deprecated? Visibility visible() override; bool isSpecial() const; - EnumDeclaration *isEnumDeclaration() override { return this; } #if !IN_LLVM Symbol *sinit; @@ -89,6 +87,5 @@ class EnumMember final : public VarDeclaration EnumMember *syntaxCopy(Dsymbol *s) override; const char *kind() const override; - EnumMember *isEnumMember() override { return this; } void accept(Visitor *v) override { v->visit(this); } }; diff --git a/dmd/enumsem.d b/dmd/enumsem.d index 0aa26cf47b..4f0d4e5f53 100644 --- a/dmd/enumsem.d +++ b/dmd/enumsem.d @@ -1,12 +1,12 @@ /** * Does the semantic passes on enums. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/enumsem.d, _enumsem.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/enumsem.d, _enumsem.d) * Documentation: https://dlang.org/phobos/dmd_enumsem.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/enumsem.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/enumsem.d */ module dmd.enumsem; @@ -30,7 +30,6 @@ import dmd.declaration; import dmd.denum; import dmd.dimport; import dmd.dinterpret; -import dmd.dmangle; import dmd.dmodule; import dmd.dscope; import dmd.dstruct; @@ -119,7 +118,7 @@ void enumSemantic(Scope* sc, EnumDeclaration ed) ed.cppnamespace = sc.namespace; ed.semanticRun = PASS.semantic; - UserAttributeDeclaration.checkGNUABITag(ed, sc.linkage); + checkGNUABITag(ed, sc.linkage); checkMustUseReserved(ed); if (!ed.members && !ed.memtype) // enum ident; @@ -192,7 +191,7 @@ void enumSemantic(Scope* sc, EnumDeclaration ed) return; } - if (!(sc.flags & SCOPE.Cfile)) // C enum remains incomplete until members are done + if (!sc.inCfile) // C enum remains incomplete until members are done ed.semanticRun = PASS.semanticdone; version (none) @@ -219,8 +218,7 @@ void enumSemantic(Scope* sc, EnumDeclaration ed) */ ed.members.foreachDsymbol( (s) { - EnumMember em = s.isEnumMember(); - if (em) + if (EnumMember em = s.isEnumMember()) em._scope = sce; }); @@ -230,7 +228,7 @@ void enumSemantic(Scope* sc, EnumDeclaration ed) */ addEnumMembersToSymtab(ed, sc, sc.getScopesym()); - if (sc.flags & SCOPE.Cfile) + if (sc.inCfile) { /* C11 6.7.2.2 */ @@ -349,7 +347,7 @@ void enumSemantic(Scope* sc, EnumDeclaration ed) //printf("members = %s\n", members.toChars()); } -Expression getDefaultValue(EnumDeclaration ed, const ref Loc loc) +Expression getDefaultValue(EnumDeclaration ed, Loc loc) { Expression handleErrors(){ ed.defaultval = ErrorExp.get(); @@ -386,8 +384,7 @@ Expression getDefaultValue(EnumDeclaration ed, const ref Loc loc) foreach (const i; 0 .. ed.members.length) { - EnumMember em = (*ed.members)[i].isEnumMember(); - if (em) + if (EnumMember em = (*ed.members)[i].isEnumMember()) { if (em.semanticRun < PASS.semanticdone) { @@ -402,7 +399,7 @@ Expression getDefaultValue(EnumDeclaration ed, const ref Loc loc) return handleErrors(); } -Type getMemtype(EnumDeclaration ed, const ref Loc loc) +Type getMemtype(EnumDeclaration ed, Loc loc) { if (ed._scope) { @@ -701,7 +698,7 @@ void enumMemberSemantic(Scope* sc, EnumMember em) if (e.op == EXP.error) return errorReturn(); - if (e.type.isfloating()) + if (e.type.isFloating()) { // Check that e != eprev (not always true for floats) Expression etest = new EqualExp(EXP.equal, em.loc, e, eprev); diff --git a/dmd/errors.d b/dmd/errors.d index 3b81248207..aa0be3a161 100644 --- a/dmd/errors.d +++ b/dmd/errors.d @@ -1,17 +1,18 @@ /** * Functions for raising errors. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/errors.d, _errors.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/errors.d, _errors.d) * Documentation: https://dlang.org/phobos/dmd_errors.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/errors.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/errors.d */ module dmd.errors; public import core.stdc.stdarg; +public import dmd.root.string: fTuple; import core.stdc.stdio; import core.stdc.stdlib; import core.stdc.string; @@ -22,6 +23,8 @@ import dmd.common.outbuffer; import dmd.root.rmem; import dmd.root.string; import dmd.console; +import dmd.root.filename; +import dmd.sarif; nothrow: @@ -35,6 +38,19 @@ enum ErrorKind message, } +/******************************** + * Represents a diagnostic message generated during compilation, such as errors, + * warnings, or other messages. + */ +struct Diagnostic +{ + SourceLoc loc; // The location in the source code where the diagnostic was generated (includes file, line, and column). + string message; // The text of the diagnostic message, describing the issue. + ErrorKind kind; // The type of diagnostic, indicating whether it is an error, warning, deprecation, etc. +} + +__gshared Diagnostic[] diagnostics = []; + /*************************** * Error message sink for D compiler. */ @@ -44,60 +60,51 @@ class ErrorSinkCompiler : ErrorSink extern (C++): override: - void error(const ref Loc loc, const(char)* format, ...) + void verror(Loc loc, const(char)* format, va_list ap) { - va_list ap; - va_start(ap, format); verrorReport(loc, format, ap, ErrorKind.error); - va_end(ap); } - void errorSupplemental(const ref Loc loc, const(char)* format, ...) + void verrorSupplemental(Loc loc, const(char)* format, va_list ap) { - va_list ap; - va_start(ap, format); verrorReportSupplemental(loc, format, ap, ErrorKind.error); - va_end(ap); } - void warning(const ref Loc loc, const(char)* format, ...) + void vwarning(Loc loc, const(char)* format, va_list ap) { - va_list ap; - va_start(ap, format); verrorReport(loc, format, ap, ErrorKind.warning); - va_end(ap); } - void warningSupplemental(const ref Loc loc, const(char)* format, ...) + void vwarningSupplemental(Loc loc, const(char)* format, va_list ap) { - va_list ap; - va_start(ap, format); verrorReportSupplemental(loc, format, ap, ErrorKind.warning); - va_end(ap); } - void deprecation(const ref Loc loc, const(char)* format, ...) + void vdeprecation(Loc loc, const(char)* format, va_list ap) { - va_list ap; - va_start(ap, format); verrorReport(loc, format, ap, ErrorKind.deprecation); - va_end(ap); } - void deprecationSupplemental(const ref Loc loc, const(char)* format, ...) + void vdeprecationSupplemental(Loc loc, const(char)* format, va_list ap) { - va_list ap; - va_start(ap, format); verrorReportSupplemental(loc, format, ap, ErrorKind.deprecation); - va_end(ap); } - void message(const ref Loc loc, const(char)* format, ...) + void vmessage(Loc loc, const(char)* format, va_list ap) { - va_list ap; - va_start(ap, format); verrorReport(loc, format, ap, ErrorKind.message); - va_end(ap); + } + + void plugSink() + { + // Exit if there are no collected diagnostics + if (!diagnostics.length) return; + + // Generate the SARIF report with the current diagnostics + generateSarifReport(false); + + // Clear diagnostics after generating the report + diagnostics.length = 0; } } @@ -116,9 +123,9 @@ enum Classification : Color static if (__VERSION__ < 2092) - private extern (C++) void noop(const ref Loc loc, const(char)* format, ...) {} + private extern (C++) void noop(Loc loc, const(char)* format, ...) {} else - pragma(printf) private extern (C++) void noop(const ref Loc loc, const(char)* format, ...) {} + pragma(printf) private extern (C++) void noop(Loc loc, const(char)* format, ...) {} package auto previewErrorFunc(bool isDeprecated, FeatureState featureState) @safe @nogc pure nothrow @@ -160,7 +167,7 @@ package auto previewSupplementalFunc(bool isDeprecated, FeatureState featureStat * ... = printf-style variadic arguments */ static if (__VERSION__ < 2092) - extern (C++) void error(const ref Loc loc, const(char)* format, ...) + extern (C++) void error(Loc loc, const(char)* format, ...) { va_list ap; va_start(ap, format); @@ -168,7 +175,7 @@ static if (__VERSION__ < 2092) va_end(ap); } else - pragma(printf) extern (C++) void error(const ref Loc loc, const(char)* format, ...) + pragma(printf) extern (C++) void error(Loc loc, const(char)* format, ...) { va_list ap; va_start(ap, format); @@ -188,7 +195,7 @@ else static if (__VERSION__ < 2092) extern (C++) void error(const(char)* filename, uint linnum, uint charnum, const(char)* format, ...) { - const loc = Loc(filename, linnum, charnum); + const loc = SourceLoc(filename.toDString, linnum, charnum); va_list ap; va_start(ap, format); verrorReport(loc, format, ap, ErrorKind.error); @@ -197,13 +204,23 @@ static if (__VERSION__ < 2092) else pragma(printf) extern (C++) void error(const(char)* filename, uint linnum, uint charnum, const(char)* format, ...) { - const loc = Loc(filename, linnum, charnum); + const loc = SourceLoc(filename.toDString, linnum, charnum); va_list ap; va_start(ap, format); verrorReport(loc, format, ap, ErrorKind.error); va_end(ap); } +/// Callback for when the backend wants to report an error +extern(C++) void errorBackend(const(char)* filename, uint linnum, uint charnum, const(char)* format, ...) +{ + const loc = SourceLoc(filename.toDString, linnum, charnum); + va_list ap; + va_start(ap, format); + verrorReport(loc, format, ap, ErrorKind.error); + va_end(ap); +} + /** * Print additional details about an error message. * Doesn't increase the error count or print an additional error prefix. @@ -213,7 +230,7 @@ else * ... = printf-style variadic arguments */ static if (__VERSION__ < 2092) - extern (C++) void errorSupplemental(const ref Loc loc, const(char)* format, ...) + extern (C++) void errorSupplemental(Loc loc, const(char)* format, ...) { va_list ap; va_start(ap, format); @@ -221,7 +238,7 @@ static if (__VERSION__ < 2092) va_end(ap); } else - pragma(printf) extern (C++) void errorSupplemental(const ref Loc loc, const(char)* format, ...) + pragma(printf) extern (C++) void errorSupplemental(Loc loc, const(char)* format, ...) { va_list ap; va_start(ap, format); @@ -237,7 +254,7 @@ else * ... = printf-style variadic arguments */ static if (__VERSION__ < 2092) - extern (C++) void warning(const ref Loc loc, const(char)* format, ...) + extern (C++) void warning(Loc loc, const(char)* format, ...) { va_list ap; va_start(ap, format); @@ -245,7 +262,7 @@ static if (__VERSION__ < 2092) va_end(ap); } else - pragma(printf) extern (C++) void warning(const ref Loc loc, const(char)* format, ...) + pragma(printf) extern (C++) void warning(Loc loc, const(char)* format, ...) { va_list ap; va_start(ap, format); @@ -262,7 +279,7 @@ else * ... = printf-style variadic arguments */ static if (__VERSION__ < 2092) - extern (C++) void warningSupplemental(const ref Loc loc, const(char)* format, ...) + extern (C++) void warningSupplemental(Loc loc, const(char)* format, ...) { va_list ap; va_start(ap, format); @@ -270,7 +287,7 @@ static if (__VERSION__ < 2092) va_end(ap); } else - pragma(printf) extern (C++) void warningSupplemental(const ref Loc loc, const(char)* format, ...) + pragma(printf) extern (C++) void warningSupplemental(Loc loc, const(char)* format, ...) { va_list ap; va_start(ap, format); @@ -287,7 +304,7 @@ else * ... = printf-style variadic arguments */ static if (__VERSION__ < 2092) - extern (C++) void deprecation(const ref Loc loc, const(char)* format, ...) + extern (C++) void deprecation(Loc loc, const(char)* format, ...) { va_list ap; va_start(ap, format); @@ -295,7 +312,7 @@ static if (__VERSION__ < 2092) va_end(ap); } else - pragma(printf) extern (C++) void deprecation(const ref Loc loc, const(char)* format, ...) + pragma(printf) extern (C++) void deprecation(Loc loc, const(char)* format, ...) { va_list ap; va_start(ap, format); @@ -312,7 +329,7 @@ else * ... = printf-style variadic arguments */ static if (__VERSION__ < 2092) - extern (C++) void deprecationSupplemental(const ref Loc loc, const(char)* format, ...) + extern (C++) void deprecationSupplemental(Loc loc, const(char)* format, ...) { va_list ap; va_start(ap, format); @@ -320,7 +337,7 @@ static if (__VERSION__ < 2092) va_end(ap); } else - pragma(printf) extern (C++) void deprecationSupplemental(const ref Loc loc, const(char)* format, ...) + pragma(printf) extern (C++) void deprecationSupplemental(Loc loc, const(char)* format, ...) { va_list ap; va_start(ap, format); @@ -337,7 +354,7 @@ else * ... = printf-style variadic arguments */ static if (__VERSION__ < 2092) - extern (C++) void message(const ref Loc loc, const(char)* format, ...) + extern (C++) void message(Loc loc, const(char)* format, ...) { va_list ap; va_start(ap, format); @@ -345,7 +362,7 @@ static if (__VERSION__ < 2092) va_end(ap); } else - pragma(printf) extern (C++) void message(const ref Loc loc, const(char)* format, ...) + pragma(printf) extern (C++) void message(Loc loc, const(char)* format, ...) { va_list ap; va_start(ap, format); @@ -381,7 +398,7 @@ else * see verrorReport for arguments * Returns: true if error handling is done, false to continue printing to stderr */ -alias DiagnosticHandler = bool delegate(const ref Loc location, Color headerColor, const(char)* header, const(char)* messageFormat, va_list args, const(char)* prefix1, const(char)* prefix2); +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); /** * The diagnostic handler. @@ -417,7 +434,7 @@ else // Encapsulates an error as described by its location, format message, and kind. private struct ErrorInfo { - this(const ref Loc loc, const ErrorKind kind, const(char)* p1 = null, const(char)* p2 = null) @safe @nogc pure nothrow + this(const ref SourceLoc loc, const ErrorKind kind, const(char)* p1 = null, const(char)* p2 = null) @safe @nogc pure nothrow { this.loc = loc; this.p1 = p1; @@ -425,7 +442,7 @@ private struct ErrorInfo this.kind = kind; } - const Loc loc; // location of error + const SourceLoc loc; // location of error Classification headerColor; // color to set `header` output to const(char)* p1; // additional message prefix const(char)* p2; // additional message prefix @@ -446,9 +463,16 @@ private struct ErrorInfo * p1 = additional message prefix * p2 = additional message prefix */ -extern (C++) void verrorReport(const ref Loc loc, const(char)* format, va_list ap, ErrorKind kind, const(char)* p1 = null, const(char)* p2 = null) +private extern(C++) void verrorReport(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); +} + +/// 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) { auto info = ErrorInfo(loc, kind, p1, p2); + final switch (info.kind) { case ErrorKind.error: @@ -456,9 +480,17 @@ extern (C++) void verrorReport(const ref Loc loc, const(char)* format, va_list a if (!global.gag) { info.headerColor = Classification.error; + if (global.params.v.messageStyle == MessageStyle.sarif) + { + addSarifDiagnostic(loc, format, ap, kind); + return; + } verrorPrint(format, ap, info); if (global.params.v.errorLimit && global.errors >= global.params.v.errorLimit) + { + fprintf(stderr, "error limit (%d) reached, use `-verrors=0` to show all\n", global.params.v.errorLimit); fatal(); // moderate blizzard of cascading messages + } } else { @@ -469,7 +501,7 @@ extern (C++) void verrorReport(const ref Loc loc, const(char)* format, va_list a } global.gaggedErrors++; } - break; + return; case ErrorKind.deprecation: if (global.params.useDeprecated == DiagnosticReporting.error) @@ -482,6 +514,11 @@ extern (C++) void verrorReport(const ref Loc loc, const(char)* format, va_list a if (global.params.v.errorLimit == 0 || global.deprecations <= global.params.v.errorLimit) { info.headerColor = Classification.deprecation; + if (global.params.v.messageStyle == MessageStyle.sarif) + { + addSarifDiagnostic(loc, format, ap, kind); + return; + } verrorPrint(format, ap, info); } } @@ -490,16 +527,21 @@ extern (C++) void verrorReport(const ref Loc loc, const(char)* format, va_list a global.gaggedWarnings++; } } - break; + return; case ErrorKind.warning: - if (global.params.warnings != DiagnosticReporting.off) + if (global.params.useWarnings != DiagnosticReporting.off) { if (!global.gag) { info.headerColor = Classification.warning; + if (global.params.v.messageStyle == MessageStyle.sarif) + { + addSarifDiagnostic(loc, format, ap, kind); + return; + } verrorPrint(format, ap, info); - if (global.params.warnings == DiagnosticReporting.error) + if (global.params.useWarnings == DiagnosticReporting.error) global.warnings++; } else @@ -507,29 +549,38 @@ extern (C++) void verrorReport(const ref Loc loc, const(char)* format, va_list a global.gaggedWarnings++; } } - break; + return; case ErrorKind.tip: if (!global.gag) { info.headerColor = Classification.tip; + if (global.params.v.messageStyle == MessageStyle.sarif) + { + addSarifDiagnostic(loc, format, ap, kind); + return; + } verrorPrint(format, ap, info); } - break; + return; case ErrorKind.message: - const p = info.loc.toChars(); - if (*p) - { - fprintf(stdout, "%s: ", p); - mem.xfree(cast(void*)p); - } OutBuffer tmp; + writeSourceLoc(tmp, info.loc, Loc.showColumns, Loc.messageStyle); + if (tmp.length) + fprintf(stdout, "%s: ", tmp.extractChars()); + + tmp.reset(); tmp.vprintf(format, ap); fputs(tmp.peekChars(), stdout); fputc('\n', stdout); fflush(stdout); // ensure it gets written out in case of compiler aborts - break; + if (global.params.v.messageStyle == MessageStyle.sarif) + { + addSarifDiagnostic(loc, format, ap, kind); + return; + } + return; } } @@ -544,7 +595,13 @@ extern (C++) void verrorReport(const ref Loc loc, const(char)* format, va_list a * ap = printf-style variadic arguments * kind = kind of error being printed */ -extern (C++) void verrorReportSupplemental(const ref Loc loc, const(char)* format, va_list ap, ErrorKind kind) +private extern(C++) void verrorReportSupplemental(Loc loc, const(char)* format, va_list ap, ErrorKind kind) +{ + return verrorReportSupplemental(loc.SourceLoc, format, ap, kind); +} + +/// ditto +private extern(C++) void verrorReportSupplemental(const SourceLoc loc, const(char)* format, va_list ap, ErrorKind kind) { auto info = ErrorInfo(loc, kind); info.supplemental = true; @@ -560,7 +617,7 @@ extern (C++) void verrorReportSupplemental(const ref Loc loc, const(char)* forma else info.headerColor = Classification.error; verrorPrint(format, ap, info); - break; + return; case ErrorKind.deprecation: if (global.params.useDeprecated == DiagnosticReporting.error) @@ -573,15 +630,15 @@ extern (C++) void verrorReportSupplemental(const ref Loc loc, const(char)* forma verrorPrint(format, ap, info); } } - break; + return; case ErrorKind.warning: - if (global.params.warnings != DiagnosticReporting.off && !global.gag) + if (global.params.useWarnings != DiagnosticReporting.off && !global.gag) { info.headerColor = Classification.warning; verrorPrint(format, ap, info); } - break; + return; default: assert(false, "internal error: unhandled kind in error report"); @@ -613,27 +670,32 @@ private void verrorPrint(const(char)* format, va_list ap, ref ErrorInfo info) } } - if (diagnosticHandler !is null && - diagnosticHandler(info.loc, info.headerColor, header, format, ap, info.p1, info.p2)) - return; + if (diagnosticHandler !is null) + { + if (diagnosticHandler(info.loc, info.headerColor, header, format, ap, info.p1, info.p2)) + return; + } if (global.params.v.showGaggedErrors && global.gag) fprintf(stderr, "(spec:%d) ", global.gag); auto con = cast(Console) global.console; - const p = info.loc.toChars(); + + OutBuffer tmp; + writeSourceLoc(tmp, info.loc, Loc.showColumns, Loc.messageStyle); + const locString = tmp.extractSlice(); if (con) con.setColorBright(true); - if (*p) + if (locString.length) { - fprintf(stderr, "%s: ", p); - mem.xfree(cast(void*)p); + fprintf(stderr, "%.*s: ", cast(int) locString.length, locString.ptr); } if (con) con.setColor(info.headerColor); fputs(header, stderr); if (con) con.resetColor(); - OutBuffer tmp; + + tmp.reset(); if (info.p1) { tmp.writestring(info.p1); @@ -652,57 +714,97 @@ private void verrorPrint(const(char)* format, va_list ap, ref ErrorInfo info) writeHighlights(con, tmp); } else + { + unescapeBackticks(tmp); fputs(tmp.peekChars(), stderr); + } fputc('\n', stderr); - __gshared Loc old_loc; - Loc loc = info.loc; - if (global.params.v.printErrorContext && + __gshared SourceLoc old_loc; + auto loc = info.loc; + if (global.params.v.errorPrintMode != ErrorPrintMode.simpleError && // ignore supplemental messages with same loc (loc != old_loc || !info.supplemental) && // ignore invalid files - loc != Loc.initial && + loc != SourceLoc.init && // ignore mixins for now - !loc.filename.strstr(".d-mixin-") && + !loc.filename.startsWith(".d-mixin-") && !global.params.mixinOut.doOutput) { - import dmd.root.filename : FileName; - const fileName = FileName(loc.filename.toDString); - if (auto text = global.fileManager.getFileContents(fileName)) - { - auto range = dmd.root.string.splitLines(cast(const(char[])) text); - size_t linnum; - foreach (line; range) - { - ++linnum; - if (linnum != loc.linnum) - continue; - if (loc.charnum < line.length) - { - fprintf(stderr, "%.*s\n", cast(int)line.length, line.ptr); - // The number of column bytes and the number of display columns - // occupied by a character are not the same for non-ASCII charaters. - // https://issues.dlang.org/show_bug.cgi?id=21849 - size_t col = 0; - while (col < loc.charnum - 1) - { - import dmd.root.utf : utf_decodeChar; - dchar u; - const msg = utf_decodeChar(line, col, u); - assert(msg is null, msg); - fputc(' ', stderr); - } - fputc('^', stderr); - fputc('\n', stderr); - } - break; - } - } + tmp.reset(); + printErrorLineContext(tmp, loc.fileContent, loc.fileOffset); + fputs(tmp.peekChars(), stderr); } old_loc = loc; fflush(stderr); // ensure it gets written out in case of compiler aborts } +// Given an error happening in source code `text`, at index `offset`, print the offending line +// and a caret pointing to the error into `buf` +private void printErrorLineContext(ref OutBuffer buf, const(char)[] text, size_t offset) @safe +{ + import dmd.root.utf : utf_decodeChar; + + if (offset >= text.length) + return; // Out of bounds (missing source content in SourceLoc) + + // Scan backwards for beginning of line + size_t s = offset; + while (s > 0 && text[s - 1] != '\n') + s--; + + const line = text[s .. $]; + const byteColumn = offset - s; // column as reported in the error message (byte offset) + enum tabWidth = 4; + + // The number of column bytes and the number of display columns + // occupied by a character are not the same for non-ASCII charaters. + // https://issues.dlang.org/show_bug.cgi?id=21849 + size_t currentColumn = 0; + size_t caretColumn = 0; // actual display column taking into account tabs and unicode characters + for (size_t i = 0; i < line.length; ) + { + dchar u; + const start = i; + const msg = utf_decodeChar(line, i, u); + assert(msg is null, msg); + if (u == '\t') + { + // How many spaces until column is the next multiple of tabWidth + const equivalentSpaces = tabWidth - (currentColumn % tabWidth); + foreach (j; 0 .. equivalentSpaces) + buf.writeByte(' '); + currentColumn += equivalentSpaces; + } + else if (u == '\r' || u == '\n') + break; + else + { + buf.writestring(line[start .. i]); + currentColumn++; + } + if (i <= byteColumn) + caretColumn = currentColumn; + } + buf.writeByte('\n'); + + foreach (i; 0 .. caretColumn) + buf.writeByte(' '); + + buf.writeByte('^'); + buf.writeByte('\n'); +} + +unittest +{ + OutBuffer buf; + printErrorLineContext(buf, "int ɷ = 3;", 9); + assert(buf.peekSlice() == + "int ɷ = 3;\n"~ + " ^\n" + ); +} + /** * The type of the fatal error handler * Returns: true if error handling is done, false to do exit(EXIT_FAILURE) @@ -724,6 +826,8 @@ extern (C++) void fatal() if (fatalErrorHandler && fatalErrorHandler()) return; + global.plugErrorSinks(); + exit(EXIT_FAILURE); } @@ -741,6 +845,7 @@ extern (C++) void halt() @safe * is D source code, and color syntax highlight it. * Modify contents of `buf` with highlighted result. * Many parallels to ddoc.highlightText(). + * Double backticks are replaced by a single backtick without coloring. * Params: * buf = text containing `...` code to highlight */ @@ -756,6 +861,13 @@ private void colorSyntaxHighlight(ref OutBuffer buf) switch (c) { case '`': + // A double backtick means it's part of the content, don't color + if (i + 1 < buf.length && buf[i + 1] == '`') + { + buf.remove(i, 1); + continue; + } + if (inBacktick) { inBacktick = false; @@ -780,6 +892,27 @@ private void colorSyntaxHighlight(ref OutBuffer buf) } } +/// Replace double backticks in `buf` with a single backtick +void unescapeBackticks(ref OutBuffer buf) +{ + for (size_t i = 0; i + 1 < buf.length; ++i) + { + if (buf[i] == '`' && buf[i + 1] == '`') + buf.remove(i, 1); + } +} + +unittest +{ + OutBuffer buf; + buf.writestring("x````"); + unescapeBackticks(buf); + assert(buf.extractSlice() == "x``"); + + buf.writestring("x````"); + colorSyntaxHighlight(buf); + assert(buf.extractSlice() == "x``"); +} /** * Embed these highlighting commands in the text stream. @@ -889,28 +1022,29 @@ private void writeHighlights(Console con, ref const OutBuffer buf) for (size_t i = 0; i < buf.length; ++i) { const c = buf[i]; - if (c == HIGHLIGHT.Escape) + if (c != HIGHLIGHT.Escape) { - const color = buf[++i]; - if (color == HIGHLIGHT.Default) - { - con.resetColor(); - colors = false; - } - else - if (color == Color.white) - { - con.resetColor(); - con.setColorBright(true); - colors = true; - } - else - { - con.setColor(cast(Color)color); - colors = true; - } + fputc(c, con.fp); + continue; + } + + const color = buf[++i]; + if (color == HIGHLIGHT.Default) + { + con.resetColor(); + colors = false; } else - fputc(c, con.fp); + if (color == Color.white) + { + con.resetColor(); + con.setColorBright(true); + colors = true; + } + else + { + con.setColor(cast(Color)color); + colors = true; + } } } diff --git a/dmd/errors.h b/dmd/errors.h index 7a9683b9e6..a2d0f36e03 100644 --- a/dmd/errors.h +++ b/dmd/errors.h @@ -1,6 +1,6 @@ /* Compiler implementation of the D programming language - * Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * written by Walter Bright * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. @@ -31,20 +31,17 @@ enum class ErrorKind #endif // Print a warning, deprecation, or error, accepts printf-like format specifiers. -D_ATTRIBUTE_FORMAT(2, 3) void warning(const Loc& loc, const char *format, ...); -D_ATTRIBUTE_FORMAT(2, 3) void warningSupplemental(const Loc& loc, const char *format, ...); -D_ATTRIBUTE_FORMAT(2, 3) void deprecation(const Loc& loc, const char *format, ...); -D_ATTRIBUTE_FORMAT(2, 3) void deprecationSupplemental(const Loc& loc, const char *format, ...); -D_ATTRIBUTE_FORMAT(2, 3) void error(const Loc& loc, const char *format, ...); +D_ATTRIBUTE_FORMAT(2, 3) void warning(Loc loc, const char *format, ...); +D_ATTRIBUTE_FORMAT(2, 3) void warningSupplemental(Loc loc, const char *format, ...); +D_ATTRIBUTE_FORMAT(2, 3) void deprecation(Loc loc, const char *format, ...); +D_ATTRIBUTE_FORMAT(2, 3) void deprecationSupplemental(Loc loc, const char *format, ...); +D_ATTRIBUTE_FORMAT(2, 3) void error(Loc loc, const char *format, ...); D_ATTRIBUTE_FORMAT(4, 5) void error(const char *filename, unsigned linnum, unsigned charnum, const char *format, ...); -D_ATTRIBUTE_FORMAT(2, 3) void errorSupplemental(const Loc& loc, const char *format, ...); +D_ATTRIBUTE_FORMAT(2, 3) void errorSupplemental(Loc loc, const char *format, ...); D_ATTRIBUTE_FORMAT(1, 2) void message(const char *format, ...); -D_ATTRIBUTE_FORMAT(2, 3) void message(const Loc& loc, const char *format, ...); +D_ATTRIBUTE_FORMAT(2, 3) void message(Loc loc, const char *format, ...); D_ATTRIBUTE_FORMAT(1, 2) void tip(const char *format, ...); -D_ATTRIBUTE_FORMAT(2, 0) void verrorReport(const Loc& loc, const char *format, va_list ap, ErrorKind kind, const char *p1 = nullptr, const char *p2 = nullptr); -D_ATTRIBUTE_FORMAT(2, 0) void verrorReportSupplemental(const Loc& loc, const char* format, va_list ap, ErrorKind kind); - #if defined(__GNUC__) || defined(__clang__) #define D_ATTRIBUTE_NORETURN __attribute__((noreturn)) #elif _MSC_VER diff --git a/dmd/errorsink.d b/dmd/errorsink.d index afea689546..5793ef1c0e 100644 --- a/dmd/errorsink.d +++ b/dmd/errorsink.d @@ -1,16 +1,18 @@ /** * Provides an abstraction for what to do with error messages. * - * Copyright: Copyright (C) 2023-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 2023-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/src/dmd/errorsink.d, _errorsink.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/errorsink.d, _errorsink.d) * Documentation: https://dlang.org/phobos/dmd_errorsink.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/errorsink.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/errorsink.d */ module dmd.errorsink; +import core.stdc.stdarg; + import dmd.location; /*************************************** @@ -21,19 +23,78 @@ abstract class ErrorSink nothrow: extern (C++): - void error(const ref Loc loc, const(char)* format, ...); + void verror(Loc loc, const(char)* format, va_list ap); + void verrorSupplemental(Loc loc, const(char)* format, va_list ap); + void vwarning(Loc loc, const(char)* format, va_list ap); + void vwarningSupplemental(Loc loc, const(char)* format, va_list ap); + void vmessage(Loc loc, const(char)* format, va_list ap); + void vdeprecation(Loc loc, const(char)* format, va_list ap); + void vdeprecationSupplemental(Loc loc, const(char)* format, va_list ap); - void errorSupplemental(const ref Loc loc, const(char)* format, ...); + void error(Loc loc, const(char)* format, ...) + { + va_list ap; + va_start(ap, format); + verror(loc, format, ap); + va_end(ap); + } - void warning(const ref Loc loc, const(char)* format, ...); + void errorSupplemental(Loc loc, const(char)* format, ...) + { + va_list ap; + va_start(ap, format); + verrorSupplemental(loc, format, ap); + va_end(ap); + } - void warningSupplemental(const ref Loc loc, const(char)* format, ...); + void warning(Loc loc, const(char)* format, ...) + { + va_list ap; + va_start(ap, format); + vwarning(loc, format, ap); + va_end(ap); + } + + void warningSupplemental(Loc loc, const(char)* format, ...) + { + va_list ap; + va_start(ap, format); + vwarningSupplemental(loc, format, ap); + va_end(ap); + } + + void message(Loc loc, const(char)* format, ...) + { + va_list ap; + va_start(ap, format); + vmessage(loc, format, ap); + va_end(ap); + } - void message(const ref Loc loc, const(char)* format, ...); + void deprecation(Loc loc, const(char)* format, ...) + { + va_list ap; + va_start(ap, format); + vdeprecation(loc, format, ap); + va_end(ap); + } - void deprecation(const ref Loc loc, const(char)* format, ...); + void deprecationSupplemental(Loc loc, const(char)* format, ...) + { + va_list ap; + va_start(ap, format); + vdeprecationSupplemental(loc, format, ap); + va_end(ap); + } - void deprecationSupplemental(const ref Loc loc, const(char)* format, ...); + /** + * This will be called to indicate compilation has either + * finished or terminated, no more errors are possible - it's + * now the time to print any stored errors. + * + * The default implementation does nothing since most error sinks have no state + */ + void plugSink() {} } /***************************************** @@ -45,19 +106,19 @@ class ErrorSinkNull : ErrorSink extern (C++): override: - void error(const ref Loc loc, const(char)* format, ...) { } + void verror(Loc loc, const(char)* format, va_list ap) { } - void errorSupplemental(const ref Loc loc, const(char)* format, ...) { } + void verrorSupplemental(Loc loc, const(char)* format, va_list ap) { } - void warning(const ref Loc loc, const(char)* format, ...) { } + void vwarning(Loc loc, const(char)* format, va_list ap) { } - void warningSupplemental(const ref Loc loc, const(char)* format, ...) { } + void vwarningSupplemental(Loc loc, const(char)* format, va_list ap) { } - void message(const ref Loc loc, const(char)* format, ...) { } + void vmessage(Loc loc, const(char)* format, va_list ap) { } - void deprecation(const ref Loc loc, const(char)* format, ...) { } + void vdeprecation(Loc loc, const(char)* format, va_list ap) { } - void deprecationSupplemental(const ref Loc loc, const(char)* format, ...) { } + void vdeprecationSupplemental(Loc loc, const(char)* format, va_list ap) { } } /***************************************** @@ -71,7 +132,7 @@ class ErrorSinkLatch : ErrorSinkNull bool sawErrors; - void error(const ref Loc loc, const(char)* format, ...) { sawErrors = true; } + void verror(Loc loc, const(char)* format, va_list ap) { sawErrors = true; } } /***************************************** @@ -87,7 +148,7 @@ class ErrorSinkStderr : ErrorSink extern (C++): override: - void error(const ref Loc loc, const(char)* format, ...) + void verror(Loc loc, const(char)* format, va_list ap) { fputs("Error: ", stderr); const p = loc.toChars(); @@ -97,16 +158,13 @@ class ErrorSinkStderr : ErrorSink //mem.xfree(cast(void*)p); // loc should provide the free() } - va_list ap; - va_start(ap, format); vfprintf(stderr, format, ap); fputc('\n', stderr); - va_end(ap); } - void errorSupplemental(const ref Loc loc, const(char)* format, ...) { } + void verrorSupplemental(Loc loc, const(char)* format, va_list ap) { } - void warning(const ref Loc loc, const(char)* format, ...) + void vwarning(Loc loc, const(char)* format, va_list ap) { fputs("Warning: ", stderr); const p = loc.toChars(); @@ -116,16 +174,13 @@ class ErrorSinkStderr : ErrorSink //mem.xfree(cast(void*)p); // loc should provide the free() } - va_list ap; - va_start(ap, format); vfprintf(stderr, format, ap); fputc('\n', stderr); - va_end(ap); } - void warningSupplemental(const ref Loc loc, const(char)* format, ...) { } + void vwarningSupplemental(Loc loc, const(char)* format, va_list ap) { } - void deprecation(const ref Loc loc, const(char)* format, ...) + void vdeprecation(Loc loc, const(char)* format, va_list ap) { fputs("Deprecation: ", stderr); const p = loc.toChars(); @@ -135,14 +190,11 @@ class ErrorSinkStderr : ErrorSink //mem.xfree(cast(void*)p); // loc should provide the free() } - va_list ap; - va_start(ap, format); vfprintf(stderr, format, ap); fputc('\n', stderr); - va_end(ap); } - void message(const ref Loc loc, const(char)* format, ...) + void vmessage(Loc loc, const(char)* format, va_list ap) { const p = loc.toChars(); if (*p) @@ -151,12 +203,9 @@ class ErrorSinkStderr : ErrorSink //mem.xfree(cast(void*)p); // loc should provide the free() } - va_list ap; - va_start(ap, format); vfprintf(stderr, format, ap); fputc('\n', stderr); - va_end(ap); } - void deprecationSupplemental(const ref Loc loc, const(char)* format, ...) { } + void vdeprecationSupplemental(Loc loc, const(char)* format, va_list ap) { } } diff --git a/dmd/escape.d b/dmd/escape.d index 08bd6fd2fd..a0c5472375 100644 --- a/dmd/escape.d +++ b/dmd/escape.d @@ -1,12 +1,12 @@ /** * Most of the logic to implement scoped pointers and scoped references is here. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/escape.d, _escape.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/escape.d, _escape.d) * Documentation: https://dlang.org/phobos/dmd_escape.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/escape.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/escape.d */ module dmd.escape; @@ -34,6 +34,7 @@ import dmd.location; import dmd.mtype; import dmd.printast; import dmd.rootobject; +import dmd.safe; import dmd.tokens; import dmd.typesem : hasPointers, parameterStorageClass; import dmd.visitor; @@ -179,7 +180,7 @@ bool checkMutableArguments(ref Scope sc, FuncDeclaration fd, TypeFunction tf, if (!(eb.isMutable || eb2.isMutable)) return; - if (!tf.islive && !(sc.useDIP1000 == FeatureState.enabled && sc.func && sc.func.setUnsafe())) + if (!tf.isLive && !(sc.useDIP1000 == FeatureState.enabled && sc.func && setFunctionToUnsafe(sc.func))) return; if (!gag) @@ -353,7 +354,7 @@ bool checkParamArgumentEscape(ref Scope sc, FuncDeclaration fdc, Identifier parI if (assertmsg) { result |= sc.setUnsafeDIP1000(gag, arg.loc, - desc ~ " `%s` assigned to non-scope parameter calling `assert()`", v); + "assigning" ~ desc ~ " `%s` to non-scope parameter calling `assert()`", v); return; } @@ -361,9 +362,9 @@ bool checkParamArgumentEscape(ref Scope sc, FuncDeclaration fdc, Identifier parI const(char)* msg = (isThis) ? (desc ~ " `%s` calling non-scope member function `%s.%s()`") : - (fdc && parId) ? (desc ~ " `%s` assigned to non-scope parameter `%s` calling `%s`") : - (fdc && !parId) ? (desc ~ " `%s` assigned to non-scope anonymous parameter calling `%s`") : - (!fdc && parId) ? (desc ~ " `%s` assigned to non-scope parameter `%s`") : + (fdc && parId) ? ("assigning " ~ desc ~ " `%s` to non-scope parameter `%s` calling `%s`") : + (fdc && !parId) ? ("assigning " ~ desc ~ " `%s` to non-scope anonymous parameter calling `%s`") : + (!fdc && parId) ? ("assigning " ~ desc ~ " `%s` to non-scope parameter `%s`") : (desc ~ " `%s` assigned to non-scope anonymous parameter"); if (isThis ? @@ -378,32 +379,24 @@ bool checkParamArgumentEscape(ref Scope sc, FuncDeclaration fdc, Identifier parI void onValue(VarDeclaration v) { if (log) printf("byvalue %s\n", v.toChars()); - if (parStc & STC.scope_ || v.isDataseg()) + if (parStc & STC.scope_) return; - Dsymbol p = v.toParent2(); - - notMaybeScope(v, vPar); + doNotInferScope(v, vPar); if (v.isScope()) { unsafeAssign!"scope variable"(v); } - else if (v.isTypesafeVariadicArray && p == sc.func) - { - unsafeAssign!"variadic variable"(v); - } } void onRef(VarDeclaration v, bool retRefTransition) { if (log) printf("byref %s\n", v.toChars()); - if (v.isDataseg()) - return; Dsymbol p = v.toParent2(); - notMaybeScope(v, arg); + doNotInferScope(v, arg); if (checkScopeVarAddr(v, arg, sc, gag)) { result = true; @@ -432,7 +425,7 @@ bool checkParamArgumentEscape(ref Scope sc, FuncDeclaration fdc, Identifier parI Dsymbol p = v.toParent2(); - notMaybeScope(v, arg); + doNotInferScope(v, arg); if ((v.isReference() || v.isScope()) && p == sc.func) { @@ -447,8 +440,8 @@ bool checkParamArgumentEscape(ref Scope sc, FuncDeclaration fdc, Identifier parI if (parStc & STC.scope_) return; const(char)* msg = parId ? - "reference to stack allocated value returned by `%s` assigned to non-scope parameter `%s`" : - "reference to stack allocated value returned by `%s` assigned to non-scope anonymous parameter"; + "assigning reference to stack allocated value returned by `%s` to non-scope parameter `%s`" : + "assigning reference to stack allocated value returned by `%s` to non-scope anonymous parameter"; result |= sc.setUnsafeDIP1000(gag, ee.loc, msg, ee, parId); } @@ -575,7 +568,7 @@ public ReturnParamDest returnParamDest(TypeFunction tf, Type tthis) { assert(tf); - if (tf.isctor) + if (tf.isCtor) return ReturnParamDest.this_; if (!tf.nextOf() || (tf.nextOf().ty != Tvoid)) @@ -620,17 +613,6 @@ bool checkAssignEscape(ref Scope sc, Expression e, bool gag, bool byRef) if (!e1.type.hasPointers()) return false; - if (e1.isSliceExp()) - { - if (VarDeclaration va = expToVariable(e1)) - { - if (!va.type.toBasetype().isTypeSArray() || // treat static array slice same as a variable - !va.type.hasPointers()) - return false; - } - else - return false; - } /* The struct literal case can arise from the S(e2) constructor call: * return S(e2); @@ -641,7 +623,24 @@ bool checkAssignEscape(ref Scope sc, Expression e, bool gag, bool byRef) if (e1.isStructLiteralExp()) return false; - VarDeclaration va = expToVariable(e1); + int deref; + VarDeclaration va = expToVariable(e1, deref); + // transitive scope not implemented, so can't assign scope pointers to a dereferenced variable + if (deref > 0) + va = null; + + if (e1.isSliceExp()) + { + // slice-copy is not assigning a pointer, but copying array content + if (va) + { + if (!va.type.toBasetype().isTypeSArray() || // treat static array slice same as a variable + !va.type.hasPointers()) + return false; + } + else + return false; + } if (va && e.op == EXP.concatenateElemAssign) { @@ -655,16 +654,11 @@ bool checkAssignEscape(ref Scope sc, Expression e, bool gag, bool byRef) va = null; } - if (va && e1.isDotVarExp() && va.type.toBasetype().isTypeClass()) + if (e.op == EXP.construct && va && (va.storage_class & STC.temp) && va._init) { - /* https://issues.dlang.org/show_bug.cgi?id=17949 - * Draw an equivalence between: - * *q = p; - * and: - * va.field = e2; - * since we are not assigning to va, but are assigning indirectly through class reference va. - */ - va = null; + // Initializing a temporary is safe, `escapeExp` will forward such vars + // to their `va._init` if needed. + return false; } if (log && va) printf("va: %s\n", va.toChars()); @@ -701,32 +695,19 @@ bool checkAssignEscape(ref Scope sc, Expression e, bool gag, bool byRef) void onValue(VarDeclaration v) { if (log) printf("byvalue: %s\n", v.toChars()); - if (v.isDataseg()) - return; if (v == va) return; Dsymbol p = v.toParent2(); - if (va && !vaIsRef && !va.isScope() && !v.isScope() && - !v.isTypesafeVariadicArray && !va.isTypesafeVariadicArray && - (va.isParameter() && va.maybeScope && v.isParameter() && v.maybeScope) && - p == fd) - { - /* Add v to va's list of dependencies - */ - va.addMaybe(v); - return; - } - if (vaIsFirstRef && p == fd) { inferReturn(fd, v, /*returnScope:*/ true); } if (!(va && va.isScope()) || vaIsRef) - notMaybeScope(v, e); + doNotInferScope(v, e); if (v.isScope()) { @@ -745,18 +726,16 @@ bool checkAssignEscape(ref Scope sc, Expression e, bool gag, bool byRef) { case EnclosedBy.none: assert(0); case EnclosedBy.returnScope: - msg = "scope variable `%s` assigned to return scope `%s`"; + msg = "assigning scope variable `%s` to return scope `%s`"; break; case EnclosedBy.longerScope: - if (v.storage_class & STC.temp) - return; - msg = "scope variable `%s` assigned to `%s` with longer lifetime"; + msg = "assigning scope variable `%s` to `%s` with longer lifetime"; break; case EnclosedBy.refVar: - msg = "scope variable `%s` assigned to `ref` variable `%s` with longer lifetime"; + msg = "assigning scope variable `%s` to `ref` variable `%s` with longer lifetime"; break; case EnclosedBy.global: - msg = "scope variable `%s` assigned to global variable `%s`"; + msg = "assigning scope variable `%s` to global variable `%s`"; break; } @@ -783,13 +762,7 @@ bool checkAssignEscape(ref Scope sc, Expression e, bool gag, bool byRef) } return; } - result |= sc.setUnsafeDIP1000(gag, ae.loc, "scope variable `%s` assigned to non-scope `%s`", v, e1); - } - else if (v.isTypesafeVariadicArray && p == fd) - { - if (inferScope(va)) - return; - result |= sc.setUnsafeDIP1000(gag, ae.loc, "variadic variable `%s` assigned to non-scope `%s`", v, e1); + result |= sc.setUnsafeDIP1000(gag, ae.loc, "assigning scope variable `%s` to non-scope `%s`", v, e1); } else { @@ -797,15 +770,14 @@ bool checkAssignEscape(ref Scope sc, Expression e, bool gag, bool byRef) * It may escape via that assignment, therefore, v can never be 'scope'. */ //printf("no infer for %s in %s, %d\n", v.toChars(), fd.ident.toChars(), __LINE__); - doNotInferScope(v, e); + if (!v.isParameter) + doNotInferScope(v, e); } } void onRef(VarDeclaration v, bool retRefTransition) { if (log) printf("byref: %s\n", v.toChars()); - if (v.isDataseg()) - return; if (checkScopeVarAddr(v, ae, sc, gag)) { @@ -822,7 +794,7 @@ bool checkAssignEscape(ref Scope sc, Expression e, bool gag, bool byRef) else { result |= sc.setUnsafeDIP1000(gag, ae.loc, - "address of local variable `%s` assigned to return scope `%s`", v, va); + "assigning address of local variable `%s` to return scope `%s`", v, va); } } @@ -837,7 +809,7 @@ bool checkAssignEscape(ref Scope sc, Expression e, bool gag, bool byRef) // If va's lifetime encloses v's, then error if (va && !(vaIsFirstRef && v.isReturn()) && va.enclosesLifetimeOf(v)) { - if (sc.setUnsafeDIP1000(gag, ae.loc, "address of variable `%s` assigned to `%s` with longer lifetime", v, va)) + if (sc.setUnsafeDIP1000(gag, ae.loc, "assigning address of variable `%s` to `%s` with longer lifetime", v, va)) { result = true; return; @@ -845,7 +817,7 @@ bool checkAssignEscape(ref Scope sc, Expression e, bool gag, bool byRef) } if (!(va && va.isScope())) - notMaybeScope(v, e); + doNotInferScope(v, e); if (p != sc.func) return; @@ -856,10 +828,8 @@ bool checkAssignEscape(ref Scope sc, Expression e, bool gag, bool byRef) va.storage_class |= STC.return_ | STC.returninferred; return; } - if (e1.op == EXP.structLiteral) - return; - result |= sc.setUnsafeDIP1000(gag, ae.loc, "reference to local variable `%s` assigned to non-scope `%s`", v, e1); + result |= sc.setUnsafeDIP1000(gag, ae.loc, "assigning reference to local variable `%s` to non-scope `%s`", v, e1); } void onFunc(FuncDeclaration func, bool called) @@ -884,7 +854,7 @@ bool checkAssignEscape(ref Scope sc, Expression e, bool gag, bool byRef) Dsymbol p = v.toParent2(); if (!(va && va.isScope())) - notMaybeScope(v, e); + doNotInferScope(v, e); if (!(v.isReference() || v.isScope()) || p != fd) return; @@ -899,7 +869,7 @@ bool checkAssignEscape(ref Scope sc, Expression e, bool gag, bool byRef) return; } result |= sc.setUnsafeDIP1000(gag, ae.loc, - "reference to local `%s` assigned to non-scope `%s` in @safe code", v, e1); + "assigning reference to local `%s` to non-scope `%s`", v, e1); } } @@ -909,8 +879,7 @@ bool checkAssignEscape(ref Scope sc, Expression e, bool gag, bool byRef) /* Do not allow slicing of a static array returned by a function */ - if (ee.op == EXP.call && ee.type.toBasetype().isTypeSArray() && e1.type.toBasetype().isTypeDArray() && - !(va && va.storage_class & STC.temp)) + if (ee.op == EXP.call && ee.type.toBasetype().isTypeSArray() && e1.type.toBasetype().isTypeDArray()) { if (!gag) sc.eSink.deprecation(ee.loc, "slice of static array temporary returned by `%s` assigned to longer lived variable `%s`", @@ -919,31 +888,11 @@ bool checkAssignEscape(ref Scope sc, Expression e, bool gag, bool byRef) return; } - if (ee.op == EXP.call && ee.type.toBasetype().isTypeStruct() && - (!va || !(va.storage_class & STC.temp) && !va.isScope())) - { - if (sc.setUnsafeDIP1000(gag, ee.loc, "address of struct temporary returned by `%s` assigned to longer lived variable `%s`", ee, e1)) - { - result = true; - return; - } - } - - if (ee.op == EXP.structLiteral && - (!va || !(va.storage_class & STC.temp))) - { - if (sc.setUnsafeDIP1000(gag, ee.loc, "address of struct literal `%s` assigned to longer lived variable `%s`", ee, e1)) - { - result = true; - return; - } - } - - if (inferScope(va)) - return; + const(char)* msg = (ee.op == EXP.structLiteral) ? + "assigning address of struct literal `%s` to `%s` with longer lifetime" : + "assigning address of expression temporary returned by `%s` to `%s` with longer lifetime"; - result |= sc.setUnsafeDIP1000(gag, ee.loc, - "reference to stack allocated value returned by `%s` assigned to non-scope `%s`", ee, e1); + result |= sc.setUnsafeDIP1000(gag, ee.loc, msg, ee, e1); } scope EscapeByResults er = EscapeByResults(&onRef, &onValue, &onFunc, &onExp); @@ -977,19 +926,16 @@ bool checkThrowEscape(ref Scope sc, Expression e, bool gag) void onValue(VarDeclaration v) { //printf("byvalue %s\n", v.toChars()); - if (v.isDataseg()) - return; - if (v.isScope() && !v.iscatchvar) // special case: allow catch var to be rethrown // despite being `scope` { // https://issues.dlang.org/show_bug.cgi?id=17029 - result |= sc.setUnsafeDIP1000(gag, e.loc, "scope variable `%s` may not be thrown", v); + result |= sc.setUnsafeDIP1000(gag, e.loc, "throwing scope variable `%s`", v); return; } else { - notMaybeScope(v, new ThrowExp(e.loc, e)); + doNotInferScope(v, new ThrowExp(e.loc, e)); } } void onFunc(FuncDeclaration fd, bool called) {} @@ -1025,8 +971,6 @@ bool checkNewEscape(ref Scope sc, Expression e, bool gag) void onValue(VarDeclaration v) { if (log) printf("byvalue `%s`\n", v.toChars()); - if (v.isDataseg()) - return; Dsymbol p = v.toParent2(); @@ -1045,19 +989,14 @@ bool checkNewEscape(ref Scope sc, Expression e, bool gag) !(p.parent == sc.func)) { // https://issues.dlang.org/show_bug.cgi?id=20868 - result |= sc.setUnsafeDIP1000(gag, e.loc, "scope variable `%s` may not be copied into allocated memory", v); + result |= sc.setUnsafeDIP1000(gag, e.loc, "copying scope variable `%s` into allocated memory", v); return; } } - else if (v.isTypesafeVariadicArray && p == sc.func) - { - result |= sc.setUnsafeDIP1000(gag, e.loc, - "copying `%s` into allocated memory escapes a reference to variadic parameter `%s`", e, v); - } else { //printf("no infer for %s in %s, %d\n", v.toChars(), sc.func.ident.toChars(), __LINE__); - notMaybeScope(v, e); + doNotInferScope(v, e); } } @@ -1070,14 +1009,11 @@ bool checkNewEscape(ref Scope sc, Expression e, bool gag) bool escapingRef(VarDeclaration v, FeatureState fs) { const(char)* msg = v.isParameter() ? - "copying `%s` into allocated memory escapes a reference to parameter `%s`" : - "copying `%s` into allocated memory escapes a reference to local variable `%s`"; - return setUnsafePreview(&sc, fs, gag, e.loc, msg, e, v); + "escaping a reference to parameter `%s` by copying `%s` into allocated memory" : + "escaping a reference to local variable `%s` by copying `%s` into allocated memory"; + return setUnsafePreview(&sc, fs, gag, e.loc, msg, v, e); } - if (v.isDataseg()) - return; - Dsymbol p = v.toParent2(); if (!v.isReference()) @@ -1109,7 +1045,7 @@ bool checkNewEscape(ref Scope sc, Expression e, bool gag) return; if (auto tf = func.type.isTypeFunction()) { - if (!tf.isref) + if (!tf.isRef) return; const(char)* msg = "storing reference to outer local variable `%s` into allocated memory causes it to escape"; @@ -1124,13 +1060,18 @@ bool checkNewEscape(ref Scope sc, Expression e, bool gag) } } - void onFunc(FuncDeclaration fd, bool called) {} + void onFunc(FuncDeclaration fd, bool called) + { + if (called) + result |= sc.setUnsafeDIP1000(gag, e.loc, + "escaping a `scope` value returned from nested function `%s` into allocated memory", fd); + } void onExp(Expression ee, bool retRefTransition) { if (log) printf("byexp %s\n", ee.toChars()); if (!gag) - sc.eSink.error(ee.loc, "storing reference to stack allocated value returned by `%s` into allocated memory causes it to escape", + sc.eSink.error(ee.loc, "escaping reference to stack allocated value returned by `%s` into allocated memory", ee.toChars()); result = true; } @@ -1203,8 +1144,6 @@ private bool checkReturnEscapeImpl(ref Scope sc, Expression e, bool refs, bool g void onValue(VarDeclaration v) { if (log) printf("byvalue `%s`\n", v.toChars()); - if (v.isDataseg()) - return; const vsr = buildScopeRef(v.storage_class); @@ -1215,7 +1154,13 @@ private bool checkReturnEscapeImpl(ref Scope sc, Expression e, bool refs, bool g return; } - if (v.isScope()) + if (v.isTypesafeVariadicArray && p == sc.func) + { + if (!gag) + sc.eSink.error(e.loc, "returning `%s` escapes a reference to variadic parameter `%s`", e.toChars(), v.toChars()); + result = false; + } + else if (v.isScope()) { /* If `return scope` applies to v. */ @@ -1265,18 +1210,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, "scope variable `%s` may not be returned", v); + result |= sc.setUnsafeDIP1000(gag, e.loc, "returning scope variable `%s`", v); return; } } } - else if (v.isTypesafeVariadicArray && p == sc.func) - { - if (!gag) - sc.eSink.error(e.loc, "returning `%s` escapes a reference to variadic parameter `%s`", e.toChars(), v.toChars()); - result = false; - } - else + else if (p == sc.func || !v.isParameter()) { //printf("no infer for %s in %s, %d\n", v.toChars(), sc.func.ident.toChars(), __LINE__); doNotInferScope(v, e); @@ -1294,13 +1233,12 @@ private bool checkReturnEscapeImpl(ref Scope sc, Expression e, bool refs, bool g // depending on the flag passed to the CLI for DIP25 void escapingRef(VarDeclaration v, FeatureState featureState) { - const(char)* msg = v.isParameter() ? - "returning `%s` escapes a reference to parameter `%s`" : - "returning `%s` escapes a reference to local variable `%s`"; - + const(char)* safeMsg = v.isParameter() ? + "escaping a reference to parameter `%s` by returning `%s`" : + "escaping a reference to local variable `%s` by returning `%s` "; if (v.isParameter() && v.isReference()) { - if (setUnsafePreview(&sc, featureState, gag, e.loc, msg, e, v) || + if (setUnsafePreview(&sc, featureState, gag, e.loc, safeMsg, v, e) || sc.func.isSafeBypassingInference()) { result = true; @@ -1321,10 +1259,13 @@ private bool checkReturnEscapeImpl(ref Scope sc, Expression e, bool refs, bool g { if (retRefTransition) { - result |= sc.setUnsafeDIP1000(gag, e.loc, msg, e, v); + result |= sc.setUnsafeDIP1000(gag, e.loc, safeMsg, v, e); } else { + const(char)* msg = v.isParameter() ? + "returning `%s` escapes a reference to parameter `%s`" : + "returning `%s` escapes a reference to local variable `%s`"; if (!gag) previewErrorFunc(sc.isDeprecated(), featureState)(e.loc, msg, e.toChars(), v.toChars()); result = true; @@ -1332,24 +1273,15 @@ private bool checkReturnEscapeImpl(ref Scope sc, Expression e, bool refs, bool g } } - if (v.isDataseg()) - return; - const vsr = buildScopeRef(v.storage_class); Dsymbol p = v.toParent2(); // https://issues.dlang.org/show_bug.cgi?id=19965 - if (!refs) + if (!refs && checkScopeVarAddr(v, e, sc, gag)) { - if (sc.func.vthis == v) - notMaybeScope(v, e); - - if (checkScopeVarAddr(v, e, sc, gag)) - { - result = true; - return; - } + result = true; + return; } if (!v.isReference()) @@ -1360,7 +1292,7 @@ private bool checkReturnEscapeImpl(ref Scope sc, Expression e, bool refs, bool g return; } FuncDeclaration fd = p.isFuncDeclaration(); - if (fd && sc.func.returnInprocess) + if (fd && sc.func.scopeInprocess) { /* Code like: * int x; @@ -1376,10 +1308,9 @@ private bool checkReturnEscapeImpl(ref Scope sc, Expression e, bool refs, bool g /* Check for returning a ref variable by 'ref', but should be 'return ref' * Infer the addition of 'return', or set result to be the offending expression. */ - if ((vsr == ScopeRef.Ref || + if (vsr == ScopeRef.Ref || vsr == ScopeRef.RefScope || - vsr == ScopeRef.Ref_ReturnScope) && - !(v.storage_class & STC.foreach_)) + vsr == ScopeRef.Ref_ReturnScope) { if (p == sc.func && (vsr == ScopeRef.Ref || vsr == ScopeRef.RefScope) && inferReturn(sc.func, v, /*returnScope:*/ false)) @@ -1402,7 +1333,7 @@ private bool checkReturnEscapeImpl(ref Scope sc, Expression e, bool refs, bool g if (fd && fd.type && fd.type.ty == Tfunction) { TypeFunction tf = fd.type.isTypeFunction(); - if (tf.isref) + if (tf.isRef) { const(char)* msg = "escaping reference to outer local variable `%s`"; if (!gag) @@ -1488,7 +1419,7 @@ private bool inferReturn(FuncDeclaration fd, VarDeclaration v, bool returnScope) if (!v.isParameter() || v.isTypesafeVariadicArray || (returnScope && v.doNotInferReturn)) return false; - if (!fd.returnInprocess) + if (!fd.scopeInprocess) return false; if (returnScope && !(v.isScope() || v.maybeScope)) @@ -1506,9 +1437,9 @@ private bool inferReturn(FuncDeclaration fd, VarDeclaration v, bool returnScope) if (auto tf = fd.type.isTypeFunction()) { //printf("'this' too %p %s\n", tf, sc.func.toChars()); - tf.isreturnscope = returnScope; - tf.isreturn = true; - tf.isreturninferred = true; + tf.isReturnScope = returnScope; + tf.isReturn = true; + tf.isReturnInferred = true; } } else @@ -1546,10 +1477,20 @@ private bool inferReturn(FuncDeclaration fd, VarDeclaration v, bool returnScope) * Params: * e = expression to be returned by value * er = where to place collected data - * retRefTransition = if `e` is returned through a `return (ref) scope` function call */ public -void escapeByValue(Expression e, ref scope EscapeByResults er, bool retRefTransition = false) +void escapeByValue(Expression e, ref scope EscapeByResults er) +{ + escapeExp(e, er, 0); +} + +// Unified implementation of `escapeByValue` and `escapeByRef` +// deref = derference level, if `p` has deref 0, then `*p` has deref 1, `&p` has -1, and `**p` has 2 etc. +// For escapeByValue, deref = 0 +// For escapeByRef, deref = -1 +// Currently, `scope` is not transitive, so deref > 0 means no escaping, but `@live` does do transitive checking, +// and future enhancements might add some form of transitive scope. +void escapeExp(Expression e, ref scope EscapeByResults er, int deref) { //printf("[%s] escapeByValue, e: %s\n", e.loc.toChars(), e.toChars()); @@ -1563,55 +1504,92 @@ void escapeByValue(Expression e, ref scope EscapeByResults er, bool retRefTransi * allowed, but CTFE can generate one out of a new expression, * but it'll be placed in static data so no need to check it. */ - if (e.e1.op != EXP.structLiteral) - escapeByRef(e.e1, er, retRefTransition); + if (deref == 0 && e.e1.op != EXP.structLiteral) + escapeExp(e.e1, er, deref - 1); } void visitSymOff(SymOffExp e) { - VarDeclaration v = e.var.isVarDeclaration(); - if (v) - er.byRef(v, retRefTransition); + if (VarDeclaration v = e.var.isVarDeclaration()) + er.varDeref(v, deref - 1); } void visitVar(VarExp e) { if (auto v = e.var.isVarDeclaration()) { - if (v.type.hasPointers() || // not tracking non-pointers - v.storage_class & STC.lazy_) // lazy variables are actually pointers - er.byValue(v); + const refAddr = deref < 0 && v.storage_class & STC.ref_ ; + const tempVar = deref == 0 && v.storage_class & STC.temp; + if ((refAddr || tempVar) && v._init && v != er.lastTemp) + { + // If compiler generated ref temporary + // (ref v = ex; ex) + // e.g. to extract side effects of `Tuple!(int, int).modify().expand[0]` + // look at the initializer instead + if (ExpInitializer ez = v._init.isExpInitializer()) + { + // Prevent endless loops. Consider: + // `__field0 = (S __tup1 = S(x, y);) , __field0 = __tup1.__fields_field_0` + // escapeExp would recurse on the lhs of the last assignment, which is __field0 + // again. In this case, we want the rhs. + // Also consider appending a struct with a `return scope` constructor: + // __appendtmp34 = __appendtmp34.this(null) + // In that case we just break the cycle using `lastTemp`. + auto lc = ez.exp.lastComma(); + auto restoreLastTemp = er.lastTemp; + er.lastTemp = v; + // printf("%s %s TO %s\n", e.loc.toChars, e.toChars, lc.toChars); + if (lc.isAssignExp || lc.isConstructExp || lc.isBlitExp) + escapeExp(lc.isBinExp().e2, er, deref); + else + escapeExp(ez.exp, er, deref); + + er.lastTemp = restoreLastTemp; + return; + } + } + + if (deref < 0 || e.type.hasPointers()) + er.varDeref(v, deref); } } void visitThis(ThisExp e) { + // Special case because `__this2` isn't `ref` internally + if (deref == -1 && e.var && e.var.toParent2().isFuncDeclaration().hasDualContext()) + { + escapeByValue(e, er); + return; + } + if (e.var) - er.byValue(e.var); + er.varDeref(e.var, deref); } void visitPtr(PtrExp e) { - if (er.live && e.type.hasPointers()) - escapeByValue(e.e1, er, retRefTransition); + if (deref < 0 || (er.live && e.type.hasPointers())) + escapeExp(e.e1, er, deref + 1); } void visitDotVar(DotVarExp e) { - auto t = e.e1.type.toBasetype(); - if (e.type.hasPointers() && (er.live || t.ty == Tstruct)) - { - escapeByValue(e.e1, er, retRefTransition); - } + auto t1b = e.e1.type.toBasetype(); + // Accessing a class field dereferences the `this` pointer + if (t1b.isTypeClass()) + escapeExp(e.e1, er, deref + 1); + else if (deref < 0 || e.type.hasPointers()) + escapeExp(e.e1, er, deref); } void visitDelegate(DelegateExp e) { Type t = e.e1.type.toBasetype(); - if (t.ty == Tclass || t.ty == Tpointer) - escapeByValue(e.e1, er, retRefTransition); + if (t.isTypeClass() || t.isTypePointer()) + escapeByValue(e.e1, er); else - escapeByRef(e.e1, er, retRefTransition); + escapeByRef(e.e1, er); er.byFunc(e.func, false); } @@ -1629,14 +1607,14 @@ void escapeByValue(Expression e, ref scope EscapeByResults er, bool retRefTransi void visitArrayLiteral(ArrayLiteralExp e) { Type tb = e.type.toBasetype(); - if (tb.ty == Tsarray || tb.ty == Tarray) + if (tb.isStaticOrDynamicArray()) { if (e.basis) - escapeByValue(e.basis, er, retRefTransition); + escapeExp(e.basis, er, deref); foreach (el; *e.elements) { if (el) - escapeByValue(el, er, retRefTransition); + escapeExp(el, er, deref); } } } @@ -1648,120 +1626,130 @@ void escapeByValue(Expression e, ref scope EscapeByResults er, bool retRefTransi foreach (ex; *e.elements) { if (ex) - escapeByValue(ex, er, retRefTransition); + escapeExp(ex, er, deref); } } + if (deref == -1) + { + er.byExp(e, er.inRetRefTransition > 0); // + } } void visitNew(NewExp e) { + if (e.placement) + escapeExp(e.placement, er, deref); + Type tb = e.newtype.toBasetype(); - if (tb.ty == Tstruct && !e.member && e.arguments) + if (tb.isTypeStruct() && !e.member && e.arguments) { foreach (ex; *e.arguments) { if (ex) - escapeByValue(ex, er, retRefTransition); + escapeExp(ex, er, deref); } } } void visitCast(CastExp e) { - if (!e.type.hasPointers()) + if (deref < 0 || !e.type.hasPointers()) return; Type tb = e.type.toBasetype(); - if (tb.ty == Tarray && e.e1.type.toBasetype().ty == Tsarray) - { - escapeByRef(e.e1, er, retRefTransition); - } + if (tb.isTypeDArray() && e.e1.type.toBasetype().isTypeSArray()) + escapeExp(e.e1, er, deref - 1); else - escapeByValue(e.e1, er, retRefTransition); + escapeExp(e.e1, er, deref); } void visitSlice(SliceExp e) { - if (auto ve = e.e1.isVarExp()) - { - VarDeclaration v = ve.var.isVarDeclaration(); - Type tb = e.type.toBasetype(); - if (v) - { - if (tb.ty == Tsarray) - return; - if (v.isTypesafeVariadicArray) - { - er.byValue(v); - return; - } - } - } - Type t1b = e.e1.type.toBasetype(); - if (t1b.ty == Tsarray) - { - Type tb = e.type.toBasetype(); - if (tb.ty != Tsarray) - escapeByRef(e.e1, er, retRefTransition); - } - else - escapeByValue(e.e1, er, retRefTransition); + // Usually: slicing a static array escapes by ref, slicing a dynamic array escapes by value. + // However, slices with compile-time known length can implicitly converted to static arrays: + // int*[3] b = sa[0 .. 3]; + // So we need to compare the type before slicing and after slicing + const bool staticBefore = e.e1.type.toBasetype().isTypeSArray() !is null; + const bool staticAfter = e.type.toBasetype().isTypeSArray() !is null; + escapeExp(e.e1, er, deref + staticAfter - staticBefore); } void visitIndex(IndexExp e) { - if (e.e1.type.toBasetype().ty == Tsarray || - er.live && e.type.hasPointers()) + Type tb = e.e1.type.toBasetype(); + + if (tb.isTypeSArray()) + { + escapeExp(e.e1, er, deref); + } + else if (tb.isTypeDArray()) { - escapeByValue(e.e1, er, retRefTransition); + escapeExp(e.e1, er, deref + 1); } } void visitBin(BinExp e) { - Type tb = e.type.toBasetype(); - if (tb.ty == Tpointer) + if (e.type.toBasetype().isTypePointer()) { - escapeByValue(e.e1, er, retRefTransition); - escapeByValue(e.e2, er, retRefTransition); + // The expression must be pointer arithmetic, e.g. `p + 1` or `1 + p` + escapeExp(e.e1, er, deref); + escapeExp(e.e2, er, deref); } } void visitBinAssign(BinAssignExp e) { - escapeByValue(e.e1, er, retRefTransition); + escapeExp(e.e1, er, deref); } void visitAssign(AssignExp e) { - escapeByValue(e.e1, er, retRefTransition); + escapeExp(e.e1, er, deref); } void visitComma(CommaExp e) { - escapeByValue(e.e2, er, retRefTransition); + escapeExp(e.e2, er, deref); } void visitCond(CondExp e) { - escapeByValue(e.e1, er, retRefTransition); - escapeByValue(e.e2, er, retRefTransition); + escapeExp(e.e1, er, deref); + escapeExp(e.e2, er, deref); } void visitCall(CallExp e) { //printf("CallExp(): %s\n", e.toChars()); - /* Check each argument that is - * passed as 'return scope'. - */ + // Check each argument that is passed as 'return scope'. TypeFunction tf = e.calledFunctionType(); - if (!tf || !e.type.hasPointers()) + if (!tf) + return; + + if (deref < 0 && !tf.isRef) + { + er.byExp(e, er.inRetRefTransition > 0); + return; + } + + // A function may have a return scope struct parameter, but only return an `int` field of that struct + if (deref >= 0 && !e.type.hasPointers()) return; + /// Given a `scope` / `return scope` / `return ref` annotation, + /// get the corresponding pointer dereference level + static int paramDeref(ScopeRef psr) + { + return + (psr == ScopeRef.ReturnRef || psr == ScopeRef.ReturnRef_Scope) ? -1 : + (psr == ScopeRef.ReturnScope || psr == ScopeRef.Ref_ReturnScope) ? 0 : + +1; + } + if (e.arguments && e.arguments.length) { - /* j=1 if _arguments[] is first argument, - * skip it because it is not passed by ref - */ + // j=1 if _arguments[] is first argument, + // skip it because it is not passed by ref int j = tf.isDstyleVariadic(); for (size_t i = j; i < e.arguments.length; ++i) { @@ -1772,139 +1760,73 @@ void escapeByValue(Expression e, ref scope EscapeByResults er, bool retRefTransi Parameter p = tf.parameterList[i - j]; const stc = tf.parameterStorageClass(null, p); ScopeRef psr = buildScopeRef(stc); - if (psr == ScopeRef.ReturnScope || psr == ScopeRef.Ref_ReturnScope) - { - if (tf.isref) - { - /* ignore `ref` on struct constructor return because - * struct S { this(return scope int* q) { this.p = q; } int* p; } - * is different from: - * ref char* front(return scope char** q) { return *q; } - * https://github.com/dlang/dmd/pull/14869 - */ - if (auto dve = e.e1.isDotVarExp()) - if (auto fd = dve.var.isFuncDeclaration()) - if (fd.isCtorDeclaration() && tf.next.toBasetype().isTypeStruct()) - { - escapeByValue(arg, er, retRefTransition); - } - } - else - escapeByValue(arg, er, true); - } - else if (psr == ScopeRef.ReturnRef || psr == ScopeRef.ReturnRef_Scope) - { - if (tf.isref) - { - /* Treat: - * ref P foo(return ref P p) - * as: - * p; - */ - escapeByValue(arg, er, retRefTransition); - } - else - escapeByRef(arg, er, retRefTransition); - } + + // For struct constructors, `tf.isRef` is true, but for escape analysis, + // it's as if they return `void` and escape through the first (`this`) parameter: + // void assign(ref S this, return scope constructorArgs...) + // If you then return the constructed result by value, it doesn't count + // as dereferencing the scope arguments, they're still escaped. + const isRef = tf.isRef && !(tf.isCtor && paramDeref(psr) == 0); + const maybeInaccurate = deref == 0 && paramDeref(psr) == 0; + er.inRetRefTransition += maybeInaccurate; + if (paramDeref(psr) <= 0) + escapeExp(arg, er, deref + paramDeref(psr) + isRef); + er.inRetRefTransition -= maybeInaccurate; } } } + // If 'this' is returned, check it too Type t1 = e.e1.type.toBasetype(); - if (e.e1.op == EXP.dotVariable && t1.ty == Tfunction) + DotVarExp dve = e.e1.isDotVarExp(); + if (dve && t1.ty == Tfunction) { - DotVarExp dve = e.e1.isDotVarExp(); FuncDeclaration fd = dve.var.isFuncDeclaration(); - if (fd && fd.isThis()) - { - /* Calling a non-static member function dve.var, which is returning `this`, and with dve.e1 representing `this` - */ - - /***************************** - * Concoct storage class for member function's implicit `this` parameter. - * Params: - * fd = member function - * Returns: - * storage class for fd's `this` - */ - StorageClass getThisStorageClass(FuncDeclaration fd) - { - StorageClass stc; - auto tf = fd.type.toBasetype().isTypeFunction(); - if (tf.isreturn) - stc |= STC.return_; - if (tf.isreturnscope) - stc |= STC.returnScope | STC.scope_; - auto ad = fd.isThis(); - if (ad.isClassDeclaration() || tf.isScopeQual) - stc |= STC.scope_; - if (ad.isStructDeclaration()) - stc |= STC.ref_; // `this` for a struct member function is passed by `ref` - return stc; - } - - const psr = buildScopeRef(getThisStorageClass(fd)); - if (psr == ScopeRef.ReturnScope || psr == ScopeRef.Ref_ReturnScope) - { - if (!tf.isref || tf.isctor) - escapeByValue(dve.e1, er, retRefTransition); - } - else if (psr == ScopeRef.ReturnRef || psr == ScopeRef.ReturnRef_Scope) - { - if (tf.isref) - { - /* Treat calling: - * struct S { ref S foo() return; } - * as: - * this; - */ - escapeByValue(dve.e1, er, retRefTransition); - } - else - escapeByRef(dve.e1, er, psr == ScopeRef.ReturnRef_Scope); - } - } + if (!fd) + return; - // If it's also a nested function that is 'return scope' - if (fd && fd.isNested()) + // https://issues.dlang.org/show_bug.cgi?id=20149#c10 + if (deref < 0 && dve.var.isCtorDeclaration()) { - if (tf.isreturn && tf.isScopeQual) - { - if (tf.isreturnscope) - er.byFunc(fd, true); - else - er.byExp(e, false); - } + er.byExp(e, false); + return; } + + // Calling a non-static member function dve.var, which is returning `this`, and with dve.e1 representing `this` + const psr = buildScopeRef(getThisStorageClass(fd)); + er.inRetRefTransition += (psr == ScopeRef.ReturnRef_Scope); + if (paramDeref(psr) <= 0) + escapeExp(dve.e1, er, deref + paramDeref(psr) + (tf.isRef && !tf.isCtor)); + er.inRetRefTransition -= (psr == ScopeRef.ReturnRef_Scope); } - /* If returning the result of a delegate call, the .ptr - * field of the delegate must be checked. - */ - if (t1.isTypeDelegate()) + // The return value of a delegate call with return (scope) may point to a closure variable, + // so escape the delegate in case it's `scope` / stack allocated. + if (t1.isTypeDelegate() && tf.isReturn) { - if (tf.isreturn) - escapeByValue(e.e1, er, retRefTransition); + escapeExp(e.e1, er, deref + tf.isRef); } - /* If it's a nested function that is 'return scope' - */ + // If `fd` is a nested function that's return ref / return scope, check that + // it doesn't escape closure vars if (auto ve = e.e1.isVarExp()) { - FuncDeclaration fd = ve.var.isFuncDeclaration(); - if (fd && fd.isNested()) + if (FuncDeclaration fd = ve.var.isFuncDeclaration()) { - if (tf.isreturn && tf.isScopeQual) + if (fd.isNested() && tf.isReturn) { - if (tf.isreturnscope) - er.byFunc(fd, true); - else - er.byExp(e, false); + er.byFunc(fd, true); } } } } + if (deref > 0 && !er.live) + return; // scope is not transitive currently, so dereferencing expressions don't escape + + if (deref >= 0 && er.live && !e.type.hasPointers()) + return; // can't escape non-pointer values by value + switch (e.op) { case EXP.address: return visitAddr(e.isAddrExp()); @@ -1929,14 +1851,36 @@ void escapeByValue(Expression e, ref scope EscapeByResults er, bool retRefTransi case EXP.question: return visitCond(e.isCondExp()); case EXP.call: return visitCall(e.isCallExp()); default: - if (auto b = e.isBinExp()) - return visitBin(b); if (auto ba = e.isBinAssignExp()) return visitBinAssign(ba); + if (auto b = e.isBinExp()) + return visitBin(b); return visit(e); } } +/***************************** + * Concoct storage class for member function's implicit `this` parameter. + * Params: + * fd = member function + * Returns: + * storage class for fd's `this` + */ +STC getThisStorageClass(FuncDeclaration fd) +{ + STC stc; + auto tf = fd.type.toBasetype().isTypeFunction(); + if (tf.isReturn) + stc |= STC.return_; + if (tf.isReturnScope) + stc |= STC.returnScope | STC.scope_; + auto ad = fd.isThis(); + if ((ad && ad.isClassDeclaration()) || tf.isScopeQual) + stc |= STC.scope_; + if (ad && ad.isStructDeclaration()) + stc |= STC.ref_; // `this` for a struct member function is passed by `ref` + return stc; +} /**************************************** * e is an expression to be returned by 'ref'. @@ -1954,238 +1898,10 @@ void escapeByValue(Expression e, ref scope EscapeByResults er, bool retRefTransi * Params: * e = expression to be returned by 'ref' * er = where to place collected data - * retRefTransition = if `e` is returned through a `return (ref) scope` function call */ -private -void escapeByRef(Expression e, ref scope EscapeByResults er, bool retRefTransition = false) +void escapeByRef(Expression e, ref scope EscapeByResults er) { - //printf("[%s] escapeByRef, e: %s, retRefTransition: %d\n", e.loc.toChars(), e.toChars(), retRefTransition); - void visit(Expression e) - { - } - - void visitVar(VarExp e) - { - auto v = e.var.isVarDeclaration(); - if (v) - { - if (v.storage_class & STC.ref_ && v.storage_class & (STC.foreach_ | STC.temp) && v._init) - { - /* If compiler generated ref temporary - * (ref v = ex; ex) - * look at the initializer instead - */ - if (ExpInitializer ez = v._init.isExpInitializer()) - { - if (auto ce = ez.exp.isConstructExp()) - escapeByRef(ce.e2, er, retRefTransition); - else - escapeByRef(ez.exp, er, retRefTransition); - } - } - else - er.byRef(v, retRefTransition); - } - } - - void visitThis(ThisExp e) - { - if (e.var && e.var.toParent2().isFuncDeclaration().hasDualContext()) - escapeByValue(e, er, retRefTransition); - else if (e.var) - er.byRef(e.var, retRefTransition); - } - - void visitPtr(PtrExp e) - { - escapeByValue(e.e1, er, retRefTransition); - } - - void visitIndex(IndexExp e) - { - Type tb = e.e1.type.toBasetype(); - if (auto ve = e.e1.isVarExp()) - { - VarDeclaration v = ve.var.isVarDeclaration(); - if (v && v.isTypesafeVariadicArray) - { - er.byRef(v, retRefTransition); - return; - } - } - if (tb.ty == Tsarray) - { - escapeByRef(e.e1, er, retRefTransition); - } - else if (tb.ty == Tarray) - { - escapeByValue(e.e1, er, retRefTransition); - } - } - - void visitStructLiteral(StructLiteralExp e) - { - if (e.elements) - { - foreach (ex; *e.elements) - { - if (ex) - escapeByRef(ex, er, retRefTransition); - } - } - er.byExp(e, retRefTransition); - } - - void visitDotVar(DotVarExp e) - { - Type t1b = e.e1.type.toBasetype(); - if (t1b.ty == Tclass) - escapeByValue(e.e1, er, retRefTransition); - else - escapeByRef(e.e1, er, retRefTransition); - } - - void visitBinAssign(BinAssignExp e) - { - escapeByRef(e.e1, er, retRefTransition); - } - - void visitAssign(AssignExp e) - { - escapeByRef(e.e1, er, retRefTransition); - } - - void visitComma(CommaExp e) - { - escapeByRef(e.e2, er, retRefTransition); - } - - void visitCond(CondExp e) - { - escapeByRef(e.e1, er, retRefTransition); - escapeByRef(e.e2, er, retRefTransition); - } - - void visitCall(CallExp e) - { - //printf("escapeByRef.CallExp(): %s\n", e.toChars()); - /* If the function returns by ref, check each argument that is - * passed as 'return ref'. - */ - TypeFunction tf = e.calledFunctionType(); - if (!tf) - return; - if (tf.isref) - { - if (e.arguments && e.arguments.length) - { - /* j=1 if _arguments[] is first argument, - * skip it because it is not passed by ref - */ - int j = tf.isDstyleVariadic(); - for (size_t i = j; i < e.arguments.length; ++i) - { - Expression arg = (*e.arguments)[i]; - size_t nparams = tf.parameterList.length; - if (i - j < nparams && i >= j) - { - Parameter p = tf.parameterList[i - j]; - const stc = tf.parameterStorageClass(null, p); - ScopeRef psr = buildScopeRef(stc); - if (psr == ScopeRef.ReturnRef || psr == ScopeRef.ReturnRef_Scope) - escapeByRef(arg, er, retRefTransition); - else if (psr == ScopeRef.ReturnScope || psr == ScopeRef.Ref_ReturnScope) - { - if (auto de = arg.isDelegateExp()) - { - if (de.func.isNested()) - er.byExp(de, false); - } - else - escapeByValue(arg, er, retRefTransition); - } - } - } - } - // If 'this' is returned by ref, check it too - Type t1 = e.e1.type.toBasetype(); - if (e.e1.op == EXP.dotVariable && t1.ty == Tfunction) - { - DotVarExp dve = e.e1.isDotVarExp(); - - // https://issues.dlang.org/show_bug.cgi?id=20149#c10 - if (dve.var.isCtorDeclaration()) - { - er.byExp(e, false); - return; - } - - StorageClass stc = dve.var.storage_class & (STC.return_ | STC.scope_ | STC.ref_); - if (tf.isreturn) - stc |= STC.return_; - if (tf.isref) - stc |= STC.ref_; - if (tf.isScopeQual) - stc |= STC.scope_; - if (tf.isreturnscope) - stc |= STC.returnScope; - - const psr = buildScopeRef(stc); - if (psr == ScopeRef.ReturnRef || psr == ScopeRef.ReturnRef_Scope) - escapeByRef(dve.e1, er, psr == ScopeRef.ReturnRef_Scope); - else if (psr == ScopeRef.ReturnScope || psr == ScopeRef.Ref_ReturnScope) - escapeByValue(dve.e1, er, retRefTransition); - - // If it's also a nested function that is 'return ref' - if (FuncDeclaration fd = dve.var.isFuncDeclaration()) - { - if (fd.isNested() && tf.isreturn) - { - er.byExp(e, false); - } - } - } - // If it's a delegate, check it too - if (e.e1.op == EXP.variable && t1.ty == Tdelegate) - { - escapeByValue(e.e1, er, retRefTransition); - } - - /* If it's a nested function that is 'return ref' - */ - if (auto ve = e.e1.isVarExp()) - { - FuncDeclaration fd = ve.var.isFuncDeclaration(); - if (fd && fd.isNested()) - { - if (tf.isreturn) - er.byExp(e, false); - } - } - } - else - er.byExp(e, retRefTransition); - } - - switch (e.op) - { - case EXP.variable: return visitVar(e.isVarExp()); - case EXP.this_: return visitThis(e.isThisExp()); - case EXP.star: return visitPtr(e.isPtrExp()); - case EXP.structLiteral: return visitStructLiteral(e.isStructLiteralExp()); - case EXP.dotVariable: return visitDotVar(e.isDotVarExp()); - case EXP.index: return visitIndex(e.isIndexExp()); - case EXP.blit: return visitAssign(e.isBlitExp()); - case EXP.construct: return visitAssign(e.isConstructExp()); - case EXP.assign: return visitAssign(e.isAssignExp()); - case EXP.comma: return visitComma(e.isCommaExp()); - case EXP.question: return visitCond(e.isCondExp()); - case EXP.call: return visitCall(e.isCallExp()); - default: - if (auto ba = e.isBinAssignExp()) - return visitBinAssign(ba); - return visit(e); - } + escapeExp(e, er, -1); } /************************************ @@ -2213,6 +1929,20 @@ struct EscapeByResults void delegate(VarDeclaration, bool retRefTransition) byRef; /// called on variables with values containing pointers void delegate(VarDeclaration) byValue; + + /// Switch over `byValue` and `byRef` based on `deref` level (-1 = by ref, 0 = by value, 1 = only for live currently) + private void varDeref(VarDeclaration var, int deref) + { + if (var.isDataseg()) + return; + if (deref == -1) + byRef(var, inRetRefTransition > 0); + else if (deref == 0) + byValue(var); + else if (deref > 0 && live) + byValue(var); + } + /// called on nested functions that are turned into delegates /// When `called` is true, it means the delegate escapes variables /// from the closure through a call to it, while `false` means the @@ -2223,6 +1953,14 @@ struct EscapeByResults /// if @live semantics apply, i.e. expressions `p`, `*p`, `**p`, etc., all return `p`. bool live = false; + + /// Incremented / decremented every time an ambiguous return ref/scope parameter is checked. + /// See retRefTransition above. + private int inRetRefTransition = 0; + + /// When forwarding a temp var to its initializer, + /// keep track of the temp var to break endless loops + private VarDeclaration lastTemp = null; } /************************* @@ -2266,7 +2004,7 @@ public void findAllOuterAccessedVariables(FuncDeclaration fd, VarDeclarations* v * - `VarDeclaration` of a non-scope parameter it was assigned to * - `null` for no reason */ -private void notMaybeScope(VarDeclaration v, RootObject o) +private void doNotInferScope(VarDeclaration v, RootObject o) { if (v.maybeScope) { @@ -2276,23 +2014,6 @@ private void notMaybeScope(VarDeclaration v, RootObject o) } } -/*********************************** - * Turn off `maybeScope` for variable `v` if it's not a parameter. - * - * This is for compatibility with the old system with both `STC.maybescope` and `VarDeclaration.doNotInferScope`, - * which is now just `VarDeclaration.maybeScope`. - * This function should probably be removed in future refactors. - * - * Params: - * v = variable - * o = reason for it being turned off - */ -private void doNotInferScope(VarDeclaration v, RootObject o) -{ - if (!v.isParameter) - notMaybeScope(v, o); -} - /*********************************** * After semantic analysis of the function body, * try to infer `scope` / `return` on the parameters @@ -2305,48 +2026,21 @@ private void doNotInferScope(VarDeclaration v, RootObject o) public void finishScopeParamInference(FuncDeclaration funcdecl, ref TypeFunction f) { - - if (funcdecl.returnInprocess) - { - funcdecl.returnInprocess = false; - if (funcdecl.storage_class & STC.return_) - { - if (funcdecl.type == f) - f = cast(TypeFunction)f.copy(); - f.isreturn = true; - f.isreturnscope = cast(bool) (funcdecl.storage_class & STC.returnScope); - if (funcdecl.storage_class & STC.returninferred) - f.isreturninferred = true; - } - } - - if (!funcdecl.inferScope) + if (!funcdecl.scopeInprocess) return; - funcdecl.inferScope = false; + funcdecl.scopeInprocess = false; - // Eliminate maybescope's + if (funcdecl.storage_class & STC.return_) { - // Create and fill array[] with maybe candidates from the `this` and the parameters - VarDeclaration[10] tmp = void; - size_t dim = (funcdecl.vthis !is null) + (funcdecl.parameters ? funcdecl.parameters.length : 0); - - import dmd.common.smallbuffer : SmallBuffer; - auto sb = SmallBuffer!VarDeclaration(dim, tmp[]); - VarDeclaration[] array = sb[]; - - size_t n = 0; - if (funcdecl.vthis) - array[n++] = funcdecl.vthis; - if (funcdecl.parameters) - { - foreach (v; *funcdecl.parameters) - { - array[n++] = v; - } - } - eliminateMaybeScopes(array[0 .. n]); + if (funcdecl.type == f) + f = cast(TypeFunction)f.copy(); + f.isReturn = true; + f.isReturnScope = cast(bool) (funcdecl.storage_class & STC.returnScope); + if (funcdecl.storage_class & STC.returninferred) + f.isReturnInferred = true; } + // Infer STC.scope_ if (funcdecl.parameters && !funcdecl.errors) { @@ -2366,65 +2060,10 @@ void finishScopeParamInference(FuncDeclaration funcdecl, ref TypeFunction f) { inferScope(funcdecl.vthis); f.isScopeQual = funcdecl.vthis.isScope(); - f.isscopeinferred = !!(funcdecl.vthis.storage_class & STC.scopeinferred); + f.isScopeInferred = !!(funcdecl.vthis.storage_class & STC.scopeinferred); } } -/********************************************** - * Have some variables that are maybescopes that were - * assigned values from other maybescope variables. - * Now that semantic analysis of the function is - * complete, we can finalize this by turning off - * maybescope for array elements that cannot be scope. - * - * $(TABLE2 Scope Table, - * $(THEAD `va`, `v`, =>, `va` , `v` ) - * $(TROW maybe, maybe, =>, scope, scope) - * $(TROW scope, scope, =>, scope, scope) - * $(TROW scope, maybe, =>, scope, scope) - * $(TROW maybe, scope, =>, scope, scope) - * $(TROW - , - , =>, - , - ) - * $(TROW - , maybe, =>, - , - ) - * $(TROW - , scope, =>, error, error) - * $(TROW maybe, - , =>, scope, - ) - * $(TROW scope, - , =>, scope, - ) - * ) - * Params: - * array = array of variables that were assigned to from maybescope variables - */ -private void eliminateMaybeScopes(VarDeclaration[] array) -{ - enum log = false; - if (log) printf("eliminateMaybeScopes()\n"); - bool changes; - do - { - changes = false; - foreach (va; array) - { - if (log) printf(" va = %s\n", va.toChars()); - if (!(va.maybeScope || va.isScope())) - { - if (va.maybes) - { - foreach (v; *va.maybes) - { - if (log) printf(" v = %s\n", v.toChars()); - if (v.maybeScope) - { - // v cannot be scope since it is assigned to a non-scope va - notMaybeScope(v, va); - if (!v.isReference()) - v.storage_class &= ~(STC.return_ | STC.returninferred); - changes = true; - } - } - } - } - } - } while (changes); -} - /************************************************ * Is type a reference to a mutable value? * @@ -2552,30 +2191,12 @@ private EnclosedBy enclosesLifetimeOf(VarDeclaration va, VarDeclaration v) return EnclosedBy.none; } -/*************************************** - * Add variable `v` to maybes[] - * - * When a maybescope variable `v` is assigned to a maybescope variable `va`, - * we cannot determine if `this` is actually scope until the semantic - * analysis for the function is completed. Thus, we save the data - * until then. - * Params: - * v = a variable with `maybeScope == true` that was assigned to `this` - */ -private void addMaybe(VarDeclaration va, VarDeclaration v) -{ - //printf("add %s to %s's list of dependencies\n", v.toChars(), toChars()); - if (!va.maybes) - va.maybes = new VarDeclarations(); - va.maybes.push(v); -} - // `setUnsafePreview` partially evaluated for dip1000 public bool setUnsafeDIP1000(ref Scope sc, bool gag, Loc loc, const(char)* msg, - RootObject arg0 = null, RootObject arg1 = null, RootObject arg2 = null) + RootObject[] args...) { - return setUnsafePreview(&sc, sc.useDIP1000, gag, loc, msg, arg0, arg1, arg2); + return setUnsafePreview(&sc, sc.useDIP1000, gag, loc, msg, args); } /*************************************** @@ -2594,12 +2215,9 @@ bool setUnsafeDIP1000(ref Scope sc, bool gag, Loc loc, const(char)* msg, */ private bool checkScopeVarAddr(VarDeclaration v, Expression e, ref Scope sc, bool gag) { - if (v.storage_class & STC.temp) - return false; - if (!v.isScope()) { - notMaybeScope(v, e); + doNotInferScope(v, e); return false; } @@ -2615,7 +2233,7 @@ private bool checkScopeVarAddr(VarDeclaration v, Expression e, ref Scope sc, boo // take address of `scope` variable not allowed, requires transitive scope return sc.setUnsafeDIP1000(gag, e.loc, - "cannot take address of `scope` variable `%s` since `scope` applies to first indirection only", v); + "taking address of `scope` variable `%s` with pointers", v); } /**************************** @@ -2630,7 +2248,7 @@ private bool isTypesafeVariadicArray(VarDeclaration v) if (v.storage_class & STC.variadic) { Type tb = v.type.toBasetype(); - if (tb.ty == Tarray || tb.ty == Tsarray) + if (tb.isStaticOrDynamicArray()) return true; } return false; diff --git a/dmd/expression.d b/dmd/expression.d index be642ad5fc..6edca1cb22 100644 --- a/dmd/expression.d +++ b/dmd/expression.d @@ -3,12 +3,12 @@ * * Specification: ($LINK2 https://dlang.org/spec/expression.html, Expressions) * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/expression.d, _expression.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/expression.d, _expression.d) * Documentation: https://dlang.org/phobos/dmd_expression.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/expression.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/expression.d */ module dmd.expression; @@ -54,29 +54,6 @@ import dmd.visitor; enum LOGSEMANTIC = false; -/// Return value for `checkModifiable` -enum Modifiable -{ - /// Not modifiable - no, - /// Modifiable (the type is mutable) - yes, - /// Modifiable because it is initialization - initialization, -} -/** - * Specifies how the checkModify deals with certain situations - */ -enum ModifyFlags -{ - /// Issue error messages on invalid modifications of the variable - none, - /// No errors are emitted for invalid modifications - noError = 0x1, - /// The modification occurs for a subfield of the current variable - fieldAssign = 0x2, -} - /**************************************** * Find the last non-comma expression. * Params: @@ -226,7 +203,7 @@ TemplateDeclaration getFuncTemplateDecl(Dsymbol s) @safe * (foo).size * cast(foo).size */ -DotIdExp typeDotIdExp(const ref Loc loc, Type type, Identifier ident) @safe +DotIdExp typeDotIdExp(Loc loc, Type type, Identifier ident) @safe { return new DotIdExp(loc, new TypeExp(loc, type), ident); } @@ -237,45 +214,49 @@ DotIdExp typeDotIdExp(const ref Loc loc, Type type, Identifier ident) @safe * 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) +VarDeclaration expToVariable(Expression e, out int deref) { + deref = 0; while (1) { switch (e.op) { case EXP.variable: - return (cast(VarExp)e).var.isVarDeclaration(); + return e.isVarExp().var.isVarDeclaration(); case EXP.dotVariable: - e = (cast(DotVarExp)e).e1; + e = e.isDotVarExp().e1; + if (e.type.toBasetype().isTypeClass()) + deref++; + continue; case EXP.index: { - IndexExp ei = cast(IndexExp)e; - e = ei.e1; - Type ti = e.type.toBasetype(); - if (ti.ty == Tsarray) - continue; - return null; + e = e.isIndexExp().e1; + if (!e.type.toBasetype().isTypeSArray()) + deref++; + + continue; } case EXP.slice: { - SliceExp ei = cast(SliceExp)e; - e = ei.e1; - Type ti = e.type.toBasetype(); - if (ti.ty == Tsarray) - continue; - return null; + e = e.isSliceExp().e1; + if (!e.type.toBasetype().isTypeSArray()) + deref++; + + continue; } - case EXP.this_: case EXP.super_: - return (cast(ThisExp)e).var.isVarDeclaration(); + 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 @@ -313,12 +294,24 @@ enum WANTexpand = 1; // expand const/immutable variables if possible // IN_LLVM: instantiated in gen/asm-x86.h (`Handled = createExpression(...)`) extern (C++) /* IN_LLVM abstract */ class Expression : ASTNode { - Type type; // !=null means that semantic() has been run + /// Usually, this starts out as `null` and gets set to the final expression type by + /// `expressionSemantic`. However, for some expressions (such as `TypeExp`,`RealExp`, + /// `VarExp`), the field can get set to an assigned type before running semantic. + /// See `expressionSemanticDone` + Type type; + Loc loc; // file location const EXP op; // to minimize use of dynamic_cast + + static struct BitFields + { bool parens; // if this is a parenthesized expression + bool rvalue; // true if this is considered to be an rvalue, even if it is an lvalue + } + import dmd.common.bitfields; + mixin(generateBitFields!(BitFields, ubyte)); - extern (D) this(const ref Loc loc, EXP op) scope @safe + extern (D) this(Loc loc, EXP op) scope @safe { //printf("Expression::Expression(op = %d) this = %p\n", op, this); this.loc = loc; @@ -389,8 +382,12 @@ extern (C++) /* IN_LLVM abstract */ class Expression : ASTNode return DYNCAST.expression; } - override const(char)* toChars() const + final override const(char)* toChars() const { + // FIXME: mangling (see runnable/mangle.d) relies on toChars outputting __lambdaXXX here + if (auto fe = isFuncExp()) + return fe.fd.toChars(); + return .toChars(this); } @@ -530,117 +527,6 @@ extern (C++) /* IN_LLVM abstract */ class Expression : ASTNode return false; } - /**************************************** - * Check that the expression has a valid value. - * If not, generates an error "... has no value". - * Returns: - * true if the expression is not valid or has void type. - */ - bool checkValue() - { - if (type && type.toBasetype().ty == Tvoid) - { - error(loc, "expression `%s` is `void` and has no value", toChars()); - //print(); assert(0); - if (!global.gag) - type = Type.terror; - return true; - } - return false; - } - - extern (D) final bool checkScalar() - { - if (op == EXP.error) - return true; - if (type.toBasetype().ty == Terror) - return true; - if (!type.isscalar()) - { - error(loc, "`%s` is not a scalar, it is a `%s`", toChars(), type.toChars()); - return true; - } - return checkValue(); - } - - extern (D) final bool checkNoBool() - { - if (op == EXP.error) - return true; - if (type.toBasetype().ty == Terror) - return true; - if (type.toBasetype().ty == Tbool) - { - error(loc, "operation not allowed on `bool` `%s`", toChars()); - return true; - } - return false; - } - - extern (D) final bool checkIntegral() - { - if (op == EXP.error) - return true; - if (type.toBasetype().ty == Terror) - return true; - if (!type.isintegral()) - { - error(loc, "`%s` is not of integral type, it is a `%s`", toChars(), type.toChars()); - return true; - } - return checkValue(); - } - - extern (D) final bool checkArithmetic(EXP op) - { - if (op == EXP.error) - return true; - if (type.toBasetype().ty == Terror) - return true; - if (!type.isintegral() && !type.isfloating()) - { - // unary aggregate ops error here - const char* msg = type.isAggregate() ? - "operator `%s` is not defined for `%s` of type `%s`" : - "illegal operator `%s` for `%s` of type `%s`"; - error(loc, msg, EXPtoString(op).ptr, toChars(), type.toChars()); - return true; - } - return checkValue(); - } - - /******************************* - * Check whether the expression allows RMW operations, error with rmw operator diagnostic if not. - * ex is the RHS expression, or NULL if ++/-- is used (for diagnostics) - * Returns true if error occurs. - */ - extern (D) final bool checkReadModifyWrite(EXP rmwOp, Expression ex = null) - { - //printf("Expression::checkReadModifyWrite() %s %s", toChars(), ex ? ex.toChars() : ""); - if (!type || !type.isShared() || type.isTypeStruct() || type.isTypeClass()) - return false; - - // atomicOp uses opAssign (+=/-=) rather than opOp (++/--) for the CT string literal. - switch (rmwOp) - { - case EXP.plusPlus: - case EXP.prePlusPlus: - rmwOp = EXP.addAssign; - break; - case EXP.minusMinus: - case EXP.preMinusMinus: - rmwOp = EXP.minAssign; - break; - default: - break; - } - - error(loc, "read-modify-write operations are not allowed for `shared` variables"); - errorSupplemental(loc, "Use `core.atomic.atomicOp!\"%s\"(%s, %s)` instead", - EXPtoString(rmwOp).ptr, toChars(), ex ? ex.toChars() : "1"); - return true; - } - /****************************** * Take address of expression. */ @@ -757,7 +643,7 @@ version (IN_LLVM) inout(TypeidExp) isTypeidExp() { return op == EXP.typeid_ ? cast(typeof(return))this : null; } inout(TraitsExp) isTraitsExp() { return op == EXP.traits ? cast(typeof(return))this : null; } inout(HaltExp) isHaltExp() { return op == EXP.halt ? cast(typeof(return))this : null; } - inout(IsExp) isExp() { return op == EXP.is_ ? cast(typeof(return))this : null; } + inout(IsExp) isIsExp() { return op == EXP.is_ ? cast(typeof(return))this : null; } inout(MixinExp) isMixinExp() { return op == EXP.mixin_ ? cast(typeof(return))this : null; } inout(ImportExp) isImportExp() { return op == EXP.import_ ? cast(typeof(return))this : null; } inout(AssertExp) isAssertExp() { return op == EXP.assert_ ? cast(typeof(return))this : null; } @@ -884,12 +770,12 @@ extern (C++) final class IntegerExp : Expression { private dinteger_t value; - extern (D) this(const ref Loc loc, dinteger_t value, Type type) + 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()) + if (!type.isScalar()) { //printf("%s, loc = %d\n", toChars(), loc.linnum); if (type.ty != Terror) @@ -907,7 +793,7 @@ extern (C++) final class IntegerExp : Expression this.value = cast(int)value; } - static IntegerExp create(const ref Loc loc, dinteger_t value, Type type) + static IntegerExp create(Loc loc, dinteger_t value, Type type) { return new IntegerExp(loc, value, type); } @@ -1101,7 +987,7 @@ extern (C++) final class ErrorExp : Expression * and we need to set the error count to prevent bogus code * generation. At least give a message. */ - .error(Loc.initial, "unknown, please file report on issues.dlang.org"); + .error(Loc.initial, "unknown, please file report at https://github.com/dlang/dmd/issues/new"); } return errorexp; @@ -1148,7 +1034,7 @@ extern (C++) final class RealExp : Expression { real_t value; - extern (D) this(const ref Loc loc, real_t value, Type type) @safe + extern (D) this(Loc loc, real_t value, Type type) @safe { super(loc, EXP.float64); //printf("RealExp::RealExp(%Lg)\n", value); @@ -1156,7 +1042,7 @@ extern (C++) final class RealExp : Expression this.type = type; } - static RealExp create(const ref Loc loc, real_t value, Type type) @safe + static RealExp create(Loc loc, real_t value, Type type) @safe { return new RealExp(loc, value, type); } @@ -1209,12 +1095,12 @@ extern (C++) final class RealExp : Expression override real_t toReal() { - return type.isreal() ? value : CTFloat.zero; + return type.isReal() ? value : CTFloat.zero; } override real_t toImaginary() { - return type.isreal() ? CTFloat.zero : value; + return type.isReal() ? CTFloat.zero : value; } override complex_t toComplex() @@ -1240,7 +1126,7 @@ extern (C++) final class ComplexExp : Expression { complex_t value; - extern (D) this(const ref Loc loc, complex_t value, Type type) @safe + extern (D) this(Loc loc, complex_t value, Type type) @safe { super(loc, EXP.complex80); this.value = value; @@ -1248,7 +1134,7 @@ extern (C++) final class ComplexExp : Expression //printf("ComplexExp::ComplexExp(%s)\n", toChars()); } - static ComplexExp create(const ref Loc loc, complex_t value, Type type) @safe + static ComplexExp create(Loc loc, complex_t value, Type type) @safe { return new ComplexExp(loc, value, type); } @@ -1327,20 +1213,20 @@ extern (C++) class IdentifierExp : Expression { Identifier ident; - extern (D) this(const ref Loc loc, Identifier ident) scope @safe + extern (D) this(Loc loc, Identifier ident) scope @safe { super(loc, EXP.identifier); this.ident = ident; } - static IdentifierExp create(const ref Loc loc, Identifier ident) @safe + static IdentifierExp create(Loc loc, Identifier ident) @safe { return new IdentifierExp(loc, ident); } override final bool isLvalue() { - return true; + return !this.rvalue; } override void accept(Visitor v) @@ -1356,7 +1242,7 @@ extern (C++) class IdentifierExp : Expression */ extern (C++) final class DollarExp : IdentifierExp { - extern (D) this(const ref Loc loc) + extern (D) this(Loc loc) { super(loc, Id.dollar); } @@ -1375,7 +1261,7 @@ extern (C++) final class DsymbolExp : Expression Dsymbol s; bool hasOverloads; - extern (D) this(const ref Loc loc, Dsymbol s, bool hasOverloads = true) @safe + extern (D) this(Loc loc, Dsymbol s, bool hasOverloads = true) @safe { super(loc, EXP.dSymbol); this.s = s; @@ -1384,7 +1270,7 @@ extern (C++) final class DsymbolExp : Expression override bool isLvalue() { - return true; + return !rvalue; } override void accept(Visitor v) @@ -1400,13 +1286,13 @@ extern (C++) class ThisExp : Expression { VarDeclaration var; - extern (D) this(const ref Loc loc) @safe + extern (D) this(Loc loc) @safe { super(loc, EXP.this_); //printf("ThisExp::ThisExp() loc = %d\n", loc.linnum); } - this(const ref Loc loc, const EXP tok) @safe + this(Loc loc, const EXP tok) @safe { super(loc, tok); //printf("ThisExp::ThisExp() loc = %d\n", loc.linnum); @@ -1430,7 +1316,7 @@ extern (C++) class ThisExp : Expression override final bool isLvalue() { // Class `this` should be an rvalue; struct `this` should be an lvalue. - return type.toBasetype().ty != Tclass; + return !rvalue && type.toBasetype().ty != Tclass; } override void accept(Visitor v) @@ -1444,7 +1330,7 @@ extern (C++) class ThisExp : Expression */ extern (C++) final class SuperExp : ThisExp { - extern (D) this(const ref Loc loc) @safe + extern (D) this(Loc loc) @safe { super(loc, EXP.super_); } @@ -1462,7 +1348,7 @@ extern (C++) final class SuperExp : ThisExp */ extern (C++) final class NullExp : Expression { - extern (D) this(const ref Loc loc, Type type = null) scope @safe + extern (D) this(Loc loc, Type type = null) scope @safe { super(loc, EXP.null_); this.type = type; @@ -1536,7 +1422,7 @@ extern (C++) final class StringExp : Expression enum char NoPostfix = 0; - extern (D) this(const ref Loc loc, const(void)[] string) scope + extern (D) this(Loc loc, const(void)[] string) scope { super(loc, EXP.string_); this.string = cast(char*)string.ptr; // note that this.string should be const @@ -1544,7 +1430,7 @@ extern (C++) final class StringExp : Expression this.sz = 1; // work around LDC bug #1286 } - extern (D) this(const ref Loc loc, const(void)[] string, size_t len, ubyte sz, char postfix = NoPostfix) scope + extern (D) this(Loc loc, const(void)[] string, size_t len, ubyte sz, char postfix = NoPostfix) scope { super(loc, EXP.string_); this.string = cast(char*)string.ptr; // note that this.string should be const @@ -1553,12 +1439,12 @@ extern (C++) final class StringExp : Expression this.postfix = postfix; } - static StringExp create(const ref Loc loc, const(char)* s) + static StringExp create(Loc loc, const(char)* s) { return new StringExp(loc, s.toDString()); } - static StringExp create(const ref Loc loc, const(void)* string, size_t len) + static StringExp create(Loc loc, const(void)* string, size_t len) { return new StringExp(loc, string[0 .. len]); } @@ -1815,7 +1701,7 @@ extern (C++) final class StringExp : Expression /* string literal is rvalue in default, but * conversion to reference of static array is only allowed. */ - return (type && type.toBasetype().ty == Tsarray); + return !rvalue && (type && type.toBasetype().ty == Tsarray); } /******************************** @@ -1890,7 +1776,7 @@ extern (C++) final class InterpExp : Expression enum char NoPostfix = 0; - extern (D) this(const ref Loc loc, InterpolatedSet* set, char postfix = NoPostfix) scope + extern (D) this(Loc loc, InterpolatedSet* set, char postfix = NoPostfix) scope @safe { super(loc, EXP.interpolated); this.interpolatedSet = set; @@ -1925,7 +1811,7 @@ extern (C++) final class TupleExp : Expression Expressions* exps; - extern (D) this(const ref Loc loc, Expression e0, Expressions* exps) @safe + extern (D) this(Loc loc, Expression e0, Expressions* exps) @safe { super(loc, EXP.tuple); //printf("TupleExp(this = %p)\n", this); @@ -1933,14 +1819,14 @@ extern (C++) final class TupleExp : Expression this.exps = exps; } - extern (D) this(const ref Loc loc, Expressions* exps) @safe + extern (D) this(Loc loc, Expressions* exps) @safe { super(loc, EXP.tuple); //printf("TupleExp(this = %p)\n", this); this.exps = exps; } - extern (D) this(const ref Loc loc, TupleDeclaration tup) + extern (D) this(Loc loc, TupleDeclaration tup) { super(loc, EXP.tuple); this.exps = new Expressions(); @@ -1974,7 +1860,7 @@ extern (C++) final class TupleExp : Expression } } - static TupleExp create(const ref Loc loc, Expressions* exps) @safe + static TupleExp create(Loc loc, Expressions* exps) @safe { return new TupleExp(loc, exps); } @@ -2030,14 +1916,14 @@ extern (C++) final class ArrayLiteralExp : Expression Expressions* elements; - extern (D) this(const ref Loc loc, Type type, Expressions* elements) @safe + extern (D) this(Loc loc, Type type, Expressions* elements) @safe { super(loc, EXP.arrayLiteral); this.type = type; this.elements = elements; } - extern (D) this(const ref Loc loc, Type type, Expression e) + extern (D) this(Loc loc, Type type, Expression e) { super(loc, EXP.arrayLiteral); this.type = type; @@ -2045,7 +1931,7 @@ extern (C++) final class ArrayLiteralExp : Expression elements.push(e); } - extern (D) this(const ref Loc loc, Type type, Expression basis, Expressions* elements) @safe + extern (D) this(Loc loc, Type type, Expression basis, Expressions* elements) @safe { super(loc, EXP.arrayLiteral); this.type = type; @@ -2053,7 +1939,7 @@ extern (C++) final class ArrayLiteralExp : Expression this.elements = elements; } - static ArrayLiteralExp create(const ref Loc loc, Expressions* elements) @safe + static ArrayLiteralExp create(Loc loc, Expressions* elements) @safe { return new ArrayLiteralExp(loc, null, elements); } @@ -2186,7 +2072,7 @@ extern (C++) final class AssocArrayLiteralExp : Expression /// Lower to core.internal.newaa for static initializaton Expression lowering; - extern (D) this(const ref Loc loc, Expressions* keys, Expressions* values) @safe + extern (D) this(Loc loc, Expressions* keys, Expressions* values) @safe { super(loc, EXP.assocArrayLiteral); assert(keys.length == values.length); @@ -2240,18 +2126,21 @@ extern (C++) final class AssocArrayLiteralExp : Expression } } -enum stageScrub = 0x1; /// scrubReturnValue is running -enum stageSearchPointers = 0x2; /// hasNonConstPointers is running -enum stageOptimize = 0x4; /// optimize is running -enum stageApply = 0x8; /// apply is running -enum stageInlineScan = 0x10; /// inlineScan is running -enum stageToCBuffer = 0x20; /// toCBuffer is running - /*********************************************************** * sd( e1, e2, e3, ... ) */ extern (C++) final class StructLiteralExp : Expression { + struct BitFields + { + bool useStaticInit; /// if this is true, use the StructDeclaration's init symbol + bool isOriginal = false; /// used when moving instances to indicate `this is this.origin` + OwnedBy ownedByCtfe = OwnedBy.code; + } + import dmd.common.bitfields; + mixin(generateBitFields!(BitFields, ubyte)); + StageFlags stageflags; + StructDeclaration sd; /// which aggregate this is for Expressions* elements; /// parallels sd.fields[] with null entries for fields to skip Type stype; /// final type of result (can be different from sd's type) @@ -2289,13 +2178,18 @@ else * 'inlinecopy' uses similar 'stageflags' and from multiple evaluation 'doInline' * (with infinite recursion) of this expression. */ - ubyte stageflags; - - bool useStaticInit; /// if this is true, use the StructDeclaration's init symbol - bool isOriginal = false; /// used when moving instances to indicate `this is this.origin` - OwnedBy ownedByCtfe = OwnedBy.code; + enum StageFlags : ubyte + { + none = 0x0, + scrub = 0x1, /// scrubReturnValue is running + searchPointers = 0x2, /// hasNonConstPointers is running + optimize = 0x4, /// optimize is running + apply = 0x8, /// apply is running + inlineScan = 0x10, /// inlineScan is running + toCBuffer = 0x20 /// toCBuffer is running + } - extern (D) this(const ref Loc loc, StructDeclaration sd, Expressions* elements, Type stype = null) @safe + extern (D) this(Loc loc, StructDeclaration sd, Expressions* elements, Type stype = null) @safe { super(loc, EXP.structLiteral); this.sd = sd; @@ -2307,7 +2201,7 @@ else //printf("StructLiteralExp::StructLiteralExp(%s)\n", toChars()); } - static StructLiteralExp create(const ref Loc loc, StructDeclaration sd, void* elements, Type stype = null) + static StructLiteralExp create(Loc loc, StructDeclaration sd, void* elements, Type stype = null) { return new StructLiteralExp(loc, sd, cast(Expressions*)elements, stype); } @@ -2438,7 +2332,7 @@ extern (C++) final class CompoundLiteralExp : Expression { Initializer initializer; /// initializer-list - extern (D) this(const ref Loc loc, Type type_name, Initializer initializer) @safe + extern (D) this(Loc loc, Type type_name, Initializer initializer) @safe { super(loc, EXP.compoundLiteral); super.type = type_name; @@ -2457,7 +2351,7 @@ extern (C++) final class CompoundLiteralExp : Expression */ extern (C++) final class TypeExp : Expression { - extern (D) this(const ref Loc loc, Type type) @safe + extern (D) this(Loc loc, Type type) @safe { super(loc, EXP.type); //printf("TypeExp::TypeExp(%s)\n", type.toChars()); @@ -2475,12 +2369,6 @@ extern (C++) final class TypeExp : Expression return true; } - override bool checkValue() - { - error(loc, "type `%s` has no value", toChars()); - return true; - } - override void accept(Visitor v) { v.visit(this); @@ -2499,7 +2387,7 @@ extern (C++) final class ScopeExp : Expression { ScopeDsymbol sds; - extern (D) this(const ref Loc loc, ScopeDsymbol sds) @safe + extern (D) this(Loc loc, ScopeDsymbol sds) @safe { super(loc, EXP.scope_); //printf("ScopeExp::ScopeExp(sds = '%s')\n", sds.toChars()); @@ -2534,12 +2422,6 @@ extern (C++) final class ScopeExp : Expression return false; } - override bool checkValue() - { - error(loc, "%s `%s` has no value", sds.kind(), sds.toChars()); - return true; - } - override void accept(Visitor v) { v.visit(this); @@ -2554,7 +2436,7 @@ extern (C++) final class TemplateExp : Expression TemplateDeclaration td; FuncDeclaration fd; - extern (D) this(const ref Loc loc, TemplateDeclaration td, FuncDeclaration fd = null) @safe + extern (D) this(Loc loc, TemplateDeclaration td, FuncDeclaration fd = null) @safe { super(loc, EXP.template_); //printf("TemplateExp(): %s\n", td.toChars()); @@ -2573,12 +2455,6 @@ extern (C++) final class TemplateExp : Expression return true; } - override bool checkValue() - { - error(loc, "%s `%s` has no value", td.kind(), toChars()); - return true; - } - override void accept(Visitor v) { v.visit(this); @@ -2594,6 +2470,7 @@ extern (C++) final class NewExp : Expression Type newtype; Expressions* arguments; // Array of Expression's Identifiers* names; // Array of names corresponding to expressions + Expression placement; // if !=null, then PlacementExpression Expression argprefix; // expression to be evaluated just before arguments[] CtorDeclaration member; // constructor function @@ -2606,23 +2483,25 @@ extern (C++) final class NewExp : Expression /// The fields are still separate for backwards compatibility extern (D) ArgumentList argumentList() { return ArgumentList(arguments, names); } - extern (D) this(const ref Loc loc, Expression thisexp, Type newtype, Expressions* arguments, Identifiers* names = null) @safe + extern (D) this(Loc loc, Expression placement, Expression thisexp, Type newtype, Expressions* arguments, Identifiers* names = null) @safe { super(loc, EXP.new_); + this.placement = placement; this.thisexp = thisexp; this.newtype = newtype; this.arguments = arguments; this.names = names; } - static NewExp create(const ref Loc loc, Expression thisexp, Type newtype, Expressions* arguments) @safe + static NewExp create(Loc loc, Expression placement, Expression thisexp, Type newtype, Expressions* arguments) @safe { - return new NewExp(loc, thisexp, newtype, arguments); + return new NewExp(loc, placement, thisexp, newtype, arguments); } override NewExp syntaxCopy() { return new NewExp(loc, + placement ? placement.syntaxCopy() : null, thisexp ? thisexp.syntaxCopy() : null, newtype.syntaxCopy(), arraySyntaxCopy(arguments), @@ -2643,10 +2522,12 @@ extern (C++) final class NewAnonClassExp : Expression Expression thisexp; // if !=null, 'this' for class being allocated ClassDeclaration cd; // class being instantiated Expressions* arguments; // Array of Expression's to call class constructor + Expression placement; // if !=null, then PlacementExpression - extern (D) this(const ref Loc loc, Expression thisexp, ClassDeclaration cd, Expressions* arguments) @safe + extern (D) this(Loc loc, Expression placement, Expression thisexp, ClassDeclaration cd, Expressions* arguments) @safe { super(loc, EXP.newAnonymousClass); + this.placement = placement; this.thisexp = thisexp; this.cd = cd; this.arguments = arguments; @@ -2654,7 +2535,9 @@ extern (C++) final class NewAnonClassExp : Expression override NewAnonClassExp syntaxCopy() { - return new NewAnonClassExp(loc, thisexp ? thisexp.syntaxCopy() : null, cd.syntaxCopy(null), arraySyntaxCopy(arguments)); + return new NewAnonClassExp(loc, placement ? placement.syntaxCopy : null, + thisexp ? thisexp.syntaxCopy() : null, + cd.syntaxCopy(null), arraySyntaxCopy(arguments)); } override void accept(Visitor v) @@ -2671,7 +2554,7 @@ extern (C++) class SymbolExp : Expression Dsymbol originalScope; // original scope before inlining bool hasOverloads; - extern (D) this(const ref Loc loc, EXP op, Declaration var, bool hasOverloads) @safe + extern (D) this(Loc loc, EXP op, Declaration var, bool hasOverloads) @safe { super(loc, op); assert(var); @@ -2692,7 +2575,7 @@ extern (C++) final class SymOffExp : SymbolExp { dinteger_t offset; - extern (D) this(const ref Loc loc, Declaration var, dinteger_t offset, bool hasOverloads = true) + extern (D) this(Loc loc, Declaration var, dinteger_t offset, bool hasOverloads = true) { if (auto v = var.isVarDeclaration()) { @@ -2736,7 +2619,7 @@ version (IN_LLVM) extern (C++) final class VarExp : SymbolExp { bool delegateWasExtracted; - extern (D) this(const ref Loc loc, Declaration var, bool hasOverloads = true) @safe + extern (D) this(Loc loc, Declaration var, bool hasOverloads = true) @safe { if (var.isVarDeclaration()) hasOverloads = false; @@ -2747,7 +2630,7 @@ extern (C++) final class VarExp : SymbolExp this.type = var.type; } - static VarExp create(const ref Loc loc, Declaration var, bool hasOverloads = true) @safe + static VarExp create(Loc loc, Declaration var, bool hasOverloads = true) @safe { return new VarExp(loc, var, hasOverloads); } @@ -2768,7 +2651,7 @@ extern (C++) final class VarExp : SymbolExp override bool isLvalue() { - if (var.storage_class & (STC.lazy_ | STC.rvalue | STC.manifest)) + if (rvalue || var.storage_class & (STC.lazy_ | STC.rvalue | STC.manifest)) return false; return true; } @@ -2786,7 +2669,7 @@ extern (C++) final class OverExp : Expression { OverloadSet vars; - extern (D) this(const ref Loc loc, OverloadSet s) + extern (D) this(Loc loc, OverloadSet s) { super(loc, EXP.overloadSet); //printf("OverExp(this = %p, '%s')\n", this, var.toChars()); @@ -2815,7 +2698,7 @@ extern (C++) final class FuncExp : Expression TemplateDeclaration td; TOK tok; // TOK.reserved, TOK.delegate_, TOK.function_ - extern (D) this(const ref Loc loc, Dsymbol s) + extern (D) this(Loc loc, Dsymbol s) { super(loc, EXP.function_); this.td = s.isTemplateDeclaration(); @@ -2848,16 +2731,11 @@ extern (C++) final class FuncExp : Expression { if (td) return new FuncExp(loc, td.syntaxCopy(null)); - else if (fd.semanticRun == PASS.initial) + if (fd.semanticRun == PASS.initial) return new FuncExp(loc, fd.syntaxCopy(null)); - else // https://issues.dlang.org/show_bug.cgi?id=13481 - // Prevent multiple semantic analysis of lambda body. - return new FuncExp(loc, fd); - } - - override const(char)* toChars() const - { - return fd.toChars(); + // https://issues.dlang.org/show_bug.cgi?id=13481 + // Prevent multiple semantic analysis of lambda body. + return new FuncExp(loc, fd); } override bool checkType() @@ -2870,16 +2748,6 @@ extern (C++) final class FuncExp : Expression return false; } - override bool checkValue() - { - if (td) - { - error(loc, "template lambda has no value"); - return true; - } - return false; - } - override void accept(Visitor v) { v.visit(this); @@ -2897,7 +2765,7 @@ extern (C++) final class DeclarationExp : Expression { Dsymbol declaration; - extern (D) this(const ref Loc loc, Dsymbol declaration) @safe + extern (D) this(Loc loc, Dsymbol declaration) @safe { super(loc, EXP.declaration); this.declaration = declaration; @@ -2930,7 +2798,7 @@ extern (C++) final class TypeidExp : Expression { RootObject obj; - extern (D) this(const ref Loc loc, RootObject o) @safe + extern (D) this(Loc loc, RootObject o) @safe { super(loc, EXP.typeid_); this.obj = o; @@ -2955,7 +2823,7 @@ extern (C++) final class TraitsExp : Expression Identifier ident; Objects* args; - extern (D) this(const ref Loc loc, Identifier ident, Objects* args) @safe + extern (D) this(Loc loc, Identifier ident, Objects* args) @safe { super(loc, EXP.traits); this.ident = ident; @@ -2980,7 +2848,7 @@ extern (C++) final class TraitsExp : Expression */ extern (C++) final class HaltExp : Expression { - extern (D) this(const ref Loc loc) @safe + extern (D) this(Loc loc) @safe { super(loc, EXP.halt); } @@ -3004,7 +2872,7 @@ extern (C++) final class IsExp : Expression TOK tok; // ':' or '==' TOK tok2; // 'struct', 'union', etc. - extern (D) this(const ref Loc loc, Type targ, Identifier id, TOK tok, Type tspec, TOK tok2, TemplateParameters* parameters) scope @safe + extern (D) this(Loc loc, Type targ, Identifier id, TOK tok, Type tspec, TOK tok2, TemplateParameters* parameters) scope @safe { super(loc, EXP.is_); this.targ = targ; @@ -3043,7 +2911,7 @@ extern (C++) abstract class UnaExp : Expression { Expression e1; - extern (D) this(const ref Loc loc, EXP op, Expression e1) scope @safe + extern (D) this(Loc loc, EXP op, Expression e1) scope @safe { super(loc, op); this.e1 = e1; @@ -3057,28 +2925,6 @@ extern (C++) abstract class UnaExp : Expression return e; } - /******************************** - * The type for a unary expression is incompatible. - * Print error message. - * Returns: - * ErrorExp - */ - extern (D) final Expression incompatibleTypes() - { - if (e1.type.toBasetype() == Type.terror) - return e1; - - if (e1.op == EXP.type) - { - error(loc, "incompatible type for `%s(%s)`: cannot use `%s` with types", EXPtoString(op).ptr, e1.toChars(), EXPtoString(op).ptr); - } - else - { - error(loc, "incompatible type for `%s(%s)`: `%s`", EXPtoString(op).ptr, e1.toChars(), e1.type.toChars()); - } - return ErrorExp.get(); - } - /********************* * Mark the operand as will never be dereferenced, * which is useful info for @safe checks. @@ -3104,10 +2950,8 @@ extern (C++) abstract class BinExp : Expression { Expression e1; Expression e2; - Type att1; // Save alias this type to detect recursion - Type att2; // Save alias this type to detect recursion - extern (D) this(const ref Loc loc, EXP op, Expression e1, Expression e2) scope @safe + extern (D) this(Loc loc, EXP op, Expression e1, Expression e2) scope @safe { super(loc, op); this.e1 = e1; @@ -3123,54 +2967,6 @@ extern (C++) abstract class BinExp : Expression return e; } - /******************************** - * The types for a binary expression are incompatible. - * Print error message. - * Returns: - * ErrorExp - */ - extern (D) final Expression incompatibleTypes() - { - if (e1.type.toBasetype() == Type.terror) - return e1; - if (e2.type.toBasetype() == Type.terror) - return e2; - - // CondExp uses 'a ? b : c' but we're comparing 'b : c' - const(char)* thisOp = (op == EXP.question) ? ":" : EXPtoString(op).ptr; - if (e1.op == EXP.type || e2.op == EXP.type) - { - error(loc, "incompatible types for `(%s) %s (%s)`: cannot use `%s` with types", - e1.toChars(), thisOp, e2.toChars(), EXPtoString(op).ptr); - } - else if (e1.type.equals(e2.type)) - { - error(loc, "incompatible types for `(%s) %s (%s)`: both operands are of type `%s`", - e1.toChars(), thisOp, e2.toChars(), e1.type.toChars()); - } - else - { - auto ts = toAutoQualChars(e1.type, e2.type); - error(loc, "incompatible types for `(%s) %s (%s)`: `%s` and `%s`", - e1.toChars(), thisOp, e2.toChars(), ts[0], ts[1]); - } - return ErrorExp.get(); - } - - extern (D) final bool checkIntegralBin() - { - bool r1 = e1.checkIntegral(); - bool r2 = e2.checkIntegral(); - return (r1 || r2); - } - - extern (D) final bool checkArithmeticBin() - { - bool r1 = e1.checkArithmetic(this.op); - bool r2 = e2.checkArithmetic(this.op); - return (r1 || r2); - } - /********************* * Mark the operands as will never be dereferenced, * which is useful info for @safe checks. @@ -3196,14 +2992,14 @@ extern (C++) abstract class BinExp : Expression */ extern (C++) class BinAssignExp : BinExp { - extern (D) this(const ref Loc loc, EXP op, Expression e1, Expression e2) scope @safe + extern (D) this(Loc loc, EXP op, Expression e1, Expression e2) scope @safe { super(loc, op, e1, e2); } override final bool isLvalue() { - return true; + return !rvalue; } override void accept(Visitor v) @@ -3221,7 +3017,7 @@ extern (C++) final class MixinExp : Expression { Expressions* exps; - extern (D) this(const ref Loc loc, Expressions* exps) @safe + extern (D) this(Loc loc, Expressions* exps) @safe { super(loc, EXP.mixin_); this.exps = exps; @@ -3269,7 +3065,7 @@ extern (C++) final class MixinExp : Expression */ extern (C++) final class ImportExp : UnaExp { - extern (D) this(const ref Loc loc, Expression e) @safe + extern (D) this(Loc loc, Expression e) @safe { super(loc, EXP.import_, e); } @@ -3289,7 +3085,7 @@ extern (C++) final class AssertExp : UnaExp { Expression msg; - extern (D) this(const ref Loc loc, Expression e, Expression msg = null) @safe + extern (D) this(Loc loc, Expression e, Expression msg = null) @safe { super(loc, EXP.assert_, e); this.msg = msg; @@ -3314,10 +3110,9 @@ extern (C++) final class AssertExp : UnaExp */ extern (C++) final class ThrowExp : UnaExp { - extern (D) this(const ref Loc loc, Expression e) + extern (D) this(Loc loc, Expression e) { super(loc, EXP.throw_, e); - this.type = Type.tnoreturn; } override ThrowExp syntaxCopy() @@ -3340,13 +3135,13 @@ extern (C++) final class DotIdExp : UnaExp bool wantsym; // do not replace Symbol with its initializer during semantic() bool arrow; // ImportC: if -> instead of . - extern (D) this(const ref Loc loc, Expression e, Identifier ident) @safe + extern (D) this(Loc loc, Expression e, Identifier ident) @safe { super(loc, EXP.dotIdentifier, e); this.ident = ident; } - static DotIdExp create(const ref Loc loc, Expression e, Identifier ident) @safe + static DotIdExp create(Loc loc, Expression e, Identifier ident) @safe { return new DotIdExp(loc, e, ident); } @@ -3364,7 +3159,7 @@ extern (C++) final class DotTemplateExp : UnaExp { TemplateDeclaration td; - extern (D) this(const ref Loc loc, Expression e, TemplateDeclaration td) @safe + extern (D) this(Loc loc, Expression e, TemplateDeclaration td) @safe { super(loc, EXP.dotTemplateDeclaration, e); this.td = td; @@ -3376,12 +3171,6 @@ extern (C++) final class DotTemplateExp : UnaExp return true; } - override bool checkValue() - { - error(loc, "%s `%s` has no value", td.kind(), toChars()); - return true; - } - override void accept(Visitor v) { v.visit(this); @@ -3395,7 +3184,7 @@ extern (C++) final class DotVarExp : UnaExp Declaration var; bool hasOverloads; - extern (D) this(const ref Loc loc, Expression e, Declaration var, bool hasOverloads = true) @safe + extern (D) this(Loc loc, Expression e, Declaration var, bool hasOverloads = true) @safe { if (var.isVarDeclaration()) hasOverloads = false; @@ -3408,6 +3197,8 @@ extern (C++) final class DotVarExp : UnaExp override bool isLvalue() { + if (rvalue) + return false; if (e1.op != EXP.structLiteral) return true; auto vd = var.isVarDeclaration(); @@ -3427,14 +3218,14 @@ extern (C++) final class DotTemplateInstanceExp : UnaExp { TemplateInstance ti; - extern (D) this(const ref Loc loc, Expression e, Identifier name, Objects* tiargs) + 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(const ref Loc loc, Expression e, TemplateInstance ti) @safe + extern (D) this(Loc loc, Expression e, TemplateInstance ti) @safe { super(loc, EXP.dotTemplateInstance, e); this.ti = ti; @@ -3458,18 +3249,6 @@ extern (C++) final class DotTemplateInstanceExp : UnaExp return false; } - override bool checkValue() - { - if (ti.tempdecl && - ti.semantictiargsdone && - ti.semanticRun == PASS.initial) - - error(loc, "partial %s `%s` has no value", ti.kind(), toChars()); - else - error(loc, "%s `%s` has no value", ti.kind(), ti.toChars()); - return true; - } - override void accept(Visitor v) { v.visit(this); @@ -3484,7 +3263,7 @@ extern (C++) final class DelegateExp : UnaExp bool hasOverloads; VarDeclaration vthis2; // container for multi-context - extern (D) this(const ref Loc loc, Expression e, FuncDeclaration f, bool hasOverloads = true, VarDeclaration vthis2 = null) @safe + extern (D) this(Loc loc, Expression e, FuncDeclaration f, bool hasOverloads = true, VarDeclaration vthis2 = null) @safe { super(loc, EXP.delegate_, e); this.func = f; @@ -3504,7 +3283,7 @@ extern (C++) final class DotTypeExp : UnaExp { Dsymbol sym; // symbol that represents a type - extern (D) this(const ref Loc loc, Expression e, Dsymbol s) @safe + extern (D) this(Loc loc, Expression e, Dsymbol s) @safe { super(loc, EXP.dotType, e); this.sym = s; @@ -3559,19 +3338,19 @@ extern (C++) final class CallExp : UnaExp /// The fields are still separate for backwards compatibility extern (D) ArgumentList argumentList() { return ArgumentList(arguments, names); } - extern (D) this(const ref Loc loc, Expression e, Expressions* exps, Identifiers* names = null) @safe + extern (D) this(Loc loc, Expression e, Expressions* exps, Identifiers* names = null) @safe { super(loc, EXP.call, e); this.arguments = exps; this.names = names; } - extern (D) this(const ref Loc loc, Expression e) @safe + extern (D) this(Loc loc, Expression e) @safe { super(loc, EXP.call, e); } - extern (D) this(const ref Loc loc, Expression e, Expression earg1) + extern (D) this(Loc loc, Expression e, Expression earg1) { super(loc, EXP.call, e); this.arguments = new Expressions(); @@ -3579,7 +3358,7 @@ extern (C++) final class CallExp : UnaExp this.arguments.push(earg1); } - extern (D) this(const ref Loc loc, Expression e, Expression earg1, Expression earg2) + extern (D) this(Loc loc, Expression e, Expression earg1, Expression earg2) { super(loc, EXP.call, e); auto arguments = new Expressions(2); @@ -3595,23 +3374,23 @@ extern (C++) final class CallExp : UnaExp * fd = the declaration of the function to call * earg1 = the function argument */ - extern(D) this(const ref Loc loc, FuncDeclaration fd, Expression earg1) + extern(D) this(Loc loc, FuncDeclaration fd, Expression earg1) { this(loc, new VarExp(loc, fd, false), earg1); this.f = fd; } - static CallExp create(const ref Loc loc, Expression e, Expressions* exps) @safe + static CallExp create(Loc loc, Expression e, Expressions* exps) @safe { return new CallExp(loc, e, exps); } - static CallExp create(const ref Loc loc, Expression e) @safe + static CallExp create(Loc loc, Expression e) @safe { return new CallExp(loc, e); } - static CallExp create(const ref Loc loc, Expression e, Expression earg1) + static CallExp create(Loc loc, Expression e, Expression earg1) { return new CallExp(loc, e, earg1); } @@ -3623,7 +3402,7 @@ extern (C++) final class CallExp : UnaExp * fd = the declaration of the function to call * earg1 = the function argument */ - static CallExp create(const ref Loc loc, FuncDeclaration fd, Expression earg1) + static CallExp create(Loc loc, FuncDeclaration fd, Expression earg1) { return new CallExp(loc, fd, earg1); } @@ -3635,11 +3414,13 @@ extern (C++) final class CallExp : UnaExp 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 (tf && tf.isRef) { if (auto dve = e1.isDotVarExp()) if (dve.var.isCtorDeclaration()) @@ -3669,10 +3450,9 @@ TypeFunction calledFunctionType(CallExp ce) t = t.toBasetype(); if (auto tf = t.isTypeFunction()) return tf; - else if (auto td = t.isTypeDelegate()) + if (auto td = t.isTypeDelegate()) return td.nextOf().isTypeFunction(); - else - return null; + return null; } FuncDeclaration isFuncAddress(Expression e, bool* hasOverloads = null) @safe @@ -3716,12 +3496,12 @@ FuncDeclaration isFuncAddress(Expression e, bool* hasOverloads = null) @safe */ extern (C++) final class AddrExp : UnaExp { - extern (D) this(const ref Loc loc, Expression e) @safe + extern (D) this(Loc loc, Expression e) @safe { super(loc, EXP.address, e); } - extern (D) this(const ref Loc loc, Expression e, Type t) @safe + extern (D) this(Loc loc, Expression e, Type t) @safe { this(loc, e); type = t; @@ -3738,14 +3518,14 @@ extern (C++) final class AddrExp : UnaExp */ extern (C++) final class PtrExp : UnaExp { - extern (D) this(const ref Loc loc, Expression e) @safe + extern (D) this(Loc loc, Expression e) @safe { super(loc, EXP.star, e); //if (e.type) // type = ((TypePointer *)e.type).next; } - extern (D) this(const ref Loc loc, Expression e, Type t) @safe + extern (D) this(Loc loc, Expression e, Type t) @safe { super(loc, EXP.star, e); type = t; @@ -3753,7 +3533,7 @@ extern (C++) final class PtrExp : UnaExp override bool isLvalue() { - return true; + return !rvalue; } override void accept(Visitor v) @@ -3767,7 +3547,7 @@ extern (C++) final class PtrExp : UnaExp */ extern (C++) final class NegExp : UnaExp { - extern (D) this(const ref Loc loc, Expression e) @safe + extern (D) this(Loc loc, Expression e) @safe { super(loc, EXP.negate, e); } @@ -3783,7 +3563,7 @@ extern (C++) final class NegExp : UnaExp */ extern (C++) final class UAddExp : UnaExp { - extern (D) this(const ref Loc loc, Expression e) scope @safe + extern (D) this(Loc loc, Expression e) scope @safe { super(loc, EXP.uadd, e); } @@ -3799,7 +3579,7 @@ extern (C++) final class UAddExp : UnaExp */ extern (C++) final class ComExp : UnaExp { - extern (D) this(const ref Loc loc, Expression e) @safe + extern (D) this(Loc loc, Expression e) @safe { super(loc, EXP.tilde, e); } @@ -3815,7 +3595,7 @@ extern (C++) final class ComExp : UnaExp */ extern (C++) final class NotExp : UnaExp { - extern (D) this(const ref Loc loc, Expression e) @safe + extern (D) this(Loc loc, Expression e) @safe { super(loc, EXP.not, e); } @@ -3835,7 +3615,7 @@ extern (C++) final class DeleteExp : UnaExp { bool isRAII; // true if called automatically as a result of scoped destruction - extern (D) this(const ref Loc loc, Expression e, bool isRAII) @safe + extern (D) this(Loc loc, Expression e, bool isRAII) @safe { super(loc, EXP.delete_, e); this.isRAII = isRAII; @@ -3860,7 +3640,7 @@ extern (C++) final class CastExp : UnaExp ubyte mod = cast(ubyte)~0; // MODxxxxx bool trusted; // assume cast is safe - extern (D) this(const ref Loc loc, Expression e, Type t) @safe + extern (D) this(Loc loc, Expression e, Type t) @safe { super(loc, EXP.cast_, e); this.to = t; @@ -3868,7 +3648,7 @@ extern (C++) final class CastExp : UnaExp /* For cast(const) and cast(immutable) */ - extern (D) this(const ref Loc loc, Expression e, ubyte mod) @safe + extern (D) this(Loc loc, Expression e, ubyte mod) @safe { super(loc, EXP.cast_, e); this.mod = mod; @@ -3882,9 +3662,10 @@ extern (C++) final class CastExp : UnaExp override bool isLvalue() { //printf("e1.type = %s, to.type = %s\n", e1.type.toChars(), to.toChars()); - if (!e1.isLvalue()) + 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()); } @@ -3902,14 +3683,14 @@ extern (C++) final class VectorExp : UnaExp uint dim = ~0; // number of elements in the vector OwnedBy ownedByCtfe = OwnedBy.code; - extern (D) this(const ref Loc loc, Expression e, Type t) @trusted + extern (D) this(Loc loc, Expression e, Type t) @trusted { super(loc, EXP.vector, e); assert(t.ty == Tvector); to = cast(TypeVector)t; } - static VectorExp create(const ref Loc loc, Expression e, Type t) @safe + static VectorExp create(Loc loc, Expression e, Type t) @safe { return new VectorExp(loc, e, t); } @@ -3932,14 +3713,14 @@ extern (C++) final class VectorExp : UnaExp */ extern (C++) final class VectorArrayExp : UnaExp { - extern (D) this(const ref Loc loc, Expression e1) @safe + extern (D) this(Loc loc, Expression e1) @safe { super(loc, EXP.vectorArray, e1); } override bool isLvalue() { - return e1.isLvalue(); + return !rvalue && e1.isLvalue(); } override void accept(Visitor v) @@ -3970,14 +3751,14 @@ extern (C++) final class SliceExp : UnaExp mixin(generateBitFields!(BitFields, ubyte)); /************************************************************/ - extern (D) this(const ref Loc loc, Expression e1, IntervalExp ie) @safe + extern (D) this(Loc loc, Expression e1, IntervalExp ie) @safe { super(loc, EXP.slice, e1); this.upr = ie ? ie.upr : null; this.lwr = ie ? ie.lwr : null; } - extern (D) this(const ref Loc loc, Expression e1, Expression lwr, Expression upr) @safe + extern (D) this(Loc loc, Expression e1, Expression lwr, Expression upr) @safe { super(loc, EXP.slice, e1); this.upr = upr; @@ -3996,7 +3777,7 @@ extern (C++) final class SliceExp : UnaExp /* slice expression is rvalue in default, but * conversion to reference of static array is only allowed. */ - return (type && type.toBasetype().ty == Tsarray); + return !rvalue && (type && type.toBasetype().ty == Tsarray); } override Optional!bool toBool() @@ -4015,7 +3796,7 @@ extern (C++) final class SliceExp : UnaExp */ extern (C++) final class ArrayLengthExp : UnaExp { - extern (D) this(const ref Loc loc, Expression e1) @safe + extern (D) this(Loc loc, Expression e1) @safe { super(loc, EXP.arrayLength, e1); } @@ -4038,7 +3819,7 @@ extern (C++) final class ArrayExp : UnaExp size_t currentDimension; // for opDollar VarDeclaration lengthVar; - extern (D) this(const ref Loc loc, Expression e1, Expression index = null) + extern (D) this(Loc loc, Expression e1, Expression index = null) { super(loc, EXP.array, e1); arguments = new Expressions(); @@ -4046,7 +3827,7 @@ extern (C++) final class ArrayExp : UnaExp arguments.push(index); } - extern (D) this(const ref Loc loc, Expression e1, Expressions* args) @safe + extern (D) this(Loc loc, Expression e1, Expressions* args) @safe { super(loc, EXP.array, e1); arguments = args; @@ -4061,6 +3842,8 @@ extern (C++) final class ArrayExp : UnaExp override bool isLvalue() { + if (rvalue) + return false; if (type && type.toBasetype().ty == Tvoid) return false; return true; @@ -4076,7 +3859,7 @@ extern (C++) final class ArrayExp : UnaExp */ extern (C++) final class DotExp : BinExp { - extern (D) this(const ref Loc loc, Expression e1, Expression e2) @safe + extern (D) this(Loc loc, Expression e1, Expression e2) @safe { super(loc, EXP.dot, e1, e2); } @@ -4102,7 +3885,7 @@ extern (C++) final class CommaExp : BinExp bool allowCommaExp; - extern (D) this(const ref Loc loc, Expression e1, Expression e2, bool generated = true) @safe + extern (D) this(Loc loc, Expression e1, Expression e2, bool generated = true) @safe { super(loc, EXP.comma, e1, e2); allowCommaExp = isGenerated = generated; @@ -4110,7 +3893,7 @@ extern (C++) final class CommaExp : BinExp override bool isLvalue() { - return e2.isLvalue(); + return !rvalue && e2.isLvalue(); } override Optional!bool toBool() @@ -4153,7 +3936,7 @@ extern (C++) final class IntervalExp : Expression Expression lwr; Expression upr; - extern (D) this(const ref Loc loc, Expression lwr, Expression upr) @safe + extern (D) this(Loc loc, Expression lwr, Expression upr) @safe { super(loc, EXP.interval); this.lwr = lwr; @@ -4178,14 +3961,14 @@ extern (C++) final class IntervalExp : Expression */ extern (C++) final class DelegatePtrExp : UnaExp { - extern (D) this(const ref Loc loc, Expression e1) @safe + extern (D) this(Loc loc, Expression e1) @safe { super(loc, EXP.delegatePointer, e1); } override bool isLvalue() { - return e1.isLvalue(); + return !rvalue && e1.isLvalue(); } override void accept(Visitor v) @@ -4201,14 +3984,14 @@ extern (C++) final class DelegatePtrExp : UnaExp */ extern (C++) final class DelegateFuncptrExp : UnaExp { - extern (D) this(const ref Loc loc, Expression e1) @safe + extern (D) this(Loc loc, Expression e1) @safe { super(loc, EXP.delegateFunctionPointer, e1); } override bool isLvalue() { - return e1.isLvalue(); + return !rvalue && e1.isLvalue(); } override void accept(Visitor v) @@ -4226,13 +4009,13 @@ extern (C++) final class IndexExp : BinExp bool modifiable = false; // assume it is an rvalue bool indexIsInBounds; // true if 0 <= e2 && e2 <= e1.length - 1 - extern (D) this(const ref Loc loc, Expression e1, Expression e2) @safe + extern (D) this(Loc loc, Expression e1, Expression e2) @safe { super(loc, EXP.index, e1, e2); //printf("IndexExp::IndexExp('%s')\n", toChars()); } - extern (D) this(const ref Loc loc, Expression e1, Expression e2, bool indexIsInBounds) @safe + extern (D) this(Loc loc, Expression e1, Expression e2, bool indexIsInBounds) @safe { super(loc, EXP.index, e1, e2); this.indexIsInBounds = indexIsInBounds; @@ -4248,10 +4031,11 @@ extern (C++) final class IndexExp : BinExp override bool isLvalue() { - if (e1.op == EXP.assocArrayLiteral) + if (rvalue) return false; - if (e1.type.ty == Tsarray || - (e1.op == EXP.index && e1.type.ty != Tarray)) + auto t1b = e1.type.toBasetype(); + if (t1b.isTypeAArray() || t1b.isTypeSArray() || + (e1.isIndexExp() && t1b != t1b.isTypeDArray())) { return e1.isLvalue(); } @@ -4292,7 +4076,7 @@ extern (C++) final class IndexExp : BinExp */ extern (C++) final class PostExp : BinExp { - extern (D) this(EXP op, const ref Loc loc, Expression e) + extern (D) this(EXP op, Loc loc, Expression e) { super(loc, op, e, IntegerExp.literal!1); assert(op == EXP.minusMinus || op == EXP.plusPlus); @@ -4309,7 +4093,7 @@ extern (C++) final class PostExp : BinExp */ extern (C++) final class PreExp : UnaExp { - extern (D) this(EXP op, const ref Loc loc, Expression e) @safe + extern (D) this(EXP op, Loc loc, Expression e) @safe { super(loc, op, e); assert(op == EXP.preMinusMinus || op == EXP.prePlusPlus); @@ -4339,12 +4123,12 @@ extern (C++) class AssignExp : BinExp /************************************************************/ /* op can be EXP.assign, EXP.construct, or EXP.blit */ - extern (D) this(const ref Loc loc, Expression e1, Expression e2) @safe + extern (D) this(Loc loc, Expression e1, Expression e2) @safe { super(loc, EXP.assign, e1, e2); } - this(const ref Loc loc, EXP tok, Expression e1, Expression e2) @safe + this(Loc loc, EXP tok, Expression e1, Expression e2) @safe { super(loc, tok, e1, e2); } @@ -4357,7 +4141,7 @@ extern (C++) class AssignExp : BinExp { return false; } - return true; + return !rvalue; } override void accept(Visitor v) @@ -4382,10 +4166,6 @@ extern (C++) final class LoweredAssignExp : AssignExp this.lowering = lowering; } - override const(char)* toChars() const - { - return lowering.toChars(); - } override void accept(Visitor v) { v.visit(this); @@ -4396,14 +4176,14 @@ extern (C++) final class LoweredAssignExp : AssignExp */ extern (C++) final class ConstructExp : AssignExp { - extern (D) this(const ref Loc loc, Expression e1, Expression e2) @safe + extern (D) this(Loc loc, Expression e1, Expression e2) @safe { super(loc, EXP.construct, e1, e2); } // Internal use only. If `v` is a reference variable, the assignment // will become a reference initialization automatically. - extern (D) this(const ref Loc loc, VarDeclaration v, Expression e2) @safe + extern (D) this(Loc loc, VarDeclaration v, Expression e2) @safe { auto ve = new VarExp(loc, v); assert(v.type && ve.type); @@ -4425,14 +4205,14 @@ extern (C++) final class ConstructExp : AssignExp */ extern (C++) final class BlitExp : AssignExp { - extern (D) this(const ref Loc loc, Expression e1, Expression e2) @safe + extern (D) this(Loc loc, Expression e1, Expression e2) @safe { super(loc, EXP.blit, e1, e2); } // Internal use only. If `v` is a reference variable, the assinment // will become a reference rebinding automatically. - extern (D) this(const ref Loc loc, VarDeclaration v, Expression e2) @safe + extern (D) this(Loc loc, VarDeclaration v, Expression e2) @safe { auto ve = new VarExp(loc, v); assert(v.type && ve.type); @@ -4454,7 +4234,7 @@ extern (C++) final class BlitExp : AssignExp */ extern (C++) final class AddAssignExp : BinAssignExp { - extern (D) this(const ref Loc loc, Expression e1, Expression e2) @safe + extern (D) this(Loc loc, Expression e1, Expression e2) @safe { super(loc, EXP.addAssign, e1, e2); } @@ -4470,7 +4250,7 @@ extern (C++) final class AddAssignExp : BinAssignExp */ extern (C++) final class MinAssignExp : BinAssignExp { - extern (D) this(const ref Loc loc, Expression e1, Expression e2) @safe + extern (D) this(Loc loc, Expression e1, Expression e2) @safe { super(loc, EXP.minAssign, e1, e2); } @@ -4486,7 +4266,7 @@ extern (C++) final class MinAssignExp : BinAssignExp */ extern (C++) final class MulAssignExp : BinAssignExp { - extern (D) this(const ref Loc loc, Expression e1, Expression e2) @safe + extern (D) this(Loc loc, Expression e1, Expression e2) @safe { super(loc, EXP.mulAssign, e1, e2); } @@ -4502,7 +4282,7 @@ extern (C++) final class MulAssignExp : BinAssignExp */ extern (C++) final class DivAssignExp : BinAssignExp { - extern (D) this(const ref Loc loc, Expression e1, Expression e2) @safe + extern (D) this(Loc loc, Expression e1, Expression e2) @safe { super(loc, EXP.divAssign, e1, e2); } @@ -4518,7 +4298,7 @@ extern (C++) final class DivAssignExp : BinAssignExp */ extern (C++) final class ModAssignExp : BinAssignExp { - extern (D) this(const ref Loc loc, Expression e1, Expression e2) @safe + extern (D) this(Loc loc, Expression e1, Expression e2) @safe { super(loc, EXP.modAssign, e1, e2); } @@ -4534,7 +4314,7 @@ extern (C++) final class ModAssignExp : BinAssignExp */ extern (C++) final class AndAssignExp : BinAssignExp { - extern (D) this(const ref Loc loc, Expression e1, Expression e2) @safe + extern (D) this(Loc loc, Expression e1, Expression e2) @safe { super(loc, EXP.andAssign, e1, e2); } @@ -4550,7 +4330,7 @@ extern (C++) final class AndAssignExp : BinAssignExp */ extern (C++) final class OrAssignExp : BinAssignExp { - extern (D) this(const ref Loc loc, Expression e1, Expression e2) @safe + extern (D) this(Loc loc, Expression e1, Expression e2) @safe { super(loc, EXP.orAssign, e1, e2); } @@ -4566,7 +4346,7 @@ extern (C++) final class OrAssignExp : BinAssignExp */ extern (C++) final class XorAssignExp : BinAssignExp { - extern (D) this(const ref Loc loc, Expression e1, Expression e2) @safe + extern (D) this(Loc loc, Expression e1, Expression e2) @safe { super(loc, EXP.xorAssign, e1, e2); } @@ -4582,7 +4362,7 @@ extern (C++) final class XorAssignExp : BinAssignExp */ extern (C++) final class PowAssignExp : BinAssignExp { - extern (D) this(const ref Loc loc, Expression e1, Expression e2) @safe + extern (D) this(Loc loc, Expression e1, Expression e2) @safe { super(loc, EXP.powAssign, e1, e2); } @@ -4598,7 +4378,7 @@ extern (C++) final class PowAssignExp : BinAssignExp */ extern (C++) final class ShlAssignExp : BinAssignExp { - extern (D) this(const ref Loc loc, Expression e1, Expression e2) @safe + extern (D) this(Loc loc, Expression e1, Expression e2) @safe { super(loc, EXP.leftShiftAssign, e1, e2); } @@ -4614,7 +4394,7 @@ extern (C++) final class ShlAssignExp : BinAssignExp */ extern (C++) final class ShrAssignExp : BinAssignExp { - extern (D) this(const ref Loc loc, Expression e1, Expression e2) @safe + extern (D) this(Loc loc, Expression e1, Expression e2) @safe { super(loc, EXP.rightShiftAssign, e1, e2); } @@ -4630,7 +4410,7 @@ extern (C++) final class ShrAssignExp : BinAssignExp */ extern (C++) final class UshrAssignExp : BinAssignExp { - extern (D) this(const ref Loc loc, Expression e1, Expression e2) @safe + extern (D) this(Loc loc, Expression e1, Expression e2) @safe { super(loc, EXP.unsignedRightShiftAssign, e1, e2); } @@ -4657,12 +4437,12 @@ extern (C++) class CatAssignExp : BinAssignExp { Expression lowering; // lowered druntime hook `_d_arrayappend{cTX,T}` - extern (D) this(const ref Loc loc, Expression e1, Expression e2) + extern (D) this(Loc loc, Expression e1, Expression e2) @safe { super(loc, EXP.concatenateAssign, e1, e2); } - extern (D) this(const ref Loc loc, EXP tok, Expression e1, Expression e2) @safe + extern (D) this(Loc loc, EXP tok, Expression e1, Expression e2) @safe { super(loc, tok, e1, e2); } @@ -4678,7 +4458,7 @@ extern (C++) class CatAssignExp : BinAssignExp */ extern (C++) final class CatElemAssignExp : CatAssignExp { - extern (D) this(const ref Loc loc, Type type, Expression e1, Expression e2) @safe + extern (D) this(Loc loc, Type type, Expression e1, Expression e2) @safe { super(loc, EXP.concatenateElemAssign, e1, e2); this.type = type; @@ -4695,7 +4475,7 @@ extern (C++) final class CatElemAssignExp : CatAssignExp */ extern (C++) final class CatDcharAssignExp : CatAssignExp { - extern (D) this(const ref Loc loc, Type type, Expression e1, Expression e2) @safe + extern (D) this(Loc loc, Type type, Expression e1, Expression e2) @safe { super(loc, EXP.concatenateDcharAssign, e1, e2); this.type = type; @@ -4714,7 +4494,7 @@ extern (C++) final class CatDcharAssignExp : CatAssignExp */ extern (C++) final class AddExp : BinExp { - extern (D) this(const ref Loc loc, Expression e1, Expression e2) @safe + extern (D) this(Loc loc, Expression e1, Expression e2) @safe { super(loc, EXP.add, e1, e2); } @@ -4732,7 +4512,7 @@ extern (C++) final class AddExp : BinExp */ extern (C++) final class MinExp : BinExp { - extern (D) this(const ref Loc loc, Expression e1, Expression e2) @safe + extern (D) this(Loc loc, Expression e1, Expression e2) @safe { super(loc, EXP.min, e1, e2); } @@ -4752,7 +4532,7 @@ extern (C++) final class CatExp : BinExp { Expression lowering; // call to druntime hook `_d_arraycatnTX` - extern (D) this(const ref Loc loc, Expression e1, Expression e2) scope @safe + extern (D) this(Loc loc, Expression e1, Expression e2) scope @safe { super(loc, EXP.concatenate, e1, e2); } @@ -4770,7 +4550,7 @@ extern (C++) final class CatExp : BinExp */ extern (C++) final class MulExp : BinExp { - extern (D) this(const ref Loc loc, Expression e1, Expression e2) @safe + extern (D) this(Loc loc, Expression e1, Expression e2) @safe { super(loc, EXP.mul, e1, e2); } @@ -4788,7 +4568,7 @@ extern (C++) final class MulExp : BinExp */ extern (C++) final class DivExp : BinExp { - extern (D) this(const ref Loc loc, Expression e1, Expression e2) @safe + extern (D) this(Loc loc, Expression e1, Expression e2) @safe { super(loc, EXP.div, e1, e2); } @@ -4806,7 +4586,7 @@ extern (C++) final class DivExp : BinExp */ extern (C++) final class ModExp : BinExp { - extern (D) this(const ref Loc loc, Expression e1, Expression e2) @safe + extern (D) this(Loc loc, Expression e1, Expression e2) @safe { super(loc, EXP.mod, e1, e2); } @@ -4824,7 +4604,7 @@ extern (C++) final class ModExp : BinExp */ extern (C++) final class PowExp : BinExp { - extern (D) this(const ref Loc loc, Expression e1, Expression e2) @safe + extern (D) this(Loc loc, Expression e1, Expression e2) @safe { super(loc, EXP.pow, e1, e2); } @@ -4842,7 +4622,7 @@ extern (C++) final class PowExp : BinExp */ extern (C++) final class ShlExp : BinExp { - extern (D) this(const ref Loc loc, Expression e1, Expression e2) @safe + extern (D) this(Loc loc, Expression e1, Expression e2) @safe { super(loc, EXP.leftShift, e1, e2); } @@ -4860,7 +4640,7 @@ extern (C++) final class ShlExp : BinExp */ extern (C++) final class ShrExp : BinExp { - extern (D) this(const ref Loc loc, Expression e1, Expression e2) @safe + extern (D) this(Loc loc, Expression e1, Expression e2) @safe { super(loc, EXP.rightShift, e1, e2); } @@ -4878,7 +4658,7 @@ extern (C++) final class ShrExp : BinExp */ extern (C++) final class UshrExp : BinExp { - extern (D) this(const ref Loc loc, Expression e1, Expression e2) @safe + extern (D) this(Loc loc, Expression e1, Expression e2) @safe { super(loc, EXP.unsignedRightShift, e1, e2); } @@ -4896,7 +4676,7 @@ extern (C++) final class UshrExp : BinExp */ extern (C++) final class AndExp : BinExp { - extern (D) this(const ref Loc loc, Expression e1, Expression e2) @safe + extern (D) this(Loc loc, Expression e1, Expression e2) @safe { super(loc, EXP.and, e1, e2); } @@ -4914,7 +4694,7 @@ extern (C++) final class AndExp : BinExp */ extern (C++) final class OrExp : BinExp { - extern (D) this(const ref Loc loc, Expression e1, Expression e2) @safe + extern (D) this(Loc loc, Expression e1, Expression e2) @safe { super(loc, EXP.or, e1, e2); } @@ -4932,7 +4712,7 @@ extern (C++) final class OrExp : BinExp */ extern (C++) final class XorExp : BinExp { - extern (D) this(const ref Loc loc, Expression e1, Expression e2) @safe + extern (D) this(Loc loc, Expression e1, Expression e2) @safe { super(loc, EXP.xor, e1, e2); } @@ -4951,7 +4731,7 @@ extern (C++) final class XorExp : BinExp */ extern (C++) final class LogicalExp : BinExp { - extern (D) this(const ref Loc loc, EXP op, Expression e1, Expression e2) @safe + extern (D) this(Loc loc, EXP op, Expression e1, Expression e2) @safe { super(loc, op, e1, e2); assert(op == EXP.andAnd || op == EXP.orOr); @@ -4973,7 +4753,7 @@ extern (C++) final class LogicalExp : BinExp */ extern (C++) final class CmpExp : BinExp { - extern (D) this(EXP op, const ref Loc loc, Expression e1, Expression e2) @safe + extern (D) this(EXP op, Loc loc, Expression e1, Expression e2) @safe { super(loc, op, e1, e2); assert(op == EXP.lessThan || op == EXP.lessOrEqual || op == EXP.greaterThan || op == EXP.greaterOrEqual); @@ -4994,7 +4774,7 @@ extern (C++) final class CmpExp : BinExp */ extern (C++) final class InExp : BinExp { - extern (D) this(const ref Loc loc, Expression e1, Expression e2) @safe + extern (D) this(Loc loc, Expression e1, Expression e2) @safe { super(loc, EXP.in_, e1, e2); } @@ -5012,7 +4792,7 @@ extern (C++) final class InExp : BinExp */ extern (C++) final class RemoveExp : BinExp { - extern (D) this(const ref Loc loc, Expression e1, Expression e2) + extern (D) this(Loc loc, Expression e1, Expression e2) { super(loc, EXP.remove, e1, e2); type = Type.tbool; @@ -5033,7 +4813,7 @@ extern (C++) final class RemoveExp : BinExp */ extern (C++) final class EqualExp : BinExp { - extern (D) this(EXP op, const ref Loc loc, Expression e1, Expression e2) @safe + extern (D) this(EXP op, Loc loc, Expression e1, Expression e2) @safe { super(loc, op, e1, e2); assert(op == EXP.equal || op == EXP.notEqual); @@ -5054,7 +4834,7 @@ extern (C++) final class EqualExp : BinExp */ extern (C++) final class IdentityExp : BinExp { - extern (D) this(EXP op, const ref Loc loc, Expression e1, Expression e2) @safe + extern (D) this(EXP op, Loc loc, Expression e1, Expression e2) @safe { super(loc, op, e1, e2); assert(op == EXP.identity || op == EXP.notIdentity); @@ -5075,7 +4855,7 @@ extern (C++) final class CondExp : BinExp { Expression econd; - extern (D) this(const ref Loc loc, Expression econd, Expression e1, Expression e2) scope @safe + extern (D) this(Loc loc, Expression econd, Expression e1, Expression e2) scope @safe { super(loc, EXP.question, e1, e2); this.econd = econd; @@ -5088,7 +4868,7 @@ extern (C++) final class CondExp : BinExp override bool isLvalue() { - return e1.isLvalue() && e2.isLvalue(); + return !rvalue && e1.isLvalue() && e2.isLvalue(); } override void accept(Visitor v) @@ -5119,7 +4899,7 @@ extern (C++) class DefaultInitExp : Expression * op = EXP.prettyFunction, EXP.functionString, EXP.moduleString, * EXP.line, EXP.file, EXP.fileFullPath */ - extern (D) this(const ref Loc loc, EXP op) @safe + extern (D) this(Loc loc, EXP op) @safe { super(loc, op); } @@ -5135,7 +4915,7 @@ extern (C++) class DefaultInitExp : Expression */ extern (C++) final class FileInitExp : DefaultInitExp { - extern (D) this(const ref Loc loc, EXP tok) @safe + extern (D) this(Loc loc, EXP tok) @safe { super(loc, tok); } @@ -5151,7 +4931,7 @@ extern (C++) final class FileInitExp : DefaultInitExp */ extern (C++) final class LineInitExp : DefaultInitExp { - extern (D) this(const ref Loc loc) @safe + extern (D) this(Loc loc) @safe { super(loc, EXP.line); } @@ -5167,7 +4947,7 @@ extern (C++) final class LineInitExp : DefaultInitExp */ extern (C++) final class ModuleInitExp : DefaultInitExp { - extern (D) this(const ref Loc loc) @safe + extern (D) this(Loc loc) @safe { super(loc, EXP.moduleString); } @@ -5183,7 +4963,7 @@ extern (C++) final class ModuleInitExp : DefaultInitExp */ extern (C++) final class FuncInitExp : DefaultInitExp { - extern (D) this(const ref Loc loc) @safe + extern (D) this(Loc loc) @safe { super(loc, EXP.functionString); } @@ -5199,7 +4979,7 @@ extern (C++) final class FuncInitExp : DefaultInitExp */ extern (C++) final class PrettyFuncInitExp : DefaultInitExp { - extern (D) this(const ref Loc loc) @safe + extern (D) this(Loc loc) @safe { super(loc, EXP.prettyFunction); } @@ -5218,7 +4998,7 @@ extern (C++) final class ClassReferenceExp : Expression { StructLiteralExp value; - extern (D) this(const ref Loc loc, StructLiteralExp lit, Type type) @safe + extern (D) this(Loc loc, StructLiteralExp lit, Type type) @safe { super(loc, EXP.classReference); assert(lit && lit.sd && lit.sd.isClassDeclaration()); @@ -5291,27 +5071,6 @@ extern (C++) final class CTFEExp : Expression type = Type.tvoid; } - override const(char)* toChars() const - { - switch (op) - { - case EXP.cantExpression: - return ""; - case EXP.voidExpression: - return "cast(void)0"; - case EXP.showCtfeContext: - return ""; - case EXP.break_: - return ""; - case EXP.continue_: - return ""; - case EXP.goto_: - return ""; - default: - assert(0); - } - } - extern (D) __gshared CTFEExp cantexp; extern (D) __gshared CTFEExp voidexp; extern (D) __gshared CTFEExp breakexp; @@ -5341,18 +5100,13 @@ extern (C++) final class ThrownExceptionExp : Expression { ClassReferenceExp thrown; // the thing being tossed - extern (D) this(const ref Loc loc, ClassReferenceExp victim) @safe + extern (D) this(Loc loc, ClassReferenceExp victim) @safe { super(loc, EXP.thrownException); this.thrown = victim; this.type = victim.type; } - override const(char)* toChars() const - { - return "CTFE ThrownException"; - } - override void accept(Visitor v) { v.visit(this); @@ -5368,7 +5122,7 @@ extern (C++) final class ObjcClassReferenceExp : Expression { ClassDeclaration classDeclaration; - extern (D) this(const ref Loc loc, ClassDeclaration classDeclaration) + extern (D) this(Loc loc, ClassDeclaration classDeclaration) @safe { super(loc, EXP.objcClassReference); this.classDeclaration = classDeclaration; @@ -5390,7 +5144,7 @@ extern (C++) final class GenericExp : Expression Types* types; /// type-names for generic associations (null entry for `default`) Expressions* exps; /// 1:1 mapping of typeNames to exps - extern (D) this(const ref Loc loc, Expression cntlExp, Types* types, Expressions* exps) @safe + extern (D) this(Loc loc, Expression cntlExp, Types* types, Expressions* exps) @safe { super(loc, EXP._Generic); this.cntlExp = cntlExp; diff --git a/dmd/expression.h b/dmd/expression.h index 64f059cd4f..5f637b4c2e 100644 --- a/dmd/expression.h +++ b/dmd/expression.h @@ -1,6 +1,6 @@ /* Compiler implementation of the D programming language - * Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * written by Walter Bright * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. @@ -40,7 +40,7 @@ class OverloadSet; class StringExp; class InterpExp; class LoweredAssignExp; -class EnumDeclaration; +class StaticForeach; #ifdef IN_GCC typedef union tree_node Symbol; #elif IN_LLVM @@ -53,10 +53,9 @@ namespace dmd { // in expressionsem.d Expression *expressionSemantic(Expression *e, Scope *sc); + void lowerNonArrayAggregate(StaticForeach *sfe, Scope *sc); // in typesem.d - Expression *defaultInit(Type *mt, const Loc &loc, const bool isCfile = false); - // in enumsem.d - Expression *getDefaultValue(EnumDeclaration *ed, const Loc &loc); + Expression *defaultInit(Type *mt, Loc loc, const bool isCfile = false); // Entry point for CTFE. // A compile-time result is required. Give an error if not possible @@ -76,26 +75,18 @@ enum #define WANTvalue 0 // default #define WANTexpand 1 // expand const/immutable variables if possible -/** - * Specifies how the checkModify deals with certain situations - */ -enum class ModifyFlags -{ - /// Issue error messages on invalid modifications of the variable - none, - /// No errors are emitted for invalid modifications - noError = 0x1, - /// The modification occurs for a subfield of the current variable - fieldAssign = 0x2, -}; - class Expression : public ASTNode { public: Type *type; // !=NULL means that semantic() has been run Loc loc; // file location EXP op; // to minimize use of dynamic_cast - d_bool parens; // if this is a parenthesized expression + uint8_t bitFields; + + bool parens() const; + bool parens(bool v); + bool rvalue() const; + bool rvalue(bool v); size_t size() const; static void _init(); @@ -104,7 +95,7 @@ class Expression : public ASTNode // kludge for template.isExpression() DYNCAST dyncast() const override final { return DYNCAST_EXPRESSION; } - const char *toChars() const override; + const char* toChars() const final override; virtual dinteger_t toInteger(); virtual uinteger_t toUInteger(); @@ -114,7 +105,6 @@ class Expression : public ASTNode virtual StringExp *toStringExp(); virtual bool isLvalue(); virtual bool checkType(); - virtual bool checkValue(); Expression *addressOf(); Expression *deref(); @@ -156,7 +146,7 @@ class Expression : public ASTNode TypeidExp* isTypeidExp(); TraitsExp* isTraitsExp(); HaltExp* isHaltExp(); - IsExp* isExp(); + IsExp* isIsExp(); MixinExp* isMixinExp(); ImportExp* isImportExp(); AssertExp* isAssertExp(); @@ -247,7 +237,7 @@ class IntegerExp final : public Expression public: dinteger_t value; - static IntegerExp *create(const Loc &loc, dinteger_t value, Type *type); + 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; @@ -273,7 +263,7 @@ class RealExp final : public Expression public: real_t value; - static RealExp *create(const Loc &loc, real_t value, Type *type); + 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; @@ -290,7 +280,7 @@ class ComplexExp final : public Expression public: complex_t value; - static ComplexExp *create(const Loc &loc, complex_t value, Type *type); + 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; @@ -307,7 +297,7 @@ class IdentifierExp : public Expression public: Identifier *ident; - static IdentifierExp *create(const Loc &loc, Identifier *ident); + static IdentifierExp *create(Loc loc, Identifier *ident); bool isLvalue() override final; void accept(Visitor *v) override { v->visit(this); } }; @@ -367,8 +357,8 @@ class StringExp final : public Expression d_bool committed; // if type is committed d_bool hexString; // if string is parsed from a hex string literal - static StringExp *create(const Loc &loc, const char *s); - static StringExp *create(const Loc &loc, const void *s, d_size_t len); + 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; @@ -418,7 +408,7 @@ class TupleExp final : public Expression */ Expressions *exps; - static TupleExp *create(const Loc &loc, Expressions *exps); + static TupleExp *create(Loc loc, Expressions *exps); TupleExp *syntaxCopy() override; bool equals(const RootObject * const o) const override; @@ -433,7 +423,7 @@ class ArrayLiteralExp final : public Expression Expression *basis; Expressions *elements; - static ArrayLiteralExp *create(const Loc &loc, Expressions *elements); + static ArrayLiteralExp *create(Loc loc, Expressions *elements); ArrayLiteralExp *syntaxCopy() override; bool equals(const RootObject * const o) const override; Expression *getElement(d_size_t i); @@ -461,6 +451,24 @@ class AssocArrayLiteralExp final : public Expression class StructLiteralExp final : public Expression { public: + uint8_t bitFields; + + // if this is true, use the StructDeclaration's init symbol + bool useStaticInit() const; + bool useStaticInit(bool v); + // used when moving instances to indicate `this is this.origin` + bool isOriginal() const; + bool isOriginal(bool v); + OwnedBy ownedByCtfe() const; + OwnedBy ownedByCtfe(OwnedBy v); + + /** anytime when recursive function is calling, 'stageflags' marks with bit flag of + * current stage and unmarks before return from this function. + * 'inlinecopy' uses similar 'stageflags' and from multiple evaluation 'doInline' + * (with infinite recursion) of this expression. + */ + uint8_t stageflags; + StructDeclaration *sd; // which aggregate this is for Expressions *elements; // parallels sd->fields[] with NULL entries for fields to skip Type *stype; // final type of result (can be different from sd's type) @@ -488,18 +496,7 @@ class StructLiteralExp final : public Expression StructLiteralExp *origin; - /** anytime when recursive function is calling, 'stageflags' marks with bit flag of - * current stage and unmarks before return from this function. - * 'inlinecopy' uses similar 'stageflags' and from multiple evaluation 'doInline' - * (with infinite recursion) of this expression. - */ - uint8_t stageflags; - - d_bool useStaticInit; // if this is true, use the StructDeclaration's init symbol - d_bool isOriginal; // used when moving instances to indicate `this is this.origin` - OwnedBy ownedByCtfe; - - static StructLiteralExp *create(const Loc &loc, StructDeclaration *sd, void *elements, Type *stype = nullptr); + static StructLiteralExp *create(Loc loc, StructDeclaration *sd, void *elements, Type *stype = nullptr); bool equals(const RootObject * const o) const override; StructLiteralExp *syntaxCopy() override; @@ -511,7 +508,6 @@ class TypeExp final : public Expression public: TypeExp *syntaxCopy() override; bool checkType() override; - bool checkValue() override; void accept(Visitor *v) override { v->visit(this); } }; @@ -522,7 +518,6 @@ class ScopeExp final : public Expression ScopeExp *syntaxCopy() override; bool checkType() override; - bool checkValue() override; void accept(Visitor *v) override { v->visit(this); } }; @@ -534,7 +529,6 @@ class TemplateExp final : public Expression bool isLvalue() override; bool checkType() override; - bool checkValue() override; void accept(Visitor *v) override { v->visit(this); } }; @@ -547,6 +541,7 @@ class NewExp final : public Expression Type *newtype; Expressions *arguments; // Array of Expression's Identifiers *names; // Array of names corresponding to expressions + Expression *placement; // if !NULL, placement expression Expression *argprefix; // expression to be evaluated just before arguments[] @@ -556,7 +551,7 @@ class NewExp final : public Expression Expression *lowering; // lowered druntime hook: `_d_newclass` - static NewExp *create(const Loc &loc, Expression *thisexp, Type *newtype, Expressions *arguments); + static NewExp *create(Loc loc, Expression *placement, Expression *thisexp, Type *newtype, Expressions *arguments); NewExp *syntaxCopy() override; void accept(Visitor *v) override { v->visit(this); } @@ -570,6 +565,7 @@ class NewAnonClassExp final : public Expression Expression *thisexp; // if !NULL, 'this' for class being allocated ClassDeclaration *cd; // class being instantiated Expressions *arguments; // Array of Expression's to call class constructor + Expression *placement; // if !NULL, placement expression NewAnonClassExp *syntaxCopy() override; void accept(Visitor *v) override { v->visit(this); } @@ -603,7 +599,7 @@ class VarExp final : public SymbolExp { public: d_bool delegateWasExtracted; - static VarExp *create(const Loc &loc, Declaration *var, bool hasOverloads = true); + static VarExp *create(Loc loc, Declaration *var, bool hasOverloads = true); bool equals(const RootObject * const o) const override; bool isLvalue() override; @@ -632,9 +628,7 @@ class FuncExp final : public Expression bool equals(const RootObject * const o) const override; FuncExp *syntaxCopy() override; - const char *toChars() const override; bool checkType() override; - bool checkValue() override; void accept(Visitor *v) override { v->visit(this); } }; @@ -716,9 +710,6 @@ class BinExp : public Expression Expression *e1; Expression *e2; - Type *att1; // Save alias this type to detect recursion - Type *att2; // Save alias this type to detect recursion - BinExp *syntaxCopy() override; void accept(Visitor *v) override { v->visit(this); } @@ -771,7 +762,7 @@ class DotIdExp final : public UnaExp d_bool wantsym; // do not replace Symbol with its initializer during semantic() d_bool arrow; // ImportC: if -> instead of . - static DotIdExp *create(const Loc &loc, Expression *e, Identifier *ident); + static DotIdExp *create(Loc loc, Expression *e, Identifier *ident); void accept(Visitor *v) override { v->visit(this); } }; @@ -781,7 +772,6 @@ class DotTemplateExp final : public UnaExp TemplateDeclaration *td; bool checkType() override; - bool checkValue() override; void accept(Visitor *v) override { v->visit(this); } }; @@ -802,7 +792,6 @@ class DotTemplateInstanceExp final : public UnaExp DotTemplateInstanceExp *syntaxCopy() override; bool checkType() override; - bool checkValue() override; void accept(Visitor *v) override { v->visit(this); } }; @@ -852,10 +841,10 @@ class CallExp final : public UnaExp d_bool isUfcsRewrite; // the first argument was pushed in here by a UFCS rewrite VarDeclaration *vthis2; // container for multi-context - static CallExp *create(const Loc &loc, Expression *e, Expressions *exps); - static CallExp *create(const Loc &loc, Expression *e); - static CallExp *create(const Loc &loc, Expression *e, Expression *earg1); - static CallExp *create(const Loc &loc, FuncDeclaration *fd, Expression *earg1); + static CallExp *create(Loc loc, Expression *e, Expressions *exps); + static CallExp *create(Loc loc, Expression *e); + static CallExp *create(Loc loc, Expression *e, Expression *earg1); + static CallExp *create(Loc loc, FuncDeclaration *fd, Expression *earg1); CallExp *syntaxCopy() override; bool isLvalue() override; @@ -929,7 +918,7 @@ class VectorExp final : public UnaExp unsigned dim; // number of elements in the vector OwnedBy ownedByCtfe; - static VectorExp *create(const Loc &loc, Expression *e, Type *t); + static VectorExp *create(Loc loc, Expression *e, Type *t); VectorExp *syntaxCopy() override; void accept(Visitor *v) override { v->visit(this); } }; @@ -1105,7 +1094,6 @@ class LoweredAssignExp final : public AssignExp public: Expression *lowering; - const char *toChars() const override; void accept(Visitor *v) override { v->visit(this); } }; diff --git a/dmd/expressionsem.d b/dmd/expressionsem.d index 931c298e31..6ad69149c9 100644 --- a/dmd/expressionsem.d +++ b/dmd/expressionsem.d @@ -3,12 +3,12 @@ * * Specification: ($LINK2 https://dlang.org/spec/expression.html, Expressions) * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/expressionsem.d, _expressionsem.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/expressionsem.d, _expressionsem.d) * Documentation: https://dlang.org/phobos/dmd_expressionsem.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/expressionsem.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/expressionsem.d */ module dmd.expressionsem; @@ -25,6 +25,7 @@ import dmd.astcodegen; import dmd.astenums; import dmd.canthrow; import dmd.chkformat; +import dmd.cond; import dmd.ctorflow; import dmd.dscope; import dmd.dsymbol; @@ -33,9 +34,9 @@ import dmd.dclass; import dmd.dcast; import dmd.delegatize; import dmd.denum; +import dmd.deps; import dmd.dimport; import dmd.dinterpret; -import dmd.dmangle; import dmd.dmodule; import dmd.dstruct; import dmd.dsymbolsem; @@ -59,15 +60,16 @@ import dmd.initsem; import dmd.inline; import dmd.intrange; import dmd.location; +import dmd.mangle; import dmd.mtype; import dmd.mustuse; import dmd.nspace; +import dmd.nogc; import dmd.objc; import dmd.opover; import dmd.optimize; import dmd.parse; import dmd.printast; -import dmd.postordervisitor; import dmd.root.array; import dmd.root.ctfloat; import dmd.root.filename; @@ -80,6 +82,7 @@ import dmd.semantic3; import dmd.sideeffect; import dmd.safe; import dmd.target; +import dmd.templatesem : matchWithInstance; import dmd.tokens; import dmd.traits; import dmd.typesem; @@ -87,6 +90,7 @@ import dmd.typinf; import dmd.utils; import dmd.utils : arrayCastBigEndian; import dmd.visitor; +import dmd.visitor.postorder; enum LOGSEMANTIC = false; @@ -115,10 +119,9 @@ private bool isNeedThisScope(Scope* sc, Declaration d) { if (ad2 == ad) return false; - else if (ad2.isNested()) + if (ad2.isNested()) continue; - else - return true; + return true; } if (FuncDeclaration f = s.isFuncDeclaration()) { @@ -306,7 +309,7 @@ extern (D) bool findTempDecl(DotTemplateInstanceExp exp, Scope* sc) * Returns: * String literal, or `null` if error happens. */ -StringExp semanticString(Scope *sc, Expression exp, const char* s) +StringExp semanticString(Scope* sc, Expression exp, const char* s) { sc = sc.startCTFE(); exp = exp.expressionSemantic(sc); @@ -324,14 +327,11 @@ StringExp semanticString(Scope *sc, Expression exp, const char* s) return null; } - auto se = e.toStringExp(); - if (!se) - { - error(exp.loc, "`string` expected for %s, not `(%s)` of type `%s`", - s, exp.toChars(), exp.type.toChars()); - return null; - } - return se; + if (auto se = e.toStringExp()) + return se; + error(exp.loc, "`string` expected for %s, not `(%s)` of type `%s`", + s, exp.toChars(), exp.type.toChars()); + return null; } /**************************************** @@ -339,18 +339,75 @@ StringExp semanticString(Scope *sc, Expression exp, const char* s) */ StringExp toUTF8(StringExp se, Scope* sc) { - if (se.sz != 1) + if (se.sz == 1) + return se; + // Convert to UTF-8 string + se.committed = false; + Expression e = castTo(se, sc, Type.tchar.arrayOf()); + e = e.optimize(WANTvalue); + auto result = e.isStringExp(); + assert(result); + assert(result.sz == 1); + return result; +} +/******************************** + * The type for a unary expression is incompatible. + * Print error message. + * Returns: + * ErrorExp + */ +private Expression incompatibleTypes(UnaExp e) +{ + if (e.e1.type.toBasetype() == Type.terror) + return e.e1; + + if (e.e1.op == EXP.type) { - // Convert to UTF-8 string - se.committed = false; - Expression e = castTo(se, sc, Type.tchar.arrayOf()); - e = e.optimize(WANTvalue); - auto result = e.isStringExp(); - assert(result); - assert(result.sz == 1); - return result; + error(e.loc, "incompatible type for `%s(%s)`: cannot use `%s` with types", EXPtoString(e.op).ptr, e.e1.toErrMsg(), EXPtoString(e.op).ptr); + } + else + { + error(e.loc, "incompatible type for `%s(%s)`: `%s`", EXPtoString(e.op).ptr, e.e1.toErrMsg(), e.e1.type.toChars()); + } + return ErrorExp.get(); +} + +/******************************** + * The types for a binary expression are incompatible. + * Print error message. + * Returns: + * ErrorExp + */ +extern (D) Expression incompatibleTypes(BinExp e, Scope* sc = null) +{ + if (e.e1.type.toBasetype() == Type.terror) + return e.e1; + if (e.e2.type.toBasetype() == Type.terror) + return e.e2; + + // CondExp uses 'a ? b : c' but we're comparing 'b : c' + const(char)* thisOp = (e.op == EXP.question) ? ":" : EXPtoString(e.op).ptr; + + if (sc && suggestBinaryOverloads(e, sc)) + return ErrorExp.get(); + + if (e.e1.op == EXP.type || e.e2.op == EXP.type) + { + error(e.loc, "incompatible types for `(%s) %s (%s)`: cannot use `%s` with types", + e.e1.toErrMsg(), thisOp, e.e2.toErrMsg(), EXPtoString(e.op).ptr); + } + else if (e.e1.type.equals(e.e2.type)) + { + error(e.loc, "incompatible types for `(%s) %s (%s)`: both operands are of type `%s`", + e.e1.toErrMsg(), thisOp, e.e2.toErrMsg(), e.e1.type.toChars()); + } + else + { + auto ts = toAutoQualChars(e.e1.type, e.e2.type); + error(e.loc, "incompatible types for `(%s) %s (%s)`: `%s` and `%s`", + e.e1.toErrMsg(), thisOp, e.e2.toErrMsg(), ts[0], ts[1]); } - return se; + return ErrorExp.get(); } private Expression reorderSettingAAElem(BinExp exp, Scope* sc) @@ -415,12 +472,12 @@ private Expression checkOpAssignTypes(BinExp binExp, Scope* sc) // 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()) ? + // 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 ((type.isintegral() && t2.isfloating())) + if ((type.isIntegral() && t2.isFloating())) { warning(loc, "`%s %s %s` is performing truncating conversion", type.toChars(), EXPtoString(op).ptr, t2.toChars()); } @@ -432,17 +489,17 @@ private Expression checkOpAssignTypes(BinExp binExp, Scope* sc) // Any multiplication by an imaginary or complex number yields a complex result. // r *= c, i*=c, r*=i, i*=i are all forbidden operations. const(char)* opstr = EXPtoString(op).ptr; - if (t1.isreal() && t2.iscomplex()) + if (t1.isReal() && t2.isComplex()) { error(loc, "`%s %s %s` is undefined. Did you mean `%s %s %s.re`?", t1.toChars(), opstr, t2.toChars(), t1.toChars(), opstr, t2.toChars()); return ErrorExp.get(); } - else if (t1.isimaginary() && t2.iscomplex()) + else if (t1.isImaginary() && t2.isComplex()) { error(loc, "`%s %s %s` is undefined. Did you mean `%s %s %s.im`?", t1.toChars(), opstr, t2.toChars(), t1.toChars(), opstr, t2.toChars()); return ErrorExp.get(); } - else if ((t1.isreal() || t1.isimaginary()) && t2.isimaginary()) + else if ((t1.isReal() || t1.isImaginary()) && t2.isImaginary()) { error(loc, "`%s %s %s` is an undefined operation", t1.toChars(), opstr, t2.toChars()); return ErrorExp.get(); @@ -454,98 +511,92 @@ private Expression checkOpAssignTypes(BinExp binExp, Scope* sc) { // Addition or subtraction of a real and an imaginary is a complex result. // Thus, r+=i, r+=c, i+=r, i+=c are all forbidden operations. - if ((t1.isreal() && (t2.isimaginary() || t2.iscomplex())) || (t1.isimaginary() && (t2.isreal() || t2.iscomplex()))) + if ((t1.isReal() && (t2.isImaginary() || t2.isComplex())) || (t1.isImaginary() && (t2.isReal() || t2.isComplex()))) { error(loc, "`%s %s %s` is undefined (result is complex)", t1.toChars(), EXPtoString(op).ptr, t2.toChars()); return ErrorExp.get(); } - if (type.isreal() || type.isimaginary()) + if (type.isReal() || type.isImaginary()) { - assert(global.errors || t2.isfloating()); + assert(global.errors || t2.isFloating()); e2 = e2.castTo(sc, t1); } } - if (op == EXP.mulAssign) + if (op == EXP.mulAssign && t2.isFloating()) { - if (t2.isfloating()) + if (t1.isReal()) { - if (t1.isreal()) - { - if (t2.isimaginary() || t2.iscomplex()) - { - e2 = e2.castTo(sc, t1); - } - } - else if (t1.isimaginary()) + if (t2.isImaginary() || t2.isComplex()) { - if (t2.isimaginary() || t2.iscomplex()) - { - switch (t1.ty) - { - case Timaginary32: - t2 = Type.tfloat32; - break; - - case Timaginary64: - t2 = Type.tfloat64; - break; - - case Timaginary80: - t2 = Type.tfloat80; - break; - - default: - assert(0); - } - e2 = e2.castTo(sc, t2); - } + e2 = e2.castTo(sc, t1); } } - } - else if (op == EXP.divAssign) - { - if (t2.isimaginary()) + else if (t1.isImaginary()) { - if (t1.isreal()) + if (t2.isImaginary() || t2.isComplex()) { - // x/iv = i(-x/v) - // Therefore, the result is 0 - e2 = new CommaExp(loc, e2, new RealExp(loc, CTFloat.zero, t1)); - e2.type = t1; - Expression e = new AssignExp(loc, e1, e2); - e.type = t1; - return e; - } - else if (t1.isimaginary()) - { - Type t3; switch (t1.ty) { case Timaginary32: - t3 = Type.tfloat32; + t2 = Type.tfloat32; break; case Timaginary64: - t3 = Type.tfloat64; + t2 = Type.tfloat64; break; case Timaginary80: - t3 = Type.tfloat80; + t2 = Type.tfloat80; break; default: assert(0); } - e2 = e2.castTo(sc, t3); - Expression e = new AssignExp(loc, e1, e2); - e.type = t1; - return e; + e2 = e2.castTo(sc, t2); + } + } + } + else if (op == EXP.divAssign && t2.isImaginary()) + { + if (t1.isReal()) + { + // x/iv = i(-x/v) + // Therefore, the result is 0 + e2 = new CommaExp(loc, e2, new RealExp(loc, CTFloat.zero, t1)); + e2.type = t1; + Expression e = new AssignExp(loc, e1, e2); + e.type = t1; + return e; + } + else if (t1.isImaginary()) + { + Type t3; + switch (t1.ty) + { + case Timaginary32: + t3 = Type.tfloat32; + break; + + case Timaginary64: + t3 = Type.tfloat64; + break; + + case Timaginary80: + t3 = Type.tfloat80; + break; + + default: + assert(0); } + e2 = e2.castTo(sc, t3); + Expression e = new AssignExp(loc, e1, e2); + e.type = t1; + return e; } } else if (op == EXP.modAssign) { - if (t2.iscomplex()) + if (t2.isComplex()) { error(loc, "cannot perform modulo complex arithmetic"); return ErrorExp.get(); @@ -561,7 +612,7 @@ private Expression extractOpDollarSideEffect(Scope* sc, UnaExp ue) // https://issues.dlang.org/show_bug.cgi?id=12585 // Extract the side effect part if ue.e1 is comma. - if ((sc.flags & SCOPE.ctfe) ? hasSideEffect(e1) : !isTrivialExp(e1)) // match logic in extractSideEffect() + if (sc.ctfe ? hasSideEffect(e1) : !isTrivialExp(e1)) // match logic in extractSideEffect() { /* Even if opDollar is needed, 'e1' should be evaluate only once. So * Rewrite: @@ -616,25 +667,27 @@ TupleDeclaration isAliasThisTuple(Expression e) * Runs semantic on ae.arguments. Declares temporary variables * if '$' was used. */ -Expression resolveOpDollar(Scope* sc, ArrayExp ae, Expression* pe0) +Expression resolveOpDollar(Scope* sc, ArrayExp ae, out Expression pe0) { assert(!ae.lengthVar); - *pe0 = null; AggregateDeclaration ad = isAggregate(ae.e1.type); - Dsymbol slice = search_function(ad, Id.slice); + Dsymbol slice = search_function(ad, Id.opSlice); //printf("slice = %s %s\n", slice.kind(), slice.toChars()); + Expression fallback() + { + if (ae.arguments.length == 1) + return null; + error(ae.loc, "multi-dimensional slicing requires template `opSlice`"); + return ErrorExp.get(); + } foreach (i, e; *ae.arguments) { if (i == 0) - *pe0 = extractOpDollarSideEffect(sc, ae); + pe0 = extractOpDollarSideEffect(sc, ae); if (e.op == EXP.interval && !(slice && slice.isTemplateDeclaration())) { - Lfallback: - if (ae.arguments.length == 1) - return null; - error(ae.loc, "multi-dimensional slicing requires template `opSlice`"); - return ErrorExp.get(); + return fallback(); } //printf("[%d] e = %s\n", i, e.toChars()); @@ -653,7 +706,7 @@ Expression resolveOpDollar(Scope* sc, ArrayExp ae, Expression* pe0) // If $ was used, declare it now Expression de = new DeclarationExp(ae.loc, ae.lengthVar); de = de.expressionSemantic(sc); - *pe0 = Expression.combine(*pe0, de); + pe0 = Expression.combine(pe0, de); } sc = sc.pop(); @@ -668,22 +721,22 @@ Expression resolveOpDollar(Scope* sc, ArrayExp ae, Expression* pe0) (*fargs)[0] = ie.lwr; (*fargs)[1] = ie.upr; - uint xerrors = global.startGagging(); + const xerrors = global.startGagging(); sc = sc.push(); FuncDeclaration fslice = resolveFuncCall(ae.loc, sc, slice, tiargs, ae.e1.type, ArgumentList(fargs), FuncResolveFlag.quiet); sc = sc.pop(); global.endGagging(xerrors); if (!fslice) - goto Lfallback; + return fallback(); - e = new DotTemplateInstanceExp(ae.loc, ae.e1, slice.ident, tiargs); + e = new DotTemplateInstanceExp(ae.loc, ae.e1, Id.opSlice, tiargs); e = new CallExp(ae.loc, e, fargs); e = e.expressionSemantic(sc); } if (!e.type) { - error(ae.loc, "`%s` has no value", e.toChars()); + error(ae.loc, "`%s` has no value", e.toErrMsg()); e = ErrorExp.get(); } if (e.op == EXP.error) @@ -700,7 +753,7 @@ Expression resolveOpDollar(Scope* sc, ArrayExp ae, Expression* pe0) * Returns: * ae, or ErrorExp if errors occurred */ -Expression resolveOpDollar(Scope* sc, ArrayExp ae, IntervalExp ie, Expression* pe0) +Expression resolveOpDollar(Scope* sc, ArrayExp ae, IntervalExp ie, ref Expression pe0) { //assert(!ae.lengthVar); if (!ie) @@ -720,7 +773,7 @@ Expression resolveOpDollar(Scope* sc, ArrayExp ae, IntervalExp ie, Expression* p e = resolveProperties(sc, e); if (!e.type) { - error(ae.loc, "`%s` has no value", e.toChars()); + error(ae.loc, "`%s` has no value", e.toErrMsg()); errors = true; } return e; @@ -737,7 +790,7 @@ Expression resolveOpDollar(Scope* sc, ArrayExp ae, IntervalExp ie, Expression* p // If $ was used, declare it now Expression de = new DeclarationExp(ae.loc, ae.lengthVar); de = de.expressionSemantic(sc); - *pe0 = Expression.combine(*pe0, de); + pe0 = Expression.combine(pe0, de); } sc = sc.pop(); @@ -770,21 +823,58 @@ extern(D) bool arrayExpressionSemantic( * Params: * sc = the scope where the expression is encountered * e = the expression the needs to be moved or copied (source) - * t = if the struct defines a copy constructor, the type of the destination - * + * t = if the struct defines a copy constructor, the type of the destination (can be NULL) + * nrvo = true if the generated copy can be treated as NRVO + * move = true to allow a move constructor to be used, false to prevent infinite recursion * Returns: * The expression that copy constructs or moves the value. */ -extern (D) Expression doCopyOrMove(Scope *sc, Expression e, Type t = null) +extern (D) Expression doCopyOrMove(Scope* sc, Expression e, Type t, bool nrvo, bool move = false) { + //printf("doCopyOrMove() %s\n", toChars(e)); + StructDeclaration sd; + if (t) + { + if (auto ts = t.isTypeStruct()) + sd = ts.sym; + } + if (auto ce = e.isCondExp()) { - ce.e1 = doCopyOrMove(sc, ce.e1); - ce.e2 = doCopyOrMove(sc, ce.e2); + ce.e1 = doCopyOrMove(sc, ce.e1, null, nrvo); + ce.e2 = doCopyOrMove(sc, ce.e2, null, nrvo); + } + else if (e.isLvalue()) + { + e = callCpCtor(sc, e, t, nrvo); + } + else if (move && sd && sd.hasMoveCtor && !e.isCallExp() && !e.isStructLiteralExp()) + { + // #move + /* Rewrite as: + * S __copyrvalue; + * __copyrvalue.moveCtor(e); + * __copyrvalue; + */ + VarDeclaration vd = new VarDeclaration(e.loc, e.type, Identifier.generateId("__copyrvalue"), null); + if (nrvo) + vd.nrvo = true; + vd.storage_class |= STC.nodtor; + vd.dsymbolSemantic(sc); + Expression de = new DeclarationExp(e.loc, vd); + Expression ve = new VarExp(e.loc, vd); + + Expression er; + er = new DotIdExp(e.loc, ve, Id.ctor); // ve.ctor + er = new CallExp(e.loc, er, e); // ve.ctor(e) + er = new CommaExp(e.loc, er, new VarExp(e.loc, vd)); // ve.ctor(e),vd + er = Expression.combine(de, er); // de,ve.ctor(e),vd + + e = er.expressionSemantic(sc); } else { - e = e.isLvalue() ? callCpCtor(sc, e, t) : valueNoDtor(e); + e = valueNoDtor(e); } return e; } @@ -793,47 +883,51 @@ extern (D) Expression doCopyOrMove(Scope *sc, Expression e, Type t = null) * If e is an instance of a struct, and that struct has a copy constructor, * rewrite e as: * (tmp = e),tmp - * Input: + * Params: * sc = just used to specify the scope of created temporary variable * destinationType = the type of the object on which the copy constructor is called; * may be null if the struct defines a postblit + * nrvo = true if the generated copy can be treated as NRVO */ -private Expression callCpCtor(Scope* sc, Expression e, Type destinationType) +private Expression callCpCtor(Scope* sc, Expression e, Type destinationType, bool nrvo) { - if (auto ts = e.type.baseElemOf().isTypeStruct()) - { - StructDeclaration sd = ts.sym; - if (sd.postblit || sd.hasCopyCtor) - { - /* Create a variable tmp, and replace the argument e with: - * (tmp = e),tmp - * and let AssignExp() handle the construction. - * This is not the most efficient, ideally tmp would be constructed - * directly onto the stack. - */ - auto tmp = copyToTemp(STC.rvalue, "__copytmp", e); - if (sd.hasCopyCtor && destinationType) - { - // https://issues.dlang.org/show_bug.cgi?id=22619 - // If the destination type is inout we can preserve it - // only if inside an inout function; if we are not inside - // an inout function, then we will preserve the type of - // the source - if (destinationType.hasWild && !(sc.func.storage_class & STC.wild)) - tmp.type = e.type; - else - tmp.type = destinationType; - } - tmp.storage_class |= STC.nodtor; - tmp.dsymbolSemantic(sc); - Expression de = new DeclarationExp(e.loc, tmp); - Expression ve = new VarExp(e.loc, tmp); - de.type = Type.tvoid; - ve.type = e.type; - return Expression.combine(de, ve); - } - } - return e; + //printf("callCpCtor(e: %s et: %s destinationType: %s\n", toChars(e), toChars(e.type), toChars(destinationType)); + auto ts = e.type.baseElemOf().isTypeStruct(); + + if (!ts) + return e; + StructDeclaration sd = ts.sym; + if (!sd.postblit && !sd.hasCopyCtor) + return e; + + /* Create a variable tmp, and replace the argument e with: + * (tmp = e),tmp + * and let AssignExp() handle the construction. + * This is not the most efficient, ideally tmp would be constructed + * directly onto the stack. + */ + VarDeclaration tmp = copyToTemp(STC.rvalue, "__copytmp", e); + if (nrvo) + tmp.nrvo = true; + if (sd.hasCopyCtor && destinationType) + { + // https://issues.dlang.org/show_bug.cgi?id=22619 + // If the destination type is inout we can preserve it + // only if inside an inout function; if we are not inside + // an inout function, then we will preserve the type of + // the source + if (destinationType.hasWild && !(sc.func.storage_class & STC.wild)) + tmp.type = e.type; + else + tmp.type = destinationType; + } + tmp.storage_class |= STC.nodtor; + tmp.dsymbolSemantic(sc); + Expression de = new DeclarationExp(e.loc, tmp); + Expression ve = new VarExp(e.loc, tmp); + de.type = Type.tvoid; + ve.type = e.type; + return Expression.combine(de, ve); } /************************************************ @@ -842,6 +936,7 @@ private Expression callCpCtor(Scope* sc, Expression e, Type destinationType) */ Expression valueNoDtor(Expression e) { + //printf("valueNoDtor() %s\n", toChars(e)); auto ex = lastComma(e); if (auto ce = ex.isCallExp()) @@ -962,7 +1057,7 @@ private Expression searchUFCS(Scope* sc, UnaExp ue, Identifier ident) SearchOptFlags flags = SearchOpt.all; Dsymbol s; - if (sc.flags & SCOPE.ignoresymbolvisibility) + if (sc.ignoresymbolvisibility) flags |= SearchOpt.ignoreVisibility; // First look in local scopes @@ -979,8 +1074,7 @@ private Expression searchUFCS(Scope* sc, UnaExp ue, Identifier ident) FuncDeclaration f = s.isFuncDeclaration(); if (f) { - TemplateDeclaration td = getFuncTemplateDecl(f); - if (td) + if (TemplateDeclaration td = getFuncTemplateDecl(f)) { if (td.overroot) td = td.overroot; @@ -1047,41 +1141,41 @@ private void hookDtors(CondExp ce, Scope* sc) override void visit(DeclarationExp e) { auto v = e.declaration.isVarDeclaration(); - if (v && !v.isDataseg()) + if (!v || v.isDataseg()) + return; + + if (v._init) { - if (v._init) - { - if (auto ei = v._init.isExpInitializer()) - walkPostorder(ei.exp, this); - } + if (auto ei = v._init.isExpInitializer()) + walkPostorder(ei.exp, this); + } - if (v.edtor) - walkPostorder(v.edtor, this); + if (v.edtor) + walkPostorder(v.edtor, this); - if (v.needsScopeDtor()) - { - if (!vcond) - { - vcond = copyToTemp(STC.volatile_ | STC.const_, "__cond", ce.econd); - vcond.dsymbolSemantic(sc); + if (!v.needsScopeDtor()) + return; - Expression de = new DeclarationExp(ce.econd.loc, vcond); - de = de.expressionSemantic(sc); + if (!vcond) + { + vcond = copyToTemp(STC.volatile_ | STC.const_, "__cond", ce.econd); + vcond.dsymbolSemantic(sc); - Expression ve = new VarExp(ce.econd.loc, vcond); - ce.econd = Expression.combine(de, ve); - } + Expression de = new DeclarationExp(ce.econd.loc, vcond); + de = de.expressionSemantic(sc); - //printf("\t++v = %s, v.edtor = %s\n", v.toChars(), v.edtor.toChars()); - Expression ve = new VarExp(vcond.loc, vcond); - if (isThen) - v.edtor = new LogicalExp(v.edtor.loc, EXP.andAnd, ve, v.edtor); - else - v.edtor = new LogicalExp(v.edtor.loc, EXP.orOr, ve, v.edtor); - v.edtor = v.edtor.expressionSemantic(sc); - //printf("\t--v = %s, v.edtor = %s\n", v.toChars(), v.edtor.toChars()); - } + Expression ve = new VarExp(ce.econd.loc, vcond); + ce.econd = Expression.combine(de, ve); } + + //printf("\t++v = %s, v.edtor = %s\n", v.toChars(), v.edtor.toChars()); + Expression ve = new VarExp(vcond.loc, vcond); + if (isThen) + v.edtor = new LogicalExp(v.edtor.loc, EXP.andAnd, ve, v.edtor); + else + v.edtor = new LogicalExp(v.edtor.loc, EXP.orOr, ve, v.edtor); + v.edtor = v.edtor.expressionSemantic(sc); + //printf("\t--v = %s, v.edtor = %s\n", v.toChars(), v.edtor.toChars()); } } @@ -1117,7 +1211,7 @@ private Expression resolveUFCS(Scope* sc, CallExp ce) eleft = die.e1; Type t = eleft.type.toBasetype(); - if (t.ty == Tarray || t.ty == Tsarray || t.ty == Tnull || (t.isTypeBasic() && t.ty != Tvoid)) + if (t.isStaticOrDynamicArray() || t.ty == Tnull || (t.isTypeBasic() && t.ty != Tvoid)) { /* Built-in types and arrays have no callable properties, so do shortcut. * It is necessary in: e.init() @@ -1157,6 +1251,9 @@ private Expression resolveUFCS(Scope* sc, CallExp ce) } else { + if (arrayExpressionSemantic(ce.arguments.peekSlice(), sc)) + return ErrorExp.get(); + if (Expression ey = die.dotIdSemanticProp(sc, 1)) { if (ey.op == EXP.error) @@ -1164,19 +1261,11 @@ private Expression resolveUFCS(Scope* sc, CallExp ce) ce.e1 = ey; if (isDotOpDispatch(ey)) { - // even opDispatch and UFCS must have valid arguments, - // so now that we've seen indication of a problem, - // check them for issues. - Expressions* originalArguments = Expression.arraySyntaxCopy(ce.arguments); - - uint errors = global.startGagging(); + const errors = global.startGagging(); e = ce.expressionSemantic(sc); if (!global.endGagging(errors)) return e; - if (arrayExpressionSemantic(originalArguments.peekSlice(), sc)) - return ErrorExp.get(); - /* fall down to UFCS */ } else @@ -1384,12 +1473,12 @@ Expression resolvePropertiesOnly(Scope* sc, Expression e1) auto td = s.isTemplateDeclaration(); if (fd) { - if (fd.type.isTypeFunction().isproperty) + 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 || + if (fd.type.isTypeFunction().isProperty || (fd.storage_class2 & STC.property) || (td._scope.stc & STC.property)) return resolveProperties(sc, e1); @@ -1405,7 +1494,7 @@ Expression resolvePropertiesOnly(Scope* sc, Expression e1) { if (auto fd = td.onemember.isFuncDeclaration()) { - if (fd.type.isTypeFunction().isproperty || + if (fd.type.isTypeFunction().isProperty || (fd.storage_class2 & STC.property) || (td._scope.stc & STC.property)) return resolveProperties(sc, e1); @@ -1417,7 +1506,7 @@ Expression resolvePropertiesOnly(Scope* sc, Expression e1) Expression handleFuncDecl(FuncDeclaration fd) { assert(fd); - if (fd.type.isTypeFunction().isproperty) + if (fd.type.isTypeFunction().isProperty) return resolveProperties(sc, e1); return e1; } @@ -1429,7 +1518,7 @@ Expression resolvePropertiesOnly(Scope* sc, Expression e1) } else if (auto oe = e1.isOverExp()) return handleOverloadSet(oe.vars); - else if (auto dti = e1.isDotTemplateInstanceExp()) + if (auto dti = e1.isDotTemplateInstanceExp()) { if (dti.ti.tempdecl) if (auto td = dti.ti.tempdecl.isTemplateDeclaration()) @@ -1437,7 +1526,7 @@ Expression resolvePropertiesOnly(Scope* sc, Expression e1) } else if (auto dte = e1.isDotTemplateExp()) return handleTemplateDecl(dte.td); - else if (auto se = e1.isScopeExp()) + if (auto se = e1.isScopeExp()) { Dsymbol s = se.sds; TemplateInstance ti = s.isTemplateInstance(); @@ -1447,7 +1536,7 @@ Expression resolvePropertiesOnly(Scope* sc, Expression e1) } else if (auto et = e1.isTemplateExp()) return handleTemplateDecl(et.td); - else if (e1.isDotVarExp() && e1.type.isTypeFunction()) + if (e1.isDotVarExp() && e1.type.isTypeFunction()) { DotVarExp dve = e1.isDotVarExp(); return handleFuncDecl(dve.var.isFuncDeclaration()); @@ -1470,7 +1559,7 @@ Expression resolvePropertiesOnly(Scope* sc, Expression e1) * Returns: * `s` turned into an expression, `ErrorExp` if an error occurred */ -Expression symbolToExp(Dsymbol s, const ref Loc loc, Scope *sc, bool hasOverloads) +Expression symbolToExp(Dsymbol s, Loc loc, Scope* sc, bool hasOverloads) { static if (LOGSEMANTIC) { @@ -1515,8 +1604,8 @@ Lagain: { if (sd.isSystem()) { - if (sc.setUnsafePreview(global.params.systemVariables, false, loc, - "cannot access `@system` variable `%s` in @safe code", sd)) + if (sc.setUnsafePreview(sc.previews.systemVariables, false, loc, + "access `@system` variable `%s`", sd)) { if (auto v = sd.isVarDeclaration()) { @@ -1699,7 +1788,7 @@ Lagain: * Returns: * Expression representing the `this` for the var */ -private Expression getRightThis(const ref Loc loc, Scope* sc, AggregateDeclaration ad, Expression e1, Dsymbol var, int flag = 0) +private Expression getRightThis(Loc loc, Scope* sc, AggregateDeclaration ad, Expression e1, Dsymbol var, int flag = 0) { //printf("\ngetRightThis(e1 = %s, ad = %s, var = %s)\n", e1.toChars(), ad.toChars(), var.toChars()); L1: @@ -1866,7 +1955,7 @@ private bool haveSameThis(FuncDeclaration outerFunc, FuncDeclaration calledFunc) * we can only call other pure functions. * Returns true if error occurs. */ -private bool checkPurity(FuncDeclaration f, const ref Loc loc, Scope* sc) +private bool checkPurity(FuncDeclaration f, Loc loc, Scope* sc) { if (!sc.func) return false; @@ -1874,7 +1963,7 @@ private bool checkPurity(FuncDeclaration f, const ref Loc loc, Scope* sc) return false; if (sc.intypeof == 1) return false; - if (sc.flags & (SCOPE.ctfe | SCOPE.debug_)) + if (sc.ctfe || sc.debug_) return false; // If the call has a pure parent, then the called func must be pure. @@ -1885,7 +1974,7 @@ private bool checkPurity(FuncDeclaration f, const ref Loc loc, Scope* sc) f.toPrettyChars()); if (!f.isDtorDeclaration()) - errorSupplementalInferredAttr(f, /*max depth*/ 10, /*deprecation*/ false, STC.pure_); + errorSupplementalInferredAttr(f, /*max depth*/ 10, /*deprecation*/ false, STC.pure_, global.errorSink); f.checkOverriddenDtor(sc, loc, dd => dd.type.toTypeFunction().purity != PURE.impure, "impure"); return true; @@ -1907,7 +1996,7 @@ private bool checkPurity(FuncDeclaration f, const ref Loc loc, Scope* sc) * check = current check (e.g. whether it's pure) * checkName = the kind of check (e.g. `"pure"`) */ -void checkOverriddenDtor(FuncDeclaration f, Scope* sc, const ref Loc loc, +void checkOverriddenDtor(FuncDeclaration f, Scope* sc, Loc loc, scope bool function(DtorDeclaration) check, const string checkName) { auto dd = f.isDtorDeclaration(); @@ -1968,12 +2057,67 @@ void checkOverriddenDtor(FuncDeclaration f, Scope* sc, const ref Loc loc, } } +/******************************************** + * Print the reason why `fd` was inferred `@system` as a supplemental error + * Params: + * fd = function to check + * maxDepth = up to how many functions deep to report errors + * deprecation = print deprecations instead of errors + * stc = storage class of attribute to check + * eSink = where the error messages go + */ +public void errorSupplementalInferredAttr(FuncDeclaration fd, int maxDepth, bool deprecation, STC stc, ErrorSink eSink) +{ + auto errorFunc = deprecation ? &eSink.deprecationSupplemental : &eSink.errorSupplemental; + + AttributeViolation* s; + string attr; + if (stc & STC.safe) + { + s = fd.safetyViolation; + attr = "@safe"; + } + else if (stc & STC.pure_) + { + s = fd.pureViolation; + attr = "pure"; + } + else if (stc & STC.nothrow_) + { + s = fd.nothrowViolation; + attr = "nothrow"; + } + else if (stc & STC.nogc) + { + s = fd.nogcViolation; + attr = "@nogc"; + } + + if (!s) + return; + + if (s.action.length > 0) + { + errorFunc(s.loc, "and %.*s makes it fail to infer `%.*s`", s.action.fTuple.expand, attr.fTuple.expand); + } + else if (s.fd) + { + if (maxDepth > 0) + { + errorFunc(s.loc, "which calls `%s`", s.fd.toErrMsg()); + errorSupplementalInferredAttr(s.fd, maxDepth - 1, deprecation, stc, eSink); + } + } + else + assert(0); +} + /******************************************* * Accessing variable v. * Check for purity and safety violations. * Returns true if error occurs. */ -private bool checkPurity(VarDeclaration v, const ref Loc loc, Scope* sc) +private bool checkPurity(VarDeclaration v, Loc loc, Scope* sc) { //printf("v = %s %s\n", v.type.toChars(), v.toChars()); /* Look for purity and safety violations when accessing variable v @@ -1983,7 +2127,7 @@ private bool checkPurity(VarDeclaration v, const ref Loc loc, Scope* sc) return false; if (sc.intypeof == 1) return false; // allow violations inside typeof(expression) - if (sc.flags & (SCOPE.ctfe | SCOPE.debug_)) + if (sc.ctfe || sc.debug_) return false; // allow violations inside compile-time evaluated expressions and debug conditionals if (v.ident == Id.ctfe) return false; // magic variable never violates pure and safe @@ -2018,7 +2162,7 @@ private bool checkPurity(VarDeclaration v, const ref Loc loc, Scope* sc) if (v.ident == Id.gate) return false; - if (checkImpure(sc, loc, "`pure` %s `%s` cannot access mutable static data `%s`", v)) + if (checkImpure(sc, loc, "accessing mutable static data `%s`", v)) { error(loc, "`pure` %s `%s` cannot access mutable static data `%s`", sc.func.kind(), sc.func.toPrettyChars(), v.toChars()); @@ -2056,23 +2200,21 @@ private bool checkPurity(VarDeclaration v, const ref Loc loc, Scope* sc) FuncDeclaration ff = s.isFuncDeclaration(); if (!ff) break; - if (ff.isNested() || ff.isThis()) - { - if (ff.type.isImmutable() || - ff.type.isShared() && !MODimplicitConv(ff.type.mod, v.type.mod)) - { - OutBuffer ffbuf; - OutBuffer vbuf; - MODMatchToBuffer(&ffbuf, ff.type.mod, v.type.mod); - MODMatchToBuffer(&vbuf, v.type.mod, ff.type.mod); - error(loc, "%s%s `%s` cannot access %sdata `%s`", - ffbuf.peekChars(), ff.kind(), ff.toPrettyChars(), vbuf.peekChars(), v.toChars()); - err = true; - break; - } - continue; + if (!ff.isNested() && !ff.isThis()) + break; + if (ff.type.isImmutable() || + ff.type.isShared() && !MODimplicitConv(ff.type.mod, v.type.mod)) + { + OutBuffer ffbuf; + OutBuffer vbuf; + MODMatchToBuffer(&ffbuf, ff.type.mod, v.type.mod); + MODMatchToBuffer(&vbuf, v.type.mod, ff.type.mod); + error(loc, "%s%s `%s` cannot access %sdata `%s`", + ffbuf.peekChars(), ff.kind(), ff.toPrettyChars(), vbuf.peekChars(), v.toChars()); + err = true; + break; } - break; + continue; } } @@ -2080,8 +2222,7 @@ private bool checkPurity(VarDeclaration v, const ref Loc loc, Scope* sc) */ if (v.storage_class & STC.gshared) { - if (sc.setUnsafe(false, loc, - "`@safe` function `%s` cannot access `__gshared` data `%s`", sc.func, v)) + if (sc.setUnsafe(false, loc, "accessing `__gshared` data `%s`", v)) { err = true; } @@ -2113,9 +2254,9 @@ private bool checkSafety(FuncDeclaration f, ref Loc loc, Scope* sc) return false; if (sc.intypeof == 1) return false; - if (sc.flags & SCOPE.debug_) + if (sc.debug_) return false; - if ((sc.flags & SCOPE.ctfe) && sc.func) + if (sc.ctfe && sc.func) return false; if (!sc.func) @@ -2149,7 +2290,7 @@ private bool checkSafety(FuncDeclaration f, ref Loc loc, Scope* sc) sc.func.kind(), sc.func.toPrettyChars(), f.kind(), prettyChars); if (!f.isDtorDeclaration) - errorSupplementalInferredAttr(f, /*max depth*/ 10, /*deprecation*/ false, STC.safe); + errorSupplementalInferredAttr(f, /*max depth*/ 10, /*deprecation*/ false, STC.safe, global.errorSink); .errorSupplemental(f.loc, "`%s` is declared here", prettyChars); f.checkOverriddenDtor(sc, loc, dd => dd.type.toTypeFunction().trust > TRUST.system, "@system"); @@ -2163,12 +2304,12 @@ private bool checkSafety(FuncDeclaration f, ref Loc loc, Scope* sc) if (sc.func.isSafeBypassingInference()) { .deprecation(loc, "`@safe` function `%s` calling `%s`", sc.func.toChars(), f.toChars()); - errorSupplementalInferredAttr(f, 10, true, STC.safe); + errorSupplementalInferredAttr(f, 10, true, STC.safe, global.errorSink); } else if (!sc.func.safetyViolation) { import dmd.func : AttributeViolation; - sc.func.safetyViolation = new AttributeViolation(loc, null, f, null, null); + sc.func.safetyViolation = new AttributeViolation(loc, f); } } return false; @@ -2188,7 +2329,7 @@ private bool checkNogc(FuncDeclaration f, ref Loc loc, Scope* sc) return false; if (sc.intypeof == 1) return false; - if (sc.flags & (SCOPE.ctfe | SCOPE.debug_)) + if (sc.ctfe || sc.debug_) return false; /* The original expressions (`new S(...)` or `new S[...]``) will be * verified instead. This is to keep errors related to the original code @@ -2197,70 +2338,76 @@ private bool checkNogc(FuncDeclaration f, ref Loc loc, Scope* sc) if (f.ident == Id._d_newitemT || f.ident == Id._d_newarrayT || f.ident == Id._d_newarraymTX) return false; - if (!f.isNogc()) - { - if (isRootTraitsCompilesScope(sc) ? sc.func.isNogcBypassingInference() : sc.func.setGCCall(f)) - { - if (loc.linnum == 0) // e.g. implicitly generated dtor - loc = sc.func.loc; + if (f.isNogc()) + return false; - // Lowered non-@nogc'd hooks will print their own error message inside of nogc.d (NOGCVisitor.visit(CallExp e)), - // so don't print anything to avoid double error messages. - if (!(f.ident == Id._d_HookTraceImpl || f.ident == Id._d_arraysetlengthT - || f.ident == Id._d_arrayappendT || f.ident == Id._d_arrayappendcTX - || f.ident == Id._d_arraycatnTX || f.ident == Id._d_newclassT)) - { - error(loc, "`@nogc` %s `%s` cannot call non-@nogc %s `%s`", - sc.func.kind(), sc.func.toPrettyChars(), f.kind(), f.toPrettyChars()); + if (isRootTraitsCompilesScope(sc) ? !sc.func.isNogcBypassingInference() : !sc.func.setGCCall(f)) + return false; - if (!f.isDtorDeclaration) - f.errorSupplementalInferredAttr(/*max depth*/ 10, /*deprecation*/ false, STC.nogc); - } + if (loc == Loc.initial) // e.g. implicitly generated dtor + loc = sc.func.loc; - f.checkOverriddenDtor(sc, loc, dd => dd.type.toTypeFunction().isnogc, "non-@nogc"); + // Lowered non-@nogc'd hooks will print their own error message inside of nogc.d (NOGCVisitor.visit(CallExp e)), + // so don't print anything to avoid double error messages. + if (!(f.ident == Id._d_HookTraceImpl || f.ident == Id._d_arraysetlengthT + || f.ident == Id._d_arrayappendT || f.ident == Id._d_arrayappendcTX + || f.ident == Id._d_arraycatnTX || f.ident == Id._d_newclassT)) + { + error(loc, "`@nogc` %s `%s` cannot call non-@nogc %s `%s`", + sc.func.kind(), sc.func.toPrettyChars(), f.kind(), f.toPrettyChars()); - return true; - } + if (!f.isDtorDeclaration) + f.errorSupplementalInferredAttr(/*max depth*/ 10, /*deprecation*/ false, STC.nogc, global.errorSink); } - return false; + + f.checkOverriddenDtor(sc, loc, dd => dd.type.toTypeFunction().isNogc, "non-@nogc"); + + return true; } /******************************************** - * Check that the postblit is callable if t is an array of structs. - * Returns true if error happens. + * Check that the postblit of `t` isn't @disabled and has the right + * function attributes for this scope. + * + * Params: + * t = struct type, or static array of struct type to check + * loc = error message location + * sc = scope in which attributes are checked + * Returns: true if there's an error */ private bool checkPostblit(Type t, ref Loc loc, Scope* sc) { - if (auto ts = t.baseElemOf().isTypeStruct()) + auto ts = t.baseElemOf().isTypeStruct(); + if (!ts) + return false; + + if (global.params.useTypeInfo && Type.dtypeinfo) { - if (global.params.useTypeInfo && Type.dtypeinfo) - { - // https://issues.dlang.org/show_bug.cgi?id=11395 - // Require TypeInfo generation for array concatenation - semanticTypeInfo(sc, t); - } + // https://issues.dlang.org/show_bug.cgi?id=11395 + // Require TypeInfo generation for array concatenation + semanticTypeInfo(sc, t); + } - StructDeclaration sd = ts.sym; - if (sd.postblit) - { - if (sd.postblit.checkDisabled(loc, sc)) - return true; + StructDeclaration sd = ts.sym; + if (!sd.postblit) + return false; - //checkDeprecated(sc, sd.postblit); // necessary? - sd.postblit.checkPurity(loc, sc); - sd.postblit.checkSafety(loc, sc); - sd.postblit.checkNogc(loc, sc); - //checkAccess(sd, loc, sc, sd.postblit); // necessary? - return false; - } - } - return false; + if (sd.postblit.checkDisabled(loc, sc)) + return true; + + //checkDeprecated(sc, sd.postblit); // necessary? + //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.checkNogc(loc, sc); + return result; } /*************************************** * Pull out any properties. */ -private Expression resolvePropertiesX(Scope* sc, Expression e1, Expression e2 = null, BinExp saveAtts = null) +private Expression resolvePropertiesX(Scope* sc, Expression e1, Expression e2 = null, Type[2]* aliasThisStop = null) { //printf("resolvePropertiesX, e1 = %s %s, e2 = %s\n", EXPtoString(e1.op).ptr, e1.toChars(), e2 ? e2.toChars() : null); Loc loc = e1.loc; @@ -2313,37 +2460,33 @@ private Expression resolvePropertiesX(Scope* sc, Expression e1, Expression e2 = return e.expressionSemantic(sc); } } + for (size_t i = 0; i < os.a.length; i++) { - for (size_t i = 0; i < os.a.length; i++) + FuncDeclaration f = resolveFuncCall(loc, sc, os.a[i], tiargs, tthis, ArgumentList(), FuncResolveFlag.quiet); + if (!f) + continue; + if (f.errors) + return ErrorExp.get(); + fd = f; + assert(fd.type.ty == Tfunction); + auto tf = fd.type.isTypeFunction(); + if (!tf.isRef && e2) { - if (FuncDeclaration f = resolveFuncCall(loc, sc, os.a[i], tiargs, tthis, ArgumentList(), FuncResolveFlag.quiet)) - { - if (f.errors) - return ErrorExp.get(); - fd = f; - assert(fd.type.ty == Tfunction); - auto tf = fd.type.isTypeFunction(); - if (!tf.isref && e2) - { - error(loc, "%s is not an lvalue", e1.toChars()); - return ErrorExp.get(); - } - } + error(loc, "%s is not an lvalue", e1.toErrMsg()); + return ErrorExp.get(); } - if (fd) + } + if (fd) + { + Expression e = new CallExp(loc, e1); + if (e2) { - Expression e = new CallExp(loc, e1); - if (e2) - { - e = new AssignExp(loc, e, e2); - if (saveAtts) - { - (cast(BinExp)e).att1 = saveAtts.att1; - (cast(BinExp)e).att2 = saveAtts.att2; - } - } + e = new AssignExp(loc, e, e2); + if (aliasThisStop) + return e.expressionSemantic(sc, *aliasThisStop); return e.expressionSemantic(sc); } + return e.expressionSemantic(sc); } if (e2) goto Leprop; @@ -2400,7 +2543,7 @@ private Expression resolvePropertiesX(Scope* sc, Expression e1, Expression e2 = tthis = dve.e1.type; goto Lfd; } - else if (sc && sc.flags & SCOPE.Cfile && e1.isVarExp() && !e2) + else if (sc && sc.inCfile && e1.isVarExp() && !e2) { // ImportC: do not implicitly call function if no ( ) are present } @@ -2431,33 +2574,28 @@ private Expression resolvePropertiesX(Scope* sc, Expression e1, Expression e2 = return e.expressionSemantic(sc); } } + FuncDeclaration fd = resolveFuncCall(loc, sc, s, tiargs, tthis, ArgumentList(), FuncResolveFlag.quiet); + if (fd && fd.type) { - FuncDeclaration fd = resolveFuncCall(loc, sc, s, tiargs, tthis, ArgumentList(), FuncResolveFlag.quiet); - if (fd && fd.type) + if (fd.errors) + return ErrorExp.get(); + TypeFunction tf = fd.type.isTypeFunction(); + if (!e2 || tf.isRef) { - if (fd.errors) - return ErrorExp.get(); - TypeFunction tf = fd.type.isTypeFunction(); - if (!e2 || tf.isref) + Expression e = new CallExp(loc, e1); + if (e2) { - Expression e = new CallExp(loc, e1); - if (e2) - { - e = new AssignExp(loc, e, e2); - if (saveAtts) - { - (cast(BinExp)e).att1 = saveAtts.att1; - (cast(BinExp)e).att2 = saveAtts.att2; - } - } - return e.expressionSemantic(sc); + e = new AssignExp(loc, e, e2); + if (aliasThisStop) + return e.expressionSemantic(sc, *aliasThisStop); } + return e.expressionSemantic(sc); } } - if (FuncDeclaration fd = s.isFuncDeclaration()) + if (FuncDeclaration fd2 = s.isFuncDeclaration()) { // Keep better diagnostic message for invalid property usage of functions - assert(fd.type.ty == Tfunction); + assert(fd2.type.ty == Tfunction); Expression e = new CallExp(loc, e1, e2); return e.expressionSemantic(sc); } @@ -2502,13 +2640,13 @@ private Expression resolvePropertiesX(Scope* sc, Expression e1, Expression e2 = if (!e1.type) { - error(loc, "cannot resolve type for %s", e1.toChars()); + error(loc, "cannot resolve type for %s", e1.toErrMsg()); e1 = ErrorExp.get(); } return e1; Leprop: - error(loc, "not a property %s", e1.toChars()); + error(loc, "not a property %s", e1.toErrMsg()); return ErrorExp.get(); } @@ -2516,24 +2654,23 @@ private bool checkRightThis(Expression e, Scope* sc) { if (e.op == EXP.error) return true; - if (e.op == EXP.variable && e.type.ty != Terror) - { - VarExp ve = cast(VarExp)e; - if (isNeedThisScope(sc, ve.var)) - { - //printf("checkRightThis sc.intypeof = %d, ad = %p, func = %p, fdthis = %p\n", - // sc.intypeof, sc.getStructClassScope(), func, fdthis); - auto t = ve.var.isThis(); - assert(t); - error(e.loc, "accessing non-static variable `%s` requires an instance of `%s`", ve.var.toChars(), t.toChars()); - return true; - } - } - return false; -} + if (e.op != EXP.variable || e.type.ty == Terror) + return false; -Expression resolveProperties(Scope* sc, Expression e) -{ + VarExp ve = cast(VarExp)e; + if (!isNeedThisScope(sc, ve.var)) + return false; + + //printf("checkRightThis sc.intypeof = %d, ad = %p, func = %p, fdthis = %p\n", + // sc.intypeof, sc.getStructClassScope(), func, fdthis); + auto t = ve.var.isThis(); + assert(t); + error(e.loc, "accessing non-static variable `%s` requires an instance of `%s`", ve.var.toChars(), t.toChars()); + return true; +} + +Expression resolveProperties(Scope* sc, Expression e) +{ //printf("resolveProperties(%s)\n", e.toChars()); e = resolvePropertiesX(sc, e); if (e.checkRightThis(sc)) @@ -2573,7 +2710,7 @@ private Type arrayExpressionToCommonType(Scope* sc, ref Expressions exps) e = resolveProperties(sc, e); if (!e.type) { - error(e.loc, "`%s` has no value", e.toChars()); + error(e.loc, "`%s` has no value", e.toErrMsg()); t0 = Type.terror; continue; } @@ -2596,7 +2733,7 @@ private Type arrayExpressionToCommonType(Scope* sc, ref Expressions exps) continue; } - e = doCopyOrMove(sc, e); + e = doCopyOrMove(sc, e, null, false); if (!foundType && t0 && !t0.equals(e.type)) { @@ -2652,7 +2789,7 @@ private Type arrayExpressionToCommonType(Scope* sc, ref Expressions exps) return t0; } -private Expression opAssignToOp(const ref Loc loc, EXP op, Expression e1, Expression e2) @safe +private Expression opAssignToOp(Loc loc, EXP op, Expression e1, Expression e2) @safe { Expression e; switch (op) @@ -2742,56 +2879,57 @@ private Expression rewriteOpAssign(BinExp exp) * Params: * sc = scope * argumentList = arguments to function - * reportErrors = whether or not to report errors here. Some callers are not + * eSink = if not null, used to report errors. Some callers are not * checking actual function params, so they'll do their own error reporting * Returns: * `true` when a semantic error occurred */ -private bool preFunctionParameters(Scope* sc, ArgumentList argumentList, const bool reportErrors = true) +private bool preFunctionParameters(Scope* sc, ArgumentList argumentList, ErrorSink eSink) { Expressions* exps = argumentList.arguments; + if (!exps) + return false; + + expandTuples(exps, argumentList.names); + bool err = false; - if (exps) + for (size_t i = 0; i < exps.length; i++) { - expandTuples(exps, argumentList.names); - - for (size_t i = 0; i < exps.length; i++) + Expression arg = (*exps)[i]; + arg = resolveProperties(sc, arg); + arg = arg.arrayFuncConv(sc); + if (arg.op == EXP.type) { - Expression arg = (*exps)[i]; - arg = resolveProperties(sc, arg); - arg = arg.arrayFuncConv(sc); - if (arg.op == EXP.type) - { - // for static alias this: https://issues.dlang.org/show_bug.cgi?id=17684 - arg = resolveAliasThis(sc, arg); + // for static alias this: https://issues.dlang.org/show_bug.cgi?id=17684 + arg = resolveAliasThis(sc, arg); - if (arg.op == EXP.type) - { - if (reportErrors) - { - error(arg.loc, "cannot pass type `%s` as a function argument", arg.toChars()); - arg = ErrorExp.get(); - } - err = true; - } - } - else if (arg.type.toBasetype().ty == Tfunction) + if (arg.op == EXP.type) { - if (reportErrors) + if (eSink) { - error(arg.loc, "cannot pass function `%s` as a function argument", arg.toChars()); + eSink.error(arg.loc, "cannot pass type `%s` as a function argument", arg.toErrMsg()); arg = ErrorExp.get(); } err = true; } - else if (checkNonAssignmentArrayOp(arg)) + } + else if (arg.type.toBasetype().ty == Tfunction) + { + if (eSink) { + eSink.error(arg.loc, "cannot pass function `%s` as a function argument", arg.toErrMsg()); arg = ErrorExp.get(); - err = true; } - (*exps)[i] = arg; + err = true; + } + else if (checkNonAssignmentArrayOp(arg)) + { + arg = ErrorExp.get(); + err = true; } + (*exps)[i] = arg; } + return err; } @@ -2836,12 +2974,12 @@ private bool checkDefCtor(Loc loc, Type t) * Returns: * true errors happened */ -private bool functionParameters(const ref Loc loc, Scope* sc, +private bool functionParameters(Loc loc, Scope* sc, TypeFunction tf, Expression ethis, Type tthis, ArgumentList argumentList, FuncDeclaration fd, Type* prettype, Expression* peprefix) { Expressions* arguments = argumentList.arguments; - //printf("functionParameters() %s\n", fd ? fd.toChars() : ""); + //printf("functionParameters() fd: %s tf: %s\n", fd ? fd.ident.toChars() : "", toChars(tf)); assert(arguments); assert(fd || tf.next); const size_t nparams = tf.parameterList.length; @@ -2852,14 +2990,14 @@ private bool functionParameters(const ref Loc loc, Scope* sc, if (argumentList.names) { - const(char)* msg = null; - auto resolvedArgs = tf.resolveNamedArgs(argumentList, &msg); + OutBuffer buf; + auto resolvedArgs = tf.resolveNamedArgs(argumentList, &buf); if (!resolvedArgs) { // while errors are usually already caught by `tf.callMatch`, // this can happen when calling `typeof(freefunc)` - if (msg) - error(loc, "%s", msg); + if (buf.length) + error(loc, "%s", buf.peekChars()); return true; } // note: the argument list should be mutated with named arguments / default arguments, @@ -2916,149 +3054,143 @@ private bool functionParameters(const ref Loc loc, Scope* sc, { Expression arg = (i < nargs) ? (*arguments)[i] : null; - if (i < nparams) + if (i >= nparams) + break; + + bool errorArgs() { - bool errorArgs() - { - error(loc, "expected %llu function arguments, not %llu", cast(ulong)nparams, cast(ulong)nargs); - return true; - } + error(loc, "expected %llu function arguments, not %llu", cast(ulong)nparams, cast(ulong)nargs); + return true; + } - Parameter p = tf.parameterList[i]; + Parameter p = tf.parameterList[i]; - if (!arg) + if (!arg) + { + if (!p.defaultArg) { - if (!p.defaultArg) - { - if (tf.parameterList.varargs == VarArg.typesafe && i + 1 == nparams) - goto L2; - return errorArgs(); - } - arg = p.defaultArg; - if (!arg.type) - arg = arg.expressionSemantic(sc); - arg = inlineCopy(arg, sc); - // __FILE__, __LINE__, __MODULE__, __FUNCTION__, and __PRETTY_FUNCTION__ - arg = arg.resolveLoc(loc, sc); - if (i >= nargs) - { - arguments.push(arg); - nargs++; - } - else - (*arguments)[i] = arg; + if (tf.parameterList.varargs == VarArg.typesafe && i + 1 == nparams) + goto L2; + return errorArgs(); } - else + arg = p.defaultArg; + if (!arg.type) + arg = arg.expressionSemantic(sc); + arg = inlineCopy(arg, sc); + // __FILE__, __LINE__, __MODULE__, __FUNCTION__, and __PRETTY_FUNCTION__ + arg = arg.resolveLoc(loc, sc); + if (i >= nargs) { - if (arg.isDefaultInitExp()) - { - arg = arg.resolveLoc(loc, sc); - (*arguments)[i] = arg; - } + arguments.push(arg); + nargs++; } + else + (*arguments)[i] = arg; + } + else if (arg.isDefaultInitExp()) + { + arg = arg.resolveLoc(loc, sc); + (*arguments)[i] = arg; + } - - if (tf.parameterList.varargs == VarArg.typesafe && i + 1 == nparams) // https://dlang.org/spec/function.html#variadic + if (tf.parameterList.varargs == VarArg.typesafe && i + 1 == nparams) // https://dlang.org/spec/function.html#variadic + { + //printf("\t\tvarargs == 2, p.type = '%s'\n", p.type.toChars()); + if (MATCH m = arg.implicitConvTo(p.type)) { - //printf("\t\tvarargs == 2, p.type = '%s'\n", p.type.toChars()); - { - MATCH m; - if ((m = arg.implicitConvTo(p.type)) > MATCH.nomatch) - { - if (p.type.nextOf() && arg.implicitConvTo(p.type.nextOf()) >= m) - goto L2; - else if (nargs != nparams) - return errorArgs(); - goto L1; - } - } - L2: - Type tb = p.type.toBasetype(); - switch (tb.ty) + if (p.type.nextOf() && arg.implicitConvTo(p.type.nextOf()) >= m) + goto L2; + else if (nargs != nparams) + return errorArgs(); + goto L1; + } + L2: + Type tb = p.type.toBasetype(); + switch (tb.ty) + { + case Tsarray: + case Tarray: { - case Tsarray: - case Tarray: - { - /* Create a static array variable v of type arg.type: - * T[dim] __arrayArg = [ arguments[i], ..., arguments[nargs-1] ]; - * - * The array literal in the initializer of the hidden variable - * is now optimized. - * https://issues.dlang.org/show_bug.cgi?id=2356 - */ - Type tbn = (cast(TypeArray)tb).next; // array element type - Type tret = p.isLazyArray(); + /* Create a static array variable v of type arg.type: + * T[dim] __arrayArg = [ arguments[i], ..., arguments[nargs-1] ]; + * + * The array literal in the initializer of the hidden variable + * is now optimized. + * https://issues.dlang.org/show_bug.cgi?id=2356 + */ + Type tbn = (cast(TypeArray)tb).next; // array element type + Type tret = p.isLazyArray(); - auto elements = new Expressions(nargs - i); - foreach (u; 0 .. elements.length) - { - Expression a = (*arguments)[i + u]; - assert(a); - if (tret && a.implicitConvTo(tret)) - { - // p is a lazy array of delegates, tret is return type of the delegates - a = a.implicitCastTo(sc, tret) - .optimize(WANTvalue) - .toDelegate(tret, sc); - } - else - a = a.implicitCastTo(sc, tbn); - a = a.addDtorHook(sc); - (*elements)[u] = a; - } - // https://issues.dlang.org/show_bug.cgi?id=14395 - // Convert to a static array literal, or its slice. - arg = new ArrayLiteralExp(loc, tbn.sarrayOf(nargs - i), elements); - if (tb.ty == Tarray) + auto elements = new Expressions(nargs - i); + foreach (u; 0 .. elements.length) + { + Expression a = (*arguments)[i + u]; + assert(a); + if (tret && a.implicitConvTo(tret)) { - arg = new SliceExp(loc, arg, null, null); - arg.type = p.type; + // p is a lazy array of delegates, tret is return type of the delegates + a = a.implicitCastTo(sc, tret) + .optimize(WANTvalue) + .toDelegate(tret, sc); } - break; - } - case Tclass: - { - /* Set arg to be: - * new Tclass(arg0, arg1, ..., argn) - */ - auto args = new Expressions(nargs - i); - foreach (u; i .. nargs) - (*args)[u - i] = (*arguments)[u]; - arg = new NewExp(loc, null, p.type, args); - break; + else + a = a.implicitCastTo(sc, tbn); + a = a.addDtorHook(sc); + (*elements)[u] = a; } - default: - if (!arg) + // https://issues.dlang.org/show_bug.cgi?id=14395 + // Convert to a static array literal, or its slice. + arg = new ArrayLiteralExp(loc, tbn.sarrayOf(nargs - i), elements); + if (tb.ty == Tarray) { - error(loc, "not enough arguments"); - return true; + arg = new SliceExp(loc, arg, null, null); + arg.type = p.type; } break; } - arg = arg.expressionSemantic(sc); - //printf("\targ = '%s'\n", arg.toChars()); - arguments.setDim(i + 1); - (*arguments)[i] = arg; - nargs = i + 1; - done = true; + case Tclass: + { + /* Set arg to be: + * new Tclass(arg0, arg1, ..., argn) + */ + auto args = new Expressions(nargs - i); + foreach (u; i .. nargs) + (*args)[u - i] = (*arguments)[u]; + arg = new NewExp(loc, null, null, p.type, args); + break; + } + default: + if (!arg) + { + error(loc, "not enough arguments"); + return true; + } + break; } + arg = arg.expressionSemantic(sc); + //printf("\targ = '%s'\n", arg.toChars()); + arguments.setDim(i + 1); + (*arguments)[i] = arg; + nargs = i + 1; + done = true; + } - L1: - if (!(p.isLazy() && p.type.ty == Tvoid)) + L1: + if (!(p.isLazy() && p.type.ty == Tvoid)) + { + if (ubyte wm = arg.type.deduceWild(p.type, p.isReference())) { - if (ubyte wm = arg.type.deduceWild(p.type, p.isReference())) - { - wildmatch = wildmatch ? MODmerge(wildmatch, wm) : wm; - //printf("[%d] p = %s, a = %s, wm = %d, wildmatch = %d\n", i, p.type.toChars(), arg.type.toChars(), wm, wildmatch); - } + wildmatch = wildmatch ? MODmerge(wildmatch, wm) : wm; + //printf("[%d] p = %s, a = %s, wm = %d, wildmatch = %d\n", i, p.type.toChars(), arg.type.toChars(), wm, wildmatch); } } + if (done) break; } if ((wildmatch == MODFlags.mutable || wildmatch == MODFlags.immutable_) && tf.next && tf.next.hasWild() && - (tf.isref || !tf.next.implicitConvTo(tf.next.immutableOf()))) + (tf.isRef || !tf.next.implicitConvTo(tf.next.immutableOf()))) { bool errorInout(MOD wildmatch) { @@ -3181,13 +3313,13 @@ private bool functionParameters(const ref Loc loc, Scope* sc, } else if (p.storageClass & STC.ref_) { - if (global.params.rvalueRefParam == FeatureState.enabled && + if (sc.previews.rvalueRefParam && !arg.isLvalue() && targ.isCopyable()) { /* allow rvalues to be passed to ref parameters by copying * them to a temp, then pass the temp as the argument */ - auto v = copyToTemp(0, "__rvalue", arg); + auto v = copyToTemp(STC.none, "__rvalue", arg); Expression ev = new DeclarationExp(arg.loc, v); ev = new CommaExp(arg.loc, ev, new VarExp(arg.loc, v)); arg = ev.expressionSemantic(sc); @@ -3269,7 +3401,7 @@ private bool functionParameters(const ref Loc loc, Scope* sc, { // allocate the array literal as temporary static array on the stack ale.type = ale.type.nextOf().sarrayOf(ale.elements.length); - auto tmp = copyToTemp(0, "__arrayliteral_on_stack", ale); + auto tmp = copyToTemp(STC.none, "__arrayliteral_on_stack", ale); tmp.storage_class |= STC.exptemp; auto declareTmp = new DeclarationExp(ale.loc, tmp); auto castToSlice = new CastExp(ale.loc, new VarExp(ale.loc, tmp), @@ -3383,7 +3515,7 @@ private bool functionParameters(const ref Loc loc, Scope* sc, { if (se.hasOverloads && !se.var.isFuncDeclaration().isUnique()) { - error(arg.loc, "function `%s` is overloaded", arg.toChars()); + error(arg.loc, "function `%s` is overloaded", arg.toErrMsg()); err = true; } } @@ -3577,7 +3709,7 @@ private bool functionParameters(const ref Loc loc, Scope* sc, */ Type tv = arg.type.baseElemOf(); if (!isRef && tv.ty == Tstruct) - arg = doCopyOrMove(sc, arg, parameter ? parameter.type : null); + arg = doCopyOrMove(sc, arg, parameter ? parameter.type : null, false); } (*arguments)[i] = arg; @@ -3587,8 +3719,8 @@ private bool functionParameters(const ref Loc loc, Scope* sc, /* Test compliance with DIP1021 Argument Ownership and Function Calls */ - if (global.params.useDIP1021 && (tf.trust == TRUST.safe || tf.trust == TRUST.default_) || - tf.islive) + if (sc.previews.dip1021 && (tf.trust == TRUST.safe || tf.trust == TRUST.default_) || + tf.isLive) err |= checkMutableArguments(*sc, fd, tf, ethis, arguments, false); // If D linkage and variadic, add _arguments[] as first argument @@ -3688,6 +3820,13 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor Scope* sc; Expression result; + // For binary expressions, stores recursive 'alias this' types of lhs and rhs to prevent endless loops. + // See tryAliasThisSemantic + Type[2] aliasThisStop; + + // (Optional) the expression this was lowered from, for better error messages + Expression parent; + this(Scope* sc) scope @safe { this.sc = sc; @@ -3738,37 +3877,37 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { if (!e.type) e.type = Type.tfloat64; - else if (e.type.isimaginary && sc.flags & SCOPE.Cfile) + else if (!e.type.isImaginary || !sc.inCfile) { - /* Convert to core.stdc.config.complex - */ - Type t = getComplexLibraryType(e.loc, sc, e.type.ty); - if (t.ty == Terror) - return setError(); + e.type = e.type.typeSemantic(e.loc, sc); + result = e; + return; + } - 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); - } + /* Convert to core.stdc.config.complex + */ + Type t = getComplexLibraryType(e.loc, sc, e.type.ty); + if (t.ty == Terror) + return setError(); - /* 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); - return; + 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); } - else - e.type = e.type.typeSemantic(e.loc, sc); - result = e; + + /* 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); } override void visit(ComplexExp e) @@ -3786,11 +3925,8 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { printf("IdentifierExp::semantic('%s')\n", exp.ident.toChars()); } - if (exp.type) // This is used as the dummy expression - { - result = exp; - return; - } + + scope (exit) result.rvalue = exp.rvalue; Dsymbol scopesym; Dsymbol s = sc.search(exp.loc, exp.ident, scopesym); @@ -3873,10 +4009,9 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } } - if (global.params.fixAliasThis) + if (sc.previews.fixAliasThis) { - ExpressionDsymbol expDsym = scopesym.isExpressionDsymbol(); - if (expDsym) + if (ExpressionDsymbol expDsym = scopesym.isExpressionDsymbol()) { //printf("expDsym = %s\n", expDsym.exp.toChars()); result = expDsym.exp.expressionSemantic(sc); @@ -3890,7 +4025,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor return; } - if (!global.params.fixAliasThis && hasThis(sc)) + if (!sc.previews.fixAliasThis && hasThis(sc)) { for (AggregateDeclaration ad = sc.getStructClassScope(); ad;) { @@ -3920,7 +4055,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (exp.ident == Id.ctfe) { - if (sc.flags & SCOPE.ctfe) + if (sc.ctfe) { error(exp.loc, "variable `__ctfe` cannot be read at compile time"); return setError(); @@ -3948,35 +4083,37 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (!sc2.scopesym) continue; - if (auto ss = sc2.scopesym.isWithScopeSymbol()) + auto ss = sc2.scopesym.isWithScopeSymbol(); + if (!ss) + continue; + + if (ss.withstate.wthis) { - if (ss.withstate.wthis) + Expression e; + e = new VarExp(exp.loc, ss.withstate.wthis); + e = new DotIdExp(exp.loc, e, exp.ident); + e = e.trySemantic(sc); + if (e) { - Expression e; - e = new VarExp(exp.loc, ss.withstate.wthis); - e = new DotIdExp(exp.loc, e, exp.ident); - e = e.trySemantic(sc); - if (e) - { - result = e; - return; - } + result = e; + return; } - // Try Type.opDispatch (so the static version) - else if (ss.withstate.exp && ss.withstate.exp.op == EXP.type) + } + // Try Type.opDispatch (so the static version) + else if (ss.withstate.exp && ss.withstate.exp.op == EXP.type) + { + Type t = ss.withstate.exp.isTypeExp().type; + if (!t) + continue; + + Expression e; + e = new TypeExp(exp.loc, t); + e = new DotIdExp(exp.loc, e, exp.ident); + e = e.trySemantic(sc); + if (e) { - if (Type t = ss.withstate.exp.isTypeExp().type) - { - Expression e; - e = new TypeExp(exp.loc, t); - e = new DotIdExp(exp.loc, e, exp.ident); - e = e.trySemantic(sc); - if (e) - { - result = e; - return; - } - } + result = e; + return; } } } @@ -4008,11 +4145,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { printf("ThisExp::semantic()\n"); } - if (e.type) - { - result = e; - return; - } FuncDeclaration fd = hasThis(sc); // fd is the uplevel function with the 'this' variable AggregateDeclaration ad; @@ -4027,7 +4159,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { if (!s) { - error(e.loc, "`%s` is not in a class or struct scope", e.toChars()); + error(e.loc, "`%s` is not in a class or struct scope", e.toErrMsg()); return setError(); } ClassDeclaration cd = s.isClassDeclaration(); @@ -4073,16 +4205,16 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { printf("SuperExp::semantic('%s')\n", e.toChars()); } - if (e.type) - { - result = e; - return; - } FuncDeclaration fd = hasThis(sc); ClassDeclaration cd; Dsymbol s; + void err() + { + error(e.loc, "`super` is only allowed in non-static class member functions"); + result = ErrorExp.get(); + } /* Special case for typeof(this) and typeof(super) since both * should work even if they are not inside a non-static member function */ @@ -4093,26 +4225,26 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { if (!s) { - error(e.loc, "`%s` is not in a class scope", e.toChars()); + error(e.loc, "`%s` is not in a class scope", e.toErrMsg()); return setError(); } cd = s.isClassDeclaration(); - if (cd) + if (!cd) + continue; + + cd = cd.baseClass; + if (!cd) { - cd = cd.baseClass; - if (!cd) - { - error(e.loc, "class `%s` has no `super`", s.toChars()); - return setError(); - } - e.type = cd.type; - result = e; - return; + error(e.loc, "class `%s` has no `super`", s.toChars()); + return setError(); } + e.type = cd.type; + result = e; + return; } } if (!fd) - goto Lerr; + return err(); e.var = fd.vthis; assert(e.var && e.var.parent); @@ -4124,7 +4256,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor cd = s.isClassDeclaration(); //printf("parent is %s %s\n", fd.toParent().kind(), fd.toParent().toChars()); if (!cd) - goto Lerr; + return err(); if (!cd.baseClass) { error(e.loc, "no base class for `%s`", cd.toChars()); @@ -4140,11 +4272,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor return setError(); result = e; - return; - - Lerr: - error(e.loc, "`super` is only allowed in non-static class member functions"); - result = ErrorExp.get(); } override void visit(NullExp e) @@ -4154,11 +4281,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor printf("NullExp::semantic('%s')\n", e.toChars()); } // NULL is the same as (void *)0 - if (e.type) - { - result = e; - return; - } e.type = Type.tnull; result = e; } @@ -4247,11 +4369,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { printf("StringExp::semantic() %s\n", e.toChars()); } - if (e.type) - { - result = e; - return; - } OutBuffer buffer; size_t newlen = 0; @@ -4304,7 +4421,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } buffer.write4(0); e.setData(buffer.extractData(), newlen, 4); - if (sc && sc.flags & SCOPE.Cfile) + if (sc && sc.inCfile) e.type = Type.tuns32.sarrayOf(e.len + 1); else e.type = Type.tdchar.immutableOf().arrayOf(); @@ -4329,7 +4446,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } buffer.writeUTF16(0); e.setData(buffer.extractData(), newlen, 2); - if (sc && sc.flags & SCOPE.Cfile) + if (sc && sc.inCfile) e.type = Type.tuns16.sarrayOf(e.len + 1); else e.type = Type.twchar.immutableOf().arrayOf(); @@ -4341,7 +4458,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor goto default; default: - if (sc && sc.flags & SCOPE.Cfile) + if (sc && sc.inCfile) e.type = Type.tchar.sarrayOf(e.len + 1); else e.type = Type.tchar.immutableOf().arrayOf(); @@ -4360,11 +4477,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { printf("+TupleExp::semantic(%s)\n", exp.toChars()); } - if (exp.type) - { - result = exp; - return; - } if (exp.e0) exp.e0 = exp.e0.expressionSemantic(sc); @@ -4377,7 +4489,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor e = e.expressionSemantic(sc); if (!e.type) { - error(exp.loc, "`%s` has no value", e.toChars()); + error(exp.loc, "`%s` has no value", e.toErrMsg()); err = true; } else if (e.op == EXP.error) @@ -4402,11 +4514,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { printf("ArrayLiteralExp::semantic('%s')\n", e.toChars()); } - if (e.type) - { - result = e; - return; - } /* Perhaps an empty array literal [ ] should be rewritten as null? */ @@ -4433,7 +4540,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor */ if (e.elements.length > 0 && t0.ty == Tvoid) { - error(e.loc, "`%s` of type `%s` has no value", e.toChars(), e.type.toChars()); + error(e.loc, "`%s` of type `%s` has no value", e.toErrMsg(), e.type.toChars()); return setError(); } @@ -4449,11 +4556,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { printf("AssocArrayLiteralExp::semantic('%s')\n", e.toChars()); } - if (e.type) - { - result = e; - return; - } // Run semantic() on each element bool err_keys = arrayExpressionSemantic(e.keys.peekSlice(), sc); @@ -4492,11 +4594,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { printf("StructLiteralExp::semantic('%s')\n", e.toChars()); } - if (e.type) - { - result = e; - return; - } e.sd.size(e.loc); if (e.sd.sizeok != Sizeok.done) @@ -4540,7 +4637,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } Type t = cle.type.typeSemantic(cle.loc, sc); auto init = initializerSemantic(cle.initializer, sc, t, INITnointerpret); - auto e = initializerToExpression(init, t, (sc.flags & SCOPE.Cfile) != 0); + auto e = initializerToExpression(init, t, sc.inCfile); if (!e) { error(cle.loc, "cannot convert initializer `%s` to expression", toChars(init)); @@ -4601,11 +4698,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { printf("+ScopeExp::semantic(%p '%s')\n", exp, exp.toChars()); } - if (exp.type) - { - result = exp; - return; - } ScopeDsymbol sds2 = exp.sds; TemplateInstance ti = sds2.isTemplateInstance(); @@ -4772,14 +4864,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor id = new DotTemplateInstanceExp(ne.loc, id, hook, tiargs); auto arguments = new Expressions(); - if (global.params.tracegc) - { - auto funcname = (sc.callsc && sc.callsc.func) ? - sc.callsc.func.toPrettyChars() : sc.func.toPrettyChars(); - arguments.push(new StringExp(ne.loc, ne.loc.filename.toDString())); - arguments.push(new IntegerExp(ne.loc, ne.loc.linnum, Type.tint32)); - arguments.push(new StringExp(ne.loc, funcname.toDString())); - } id = new CallExp(ne.loc, id, arguments); ne.lowering = id.expressionSemantic(sc); @@ -4794,10 +4878,27 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor printf("\tthisexp = %s\n", exp.thisexp.toChars()); printf("\tnewtype: %s\n", exp.newtype.toChars()); } - if (exp.type) // if semantic() already run + + if (exp.placement) { - result = exp; - return; + exp.placement = exp.placement.expressionSemantic(sc); + auto p = exp.placement; + if (p.op == EXP.error) + return setError(); + if (!p.isLvalue()) + { + 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)) + { + return setError(); + } + if (!exp.placement.type.isNaked()) + { + error(p.loc, "PlacementExpression `%s` of type `%s` be unshared and mutable", p.toChars(), toChars(p.type)); + return setError(); + } } //for error messages if the argument in [] is not convertible to size_t @@ -4875,7 +4976,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { return setError(); } - if (preFunctionParameters(sc, exp.argumentList)) + if (preFunctionParameters(sc, exp.argumentList, global.errorSink)) { return setError(); } @@ -4886,6 +4987,19 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor return setError(); } + uinteger_t placementSize; + if (exp.placement) + { + placementSize = size(exp.placement.type, exp.placement.loc); + auto objectSize = size(tb, exp.placement.loc); + //printf("placementSize: %lld objectSize: %lld\n", placementSize, objectSize); + if (!tb.isTypeClass && placementSize < objectSize) + { + error(exp.placement.loc, "new placement size %llu must be >= object size %llu", placementSize, objectSize); + return setError(); + } + } + const size_t nargs = exp.arguments ? exp.arguments.length : 0; Expression newprefix = null; @@ -4894,9 +5008,14 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor auto cd = tc.sym; if (cd.errors) return setError(); - cd.size(exp.loc); + auto objectSize = cd.size(exp.loc); if (cd.sizeok != Sizeok.done) return setError(); + if (exp.placement && placementSize < objectSize) + { + error(exp.placement.loc, "new placement size %llu must be >= class object size %llu", placementSize, objectSize); + return setError(); + } if (!cd.ctor) cd.ctor = cd.searchCtor(); if (cd.noDefaultCtor && !nargs && !cd.defaultCtor) @@ -5085,7 +5204,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor // When using `@nogc` exception handling, lower `throw new E(args)` to // `throw (__tmp = _d_newThrowable!E(), __tmp.__ctor(args), __tmp)`. - if (global.params.ehnogc && exp.thrownew && + if (sc.previews.dip1008 && exp.thrownew && !cd.isCOMclass() && !cd.isCPPclass()) { assert(cd.ctor); @@ -5095,26 +5214,20 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor auto tiargs = new Objects(); tiargs.push(exp.newtype); + id = new DotTemplateInstanceExp(exp.loc, id, Id._d_newThrowable, tiargs); - id = new CallExp(exp.loc, id).expressionSemantic(sc); - Expression idVal; - Expression tmp = extractSideEffect(sc, "__tmpThrowable", idVal, id, true); - // auto castTmp = new CastExp(exp.loc, tmp, exp.type); + id = new CallExp(exp.loc, id).expressionSemantic(sc); - auto ctor = new DotIdExp(exp.loc, tmp, Id.ctor).expressionSemantic(sc); - auto ctorCall = new CallExp(exp.loc, ctor, exp.arguments); + exp.lowering = id.expressionSemantic(sc); - id = Expression.combine(idVal, exp.argprefix).expressionSemantic(sc); - id = Expression.combine(id, ctorCall).expressionSemantic(sc); - // id = Expression.combine(id, castTmp).expressionSemantic(sc); - - result = id.expressionSemantic(sc); + result = exp; return; } else if (!IN_LLVM && // LDC: not using the `_d_newclassT` lowering yet sc.needsCodegen() && // interpreter doesn't need this lowered - !exp.onstack && !exp.type.isscope()) // these won't use the GC + !exp.placement && + !exp.onstack && !exp.type.isScopeClass()) // these won't use the GC { /* replace `new T(arguments)` with `core.lifetime._d_newclassT!T(arguments)` * or `_d_newclassTTrace` @@ -5131,14 +5244,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor tiargs.push(t); id = new DotTemplateInstanceExp(exp.loc, id, hook, tiargs); auto arguments = new Expressions(); - if (global.params.tracegc) - { - auto funcname = (sc.callsc && sc.callsc.func) ? - sc.callsc.func.toPrettyChars() : sc.func.toPrettyChars(); - arguments.push(new StringExp(exp.loc, exp.loc.filename.toDString())); - arguments.push(new IntegerExp(exp.loc, exp.loc.linnum, Type.tint32)); - arguments.push(new StringExp(exp.loc, funcname.toDString())); - } id = new CallExp(exp.loc, id, arguments); exp.lowering = id.expressionSemantic(sc); @@ -5227,10 +5332,16 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } exp.type = exp.type.pointerTo(); - tryLowerToNewItem(exp); + if (!exp.placement) + tryLowerToNewItem(exp); } else if (tb.ty == Tarray) { + if (exp.placement) + { + error(exp.placement.loc, "placement new cannot be used with dynamic arrays"); + return setError(); + } if (!nargs) { // https://issues.dlang.org/show_bug.cgi?id=20422 @@ -5280,9 +5391,9 @@ 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.toChars()); + 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.toChars()); + error(exp.loc, "expression `%s` allocates with the GC and cannot be used with switch `-betterC`", exp.toErrMsg()); return setError(); } @@ -5301,7 +5412,10 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor goto LskipNewArrayLowering; } - if (nargs == 1) + if (exp.placement) // no need to lower + { + } + else if (nargs == 1) { auto hook = global.params.tracegc ? Id._d_newarrayTTrace : Id._d_newarrayT; if (!verifyHookExist(exp.loc, *sc, hook, "new array")) @@ -5323,14 +5437,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor lowering = new DotTemplateInstanceExp(exp.loc, lowering, hook, tiargs); auto arguments = new Expressions(); - if (global.params.tracegc) - { - auto funcname = (sc.callsc && sc.callsc.func) ? - sc.callsc.func.toPrettyChars() : sc.func.toPrettyChars(); - arguments.push(new StringExp(exp.loc, exp.loc.filename.toDString())); - arguments.push(new IntegerExp(exp.loc, exp.loc.linnum, Type.tint32)); - arguments.push(new StringExp(exp.loc, funcname.toDString())); - } arguments.push((*exp.arguments)[0]); arguments.push(new IntegerExp(exp.loc, isShared, Type.tbool)); @@ -5362,14 +5468,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor lowering = new DotTemplateInstanceExp(exp.loc, lowering, hook, tiargs); auto arguments = new Expressions(); - if (global.params.tracegc) - { - auto funcname = (sc.callsc && sc.callsc.func) ? - sc.callsc.func.toPrettyChars() : sc.func.toPrettyChars(); - arguments.push(new StringExp(exp.loc, exp.loc.filename.toDString())); - arguments.push(new IntegerExp(exp.loc, exp.loc.linnum, Type.tint32)); - arguments.push(new StringExp(exp.loc, funcname.toDString())); - } arguments.push(new ArrayLiteralExp(exp.loc, Type.tsize_t.sarrayOf(nargs), exp.arguments)); arguments.push(new IntegerExp(exp.loc, tbn.isShared(), Type.tbool)); @@ -5378,7 +5476,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor exp.lowering = lowering.expressionSemantic(sc); } } - else if (tb.isscalar()) + else if (tb.isScalar()) { if (!nargs) { @@ -5401,10 +5499,16 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } exp.type = exp.type.pointerTo(); - tryLowerToNewItem(exp); + if (!exp.placement) + tryLowerToNewItem(exp); } else if (tb.ty == Taarray) { + if (exp.placement) + { + error(exp.placement.loc, "placement new cannot be used with associative arrays"); + return setError(); + } // e.g. `new Alias(args)` if (nargs) { @@ -5442,7 +5546,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor Expression d = new DeclarationExp(e.loc, e.cd); sc = sc.push(); // just create new scope - sc.flags &= ~SCOPE.ctfe; // temporary stop CTFE + sc.ctfe = false; // temporary stop CTFE d = d.expressionSemantic(sc); sc = sc.pop(); @@ -5454,7 +5558,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor sds.members.push(e.cd); } - Expression n = new NewExp(e.loc, e.thisexp, e.cd.type, e.arguments); + Expression n = new NewExp(e.loc, e.placement, e.thisexp, e.cd.type, e.arguments); Expression c = new CommaExp(e.loc, d, n); result = c.expressionSemantic(sc); @@ -5477,7 +5581,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } else if (auto f = e.var.isFuncDeclaration()) { - if (f.checkNestedReference(sc, e.loc)) + if (f.checkNestedFuncReference(sc, e.loc)) return setError(); } @@ -5531,10 +5635,10 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } else if (fd) { - // TODO: If fd isn't yet resolved its overload, the checkNestedReference + // TODO: If fd isn't yet resolved its overload, the checkNestedFuncReference // call would cause incorrect validation. // Maybe here should be moved in CallExp, or AddrExp for functions. - if (fd.checkNestedReference(sc, e.loc)) + if (fd.checkNestedFuncReference(sc, e.loc)) return setError(); } else if (auto od = e.var.isOverDeclaration()) @@ -5547,48 +5651,48 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor private void genIdent(FuncExp exp, Scope* sc) { - if (exp.fd.ident == Id.empty) - { - string s; - if (exp.fd.fes) - s = "__foreachbody"; - else if (exp.fd.tok == TOK.reserved) - s = "__lambda"; - else if (exp.fd.tok == TOK.delegate_) - s = "__dgliteral"; - else - s = "__funcliteral"; + if (exp.fd.ident != Id.empty) + return; - DsymbolTable symtab; - if (FuncDeclaration func = sc.parent.isFuncDeclaration()) + string s; + if (exp.fd.fes) + s = "__foreachbody"; + else if (exp.fd.tok == TOK.reserved) + s = "__lambda"; + else if (exp.fd.tok == TOK.delegate_) + s = "__dgliteral"; + else + s = "__funcliteral"; + + DsymbolTable symtab; + if (FuncDeclaration func = sc.parent.isFuncDeclaration()) + { + if (func.localsymtab is null) { - if (func.localsymtab is null) - { - // Inside template constraint, symtab is not set yet. - // Initialize it lazily. - func.localsymtab = new DsymbolTable(); - } - symtab = func.localsymtab; + // Inside template constraint, symtab is not set yet. + // Initialize it lazily. + func.localsymtab = new DsymbolTable(); } - else + symtab = func.localsymtab; + } + else + { + ScopeDsymbol sds = sc.parent.isScopeDsymbol(); + if (!sds.symtab) { - ScopeDsymbol sds = sc.parent.isScopeDsymbol(); - if (!sds.symtab) - { - // Inside template constraint, symtab may not be set yet. - // Initialize it lazily. - assert(sds.isTemplateInstance()); - sds.symtab = new DsymbolTable(); - } - symtab = sds.symtab; + // Inside template constraint, symtab may not be set yet. + // Initialize it lazily. + assert(sds.isTemplateInstance()); + sds.symtab = new DsymbolTable(); } - assert(symtab); - Identifier id = Identifier.generateIdWithLoc(s, exp.loc, cast(string) toDString(sc.parent.toPrettyChars())); - exp.fd.ident = id; - if (exp.td) - exp.td.ident = id; - symtab.insert(exp.td ? cast(Dsymbol)exp.td : cast(Dsymbol)exp.fd); + symtab = sds.symtab; } + assert(symtab); + Identifier id = Identifier.generateIdWithLoc(s, exp.loc, cast(string) toDString(sc.parent.toPrettyChars())); + exp.fd.ident = id; + if (exp.td) + exp.td.ident = id; + symtab.insert(exp.td ? cast(Dsymbol)exp.td : cast(Dsymbol)exp.fd); } override void visit(FuncExp exp) @@ -5600,17 +5704,11 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor printf(" treq = %s\n", exp.fd.treq.toChars()); } - if (exp.type) - { - result = exp; - return; - } Expression e = exp; - uint olderrors; sc = sc.push(); // just create new scope - sc.flags &= ~SCOPE.ctfe; // temporary stop CTFE + sc.ctfe = false; // temporary stop CTFE sc.visibility = Visibility(Visibility.Kind.public_); // https://issues.dlang.org/show_bug.cgi?id=12506 /* fd.treq might be incomplete type, @@ -5636,6 +5734,12 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } } + void done() + { + sc = sc.pop(); + result = e; + } + //printf("td = %p, treq = %p\n", td, fd.treq); if (exp.td) { @@ -5651,10 +5755,10 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor else e = ErrorExp.get(); } - goto Ldone; + return done(); } - olderrors = global.errors; + const olderrors = global.errors; exp.fd.dsymbolSemantic(sc); if (olderrors == global.errors) { @@ -5667,7 +5771,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (exp.fd.type && exp.fd.type.ty == Tfunction && !exp.fd.type.nextOf()) (cast(TypeFunction)exp.fd.type).next = Type.terror; e = ErrorExp.get(); - goto Ldone; + return done(); } // Type is a "delegate to" or "pointer to" the function literal @@ -5680,7 +5784,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (exp.fd.type.isTypeError()) { e = ErrorExp.get(); - goto Ldone; + return done(); } exp.type = new TypeDelegate(exp.fd.type.isTypeFunction()); exp.type = exp.type.typeSemantic(exp.loc, sc); @@ -5709,10 +5813,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } } exp.fd.tookAddressOf++; - - Ldone: - sc = sc.pop(); - result = e; + done(); } /** @@ -5725,70 +5826,69 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor */ Expression callExpSemantic(FuncExp exp, Scope* sc, Expressions* arguments) { - if ((!exp.type || exp.type == Type.tvoid) && exp.td && arguments && arguments.length) + if ((exp.type && exp.type != Type.tvoid) || !exp.td ||! arguments || !arguments.length) + return exp.expressionSemantic(sc); + + for (size_t k = 0; k < arguments.length; k++) { - for (size_t k = 0; k < arguments.length; k++) - { - Expression checkarg = (*arguments)[k]; - if (checkarg.op == EXP.error) - return checkarg; - } + Expression checkarg = (*arguments)[k]; + if (checkarg.op == EXP.error) + return checkarg; + } - genIdent(exp, sc); + genIdent(exp, sc); - assert(exp.td.parameters && exp.td.parameters.length); - exp.td.dsymbolSemantic(sc); + assert(exp.td.parameters && exp.td.parameters.length); + exp.td.dsymbolSemantic(sc); - TypeFunction tfl = cast(TypeFunction)exp.fd.type; - size_t dim = tfl.parameterList.length; - if (arguments.length < dim) - { - // Default arguments are always typed, so they don't need inference. - Parameter p = tfl.parameterList[arguments.length]; - if (p.defaultArg) - dim = arguments.length; - } + TypeFunction tfl = cast(TypeFunction)exp.fd.type; + size_t dim = tfl.parameterList.length; + if (arguments.length < dim) + { + // Default arguments are always typed, so they don't need inference. + Parameter p = tfl.parameterList[arguments.length]; + if (p.defaultArg) + dim = arguments.length; + } - if ((tfl.parameterList.varargs == VarArg.none && arguments.length > dim) || - arguments.length < dim) - { - OutBuffer buf; - foreach (idx, ref arg; *arguments) - buf.printf("%s%s", (idx ? ", ".ptr : "".ptr), arg.type.toChars()); - error(exp.loc, "function literal `%s%s` is not callable using argument types `(%s)`", - exp.fd.toChars(), parametersTypeToChars(tfl.parameterList), - buf.peekChars()); - errorSupplemental(exp.loc, "too %s arguments, expected %d, got %d", - arguments.length < dim ? "few".ptr : "many".ptr, - cast(int)dim, cast(int)arguments.length); - return ErrorExp.get(); - } + if ((tfl.parameterList.varargs == VarArg.none && arguments.length > dim) || + arguments.length < dim) + { + OutBuffer buf; + foreach (idx, ref arg; *arguments) + buf.printf("%s%s", (idx ? ", ".ptr : "".ptr), arg.type.toChars()); + error(exp.loc, "`%s` is not callable using argument types `(%s)`", + exp.fd.toErrMsg(), // parametersTypeToChars(tfl.parameterList), + buf.peekChars()); + errorSupplemental(exp.loc, "too %s arguments, expected %d, got %d", + arguments.length < dim ? "few".ptr : "many".ptr, + cast(int)dim, cast(int)arguments.length); + return ErrorExp.get(); + } - auto tiargs = new Objects(); - tiargs.reserve(exp.td.parameters.length); + auto tiargs = new Objects(); + tiargs.reserve(exp.td.parameters.length); - for (size_t i = 0; i < exp.td.parameters.length; i++) + for (size_t i = 0; i < exp.td.parameters.length; i++) + { + TemplateParameter tp = (*exp.td.parameters)[i]; + assert(dim <= tfl.parameterList.length); + foreach (u, p; tfl.parameterList) { - TemplateParameter tp = (*exp.td.parameters)[i]; - assert(dim <= tfl.parameterList.length); - foreach (u, p; tfl.parameterList) - { - if (u == dim) - break; + if (u == dim) + break; - if (p.type.ty == Tident && (cast(TypeIdentifier)p.type).ident == tp.ident) - { - Expression e = (*arguments)[u]; - tiargs.push(e.type); - break; - } + if (p.type.ty == Tident && (cast(TypeIdentifier)p.type).ident == tp.ident) + { + Expression e = (*arguments)[u]; + tiargs.push(e.type); + break; } } - - auto ti = new TemplateInstance(exp.loc, exp.td, tiargs); - return (new ScopeExp(exp.loc, ti)).expressionSemantic(sc); } - return exp.expressionSemantic(sc); + + auto ti = new TemplateInstance(exp.loc, exp.td, tiargs); + return (new ScopeExp(exp.loc, ti)).expressionSemantic(sc); } override void visit(CallExp exp) @@ -5797,10 +5897,18 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { printf("CallExp::semantic() %s\n", exp.toChars()); } - if (exp.type) + + scope (exit) { - result = exp; - return; // semantic() already run + if (TypeFunction tf = exp.f ? cast(TypeFunction)exp.f.type : null) + { + result.rvalue = tf.isRvalue; + if (tf.isRvalue && !tf.isRef) + { + error(exp.f.loc, "`__rvalue` only valid on functions that return by `ref`"); + setError(); + } + } } Objects* tiargs = null; // initial list of template arguments @@ -5826,7 +5934,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (FuncExp fe = exp.e1.isFuncExp()) { if (arrayExpressionSemantic(exp.arguments.peekSlice(), sc) || - preFunctionParameters(sc, exp.argumentList)) + preFunctionParameters(sc, exp.argumentList, global.errorSink)) return setError(); // Run e1 semantic even if arguments have any errors @@ -5837,7 +5945,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor return; } } - if (sc.flags & SCOPE.Cfile) + if (sc.inCfile) { /* See if need to rewrite the AST because of cast/call ambiguity */ @@ -5968,7 +6076,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor __gshared int nest; if (++nest > global.recursionLimit) { - error(exp.loc, "recursive evaluation of `%s`", exp.toChars()); + error(exp.loc, "recursive evaluation of `%s`", exp.toErrMsg()); --nest; return setError(); } @@ -6020,7 +6128,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor // Rewrite (*fp)(arguments) to fp(arguments) exp.e1 = (cast(PtrExp)exp.e1).e1; } - else if (exp.e1.op == EXP.type && (sc && sc.flags & SCOPE.Cfile)) + else if (exp.e1.op == EXP.type && (sc && sc.inCfile)) { const numArgs = exp.arguments ? exp.arguments.length : 0; @@ -6066,7 +6174,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor return; } if (arrayExpressionSemantic(exp.arguments.peekSlice(), sc) || - preFunctionParameters(sc, exp.argumentList)) + preFunctionParameters(sc, exp.argumentList, global.errorSink)) return setError(); // Check for call operator overload @@ -6091,7 +6199,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (sd.ctor) { auto ctor = sd.ctor.isCtorDeclaration(); - if (ctor && ctor.isCpCtor && ctor.isGenerated()) + if (ctor && (ctor.isCpCtor || ctor.isMoveCtor) && ctor.isGenerated()) sd.ctor = null; } @@ -6147,7 +6255,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor return; } // No constructor, look for overload of opCall - if (search_function(sd, Id.call)) + if (search_function(sd, Id.opCall)) goto L1; // overload of opCall, therefore it's a call if (exp.e1.op != EXP.type) @@ -6188,13 +6296,13 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { L1: // Rewrite as e1.call(arguments) - Expression e = new DotIdExp(exp.loc, exp.e1, Id.call); + Expression e = new DotIdExp(exp.loc, exp.e1, Id.opCall); e = new CallExp(exp.loc, e, exp.arguments, exp.names); e = e.expressionSemantic(sc); result = e; return; } - else if (exp.e1.op == EXP.type && t1.isscalar()) + else if (exp.e1.op == EXP.type && t1.isScalar()) { Expression e; @@ -6235,46 +6343,45 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { if (tiargs && s.isFuncDeclaration()) continue; - if (auto f2 = resolveFuncCall(loc, sc, s, tiargs, tthis, argumentList, FuncResolveFlag.quiet)) + auto f2 = resolveFuncCall(loc, sc, s, tiargs, tthis, argumentList, FuncResolveFlag.quiet); + if (!f2) + continue; + if (f2.errors) + return null; + if (!f) { - if (f2.errors) - return null; - if (f) - { - /* Match in more than one overload set, - * even if one is a 'better' match than the other. - */ - if (f.isCsymbol() && f2.isCsymbol()) - { - /* C has global name space, so just pick one, such as f. - * If f and f2 are not compatible, that's how C rolls. - */ - } - else - ScopeDsymbol.multiplyDefined(loc, f, f2); // issue error - } - else - f = f2; + f = f2; + continue; } - } - if (!f) - { - .error(loc, "no overload matches for `%s`", exp.toChars()); - errorSupplemental(loc, "Candidates are:"); - foreach (s; os.a) + /* Match in more than one overload set, + * even if one is a 'better' match than the other. + */ + if (f.isCsymbol() && f2.isCsymbol()) { - overloadApply(s, (ds){ - if (auto fd = ds.isFuncDeclaration()) - .errorSupplemental(ds.loc, "%s%s", fd.toChars(), - fd.type.toTypeFunction().parameterList.parametersTypeToChars()); - else - .errorSupplemental(ds.loc, "%s", ds.toChars()); - return 0; - }); + /* C has global name space, so just pick one, such as f. + * If f and f2 are not compatible, that's how C rolls. + */ } + else + ScopeDsymbol.multiplyDefined(loc, f, f2); // issue error + } + if (f && f.errors) + return null; + if (f) + return f; + .error(loc, "no overload matches for `%s`", exp.toErrMsg()); + errorSupplemental(loc, "Candidates are:"); + foreach (s; os.a) + { + overloadApply(s, (ds){ + if (auto fd = ds.isFuncDeclaration()) + .errorSupplemental(ds.loc, "%s%s", fd.toChars(), + fd.type.toTypeFunction().parameterList.parametersTypeToChars()); + else + .errorSupplemental(ds.loc, "%s", ds.toChars()); + return 0; + }); } - else if (f.errors) - f = null; return f; } @@ -6332,7 +6439,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor tthis = ue.e1.type; if (!(exp.f.type.ty == Tfunction && (cast(TypeFunction)exp.f.type).isScopeQual)) { - if (checkParamArgumentEscape(*sc, exp.f, Id.This, exp.f.vthis, STC.undefined_, ethis, false, false)) + if (checkParamArgumentEscape(*sc, exp.f, Id.This, exp.f.vthis, STC.none, ethis, false, false)) return setError(); } } @@ -6522,7 +6629,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } else if (!t1) { - error(exp.loc, "function expected before `()`, not `%s`", exp.e1.toChars()); + error(exp.loc, "function expected before `()`, not `%s`", exp.e1.toErrMsg()); return setError(); } else if (t1.ty == Terror) @@ -6605,7 +6712,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } else { - error(exp.loc, "function expected before `()`, not `%s` of type `%s`", exp.e1.toChars(), exp.e1.type.toChars()); + error(exp.loc, "function expected before `()`, not `%s` of type `%s`", exp.e1.toErrMsg(), exp.e1.type.toChars()); return setError(); } @@ -6619,13 +6726,13 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor tthis.modToBuffer(buf); //printf("tf = %s, args = %s\n", tf.deco, (*arguments)[0].type.deco); - .error(exp.loc, "%s `%s%s` is not callable using argument types `%s`", - p, exp.e1.toChars(), parametersTypeToChars(tf.parameterList), buf.peekChars()); + .error(exp.loc, "%s `%s` is not callable using argument types `%s`", + p, exp.e1.toErrMsg(), buf.peekChars()); if (failMessage) errorSupplemental(exp.loc, "%s", failMessage); } - if (tf.callMatch(null, exp.argumentList, 0, &errorHelper, sc) == MATCH.nomatch) + if (callMatch(exp.f, tf, null, exp.argumentList, 0, &errorHelper, sc) == MATCH.nomatch) return setError(); // Purity and safety check should run after testing arguments matching @@ -6634,29 +6741,29 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor exp.f.checkPurity(exp.loc, sc); exp.f.checkSafety(exp.loc, sc); exp.f.checkNogc(exp.loc, sc); - if (exp.f.checkNestedReference(sc, exp.loc)) + if (exp.f.checkNestedFuncReference(sc, exp.loc)) return setError(); } - else if (sc.func && sc.intypeof != 1 && !(sc.flags & (SCOPE.ctfe | SCOPE.debug_))) + else if (sc.func && sc.intypeof != 1 && !(sc.ctfe || sc.debug_)) { bool err = false; - if (!tf.purity && sc.func.setImpure(exp.loc, "`pure` %s `%s` cannot call impure `%s`", exp.e1)) + if (!tf.purity && sc.func.setImpure(exp.loc, "calling impure `%s`", exp.e1)) { error(exp.loc, "`pure` %s `%s` cannot call impure %s `%s`", - sc.func.kind(), sc.func.toPrettyChars(), p, exp.e1.toChars()); + sc.func.kind(), sc.func.toPrettyChars(), p, exp.e1.toErrMsg()); err = true; } - if (!tf.isnogc && sc.func.setGC(exp.loc, "`@nogc` %s `%s` cannot call non-@nogc `%s`", exp.e1)) + if (!tf.isNogc && sc.func.setGC(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.toChars()); + sc.func.kind(), sc.func.toPrettyChars(), p, exp.e1.toErrMsg()); err = true; } if (tf.trust <= TRUST.system && sc.setUnsafe(true, exp.loc, - "`@safe` function `%s` cannot call `@system` `%s`", sc.func, exp.e1)) + "calling `@system` `%s`", exp.e1)) { error(exp.loc, "`@safe` %s `%s` cannot call `@system` %s `%s`", - sc.func.kind(), sc.func.toPrettyChars(), p, exp.e1.toChars()); + sc.func.kind(), sc.func.toPrettyChars(), p, exp.e1.toErrMsg()); err = true; } if (err) @@ -6701,14 +6808,14 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } .error(exp.loc, "%s `%s` is not callable using argument types `%s`", - exp.f.kind(), exp.f.toChars(), buf.peekChars()); + exp.f.kind(), exp.f.toErrMsg(), buf.peekChars()); if (failMessage) errorSupplemental(exp.loc, "%s", failMessage); .errorSupplemental(exp.f.loc, "`%s%s` declared here", exp.f.toPrettyChars(), parametersTypeToChars(tf.parameterList)); exp.f = null; } - if (tf.callMatch(null, exp.argumentList, 0, &errorHelper2, sc) == MATCH.nomatch) + if (callMatch(exp.f, tf, null, exp.argumentList, 0, &errorHelper2, sc) == MATCH.nomatch) exp.f = null; } if (!exp.f || exp.f.errors) @@ -6717,7 +6824,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (exp.f.needThis()) { // Change the ancestor lambdas to delegate before hasThis(sc) call. - if (exp.f.checkNestedReference(sc, exp.loc)) + if (exp.f.checkNestedFuncReference(sc, exp.loc)) return setError(); auto memberFunc = hasThis(sc); @@ -6752,7 +6859,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor checkFunctionAttributes(exp, sc, exp.f); checkAccess(exp.loc, sc, null, exp.f); - if (exp.f.checkNestedReference(sc, exp.loc)) + if (exp.f.checkNestedFuncReference(sc, exp.loc)) return setError(); ethis = null; @@ -6777,7 +6884,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { exp.e1 = e1org; // https://issues.dlang.org/show_bug.cgi?id=10922 // avoid recursive expression printing - error(exp.loc, "forward reference to inferred return type of function call `%s`", exp.toChars()); + error(exp.loc, "forward reference to inferred return type of function call `%s`", exp.toErrMsg()); return setError(); } @@ -6814,10 +6921,10 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor Loc loc = exp.loc; auto vptr = new DotIdExp(loc, new ThisExp(loc), Id.__vptr); - auto vptrTmpDecl = copyToTemp(0, "__vptrTmp", vptr); + auto vptrTmpDecl = copyToTemp(STC.none, "__vptrTmp", vptr); auto declareVptrTmp = new DeclarationExp(loc, vptrTmpDecl); - auto superTmpDecl = copyToTemp(0, "__superTmp", result); + auto superTmpDecl = copyToTemp(STC.none, "__superTmp", result); auto declareSuperTmp = new DeclarationExp(loc, superTmpDecl); auto declareTmps = new CommaExp(loc, declareVptrTmp, declareSuperTmp); @@ -6861,17 +6968,12 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor override void visit(DeclarationExp e) { - if (e.type) - { - result = e; - return; - } static if (LOGSEMANTIC) { printf("DeclarationExp::semantic() %s\n", e.toChars()); } - uint olderrors = global.errors; + const olderrors = global.errors; /* This is here to support extern(linkage) declaration, * where the extern(linkage) winds up being an AttribDeclaration @@ -6882,15 +6984,10 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor while (1) { AttribDeclaration ad = s.isAttribDeclaration(); - if (ad) - { - if (ad.decl && ad.decl.length == 1) - { - s = (*ad.decl)[0]; - continue; - } - } - break; + if (!ad) + break; + if (ad.decl && ad.decl.length == 1) + s = (*ad.decl)[0]; } //printf("inserting '%s' %p into sc = %p\n", s.toChars(), s, sc); @@ -6901,7 +6998,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor VarDeclaration v = s.isVarDeclaration(); if (v) { - if (sc.flags & SCOPE.Cfile) + if (sc.inCfile) { /* Do semantic() on the type before inserting v into the symbol table */ @@ -6935,7 +7032,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor return setError(); } - if (v && (sc.flags & SCOPE.Cfile)) + if (v && sc.inCfile) { /* Do semantic() on initializer last so this will be legal: * int a = a; @@ -6973,7 +7070,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor // The mangling change only works for D mangling } - if (!(sc.flags & SCOPE.Cfile)) + if (!sc.inCfile) { /* https://issues.dlang.org/show_bug.cgi?id=21272 * If we are in a foreach body we need to extract the @@ -6986,27 +7083,26 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor // Disallow shadowing for (Scope* scx = sc.enclosing; scx && (scx.func == sc.func || (fes_enclosing_func && scx.func == fes_enclosing_func)); scx = scx.enclosing) { - Dsymbol s2; - if (scx.scopesym && scx.scopesym.symtab && (s2 = scx.scopesym.symtab.lookup(s.ident)) !is null && s != s2) + if (!scx.scopesym || !scx.scopesym.symtab) + continue; + Dsymbol s2 = scx.scopesym.symtab.lookup(s.ident); + if (s2 is null || s == s2) + continue; + // allow STC.local symbols to be shadowed + // TODO: not really an optimal design + auto decl = s2.isDeclaration(); + if (decl && (decl.storage_class & STC.local)) + continue; + if (sc.func.fes) { - // allow STC.local symbols to be shadowed - // TODO: not really an optimal design - auto decl = s2.isDeclaration(); - if (!decl || !(decl.storage_class & STC.local)) - { - if (sc.func.fes) - { - deprecation(e.loc, "%s `%s` is shadowing %s `%s`", s.kind(), s.ident.toChars(), s2.kind(), s2.toPrettyChars()); - deprecationSupplemental(s2.loc, "declared here"); - - } - else - { - error(e.loc, "%s `%s` is shadowing %s `%s`", s.kind(), s.ident.toChars(), s2.kind(), s2.toPrettyChars()); - errorSupplemental(s2.loc, "declared here"); - return setError(); - } - } + deprecation(e.loc, "%s `%s` is shadowing %s `%s`", s.kind(), s.ident.toChars(), s2.kind(), s2.toPrettyChars()); + deprecationSupplemental(s2.loc, "declared here"); + } + else + { + error(e.loc, "%s `%s` is shadowing %s `%s`", s.kind(), s.ident.toChars(), s2.kind(), s2.toPrettyChars()); + errorSupplemental(s2.loc, "declared here"); + return setError(); } } } @@ -7068,52 +7164,51 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (!ta) { //printf("ta %p ea %p sa %p\n", ta, ea, sa); - error(exp.loc, "no type for `typeid(%s)`", ea ? ea.toChars() : (sa ? sa.toChars() : "")); + error(exp.loc, "no type for `typeid(%s)`", ea ? ea.toErrMsg() : (sa ? sa.toChars() : "")); return setError(); } ta.checkComplexTransition(exp.loc, sc); - Expression e; auto tb = ta.toBasetype(); if (ea && tb.ty == Tclass) { if (tb.toDsymbol(sc).isClassDeclaration().classKind == ClassKind.cpp) { error(exp.loc, "runtime type information is not supported for `extern(C++)` classes"); - e = ErrorExp.get(); + return setError(); } else if (!Type.typeinfoclass) { error(exp.loc, "`object.TypeInfo_Class` could not be found, but is implicitly used"); - e = ErrorExp.get(); + return setError(); } else { /* Get the dynamic type, which is .classinfo */ ea = ea.expressionSemantic(sc); - e = new TypeidExp(ea.loc, ea); + Expression e = new TypeidExp(ea.loc, ea); e.type = Type.typeinfoclass.type; + result = e; + return; } } else if (ta.ty == Terror) { - e = ErrorExp.get(); + return setError(); } - else - { - // Handle this in the glue layer - e = new TypeidExp(exp.loc, ta); - e.type = getTypeInfoType(exp.loc, ta, sc); - semanticTypeInfo(sc, ta); + // Handle this in the glue layer + Expression e = new TypeidExp(exp.loc, ta); - if (ea) - { - e = new CommaExp(exp.loc, ea, e); // execute ea - e = e.expressionSemantic(sc); - } + e.type = getTypeInfoType(exp.loc, ta, sc); + semanticTypeInfo(sc, ta); + + if (ea) + { + e = new CommaExp(exp.loc, ea, e); // execute ea + e = e.expressionSemantic(sc); } result = e; } @@ -7184,7 +7279,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { printf("IsExp::semantic(%s)\n", e.toChars()); } - if (e.id && !(sc.flags & SCOPE.condition)) + if (e.id && !sc.condition) { error(e.loc, "can only declare type aliases within `static if` conditionals or `static assert`s"); return setError(); @@ -7203,7 +7298,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor return no(); if (e.tok2 == TOK.package_ && p.isModule()) // Note that isModule() will return null for package modules because they're not actually instances of Module. return no(); - else if(e.tok2 == TOK.module_ && !(p.isModule() || p.isPackageMod())) + if (e.tok2 == TOK.module_ && !(p.isModule() || p.isPackageMod())) return no(); tded = e.targ; return yes(); @@ -7213,7 +7308,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor Scope* sc2 = sc.copy(); // keep sc.flags sc2.tinst = null; sc2.minst = null; - sc2.flags |= SCOPE.fullinst; + sc2.fullinst = true; Type t = dmd.typesem.trySemantic(e.targ, e.loc, sc2); sc2.pop(); if (!t) // errors, so condition is false @@ -7393,39 +7488,37 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor //printf("targ = %s, %s\n", e.targ.toChars(), e.targ.deco); //printf("tspec = %s, %s\n", e.tspec.toChars(), e.tspec.deco); - if (e.tok == TOK.colon) + if (e.tok != TOK.colon) /* == */ { - // current scope is itself deprecated, or deprecations are not errors - const bool deprecationAllowed = sc.isDeprecated - || global.params.useDeprecated != DiagnosticReporting.error; - const bool preventAliasThis = e.targ.hasDeprecatedAliasThis && !deprecationAllowed; + if (e.targ.equals(e.tspec)) + return yes(); + else + return no(); + } - if (preventAliasThis && e.targ.ty == Tstruct) - { - if ((cast(TypeStruct) e.targ).implicitConvToWithoutAliasThis(e.tspec)) - return yes(); - else - return no(); - } - else if (preventAliasThis && e.targ.ty == Tclass) - { - if ((cast(TypeClass) e.targ).implicitConvToWithoutAliasThis(e.tspec)) - return yes(); - else - return no(); - } - else if (e.targ.implicitConvTo(e.tspec)) + // current scope is itself deprecated, or deprecations are not errors + const bool deprecationAllowed = sc.isDeprecated + || global.params.useDeprecated != DiagnosticReporting.error; + const bool preventAliasThis = e.targ.hasDeprecatedAliasThis && !deprecationAllowed; + + if (preventAliasThis && e.targ.ty == Tstruct) + { + if ((cast(TypeStruct) e.targ).implicitConvToWithoutAliasThis(e.tspec)) return yes(); else return no(); } - else /* == */ + else if (preventAliasThis && e.targ.ty == Tclass) { - if (e.targ.equals(e.tspec)) + if ((cast(TypeClass) e.targ).implicitConvToWithoutAliasThis(e.tspec)) return yes(); else return no(); } + else if (e.targ.implicitConvTo(e.tspec)) + return yes(); + else + return no(); } else if (e.tspec) { @@ -7447,41 +7540,37 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor MATCH m = deduceType(e.targ, sc, e.tspec, *e.parameters, dedtypes, null, 0, e.tok == TOK.equal); if (m == MATCH.nomatch || (m != MATCH.exact && e.tok == TOK.equal)) - { return no(); - } - else - { - tded = cast(Type)dedtypes[0]; - if (!tded) - tded = e.targ; - Objects tiargs = Objects(1); - tiargs[0] = e.targ; - /* Declare trailing parameters - */ - for (size_t i = 1; i < e.parameters.length; i++) - { - TemplateParameter tp = (*e.parameters)[i]; - Declaration s = null; + tded = cast(Type)dedtypes[0]; + if (!tded) + tded = e.targ; + Objects tiargs = Objects(1); + tiargs[0] = e.targ; - m = tp.matchArg(e.loc, sc, &tiargs, i, e.parameters, dedtypes, &s); - if (m == MATCH.nomatch) - return no(); - s.dsymbolSemantic(sc); - if (!sc.insert(s)) - { - Dsymbol pscopesym; - auto conflict = sc.search(Loc.initial, s.ident, pscopesym); - error(e.loc, "declaration `%s` is already defined", s.toPrettyChars()); - errorSupplemental(conflict.loc, "`%s` `%s` is defined here", - conflict.kind(), conflict.toChars()); - } + /* Declare trailing parameters + */ + for (size_t i = 1; i < e.parameters.length; i++) + { + TemplateParameter tp = (*e.parameters)[i]; + Declaration s = null; - unSpeculative(sc, s); + m = tp.matchArg(e.loc, sc, &tiargs, i, e.parameters, dedtypes, &s); + if (m == MATCH.nomatch) + return no(); + s.dsymbolSemantic(sc); + if (!sc.insert(s)) + { + Dsymbol pscopesym; + auto conflict = sc.search(Loc.initial, s.ident, pscopesym); + error(e.loc, "declaration `%s` is already defined", s.toPrettyChars()); + errorSupplemental(conflict.loc, "`%s` `%s` is defined here", + conflict.kind(), conflict.toChars()); } - return yes(); + + unSpeculative(sc, s); } + return yes(); } else if (e.id) { @@ -7495,19 +7584,14 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor override void visit(BinAssignExp exp) { - if (exp.type) - { - result = exp; - return; - } - Expression e = exp.op_overload(sc); - if (e) + if (Expression e = exp.opOverloadBinaryAssign(sc, aliasThisStop)) { result = e; return; } + Expression e; if (exp.e1.op == EXP.arrayLength) { // arr.length op= e2; @@ -7516,7 +7600,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor result = e; return; } - if (exp.e1.op == EXP.slice || exp.e1.type.ty == Tarray || exp.e1.type.ty == Tsarray) + if (exp.e1.op == EXP.slice || exp.e1.type.isStaticOrDynamicArray()) { if (checkNonAssignmentArrayOp(exp.e1)) return setError(); @@ -7545,15 +7629,8 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor exp.e1 = exp.e1.optimize(WANTvalue, /*keepLvalue*/ true); exp.type = exp.e1.type; - if (auto ad = isAggregate(exp.e1.type)) - { - if (const s = search_function(ad, Id.opOpAssign)) - { - error(exp.loc, "none of the `opOpAssign` overloads of `%s` are callable for `%s` of type `%s`", ad.toChars(), exp.e1.toChars(), exp.e1.type.toChars()); - return setError(); - } - } - if (exp.e1.checkScalar() || + if (exp.suggestOpOpAssign(sc, parent) || + exp.e1.checkScalar() || exp.e1.checkReadModifyWrite(exp.op, exp.e2) || exp.e1.checkSharedAccess(sc)) return setError(); @@ -7567,7 +7644,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor else if (exp.checkNoBool()) return setError(); - if ((exp.op == EXP.addAssign || exp.op == EXP.minAssign) && exp.e1.type.toBasetype().ty == Tpointer && exp.e2.type.toBasetype().isintegral()) + if ((exp.op == EXP.addAssign || exp.op == EXP.minAssign) && exp.e1.type.toBasetype().ty == Tpointer && exp.e2.type.toBasetype().isIntegral()) { result = scaleFactor(exp, sc); return; @@ -7610,19 +7687,19 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor result = (cast(BinExp)e).reorderSettingAAElem(sc); } - private Expression compileIt(MixinExp exp, Scope *sc) + private Expression compileIt(MixinExp exp, Scope* sc) { OutBuffer buf; if (expressionsToString(buf, sc, exp.exps, exp.loc, null, true)) return null; - uint errors = global.errors; + const errors = global.errors; const len = buf.length; const str = buf.extractChars()[0 .. len]; const bool doUnittests = global.params.parsingUnittestsRequired(); - auto loc = adjustLocForMixin(str, exp.loc, global.params.mixinOut); - scope p = new Parser!ASTCodegen(loc, sc._module, str, false, global.errorSink, &global.compileEnv, doUnittests); - p.transitionIn = global.params.v.vin; + scope p = new Parser!ASTCodegen(sc._module, str, false, global.errorSink, &global.compileEnv, doUnittests); + adjustLocForMixin(str, exp.loc, *p.baseLoc, global.params.mixinOut); + p.linnum = p.baseLoc.startLine; p.nextToken(); //printf("p.loc.linnum = %d\n", p.loc.linnum); @@ -7728,29 +7805,8 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor const slice = se.peekString(); message("file %.*s\t(%s)", cast(int)slice.length, slice.ptr, resolvedNamez.ptr); } - if (global.params.moduleDeps.buffer !is null) - { - OutBuffer* ob = global.params.moduleDeps.buffer; - Module imod = sc._module; - if (!global.params.moduleDeps.name) - ob.writestring("depsFile "); - ob.writestring(imod.toPrettyChars()); - ob.writestring(" ("); - escapePath(ob, imod.srcfile.toChars()); - ob.writestring(") : "); - if (global.params.moduleDeps.name) - ob.writestring("string : "); - ob.write(se.peekString()); - ob.writestring(" ("); - escapePath(ob, resolvedNamez.ptr); - ob.writestring(")"); - ob.writenl(); - } - if (global.params.makeDeps.doOutput) - { - global.params.makeDeps.files.push(resolvedNamez.ptr); - } + addImportExpDep(global.params.moduleDeps, global.params.makeDeps, resolvedNamez, se.peekString(), sc._module); { auto fileName = FileName(resolvedNamez); @@ -7780,7 +7836,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor // deprecated in 2.107 deprecation(e.loc, "assert condition cannot be a string literal"); deprecationSupplemental(e.loc, "If intentional, use `%s !is null` instead to preserve behaviour", - e.toChars()); + e.toErrMsg()); } const generateMsg = !exp.msg && @@ -7885,7 +7941,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor return res.expressionSemantic(sc); } - const stc = op.isLvalue() ? STC.ref_ : 0; + const stc = op.isLvalue() ? STC.ref_ : STC.none; auto tmp = copyToTemp(stc, "__assertOp", op); tmp.dsymbolSemantic(sc); @@ -7920,7 +7976,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { const callExpIdent = callExpFunc.ident; isEqualsCallExpression = callExpIdent == Id.__equals || - callExpIdent == Id.eq; + callExpIdent == Id.opEquals; } } if (op == EXP.equal || op == EXP.notEqual || @@ -8053,7 +8109,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor exp.msg = resolveProperties(sc, exp.msg); exp.msg = exp.msg.implicitCastTo(sc, Type.tchar.constOf().arrayOf()); exp.msg = exp.msg.optimize(WANTvalue); - checkParamArgumentEscape(*sc, null, null, null, STC.undefined_, exp.msg, true, false); + checkParamArgumentEscape(*sc, null, null, null, STC.none, exp.msg, true, false); } if (exp.msg && exp.msg.op == EXP.error) @@ -8072,8 +8128,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor /* This is an `assert(0)` which means halt program execution */ FuncDeclaration fd = sc.parent.isFuncDeclaration(); - if (fd) - fd.hasReturnExp |= 4; sc.ctorflow.orCSX(CSX.halt); if (global.params.useAssert == CHECKENABLE.off) @@ -8101,6 +8155,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { import dmd.statementsem; + te.type = Type.tnoreturn; if (throwSemantic(te.loc, te.e1, sc)) result = te; else @@ -8112,10 +8167,10 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor static if (LOGSEMANTIC) { printf("DotIdExp::semantic(this = %p, '%s')\n", exp, exp.toChars()); - //printf("e1.op = %d, '%s'\n", e1.op, Token.toChars(e1.op)); + printAST(exp); } - if (sc.flags & SCOPE.Cfile) + if (sc.inCfile) { /* See if need to rewrite the AST because of cast/call ambiguity */ @@ -8170,7 +8225,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (e && isDotOpDispatch(e)) { auto ode = e; - uint errors = global.startGagging(); + const errors = global.startGagging(); e = resolvePropertiesX(sc, e); // Any error or if 'e' is not resolved, go to UFCS if (global.endGagging(errors) || e is ode) @@ -8195,11 +8250,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor override void visit(DotTemplateExp e) { - if (e.type) - { - result = e; - return; - } if (Expression ex = unaSemantic(e, sc)) { result = ex; @@ -8216,11 +8266,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { printf("DotVarExp::semantic('%s')\n", exp.toChars()); } - if (exp.type) - { - result = exp; - return; - } exp.var = exp.var.toAlias().isDeclaration(); @@ -8388,11 +8433,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { printf("DotTemplateInstanceExp::semantic('%s')\n", exp.toChars()); } - if (exp.type) - { - result = exp; - return; - } // Indicate we need to resolve by UFCS. Expression e = exp.dotTemplateSemanticProp(sc, DotExpFlag.gag); if (!e) @@ -8408,11 +8448,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { printf("DelegateExp::semantic('%s')\n", e.toChars()); } - if (e.type) - { - result = e; - return; - } e.e1 = e.e1.expressionSemantic(sc); @@ -8433,7 +8468,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor MODMatchToBuffer(&thisBuf, e.e1.type.mod, tf.mod); MODMatchToBuffer(&funcBuf, tf.mod, e.e1.type.mod); error(e.loc, "%smethod `%s` is not callable using a %s`%s`", - funcBuf.peekChars(), f.toPrettyChars(), thisBuf.peekChars(), e.e1.toChars()); + funcBuf.peekChars(), f.toPrettyChars(), thisBuf.peekChars(), e.e1.toErrMsg()); return setError(); } } @@ -8474,11 +8509,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { printf("DotTypeExp::semantic('%s')\n", exp.toChars()); } - if (exp.type) - { - result = exp; - return; - } if (auto e = unaSemantic(exp, sc)) { @@ -8496,11 +8526,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { printf("AddrExp::semantic('%s')\n", exp.toChars()); } - if (exp.type) - { - result = exp; - return; - } if (Expression ex = unaSemantic(exp, sc)) { @@ -8508,7 +8533,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor return; } - if (sc.flags & SCOPE.Cfile) + if (sc.inCfile) { /* Special handling for &"string"/&(T[]){0, 1} * since C regards string/array literals as lvalues @@ -8600,8 +8625,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor */ if (1) { - if (sc.setUnsafe(false, exp.loc, - "cannot take address of lazy parameter `%s` in `@safe` function `%s`", ve, sc.func)) + if (sc.setUnsafe(false, exp.loc, "taking address of lazy parameter `%s`", ve)) { setError(); return; @@ -8628,7 +8652,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (!exp.e1.type) { - error(exp.loc, "cannot take address of `%s`", exp.e1.toChars()); + error(exp.loc, "cannot take address of `%s`", exp.e1.toErrMsg()); return setError(); } if (!checkAddressable(exp, sc)) @@ -8652,7 +8676,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor error(exp.loc, "forward reference to %s `%s`", d.kind(), d.toChars()); } else - error(exp.loc, "forward reference to type `%s` of expression `%s`", exp.e1.type.toChars(), exp.e1.toChars()); + error(exp.loc, "forward reference to type `%s` of expression `%s`", exp.e1.type.toChars(), exp.e1.toErrMsg()); return setError(); } } @@ -8758,11 +8782,9 @@ version (IN_LLVM) result = e; return; } - if (sc.func && !sc.intypeof && !(sc.flags & SCOPE.debug_)) + if (sc.func && !sc.intypeof && !sc.debug_) { - sc.setUnsafe(false, exp.loc, - "`this` reference necessary to take address of member `%s` in `@safe` function `%s`", - f, sc.func); + sc.setUnsafe(false, exp.loc, "taking address of member `%s` without `this` reference", f); } } } @@ -8774,7 +8796,8 @@ version (IN_LLVM) * &a[i] * check 'a' the same as for a regular variable */ - if (VarDeclaration v = expToVariable(exp.e1)) + int deref; + if (VarDeclaration v = expToVariable(exp.e1, deref)) { v.checkPurity(exp.e1.loc, sc); } @@ -8806,14 +8829,8 @@ version (IN_LLVM) { printf("PtrExp::semantic('%s')\n", exp.toChars()); } - if (exp.type) - { - result = exp; - return; - } - Expression e = exp.op_overload(sc); - if (e) + if (Expression e = exp.opOverloadUnary(sc)) { result = e; return; @@ -8832,7 +8849,7 @@ version (IN_LLVM) case Tarray: if (isNonAssignmentArrayOp(exp.e1)) goto default; - error(exp.loc, "using `*` on an array is no longer supported; use `*(%s).ptr` instead", exp.e1.toChars()); + error(exp.loc, "using `*` on an array is no longer supported; use `*(%s).ptr` instead", exp.e1.toErrMsg()); exp.type = (cast(TypeArray)tb).next; exp.e1 = exp.e1.castTo(sc, exp.type.pointerTo()); break; @@ -8849,7 +8866,7 @@ version (IN_LLVM) goto case Terror; } - if (sc.flags & SCOPE.Cfile && exp.type && exp.type.toBasetype().ty == Tvoid) + if (sc.inCfile && exp.type && exp.type.toBasetype().ty == Tvoid) { // https://issues.dlang.org/show_bug.cgi?id=23752 // `&*((void*)(0))` is allowed in C @@ -8869,14 +8886,8 @@ version (IN_LLVM) { printf("NegExp::semantic('%s')\n", exp.toChars()); } - if (exp.type) - { - result = exp; - return; - } - Expression e = exp.op_overload(sc); - if (e) + if (Expression e = exp.opOverloadUnary(sc)) { result = e; return; @@ -8885,7 +8896,7 @@ version (IN_LLVM) fix16997(sc, exp); exp.type = exp.e1.type; Type tb = exp.type.toBasetype(); - if (tb.ty == Tarray || tb.ty == Tsarray) + if (tb.isStaticOrDynamicArray()) { if (!isArrayOpValid(exp.e1)) { @@ -8900,9 +8911,8 @@ version (IN_LLVM) result = exp.incompatibleTypes(); return; } - if (exp.e1.checkNoBool()) - return setError(); - if (exp.e1.checkArithmetic(exp.op) || + if (exp.e1.checkNoBool() || + exp.e1.checkArithmetic(exp.op) || exp.e1.checkSharedAccess(sc)) return setError(); @@ -8915,10 +8925,8 @@ version (IN_LLVM) { printf("UAddExp::semantic('%s')\n", exp.toChars()); } - assert(!exp.type); - Expression e = exp.op_overload(sc); - if (e) + if (Expression e = exp.opOverloadUnary(sc)) { result = e; return; @@ -8930,11 +8938,10 @@ version (IN_LLVM) result = exp.incompatibleTypes(); return; } - if (exp.e1.checkNoBool()) - return setError(); - if (exp.e1.checkArithmetic(exp.op)) - return setError(); - if (exp.e1.checkSharedAccess(sc)) + + if (exp.e1.checkNoBool() || + exp.e1.checkArithmetic(exp.op) || + exp.e1.checkSharedAccess(sc)) return setError(); result = exp.e1; @@ -8942,14 +8949,8 @@ version (IN_LLVM) override void visit(ComExp exp) { - if (exp.type) - { - result = exp; - return; - } - Expression e = exp.op_overload(sc); - if (e) + if (Expression e = exp.opOverloadUnary(sc)) { result = e; return; @@ -8958,7 +8959,7 @@ version (IN_LLVM) fix16997(sc, exp); exp.type = exp.e1.type; Type tb = exp.type.toBasetype(); - if (tb.ty == Tarray || tb.ty == Tsarray) + if (tb.isStaticOrDynamicArray()) { if (!isArrayOpValid(exp.e1)) { @@ -8973,9 +8974,8 @@ version (IN_LLVM) result = exp.incompatibleTypes(); return; } - if (exp.e1.checkNoBool()) - return setError(); - if (exp.e1.checkIntegral() || + if (exp.e1.checkNoBool() || + exp.e1.checkIntegral() || exp.e1.checkSharedAccess(sc)) return setError(); @@ -8984,11 +8984,6 @@ version (IN_LLVM) override void visit(NotExp e) { - if (e.type) - { - result = e; - return; - } e.setNoderefOperand(); @@ -9020,23 +9015,12 @@ version (IN_LLVM) if (checkNonAssignmentArrayOp(e.e1)) return setError(); - e.type = (sc && sc.flags & SCOPE.Cfile) ? Type.tint32 : Type.tbool; + e.type = (sc && sc.inCfile) ? Type.tint32 : Type.tbool; result = e; } override void visit(DeleteExp exp) { - // @@@DEPRECATED_2.109@@@ - // 1. Deprecated since 2.079 - // 2. Error since 2.099 - // 3. Removal of keyword, "delete" can be used for other identities - if (!exp.isRAII) - { - error(exp.loc, "the `delete` keyword is obsolete"); - errorSupplemental(exp.loc, "use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead"); - return setError(); - } - Expression e = exp; if (Expression ex = unaSemantic(exp, sc)) @@ -9093,13 +9077,8 @@ version (IN_LLVM) printf("CastExp::semantic('%s')\n", exp.toChars()); } //static int x; assert(++x < 10); - if (exp.type) - { - result = exp; - return; - } - if ((sc && sc.flags & SCOPE.Cfile) && + if ((sc && sc.inCfile) && exp.to && (exp.to.ty == Tident || exp.to.ty == Tsarray) && (exp.e1.op == EXP.address || exp.e1.op == EXP.star || exp.e1.op == EXP.uadd || exp.e1.op == EXP.negate)) @@ -9153,6 +9132,14 @@ version (IN_LLVM) return; } + // https://issues.dlang.org/show_bug.cgi?id=24701 + auto r = checkNoreturnVarAccess(exp.e1); + if (r != exp.e1 && exp.to && !exp.to.isTypeNoreturn()) + { + result = r; + return; + } + if (exp.to && !exp.to.isTypeSArray() && !exp.to.isTypeFunction()) exp.e1 = exp.e1.arrayFuncConv(sc); @@ -9172,7 +9159,7 @@ version (IN_LLVM) if (!exp.e1.type) { - error(exp.loc, "cannot cast `%s`", exp.e1.toChars()); + error(exp.loc, "cannot cast `%s`", exp.e1.toErrMsg()); return setError(); } @@ -9210,7 +9197,7 @@ version (IN_LLVM) if (exp.to.ty == Ttuple) { - error(exp.loc, "cannot cast `%s` of type `%s` to type sequence `%s`", exp.e1.toChars(), exp.e1.type.toChars(), exp.to.toChars()); + error(exp.loc, "cannot cast `%s` of type `%s` to type sequence `%s`", exp.e1.toErrMsg(), exp.e1.type.toChars(), exp.to.toChars()); return setError(); } @@ -9224,7 +9211,7 @@ version (IN_LLVM) if (!exp.to.equals(exp.e1.type) && exp.mod == cast(ubyte)~0) { - if (Expression e = exp.op_overload(sc)) + if (Expression e = exp.opOverloadCast(sc)) { result = e.implicitCastTo(sc, exp.to); return; @@ -9253,7 +9240,7 @@ version (IN_LLVM) } } - if (!t1b.equals(tob) && (t1b.ty == Tarray || t1b.ty == Tsarray)) + if (!t1b.equals(tob) && t1b.isStaticOrDynamicArray()) { if (checkNonAssignmentArrayOp(exp.e1)) return setError(); @@ -9271,20 +9258,20 @@ version (IN_LLVM) if (!isSafeCast(ex, t1b, tob, msg)) { if (sc.setUnsafe(false, exp.loc, - "cast from `%s` to `%s` not allowed in safe code", exp.e1.type, exp.to)) + "cast from `%s` to `%s`", exp.e1.type, exp.to)) { if (msg.length) - errorSupplemental(exp.loc, "%s", (msg ~ '\0').ptr); + errorSupplemental(exp.loc, "%.*s", msg.fTuple.expand); return setError(); } } else if (msg.length) // deprecated unsafe { const err = sc.setUnsafePreview(FeatureState.default_, false, exp.loc, - "cast from `%s` to `%s` not allowed in safe code", exp.e1.type, exp.to); + "cast from `%s` to `%s`", exp.e1.type, exp.to); // if message was printed if (sc.func && sc.func.isSafeBypassingInference() && !sc.isDeprecated()) - deprecationSupplemental(exp.loc, "%s", (msg ~ '\0').ptr); + deprecationSupplemental(exp.loc, "%.*s", msg.fTuple.expand); if (err) return setError(); } @@ -9353,7 +9340,7 @@ version (IN_LLVM) } } - if (sc && sc.flags & SCOPE.Cfile) + if (sc && sc.inCfile) { /* C11 6.5.4-5: A cast does not yield an lvalue. * So ensure that castTo does not strip away the cast so that this @@ -9374,11 +9361,6 @@ version (IN_LLVM) { printf("VectorExp::semantic('%s')\n", exp.toChars()); } - if (exp.type) - { - result = exp; - return; - } exp.e1 = exp.e1.expressionSemantic(sc); exp.type = exp.to.typeSemantic(exp.loc, sc); @@ -9399,7 +9381,7 @@ version (IN_LLVM) if (elem.isConst() == 1) return false; - error(exp.loc, "constant expression expected, not `%s`", elem.toChars()); + error(exp.loc, "constant expression expected, not `%s`", elem.toErrMsg()); return true; } @@ -9447,11 +9429,6 @@ version (IN_LLVM) { printf("SliceExp::semantic('%s')\n", exp.toChars()); } - if (exp.type) - { - result = exp; - return; - } // operator overloading should be handled in ArrayExp already. if (Expression ex = unaSemantic(exp, sc)) @@ -9464,7 +9441,7 @@ version (IN_LLVM) { if (exp.lwr || exp.upr) { - error(exp.loc, "cannot slice type `%s`", exp.e1.toChars()); + error(exp.loc, "cannot slice type `%s`", exp.e1.toErrMsg()); return setError(); } Expression e = new TypeExp(exp.loc, exp.e1.type.arrayOf()); @@ -9516,7 +9493,7 @@ version (IN_LLVM) { if (t1b.isPtrToFunction()) { - error(exp.loc, "cannot slice function pointer `%s`", exp.e1.toChars()); + error(exp.loc, "cannot slice function pointer `%s`", exp.e1.toErrMsg()); return setError(); } if (!exp.lwr || !exp.upr) @@ -9524,8 +9501,8 @@ version (IN_LLVM) error(exp.loc, "upper and lower bounds are needed to slice a pointer"); if (auto ad = isAggregate(tp.next.toBasetype())) { - auto s = search_function(ad, Id.index); - if (!s) s = search_function(ad, Id.slice); + auto s = search_function(ad, Id.opIndex); + if (!s) s = search_function(ad, Id.opSlice); if (s) { auto fd = s.isFuncDeclaration(); @@ -9533,9 +9510,9 @@ version (IN_LLVM) { errorSupplemental(exp.loc, "pointer `%s` points to an aggregate that defines an `%s`, perhaps you meant `(*%s)[]`", - exp.e1.toChars(), + exp.e1.toErrMsg(), s.ident.toChars(), - exp.e1.toChars() + exp.e1.toErrMsg() ); } @@ -9544,7 +9521,7 @@ version (IN_LLVM) return setError(); } - if (sc.setUnsafe(false, exp.loc, "pointer slicing not allowed in safe functions")) + if (sc.setUnsafe(false, exp.loc, "pointer slicing")) return setError(); } else if (t1b.ty == Tarray) @@ -9576,14 +9553,14 @@ version (IN_LLVM) } else { - error(exp.loc, "`%s` cannot be sliced with `[]`", t1b.ty == Tvoid ? exp.e1.toChars() : t1b.toChars()); + error(exp.loc, "`%s` cannot be sliced with `[]`", t1b.ty == Tvoid ? exp.e1.toErrMsg() : t1b.toChars()); return setError(); } /* Run semantic on lwr and upr. */ Scope* scx = sc; - if (t1b.ty == Tsarray || t1b.ty == Tarray || t1b.ty == Ttuple) + if (t1b.isStaticOrDynamicArray() || t1b.ty == Ttuple) { // Create scope for 'length' variable ScopeDsymbol sym = new ArrayScopeSymbol(sc, exp); @@ -9690,7 +9667,7 @@ version (IN_LLVM) IntRange lwrRange = getIntRange(exp.lwr); IntRange uprRange = getIntRange(exp.upr); - if (t1b.ty == Tsarray || t1b.ty == Tarray) + if (t1b.isStaticOrDynamicArray()) { Expression el = new ArrayLengthExp(exp.loc, exp.e1); el = el.expressionSemantic(sc); @@ -9734,11 +9711,6 @@ version (IN_LLVM) { printf("ArrayLengthExp::semantic('%s')\n", e.toChars()); } - if (e.type) - { - result = e; - return; - } if (Expression ex = unaSemantic(e, sc)) { @@ -9757,9 +9729,8 @@ version (IN_LLVM) { printf("ArrayExp::semantic('%s')\n", exp.toChars()); } - assert(!exp.type); - if (sc.flags & SCOPE.Cfile) + if (sc.inCfile) { /* See if need to rewrite the AST because of cast/call ambiguity */ @@ -9774,15 +9745,48 @@ version (IN_LLVM) if (result) return; - Expression e = exp.op_overload(sc); - if (e) + if (Expression e = exp.opOverloadArray(sc)) { result = e; return; } - if (isAggregate(exp.e1.type)) - error(exp.loc, "no `[]` operator overload for type `%s`", exp.e1.type.toChars()); + if (auto ad = isAggregate(exp.e1.type)) + { + if (exp.arguments.length == 0) + { + error(exp.loc, "no `[]` operator overload for type `%s`", exp.e1.type.toChars()); + errorSupplemental(ad.loc, "perhaps define `auto opIndex() {}` for `%s`", ad.toPrettyChars()); + } + else + { + const(char)* typeString(Expression exp) + { + if (auto e = exp.trySemantic(sc)) + return e.type.toChars(); + else + return "__error__"; + } + + if (auto ie = (*exp.arguments)[0].isIntervalExp()) + { + error(exp.loc, "no `[%s]` operator overload for type `%s`", ie.toChars(), exp.e1.type.toChars()); + errorSupplemental(ad.loc, "perhaps define `auto opSlice(%s lower, %s upper) {}` for `%s`", + typeString(ie.lwr), typeString(ie.upr), ad.toPrettyChars()); + } + else + { + OutBuffer buf; + buf.printf("%s", typeString((*exp.arguments)[0])); + foreach (e; (*exp.arguments)[1 .. $]) + buf.printf(", %s", typeString(e)); + + error(exp.loc, "no `[]` operator overload for type `%s`", exp.e1.type.toChars()); + errorSupplemental(ad.loc, "perhaps define `auto opIndex(%s) {}` for `%s`", + buf.extractChars, ad.toPrettyChars()); + } + } + } else if (exp.e1.op == EXP.type && exp.e1.type.ty != Ttuple) error(exp.loc, "static array of `%s` with multiple lengths not allowed", exp.e1.type.toChars()); else if (isIndexableNonAggregate(exp.e1.type)) @@ -9828,11 +9832,6 @@ version (IN_LLVM) override void visit(CommaExp e) { //printf("Semantic.CommaExp() %s\n", e.toChars()); - if (e.type) - { - result = e; - return; - } // Allow `((a,b),(x,y))` if (e.allowCommaExp) @@ -9857,16 +9856,19 @@ version (IN_LLVM) e.type = e.e2.type; result = e; - if (sc.flags & SCOPE.Cfile) + if (sc.inCfile) return; - if (e.type is Type.tvoid) + if (!e.isGenerated) { - checkMustUse(e.e1, sc); - discardValue(e.e1); + if (e.allowCommaExp) + { + checkMustUse(e.e1, sc); + discardValue(e.e1); + } + else + error(e.loc, "using the result of a comma expression is not allowed"); } - else if (!e.allowCommaExp && !e.isGenerated) - error(e.loc, "using the result of a comma expression is not allowed"); } override void visit(IntervalExp e) @@ -9875,11 +9877,6 @@ version (IN_LLVM) { printf("IntervalExp::semantic('%s')\n", e.toChars()); } - if (e.type) - { - result = e; - return; - } Expression le = e.lwr; le = le.expressionSemantic(sc); @@ -9954,11 +9951,6 @@ version (IN_LLVM) { printf("IndexExp::semantic('%s')\n", exp.toChars()); } - if (exp.type) - { - result = exp; - return; - } // operator overloading should be handled in ArrayExp already. if (!exp.e1.type) @@ -9996,7 +9988,7 @@ version (IN_LLVM) t1b = t1b.castMod(tv1.mod); exp.e1 = exp.e1.castTo(sc, t1b); } - if (t1b.ty == Tsarray || t1b.ty == Tarray) + if (t1b.isStaticOrDynamicArray()) { if (!checkAddressable(exp, sc)) return setError(); @@ -10005,7 +9997,7 @@ version (IN_LLVM) /* Run semantic on e2 */ Scope* scx = sc; - if (t1b.ty == Tsarray || t1b.ty == Tarray || t1b.ty == Ttuple) + if (t1b.isStaticOrDynamicArray() || t1b.ty == Ttuple) { // Create scope for 'length' variable ScopeDsymbol sym = new ArrayScopeSymbol(sc, exp); @@ -10037,7 +10029,7 @@ version (IN_LLVM) case Tpointer: if (t1b.isPtrToFunction()) { - error(exp.loc, "cannot index function pointer `%s`", exp.e1.toChars()); + error(exp.loc, "cannot index function pointer `%s`", exp.e1.toErrMsg()); return setError(); } exp.e2 = exp.e2.implicitCastTo(sc, Type.tsize_t); @@ -10047,7 +10039,7 @@ version (IN_LLVM) if (exp.e2.op == EXP.int64 && exp.e2.toInteger() == 0) { } - else if (sc.setUnsafe(false, exp.loc, "`@safe` function `%s` cannot index pointer `%s`", sc.func, exp.e1)) + else if (sc.setUnsafe(false, exp.loc, "indexing pointer `%s`", exp.e1)) { return setError(); } @@ -10134,14 +10126,14 @@ version (IN_LLVM) return; } default: - error(exp.loc, "`%s` must be an array or pointer type, not `%s`", exp.e1.toChars(), exp.e1.type.toChars()); + error(exp.loc, "`%s` must be an array or pointer type, not `%s`", exp.e1.toErrMsg(), exp.e1.type.toChars()); return setError(); } // We might know $ now setLengthVarIfKnown(exp.lengthVar, t1b); - if (t1b.ty == Tsarray || t1b.ty == Tarray) + if (t1b.isStaticOrDynamicArray()) { Expression el = new ArrayLengthExp(exp.loc, exp.e1); el = el.expressionSemantic(sc); @@ -10156,7 +10148,7 @@ version (IN_LLVM) // OR it in, because it might already be set for C array indexing exp.indexIsInBounds |= bounds.contains(getIntRange(exp.e2)); } - else if (sc.flags & SCOPE.Cfile && t1b.ty == Tsarray) + else if (sc.inCfile && t1b.ty == Tsarray) { if (auto ve = exp.e1.isVarExp()) { @@ -10181,13 +10173,8 @@ version (IN_LLVM) { printf("PostExp::semantic('%s')\n", exp.toChars()); } - if (exp.type) - { - result = exp; - return; - } - if (sc.flags & SCOPE.Cfile) + if (sc.inCfile) { /* See if need to rewrite the AST because of cast/call ambiguity */ @@ -10211,20 +10198,13 @@ version (IN_LLVM) } exp.e1 = e1x; - Expression e = exp.op_overload(sc); - if (e) - { - result = e; - return; - } - if (exp.e1.checkReadModifyWrite(exp.op)) return setError(); if (exp.e1.op == EXP.slice) { const(char)* s = exp.op == EXP.plusPlus ? "increment" : "decrement"; - error(exp.loc, "cannot post-%s array slice `%s`, use pre-%s instead", s, exp.e1.toChars(), s); + error(exp.loc, "cannot post-%s array slice `%s`, use pre-%s instead", s, exp.e1.toErrMsg(), s); return setError(); } @@ -10249,7 +10229,7 @@ version (IN_LLVM) /* Rewrite as: * auto tmp = e1; ++e1; tmp */ - auto tmp = copyToTemp(0, "__pitmp", exp.e1); + auto tmp = copyToTemp(STC.none, "__pitmp", exp.e1); Expression ea = new DeclarationExp(exp.loc, tmp); Expression eb = exp.e1.syntaxCopy(); @@ -10260,7 +10240,7 @@ version (IN_LLVM) // Combine de,ea,eb,ec if (de) ea = new CommaExp(exp.loc, de, ea); - e = new CommaExp(exp.loc, ea, eb); + Expression e = new CommaExp(exp.loc, ea, eb); e = new CommaExp(exp.loc, e, ec); e = e.expressionSemantic(sc); result = e; @@ -10270,7 +10250,7 @@ version (IN_LLVM) exp.e1 = exp.e1.modifiableLvalue(sc); exp.e1 = exp.e1.optimize(WANTvalue, /*keepLvalue*/ true); - e = exp; + Expression e = exp; if (exp.e1.checkScalar() || exp.e1.checkSharedAccess(sc)) return setError(); @@ -10287,20 +10267,20 @@ version (IN_LLVM) override void visit(PreExp exp) { - Expression e = exp.op_overload(sc); // printf("PreExp::semantic('%s')\n", toChars()); - if (e) + if (Expression e = exp.opOverloadUnary(sc)) { result = e; return; } // Rewrite as e1+=1 or e1-=1 + Expression e; if (exp.op == EXP.prePlusPlus) e = new AddAssignExp(exp.loc, exp.e1, IntegerExp.literal!1); else e = new MinAssignExp(exp.loc, exp.e1, IntegerExp.literal!1); - result = e.expressionSemantic(sc); + result = e.expressionSemanticWithParent(sc, exp); } /* @@ -10343,9 +10323,9 @@ version (IN_LLVM) { static if (LOGSEMANTIC) { - if (exp.op == EXP.blit) printf("BlitExp.toElem('%s')\n", exp.toChars()); - if (exp.op == EXP.assign) printf("AssignExp.toElem('%s')\n", exp.toChars()); - if (exp.op == EXP.construct) printf("ConstructExp.toElem('%s')\n", exp.toChars()); + if (exp.op == EXP.blit) printf("BlitExp.semantic('%s')\n", exp.toChars()); + if (exp.op == EXP.assign) printf("AssignExp.semantic('%s')\n", exp.toChars()); + if (exp.op == EXP.construct) printf("ConstructExp.semantic('%s')\n", exp.toChars()); } void setResult(Expression e, int line = __LINE__) @@ -10354,16 +10334,11 @@ version (IN_LLVM) result = e; } - if (exp.type) - { - return setResult(exp); - } - Expression e1old = exp.e1; if (auto e2comma = exp.e2.isCommaExp()) { - if (!e2comma.isGenerated && !(sc.flags & SCOPE.Cfile)) + if (!e2comma.isGenerated && !sc.inCfile) error(exp.loc, "using the result of a comma expression is not allowed"); /* Rewrite to get rid of the comma from rvalue @@ -10411,10 +10386,10 @@ version (IN_LLVM) AggregateDeclaration ad = isAggregate(t1b); if (!ad) break; - if (search_function(ad, Id.indexass)) + if (search_function(ad, Id.opIndexAssign)) { // Deal with $ - res = resolveOpDollar(sc, ae, &e0); + res = resolveOpDollar(sc, ae, e0); if (!res) // a[i..j] = e2 might be: a.opSliceAssign(e2, i, j) goto Lfallback; if (res.op == EXP.error) @@ -10430,7 +10405,7 @@ version (IN_LLVM) */ Expressions* a = ae.arguments.copy(); a.insert(0, exp.e2); - res = new DotIdExp(exp.loc, ae.e1, Id.indexass); + res = new DotIdExp(exp.loc, ae.e1, Id.opIndexAssign); res = new CallExp(exp.loc, res, a); if (maybeSlice) // a[] = e2 might be: a.opSliceAssign(e2) res = res.trySemantic(sc); @@ -10441,10 +10416,10 @@ version (IN_LLVM) } Lfallback: - if (maybeSlice && search_function(ad, Id.sliceass)) + if (maybeSlice && search_function(ad, Id.opSliceAssign)) { // Deal with $ - res = resolveOpDollar(sc, ae, ie, &e0); + res = resolveOpDollar(sc, ae, ie, e0); if (res.op == EXP.error) return setResult(res); @@ -10464,7 +10439,7 @@ version (IN_LLVM) a.push(ie.lwr); a.push(ie.upr); } - res = new DotIdExp(exp.loc, ae.e1, Id.sliceass); + res = new DotIdExp(exp.loc, ae.e1, Id.opSliceAssign); res = new CallExp(exp.loc, res, a); res = res.expressionSemantic(sc); return setResult(Expression.combine(e0, res)); @@ -10508,7 +10483,7 @@ version (IN_LLVM) e1x = e; } - else if (sc.flags & SCOPE.Cfile && e1x.isDotIdExp()) + else if (sc.inCfile && e1x.isDotIdExp()) { auto die = e1x.isDotIdExp(); e1x = fieldLookup(die.e1, sc, die.ident, die.arrow); @@ -10528,7 +10503,7 @@ version (IN_LLVM) */ auto ode = e; exp.e2 = exp.e2.expressionSemantic(sc); - uint errors = global.startGagging(); + const errors = global.startGagging(); e = resolvePropertiesX(sc, e, exp.e2); // Any error or if 'e' is not resolved, go to UFCS if (global.endGagging(errors) || e is ode) @@ -10554,7 +10529,7 @@ version (IN_LLVM) * or: * f() = value */ - if (Expression e = resolvePropertiesX(sc, e1x, exp.e2, exp)) + if (Expression e = resolvePropertiesX(sc, e1x, exp.e2, &aliasThisStop)) return setResult(e); if (e1x.checkRightThis(sc)) @@ -10687,6 +10662,35 @@ version (IN_LLVM) if (exp.op == EXP.assign && exp.e1.checkModifiable(sc) == Modifiable.initialization) { + // Check common mistake of misspelled parameters in constructors, + // e.g. `this(int feild) { this.field = field; }` + if (auto dve1 = exp.e1.isDotVarExp) + if (auto dve2 = exp.e2.isDotVarExp) + if (sc.func && sc.func.parameters && dve1.e1.isThisExp && dve2.e1.isThisExp() + && dve1.var.ident.equals(dve2.var.ident)) + { + // @@@DEPRECATED_2.121@@@ + // Deprecated in 2.111, make it an error in 2.121 + deprecation(exp.e1.loc, "cannot initialize field `%s` with itself", dve1.var.toChars()); + auto findParameter(const(char)[] s, ref int cost) + { + foreach (p; *sc.func.parameters) + { + if (p.ident.toString == s) + { + cost = 1; + return p.ident.toString; + } + } + return null; + } + import dmd.root.speller : speller; + if (auto s = speller!findParameter(dve1.var.ident.toString)) + { + deprecationSupplemental(sc.func.loc, "did you mean to use parameter `%.*s`?\n", s.fTuple.expand); + } + } + //printf("[%s] change to init - %s\n", exp.loc.toChars(), exp.toChars()); auto t = exp.type; exp = new ConstructExp(exp.loc, exp.e1, exp.e2); @@ -10815,6 +10819,8 @@ version (IN_LLVM) /* We have a copy constructor for this */ + //printf("exp: %s\n", toChars(exp)); + //printf("e2x: %s\n", toChars(e2x)); if (e2x.isLvalue()) { if (sd.hasCopyCtor) @@ -10861,6 +10867,38 @@ version (IN_LLVM) return; } } + else if (sd.hasMoveCtor && (!e2x.isCallExp() || e2x.rvalue) && !e2x.isStructLiteralExp()) + { + // #move + /* The !e2x.isCallExp() is because it is already an rvalue + and the move constructor is unnecessary: + struct S { + alias TT this; + long TT(); + this(T)(int x) {} + this(S); + this(ref S); + ~this(); + } + S fun(ref S arg); + void test() { S st; fun(st); } + */ + /* Rewrite as: + * e1 = init, e1.moveCtor(e2); + */ + Expression einit = new BlitExp(exp.loc, exp.e1, getInitExp(sd, exp.loc, sc, t1)); + einit.type = e1x.type; + + Expression e; + e = new DotIdExp(exp.loc, e1x, Id.ctor); + e = new CallExp(exp.loc, e, e2x); + e = new CommaExp(exp.loc, einit, e); + + //printf("e: %s\n", e.toChars()); + + result = e.expressionSemantic(sc); + return; + } else { /* The struct value returned from the function is transferred @@ -10875,13 +10913,13 @@ version (IN_LLVM) if (!e2x.implicitConvTo(t1)) { AggregateDeclaration ad2 = isAggregate(e2x.type); - if (ad2 && ad2.aliasthis && !isRecursiveAliasThis(exp.att2, exp.e2.type)) + if (ad2 && ad2.aliasthis && !isRecursiveAliasThis(aliasThisStop[1], exp.e2.type)) { /* Rewrite (e1 op e2) as: * (e1 op e2.aliasthis) */ exp.e2 = new DotIdExp(exp.e2.loc, exp.e2, ad2.aliasthis.ident); - result = exp.expressionSemantic(sc); + result = exp.expressionSemantic(sc, aliasThisStop); return; } } @@ -10919,7 +10957,7 @@ version (IN_LLVM) if (newExp.newtype && newExp.newtype == t1) { error(exp.loc, "cannot implicitly convert expression `%s` of type `%s` to `%s`", - newExp.toChars(), newExp.type.toChars(), t1.toChars()); + newExp.toErrMsg(), newExp.type.toChars(), t1.toChars()); errorSupplemental(exp.loc, "Perhaps remove the `new` keyword?"); return setError(); } @@ -10936,14 +10974,14 @@ version (IN_LLVM) result = e; return; } - if (search_function(sd, Id.call)) + if (search_function(sd, Id.opCall)) { /* Look for static opCall * https://issues.dlang.org/show_bug.cgi?id=2702 * Rewrite as: * e1 = typeof(e1).opCall(arguments) */ - e2x = typeDotIdExp(e2x.loc, e1x.type, Id.call); + e2x = typeDotIdExp(e2x.loc, e1x.type, Id.opCall); e2x = new CallExp(exp.loc, e2x, exp.e2); e2x = e2x.expressionSemantic(sc); @@ -10960,13 +10998,13 @@ version (IN_LLVM) else // https://issues.dlang.org/show_bug.cgi?id=11355 { AggregateDeclaration ad2 = isAggregate(e2x.type); - if (ad2 && ad2.aliasthis && !isRecursiveAliasThis(exp.att2, exp.e2.type)) + if (ad2 && ad2.aliasthis && !isRecursiveAliasThis(aliasThisStop[1], exp.e2.type)) { /* Rewrite (e1 op e2) as: * (e1 op e2.aliasthis) */ exp.e2 = new DotIdExp(exp.e2.loc, exp.e2, ad2.aliasthis.ident); - result = exp.expressionSemantic(sc); + result = exp.expressionSemantic(sc, aliasThisStop); return; } } @@ -11007,8 +11045,7 @@ version (IN_LLVM) ae.e1 = ae.e1.expressionSemantic(sc); ae.e1 = ae.e1.optimize(WANTvalue); ae.e2 = ev; - Expression e = ae.op_overload(sc); - if (e) + if (Expression e = ae.opOverloadAssign(sc, aliasThisStop)) { Expression ey = null; if (t2.ty == Tstruct && sd == t2.toDsymbol(sc)) @@ -11058,14 +11095,10 @@ version (IN_LLVM) return; } } - else + else if (Expression e = exp.isAssignExp().opOverloadAssign(sc, aliasThisStop)) { - Expression e = exp.op_overload(sc); - if (e) - { - result = e; - return; - } + result = e; + return; } } else @@ -11082,8 +11115,7 @@ version (IN_LLVM) // Disallow assignment operator overloads for same type if (exp.op == EXP.assign && !exp.e2.implicitConvTo(exp.e1.type)) { - Expression e = exp.op_overload(sc); - if (e) + if (Expression e = exp.isAssignExp().opOverloadAssign(sc, aliasThisStop)) { result = e; return; @@ -11103,7 +11135,7 @@ version (IN_LLVM) * string to match the size of e1. */ Type t2 = e2x.type.toBasetype(); - if (sc.flags & SCOPE.Cfile && e2x.isStringExp() && t2.isTypeSArray()) + if (sc.inCfile && e2x.isStringExp() && t2.isTypeSArray()) { uinteger_t dim1 = t1.isTypeSArray().dim.toInteger(); uinteger_t dim2 = t2.isTypeSArray().dim.toInteger(); @@ -11216,6 +11248,11 @@ version (IN_LLVM) exp.e2 = e2x; t1 = e1x.type.toBasetype(); } + else if (t1.ty == Taarray) + { + // when assigning a constant, the need for TypeInfo might change + semanticTypeInfo(sc, t1); + } /* Check the mutability of e1. */ if (auto ale = exp.e1.isArrayLengthExp()) @@ -11264,14 +11301,6 @@ version (IN_LLVM) id = id.expressionSemantic(sc); auto arguments = new Expressions(); - arguments.reserve(5); - if (global.params.tracegc) - { - auto funcname = (sc.callsc && sc.callsc.func) ? sc.callsc.func.toPrettyChars() : sc.func.toPrettyChars(); - arguments.push(new StringExp(exp.loc, exp.loc.filename.toDString())); - arguments.push(new IntegerExp(exp.loc, exp.loc.linnum, Type.tint32)); - arguments.push(new StringExp(exp.loc, funcname.toDString())); - } arguments.push(ale.e1); arguments.push(exp.e2); @@ -11298,7 +11327,7 @@ version (IN_LLVM) if (exp.op == EXP.assign && !tn.baseElemOf().isAssignable()) { error(exp.loc, "slice `%s` is not mutable, struct `%s` has immutable members", - exp.e1.toChars(), tn.baseElemOf().toChars()); + exp.e1.toErrMsg(), tn.baseElemOf().toChars()); result = ErrorExp.get(); return; } @@ -11321,7 +11350,7 @@ version (IN_LLVM) if (tn && !tn.baseElemOf().isAssignable()) { error(exp.loc, "array `%s` is not mutable, struct `%s` has immutable members", - exp.e1.toChars(), tn.baseElemOf().toChars()); + exp.e1.toErrMsg(), tn.baseElemOf().toChars()); result = ErrorExp.get(); return; } @@ -11367,7 +11396,7 @@ version (IN_LLVM) return setError(); } else if (exp.e1.op == EXP.slice && - (t2.ty == Tarray || t2.ty == Tsarray) && + t2.isStaticOrDynamicArray() && t2.nextOf().implicitConvTo(t1.nextOf())) { // Check element-wise assignment. @@ -11391,7 +11420,7 @@ version (IN_LLVM) uinteger_t dim2 = tsa2.dim.toInteger(); if (dim1 != dim2) { - error(exp.loc, "mismatched array lengths %d and %d for assignment `%s`", cast(int)dim1, cast(int)dim2, exp.toChars()); + error(exp.loc, "mismatched array lengths %d and %d for assignment `%s`", cast(int)dim1, cast(int)dim2, exp.toErrMsg()); return setError(); } } @@ -11459,7 +11488,7 @@ version (IN_LLVM) if (e2x.op == EXP.error && exp.op == EXP.construct && t1.ty == Tstruct) { scope sd = (cast(TypeStruct)t1).sym; - Dsymbol opAssign = search_function(sd, Id.assign); + Dsymbol opAssign = search_function(sd, Id.opAssign); // and the struct defines an opAssign if (opAssign) @@ -11467,7 +11496,7 @@ version (IN_LLVM) // offer more information about the cause of the problem errorSupplemental(exp.loc, "`%s` is the first assignment of `%s` therefore it represents its initialization", - exp.toChars(), exp.e1.toChars()); + exp.toErrMsg(), exp.e1.toErrMsg()); errorSupplemental(exp.loc, "`opAssign` methods are not used for initialization, but for subsequent assignments"); } @@ -11476,22 +11505,22 @@ version (IN_LLVM) } if (exp.e1.op == EXP.slice && - (t1.ty == Tarray || t1.ty == Tsarray) && + t1.isStaticOrDynamicArray() && t1.nextOf().toBasetype().ty == Tvoid) { if (t2.nextOf().implicitConvTo(t1.nextOf())) { - if (sc.setUnsafe(false, exp.loc, "cannot copy `%s` to `%s` in `@safe` code", t2, t1)) + if (sc.setUnsafe(false, exp.loc, "copying `%s` to `%s`", t2, t1)) return setError(); } else { // copying from non-void to void was overlooked, deprecate if (sc.setUnsafePreview(FeatureState.default_, false, exp.loc, - "cannot copy `%s` to `%s` in `@safe` code", t2, t1)) + "copying `%s` to `%s`", t2, t1)) return setError(); } - if (global.params.fixImmutableConv && !t2.implicitConvTo(t1)) + if (sc.previews.fixImmutableConv && !t2.implicitConvTo(t1)) { error(exp.loc, "cannot copy `%s` to `%s`", t2.toChars(), t1.toChars()); @@ -11511,7 +11540,7 @@ version (IN_LLVM) /* Look for array operations */ - if ((t2.ty == Tarray || t2.ty == Tsarray) && isArrayOpValid(exp.e2)) + if (t2.isStaticOrDynamicArray() && isArrayOpValid(exp.e2)) { // Look for valid array operations if (exp.memset != MemorySet.blockAssign && @@ -11688,7 +11717,7 @@ version (IN_LLVM) return ae; const isArrayAssign = (ae.e1.isSliceExp() || ae.e1.type.ty == Tsarray) && - (ae.e2.type.ty == Tsarray || ae.e2.type.ty == Tarray) && + (ae.e2.type.isStaticOrDynamicArray()) && (ae.e1.type.nextOf() && ae.e2.type.nextOf() && ae.e1.type.nextOf.mutableOf.equals(ae.e2.type.nextOf.mutableOf())); const isArraySetAssign = (ae.e1.isSliceExp() || ae.e1.type.ty == Tsarray) && @@ -11728,7 +11757,7 @@ version (IN_LLVM) // `__assigntmp` will be destroyed together with the array `ae.e1`. // When `ae.e2` is a variadic arg array, it is also `scope`, so // `__assigntmp` may also be scope. - StorageClass stc = STC.nodtor; + STC stc = STC.nodtor; if (isArrayAssign) stc |= STC.rvalue | STC.scope_; @@ -11754,24 +11783,18 @@ version (IN_LLVM) override void visit(PowAssignExp exp) { - if (exp.type) - { - result = exp; - return; - } - - Expression e = exp.op_overload(sc); - if (e) + if (Expression e = exp.opOverloadBinaryAssign(sc, aliasThisStop)) { result = e; return; } - if (exp.e1.checkReadModifyWrite(exp.op, exp.e2)) + if (exp.suggestOpOpAssign(sc, parent) || + exp.e1.checkReadModifyWrite(exp.op, exp.e2)) return setError(); assert(exp.e1.type && exp.e2.type); - if (exp.e1.op == EXP.slice || exp.e1.type.ty == Tarray || exp.e1.type.ty == Tsarray) + if (exp.e1.op == EXP.slice || exp.e1.type.isStaticOrDynamicArray()) { if (checkNonAssignmentArrayOp(exp.e1)) return setError(); @@ -11791,9 +11814,9 @@ version (IN_LLVM) // Check element types are arithmetic Type tb1 = exp.e1.type.nextOf().toBasetype(); Type tb2 = exp.e2.type.toBasetype(); - if (tb2.ty == Tarray || tb2.ty == Tsarray) + if (tb2.isStaticOrDynamicArray()) tb2 = tb2.nextOf().toBasetype(); - if ((tb1.isintegral() || tb1.isfloating()) && (tb2.isintegral() || tb2.isfloating())) + if ((tb1.isIntegral() || tb1.isFloating()) && (tb2.isIntegral() || tb2.isFloating())) { exp.type = exp.e1.type; result = arrayOp(exp, sc); @@ -11805,10 +11828,10 @@ version (IN_LLVM) exp.e1 = exp.e1.modifiableLvalue(sc); } - if ((exp.e1.type.isintegral() || exp.e1.type.isfloating()) && (exp.e2.type.isintegral() || exp.e2.type.isfloating())) + if ((exp.e1.type.isIntegral() || exp.e1.type.isFloating()) && (exp.e2.type.isIntegral() || exp.e2.type.isFloating())) { Expression e0 = null; - e = exp.reorderSettingAAElem(sc); + Expression e = exp.reorderSettingAAElem(sc); e = Expression.extractLast(e, e0); assert(e == exp); @@ -11838,20 +11861,17 @@ version (IN_LLVM) override void visit(CatAssignExp exp) { - if (exp.type) - { - result = exp; - return; - } //printf("CatAssignExp::semantic() %s\n", exp.toChars()); - Expression e = exp.op_overload(sc); - if (e) + if (Expression e = exp.opOverloadBinaryAssign(sc, aliasThisStop)) { result = e; return; } + if (exp.suggestOpOpAssign(sc, parent)) + return setError(); + if (SliceExp se = exp.e1.isSliceExp()) { if (se.e1.type.toBasetype().ty == Tsarray) @@ -11886,11 +11906,11 @@ version (IN_LLVM) * EXP.concatenateDcharAssign: appending dchar to T[] */ if ((tb1.ty == Tarray) && - (tb2.ty == Tarray || tb2.ty == Tsarray) && + tb2.isStaticOrDynamicArray() && (exp.e2.implicitConvTo(exp.e1.type) || (tb2.nextOf().implicitConvTo(tb1next) && // Do not strip const(void)[] - (!global.params.fixImmutableConv || tb1next.ty != Tvoid) && + (!sc.previews.fixImmutableConv || tb1next.ty != Tvoid) && (tb2.nextOf().size(Loc.initial) == tb1next.size(Loc.initial))))) { // EXP.concatenateAssign @@ -11924,7 +11944,7 @@ version (IN_LLVM) ce.trusted = true; exp = new CatElemAssignExp(exp.loc, exp.type, exp.e1, ecast); - exp.e2 = doCopyOrMove(sc, exp.e2); + exp.e2 = doCopyOrMove(sc, exp.e2, null, false); } else if (tb1.ty == Tarray && (tb1next.ty == Tchar || tb1next.ty == Twchar) && @@ -11940,49 +11960,12 @@ version (IN_LLVM) } else { - // Try alias this on first operand - static Expression tryAliasThisForLhs(BinAssignExp exp, Scope* sc) - { - AggregateDeclaration ad1 = isAggregate(exp.e1.type); - if (!ad1 || !ad1.aliasthis) - return null; - - /* Rewrite (e1 op e2) as: - * (e1.aliasthis op e2) - */ - if (isRecursiveAliasThis(exp.att1, exp.e1.type)) - return null; - //printf("att %s e1 = %s\n", Token.toChars(e.op), e.e1.type.toChars()); - Expression e1 = new DotIdExp(exp.loc, exp.e1, ad1.aliasthis.ident); - BinExp be = cast(BinExp)exp.copy(); - be.e1 = e1; - return be.trySemantic(sc); - } - - // Try alias this on second operand - static Expression tryAliasThisForRhs(BinAssignExp exp, Scope* sc) - { - AggregateDeclaration ad2 = isAggregate(exp.e2.type); - if (!ad2 || !ad2.aliasthis) - return null; - /* Rewrite (e1 op e2) as: - * (e1 op e2.aliasthis) - */ - if (isRecursiveAliasThis(exp.att2, exp.e2.type)) - return null; - //printf("att %s e2 = %s\n", Token.toChars(e.op), e.e2.type.toChars()); - Expression e2 = new DotIdExp(exp.loc, exp.e2, ad2.aliasthis.ident); - BinExp be = cast(BinExp)exp.copy(); - be.e2 = e2; - return be.trySemantic(sc); - } - Laliasthis: - result = tryAliasThisForLhs(exp, sc); + result = checkAliasThisForLhs(isAggregate(exp.e1.type), sc, exp, aliasThisStop); if (result) return; - result = tryAliasThisForRhs(exp, sc); + result = checkAliasThisForRhs(isAggregate(exp.e2.type), sc, exp, aliasThisStop); if (result) return; @@ -12035,15 +12018,6 @@ version (IN_LLVM) id = new DotIdExp(exp.loc, id, hook); auto arguments = new Expressions(); - arguments.reserve(5); - if (global.params.tracegc) - { - auto funcname = (sc.callsc && sc.callsc.func) ? sc.callsc.func.toPrettyChars() : sc.func.toPrettyChars(); - arguments.push(new StringExp(exp.loc, exp.loc.filename.toDString())); - arguments.push(new IntegerExp(exp.loc, exp.loc.linnum, Type.tint32)); - arguments.push(new StringExp(exp.loc, funcname.toDString())); - } - arguments.push(exp.e1); arguments.push(exp.e2); Expression ce = new CallExp(exp.loc, id, arguments); @@ -12078,15 +12052,6 @@ version (IN_LLVM) id = new DotIdExp(exp.loc, id, hook); auto arguments = new Expressions(); - arguments.reserve(5); - if (global.params.tracegc) - { - auto funcname = (sc.callsc && sc.callsc.func) ? sc.callsc.func.toPrettyChars() : sc.func.toPrettyChars(); - arguments.push(new StringExp(exp.loc, exp.loc.filename.toDString())); - arguments.push(new IntegerExp(exp.loc, exp.loc.linnum, Type.tint32)); - arguments.push(new StringExp(exp.loc, funcname.toDString())); - } - Expression eValue1; Expression value1 = extractSideEffect(sc, "__appendtmp", eValue1, exp.e1); @@ -12134,19 +12099,8 @@ version (IN_LLVM) { printf("AddExp::semantic('%s')\n", exp.toChars()); } - if (exp.type) - { - result = exp; - return; - } - if (Expression ex = binSemanticProp(exp, sc)) - { - result = ex; - return; - } - Expression e = exp.op_overload(sc); - if (e) + if (Expression e = exp.opOverloadBinary(sc, aliasThisStop)) { result = e; return; @@ -12172,13 +12126,14 @@ version (IN_LLVM) if (err) return setError(); - if (tb1.ty == Tpointer && exp.e2.type.isintegral() || tb2.ty == Tpointer && exp.e1.type.isintegral()) + if (tb1.ty == Tpointer && exp.e2.type.isIntegral() || tb2.ty == Tpointer && exp.e1.type.isIntegral()) { result = scaleFactor(exp, sc); return; } - if (tb1.ty == Tpointer && tb2.ty == Tpointer) + if (tb1.ty == Tpointer && tb2.ty == Tpointer || + tb1.ty == Tnull && tb2.ty == Tnull) { result = exp.incompatibleTypes(); return; @@ -12191,7 +12146,7 @@ version (IN_LLVM) } Type tb = exp.type.toBasetype(); - if (tb.ty == Tarray || tb.ty == Tsarray) + if (tb.isStaticOrDynamicArray()) { if (!isArrayOpValid(exp)) { @@ -12202,13 +12157,16 @@ version (IN_LLVM) return; } + if (exp.suggestBinaryOverloads(sc) || exp.checkArithmeticBin()) + return setError(); + tb1 = exp.e1.type.toBasetype(); if (!target.isVectorOpSupported(tb1, exp.op, tb2)) { result = exp.incompatibleTypes(); return; } - if ((tb1.isreal() && exp.e2.type.isimaginary()) || (tb1.isimaginary() && exp.e2.type.isreal())) + if ((tb1.isReal() && exp.e2.type.isImaginary()) || (tb1.isImaginary() && exp.e2.type.isReal())) { switch (exp.type.toBasetype().ty) { @@ -12240,19 +12198,8 @@ version (IN_LLVM) { printf("MinExp::semantic('%s')\n", exp.toChars()); } - if (exp.type) - { - result = exp; - return; - } - if (Expression ex = binSemanticProp(exp, sc)) - { - result = ex; - return; - } - Expression e = exp.op_overload(sc); - if (e) + if (Expression e = exp.opOverloadBinary(sc, aliasThisStop)) { result = e; return; @@ -12275,11 +12222,17 @@ version (IN_LLVM) { err |= exp.e2.checkArithmetic(exp.op) || exp.e2.checkSharedAccess(sc); } + if (t1.ty == Tnull && t2.ty == Tnull) + { + exp.incompatibleTypes(); + return setError(); + } if (err) return setError(); if (t1.ty == Tpointer) { + Expression e; if (t2.ty == Tpointer) { // https://dlang.org/spec/expression.html#add_expressions @@ -12291,12 +12244,10 @@ version (IN_LLVM) if (!p1.equivalent(p2)) { - // Deprecation to remain for at least a year, after which this should be - // changed to an error // See https://github.com/dlang/dmd/pull/7332 - deprecation(exp.loc, - "cannot subtract pointers to different types: `%s` and `%s`.", + error(exp.loc, "cannot subtract pointers to different types: `%s` and `%s`.", t1.toChars(), t2.toChars()); + return setError(); } // Need to divide the result by the stride @@ -12324,7 +12275,7 @@ version (IN_LLVM) e.type = Type.tptrdiff_t; } } - else if (t2.isintegral()) + else if (t2.isIntegral()) e = scaleFactor(exp, sc); else { @@ -12348,7 +12299,7 @@ version (IN_LLVM) } Type tb = exp.type.toBasetype(); - if (tb.ty == Tarray || tb.ty == Tsarray) + if (tb.isStaticOrDynamicArray()) { if (!isArrayOpValid(exp)) { @@ -12359,6 +12310,9 @@ version (IN_LLVM) return; } + if (exp.suggestBinaryOverloads(sc) || exp.checkArithmeticBin()) + return setError(); + t1 = exp.e1.type.toBasetype(); t2 = exp.e2.type.toBasetype(); if (!target.isVectorOpSupported(t1, exp.op, t2)) @@ -12366,7 +12320,7 @@ version (IN_LLVM) result = exp.incompatibleTypes(); return; } - if ((t1.isreal() && t2.isimaginary()) || (t1.isimaginary() && t2.isreal())) + if ((t1.isReal() && t2.isImaginary()) || (t1.isImaginary() && t2.isReal())) { switch (exp.type.ty) { @@ -12419,7 +12373,7 @@ version (IN_LLVM) return result; } - void handleCatArgument(Expressions *arguments, Expression e, Type catType, bool isRightArg) + void handleCatArgument(Expressions* arguments, Expression e, Type catType, bool isRightArg) { auto tb = e.type.toBasetype(); @@ -12441,7 +12395,7 @@ version (IN_LLVM) if (hook == Id._d_arraycatnTX) arguments.pushSlice((*callExp.arguments)[]); else - arguments.pushSlice((*callExp.arguments)[3 .. $]); + arguments.pushSlice((*callExp.arguments)[0 .. $ - 3]); } } else @@ -12449,15 +12403,6 @@ version (IN_LLVM) } auto arguments = new Expressions(); - if (useTraceGCHook) - { - auto funcname = (sc.callsc && sc.callsc.func) ? - sc.callsc.func.toPrettyChars() : sc.func.toPrettyChars(); - arguments.push(new StringExp(exp.loc, exp.loc.filename.toDString())); - arguments.push(new IntegerExp(exp.loc, exp.loc.linnum, Type.tint32)); - arguments.push(new StringExp(exp.loc, funcname.toDString())); - } - handleCatArgument(arguments, exp.e1, exp.type.toBasetype(), false); handleCatArgument(arguments, exp.e2, exp.type.toBasetype(), true); @@ -12487,19 +12432,7 @@ version (IN_LLVM) { // https://dlang.org/spec/expression.html#cat_expressions //printf("CatExp.semantic() %s\n", toChars()); - if (exp.type) - { - result = exp; - return; - } - - if (Expression ex = binSemanticProp(exp, sc)) - { - result = ex; - return; - } - Expression e = exp.op_overload(sc); - if (e) + if (Expression e = exp.opOverloadBinary(sc, aliasThisStop)) { result = e; return; @@ -12539,11 +12472,11 @@ version (IN_LLVM) } // Check for: array ~ element - if ((tb1.ty == Tsarray || tb1.ty == Tarray) && tb2.ty != Tvoid) + if (tb1.isStaticOrDynamicArray() && tb2.ty != Tvoid) { if (exp.e1.op == EXP.arrayLiteral) { - exp.e2 = doCopyOrMove(sc, exp.e2); + exp.e2 = doCopyOrMove(sc, exp.e2, null, false); // https://issues.dlang.org/show_bug.cgi?id=14686 // Postblit call appears in AST, and this is // finally translated to an ArrayLiteralExp in below optimize(). @@ -12578,11 +12511,11 @@ version (IN_LLVM) } } // Check for: element ~ array - if ((tb2.ty == Tsarray || tb2.ty == Tarray) && tb1.ty != Tvoid) + if (tb2.isStaticOrDynamicArray() && tb1.ty != Tvoid) { if (exp.e2.op == EXP.arrayLiteral) { - exp.e1 = doCopyOrMove(sc, exp.e1); + exp.e1 = doCopyOrMove(sc, exp.e1, null, false); } else if (exp.e2.op == EXP.string_) { @@ -12613,7 +12546,8 @@ version (IN_LLVM) } Lpeer: - if ((tb1.ty == Tsarray || tb1.ty == Tarray) && (tb2.ty == Tsarray || tb2.ty == Tarray) && (tb1next.mod || tb2next.mod) && (tb1next.mod != tb2next.mod)) + if (tb1.isStaticOrDynamicArray() && tb2.isStaticOrDynamicArray() && + (tb1next.mod || tb2next.mod) && (tb1next.mod != tb2next.mod)) { Type t1 = tb1next.mutableOf().constOf().arrayOf(); Type t2 = tb2next.mutableOf().constOf().arrayOf(); @@ -12641,7 +12575,7 @@ version (IN_LLVM) if (exp.type.ty == Tarray && tb1next && tb2next && tb1next.mod != tb2next.mod) { // Do not strip const(void)[] - if (!global.params.fixImmutableConv || tb.nextOf().ty != Tvoid) + if (!sc.previews.fixImmutableConv || tb.nextOf().ty != Tvoid) exp.type = exp.type.nextOf().toHeadMutable().arrayOf(); } if (Type tbn = tb.nextOf()) @@ -12651,8 +12585,8 @@ version (IN_LLVM) } Type t1 = exp.e1.type.toBasetype(); Type t2 = exp.e2.type.toBasetype(); - if ((t1.ty == Tarray || t1.ty == Tsarray) && - (t2.ty == Tarray || t2.ty == Tsarray)) + Expression e; + if (t1.isStaticOrDynamicArray() && t2.isStaticOrDynamicArray()) { // Normalize to ArrayLiteralExp or StringExp as far as possible e = exp.optimize(WANTvalue); @@ -12668,67 +12602,64 @@ version (IN_LLVM) trySetCatExpLowering(result); } - override void visit(MulExp exp) + bool commonArithBinOpSemantic(BinExp exp) { - version (none) - { - printf("MulExp::semantic() %s\n", exp.toChars()); - } - if (exp.type) - { - result = exp; - return; - } - - if (Expression ex = binSemanticProp(exp, sc)) - { - result = ex; - return; - } - Expression e = exp.op_overload(sc); - if (e) + if (Expression e = exp.opOverloadBinary(sc, aliasThisStop)) { result = e; - return; + return true; } if (Expression ex = typeCombine(exp, sc)) { result = ex; - return; + return true; } Type tb = exp.type.toBasetype(); - if (tb.ty == Tarray || tb.ty == Tsarray) + if (tb.isStaticOrDynamicArray()) { if (!isArrayOpValid(exp)) { result = arrayOpInvalidError(exp); - return; + return true; } result = exp; - return; + return true; } - if (exp.checkArithmeticBin() || exp.checkSharedAccessBin(sc)) - return setError(); + if (exp.suggestBinaryOverloads(sc) || exp.checkArithmeticBin() || exp.checkSharedAccessBin(sc)) + { + setError(); + return true; + } + return false; + } + override void visit(MulExp exp) + { + version (none) + { + printf("MulExp::semantic() %s\n", exp.toChars()); + } - if (exp.type.isfloating()) + if (commonArithBinOpSemantic(exp)) + return; + if (exp.type.isFloating()) { Type t1 = exp.e1.type; Type t2 = exp.e2.type; - if (t1.isreal()) + if (t1.isReal()) { exp.type = t2; } - else if (t2.isreal()) + else if (t2.isReal()) { exp.type = t1; } - else if (t1.isimaginary()) + else if (t1.isImaginary()) { - if (t2.isimaginary()) + if (t2.isImaginary()) { switch (t1.toBasetype().ty) { @@ -12751,7 +12682,7 @@ version (IN_LLVM) // iy * iv = -yv exp.e1.type = exp.type; exp.e2.type = exp.type; - e = new NegExp(exp.loc, exp); + Expression e = new NegExp(exp.loc, exp); e = e.expressionSemantic(sc); result = e; return; @@ -12759,12 +12690,12 @@ version (IN_LLVM) else exp.type = t2; // t2 is complex } - else if (t2.isimaginary()) + else if (t2.isImaginary()) { exp.type = t1; // t1 is complex } } - else if (!target.isVectorOpSupported(tb, exp.op, exp.e2.type.toBasetype())) + else if (!target.isVectorOpSupported(exp.type.toBasetype(), exp.op, exp.e2.type.toBasetype())) { result = exp.incompatibleTypes(); return; @@ -12774,70 +12705,34 @@ version (IN_LLVM) override void visit(DivExp exp) { - if (exp.type) - { - result = exp; - return; - } - - if (Expression ex = binSemanticProp(exp, sc)) - { - result = ex; - return; - } - Expression e = exp.op_overload(sc); - if (e) - { - result = e; + if (commonArithBinOpSemantic(exp)) return; - } - if (Expression ex = typeCombine(exp, sc)) + if (exp.type.isFloating()) { - result = ex; - return; - } + Type t1 = exp.e1.type; + Type t2 = exp.e2.type; - Type tb = exp.type.toBasetype(); - if (tb.ty == Tarray || tb.ty == Tsarray) - { - if (!isArrayOpValid(exp)) - { - result = arrayOpInvalidError(exp); - return; - } - result = exp; - return; - } - - if (exp.checkArithmeticBin() || exp.checkSharedAccessBin(sc)) - return setError(); - - if (exp.type.isfloating()) - { - Type t1 = exp.e1.type; - Type t2 = exp.e2.type; - - if (t1.isreal()) + if (t1.isReal()) { exp.type = t2; - if (t2.isimaginary()) + if (t2.isImaginary()) { // x/iv = i(-x/v) exp.e2.type = t1; - e = new NegExp(exp.loc, exp); + Expression e = new NegExp(exp.loc, exp); e = e.expressionSemantic(sc); result = e; return; } } - else if (t2.isreal()) + else if (t2.isReal()) { exp.type = t1; } - else if (t1.isimaginary()) + else if (t1.isImaginary()) { - if (t2.isimaginary()) + if (t2.isImaginary()) { switch (t1.toBasetype().ty) { @@ -12860,12 +12755,12 @@ version (IN_LLVM) else exp.type = t2; // t2 is complex } - else if (t2.isimaginary()) + else if (t2.isImaginary()) { exp.type = t1; // t1 is complex } } - else if (!target.isVectorOpSupported(tb, exp.op, exp.e2.type.toBasetype())) + else if (!target.isVectorOpSupported(exp.type.toBasetype(), exp.op, exp.e2.type.toBasetype())) { result = exp.incompatibleTypes(); return; @@ -12875,54 +12770,19 @@ version (IN_LLVM) override void visit(ModExp exp) { - if (exp.type) - { - result = exp; - return; - } - - if (Expression ex = binSemanticProp(exp, sc)) - { - result = ex; - return; - } - Expression e = exp.op_overload(sc); - if (e) - { - result = e; - return; - } - - if (Expression ex = typeCombine(exp, sc)) - { - result = ex; + if (commonArithBinOpSemantic(exp)) return; - } - Type tb = exp.type.toBasetype(); - if (tb.ty == Tarray || tb.ty == Tsarray) - { - if (!isArrayOpValid(exp)) - { - result = arrayOpInvalidError(exp); - return; - } - result = exp; - return; - } - if (!target.isVectorOpSupported(tb, exp.op, exp.e2.type.toBasetype())) + if (!target.isVectorOpSupported(exp.type.toBasetype(), exp.op, exp.e2.type.toBasetype())) { result = exp.incompatibleTypes(); return; } - if (exp.checkArithmeticBin() || exp.checkSharedAccessBin(sc)) - return setError(); - - if (exp.type.isfloating()) + if (exp.type.isFloating()) { exp.type = exp.e1.type; - if (exp.e2.type.iscomplex()) + if (exp.e2.type.isComplex()) { error(exp.loc, "cannot perform modulo complex arithmetic"); return setError(); @@ -12933,54 +12793,17 @@ version (IN_LLVM) override void visit(PowExp exp) { - if (exp.type) - { - result = exp; - return; - } - - //printf("PowExp::semantic() %s\n", toChars()); - if (Expression ex = binSemanticProp(exp, sc)) - { - result = ex; - return; - } - Expression e = exp.op_overload(sc); - if (e) - { - result = e; - return; - } - - if (Expression ex = typeCombine(exp, sc)) - { - result = ex; - return; - } - - Type tb = exp.type.toBasetype(); - if (tb.ty == Tarray || tb.ty == Tsarray) - { - if (!isArrayOpValid(exp)) - { - result = arrayOpInvalidError(exp); - return; - } - result = exp; + if (commonArithBinOpSemantic(exp)) return; - } - if (exp.checkArithmeticBin() || exp.checkSharedAccessBin(sc)) - return setError(); - - if (!target.isVectorOpSupported(tb, exp.op, exp.e2.type.toBasetype())) + if (!target.isVectorOpSupported(exp.type.toBasetype(), exp.op, exp.e2.type.toBasetype())) { result = exp.incompatibleTypes(); return; } // First, attempt to fold the expression. - e = exp.optimize(WANTvalue); + Expression e = exp.optimize(WANTvalue); if (e.op != EXP.pow) { e = e.expressionSemantic(sc); @@ -12991,7 +12814,7 @@ version (IN_LLVM) Module mmath = Module.loadStdMath(); if (!mmath) { - error(e.loc, "`%s` requires `std.math` for `^^` operators", e.toChars()); + error(e.loc, "`%s` requires `std.math` for `^^` operators", e.toErrMsg()); return setError(); } e = new ScopeExp(exp.loc, mmath); @@ -13013,25 +12836,13 @@ version (IN_LLVM) private void visitShift(BinExp exp) { - if (exp.type) - { - result = exp; - return; - } - - if (Expression ex = binSemanticProp(exp, sc)) - { - result = ex; - return; - } - Expression e = exp.op_overload(sc); - if (e) + if (Expression e = exp.opOverloadBinary(sc, aliasThisStop)) { result = e; return; } - if (exp.checkIntegralBin() || exp.checkSharedAccessBin(sc)) + if (exp.suggestBinaryOverloads(sc) || exp.checkIntegralBin() || exp.checkSharedAccessBin(sc)) return setError(); if (!target.isVectorOpSupported(exp.e1.type.toBasetype(), exp.op, exp.e2.type.toBasetype())) @@ -13066,19 +12877,7 @@ version (IN_LLVM) private void visitBinaryBitOp(BinExp exp) { - if (exp.type) - { - result = exp; - return; - } - - if (Expression ex = binSemanticProp(exp, sc)) - { - result = ex; - return; - } - Expression e = exp.op_overload(sc); - if (e) + if (Expression e = exp.opOverloadBinary(sc, aliasThisStop)) { result = e; return; @@ -13098,7 +12897,7 @@ version (IN_LLVM) } Type tb = exp.type.toBasetype(); - if (tb.ty == Tarray || tb.ty == Tsarray) + if (tb.isStaticOrDynamicArray()) { if (!isArrayOpValid(exp)) { @@ -13113,7 +12912,8 @@ version (IN_LLVM) result = exp.incompatibleTypes(); return; } - if (exp.checkIntegralBin() || exp.checkSharedAccessBin(sc)) + + if (exp.suggestBinaryOverloads(sc) || exp.checkIntegralBin() || exp.checkSharedAccessBin(sc)) return setError(); result = exp; @@ -13139,11 +12939,6 @@ version (IN_LLVM) printf("LogicalExp::semantic() %s\n", exp.toChars()); } - if (exp.type) - { - result = exp; - return; - } exp.setNoderefOperands(); @@ -13156,14 +12951,14 @@ version (IN_LLVM) e1x = resolveProperties(sc, e1x); e1x = e1x.toBoolean(sc); - if (sc.flags & SCOPE.condition) + if (sc.condition) { /* If in static if, don't evaluate e2 if we don't have to. */ e1x = e1x.optimize(WANTvalue); if (e1x.toBool().hasValue(exp.op == EXP.orOr)) { - if (sc.flags & SCOPE.Cfile) + if (sc.inCfile) result = new IntegerExp(exp.op == EXP.orOr); else result = IntegerExp.createBool(exp.op == EXP.orOr); @@ -13193,7 +12988,7 @@ version (IN_LLVM) if (e2x.op == EXP.type || e2x.op == EXP.scope_) { - error(exp.loc, "`%s` is not an expression", exp.e2.toChars()); + error(exp.loc, "`%s` is not an expression", exp.e2.toErrMsg()); return setError(); } if (e1x.op == EXP.error || e1x.type.ty == Tnoreturn) @@ -13211,7 +13006,7 @@ version (IN_LLVM) if (e2x.type.ty == Tvoid) exp.type = Type.tvoid; else - exp.type = (sc && sc.flags & SCOPE.Cfile) ? Type.tint32 : Type.tbool; + exp.type = (sc && sc.inCfile) ? Type.tint32 : Type.tbool; exp.e1 = e1x; exp.e2 = e2x; @@ -13225,11 +13020,6 @@ version (IN_LLVM) { printf("CmpExp::semantic('%s')\n", exp.toChars()); } - if (exp.type) - { - result = exp; - return; - } exp.setNoderefOperands(); @@ -13246,57 +13036,12 @@ version (IN_LLVM) return setError(); } - - EXP cmpop = exp.op; - if (auto e = exp.op_overload(sc, &cmpop)) + if (auto e = exp.opOverloadCmp(sc, aliasThisStop)) { - if (!e.type.isscalar() && e.type.equals(exp.e1.type)) - { - error(exp.loc, "recursive `opCmp` expansion"); - return setError(); - } - if (e.op == EXP.call) - { - - if (t1.ty == Tclass && t2.ty == Tclass) - { - // Lower to object.__cmp(e1, e2) - Expression cl = new IdentifierExp(exp.loc, Id.empty); - cl = new DotIdExp(exp.loc, cl, Id.object); - cl = new DotIdExp(exp.loc, cl, Id.__cmp); - cl = cl.expressionSemantic(sc); - - auto arguments = new Expressions(); - // Check if op_overload found a better match by calling e2.opCmp(e1) - // If the operands were swapped, then the result must be reversed - // e1.opCmp(e2) == -e2.opCmp(e1) - // cmpop takes care of this - if (exp.op == cmpop) - { - arguments.push(exp.e1); - arguments.push(exp.e2); - } - else - { - // Use better match found by op_overload - arguments.push(exp.e2); - arguments.push(exp.e1); - } - - cl = new CallExp(exp.loc, cl, arguments); - cl = new CmpExp(cmpop, exp.loc, cl, new IntegerExp(0)); - result = cl.expressionSemantic(sc); - return; - } - - e = new CmpExp(cmpop, exp.loc, e, IntegerExp.literal!0); - e = e.expressionSemantic(sc); - } result = e; return; } - if (Expression ex = typeCombine(exp, sc)) { result = ex; @@ -13308,13 +13053,13 @@ version (IN_LLVM) if (f1 || f2) return setError(); - exp.type = (sc && sc.flags & SCOPE.Cfile) ? Type.tint32 : Type.tbool; + exp.type = (sc && sc.inCfile) ? Type.tint32 : Type.tbool; // Special handling for array comparisons Expression arrayLowering = null; t1 = exp.e1.type.toBasetype(); t2 = exp.e2.type.toBasetype(); - if ((t1.ty == Tarray || t1.ty == Tsarray || t1.ty == Tpointer) && (t2.ty == Tarray || t2.ty == Tsarray || t2.ty == Tpointer)) + if ((t1.isStaticOrDynamicArray() || t1.ty == Tpointer) && (t2.isStaticOrDynamicArray() || t2.ty == Tpointer)) { Type t1next = t1.nextOf(); Type t2next = t2.nextOf(); @@ -13324,8 +13069,7 @@ version (IN_LLVM) return setError(); } - if ((t1.ty == Tarray || t1.ty == Tsarray) && - (t2.ty == Tarray || t2.ty == Tsarray)) + if (t1.isStaticOrDynamicArray() && t2.isStaticOrDynamicArray()) { if (!verifyHookExist(exp.loc, *sc, Id.__cmp, "comparing arrays")) return setError(); @@ -13346,15 +13090,12 @@ version (IN_LLVM) arrayLowering = al; } } - else if (t1.ty == Tstruct || t2.ty == Tstruct || (t1.ty == Tclass && t2.ty == Tclass)) + else if (t1.isTypeClass() && t2.isTypeClass()) { - if (t2.ty == Tstruct) - error(exp.loc, "need member function `opCmp()` for %s `%s` to compare", t2.toDsymbol(sc).kind(), t2.toChars()); - else - error(exp.loc, "need member function `opCmp()` for %s `%s` to compare", t1.toDsymbol(sc).kind(), t1.toChars()); + error(exp.loc, "need member function `opCmp()` for %s `%s` to compare", t1.toDsymbol(sc).kind(), t1.toChars()); return setError(); } - else if (t1.iscomplex() || t2.iscomplex()) + else if (t1.isComplex() || t2.isComplex()) { error(exp.loc, "compare not defined for complex operands"); return setError(); @@ -13399,19 +13140,7 @@ version (IN_LLVM) override void visit(InExp exp) { - if (exp.type) - { - result = exp; - return; - } - - if (Expression ex = binSemanticProp(exp, sc)) - { - result = ex; - return; - } - Expression e = exp.op_overload(sc); - if (e) + if (Expression e = exp.opOverloadBinary(sc, aliasThisStop)) { result = e; return; @@ -13431,7 +13160,10 @@ version (IN_LLVM) exp.e1 = exp.e1.implicitCastTo(sc, ta.index); } - semanticTypeInfo(sc, ta.index); + // even though the glue layer only needs the type info of the index, + // this might be the first time an AA literal is accessed, so check + // the full type info + semanticTypeInfo(sc, ta); // Return type is pointer to value exp.type = ta.nextOf().pointerTo(); @@ -13442,15 +13174,15 @@ version (IN_LLVM) return setError(); case Tarray, Tsarray: - result = exp.incompatibleTypes(); + result = exp.incompatibleTypes(sc); errorSupplemental(exp.loc, "`in` is only allowed on associative arrays"); const(char)* slice = (t2b.ty == Tsarray) ? "[]" : ""; errorSupplemental(exp.loc, "perhaps use `std.algorithm.find(%s, %s%s)` instead", - exp.e1.toChars(), exp.e2.toChars(), slice); + exp.e1.toErrMsg(), exp.e2.toErrMsg(), slice); return; default: - result = exp.incompatibleTypes(); + result = exp.incompatibleTypes(sc); return; } result = exp; @@ -13469,11 +13201,6 @@ version (IN_LLVM) override void visit(EqualExp exp) { //printf("EqualExp::semantic('%s')\n", exp.toChars()); - if (exp.type) - { - result = exp; - return; - } exp.setNoderefOperands(); @@ -13580,15 +13307,13 @@ version (IN_LLVM) return false; } - if (auto e = exp.op_overload(sc)) + if (auto e = exp.opOverloadEqual(sc, aliasThisStop)) { result = e; return; } - - const isArrayComparison = (t1.ty == Tarray || t1.ty == Tsarray) && - (t2.ty == Tarray || t2.ty == Tsarray); + const isArrayComparison = t1.isStaticOrDynamicArray() && t2.isStaticOrDynamicArray(); const needsArrayLowering = isArrayComparison && needsDirectEq(t1, t2, sc); if (!needsArrayLowering) @@ -13608,11 +13333,11 @@ version (IN_LLVM) if (f1 || f2) return setError(); - exp.type = (sc && sc.flags & SCOPE.Cfile) ? Type.tint32 : Type.tbool; + exp.type = (sc && sc.inCfile) ? Type.tint32 : Type.tbool; if (!isArrayComparison) { - if (exp.e1.type != exp.e2.type && exp.e1.type.isfloating() && exp.e2.type.isfloating()) + if (exp.e1.type != exp.e2.type && exp.e1.type.isFloating() && exp.e2.type.isFloating()) { // Cast both to complex exp.e1 = exp.e1.castTo(sc, Type.tcomplex80); @@ -13675,8 +13400,9 @@ version (IN_LLVM) } if (exp.e1.type.toBasetype().ty == Taarray) + { semanticTypeInfo(sc, exp.e1.type.toBasetype()); - + } if (!target.isVectorOpSupported(t1, exp.op, t2)) { @@ -13698,11 +13424,6 @@ version (IN_LLVM) override void visit(IdentityExp exp) { - if (exp.type) - { - result = exp; - return; - } exp.setNoderefOperands(); @@ -13731,7 +13452,7 @@ version (IN_LLVM) exp.type = Type.tbool; - if (exp.e1.type != exp.e2.type && exp.e1.type.isfloating() && exp.e2.type.isfloating()) + if (exp.e1.type != exp.e2.type && exp.e1.type.isFloating() && exp.e2.type.isFloating()) { // Cast both to complex exp.e1 = exp.e1.castTo(sc, Type.tcomplex80); @@ -13772,11 +13493,6 @@ version (IN_LLVM) { printf("CondExp::semantic('%s')\n", exp.toChars()); } - if (exp.type) - { - result = exp; - return; - } if (auto die = exp.econd.isDotIdExp()) die.noderef = true; @@ -13836,7 +13552,7 @@ version (IN_LLVM) // https://issues.dlang.org/show_bug.cgi?id=23767 // `cast(void*) 0` should be treated as `null` so the ternary expression // gets the pointer type of the other branch - if (sc.flags & SCOPE.Cfile) + if (sc.inCfile) { static void rewriteCNull(ref Expression e, ref Type t) { @@ -14068,7 +13784,7 @@ version (IN_LLVM) Expression trySemantic(Expression exp, Scope* sc) { //printf("+trySemantic(%s)\n", exp.toChars()); - uint errors = global.startGagging(); + const errors = global.startGagging(); Expression e = expressionSemantic(exp, sc); if (global.endGagging(errors)) { @@ -14078,6 +13794,32 @@ Expression trySemantic(Expression exp, Scope* sc) return e; } +/********************************** + * Try expression semantic on `exp`, gagging semantic errors, + * but don't resolve alias this on a BinExp when the lhs or rhs + * has the corresponding type in `aliasThisStop` (See `isRecursiveAliasThis`). + * + * Params: + * exp = expression to try semantic on + * sc = scope + * aliasThisStop = pair of recursive alias this types to stop endless recursion + * Returns: + * exp after expression semantic, or `null` on error + */ +Expression trySemanticAliasThis(Expression exp, Scope* sc, Type[2] aliasThisStop) +{ + if (exp.expressionSemanticDone) + return exp; + + const errors = global.startGagging(); + Expression e = expressionSemantic(exp, sc, aliasThisStop); + + if (global.endGagging(errors)) + return null; + + return e; +} + /************************** * Helper function for easy error propagation. * If error occurs, returns ErrorExp. Otherwise returns NULL. @@ -14138,26 +13880,68 @@ Expression binSemanticProp(BinExp e, Scope* sc) return null; } +/// Returns: whether expressionSemantic() has been run on expression `e` +private bool expressionSemanticDone(Expression e) +{ + // Usually, Expression.type gets set by expressionSemantic and is `null` beforehand + // There are some exceptions however: + return e.type !is null && !( + e.isRealExp() // type sometimes gets set already before semantic + || e.isTypeExp() // stores its type in the Expression.type field + || e.isCompoundLiteralExp() // stores its `(type) {}` in type field, gets rewritten to struct literal + || e.isVarExp() // type sometimes gets set already before semantic + ); +} + // entrypoint for semantic ExpressionSemanticVisitor Expression expressionSemantic(Expression e, Scope* sc) { + if (e.expressionSemanticDone) + return e; + + scope v = new ExpressionSemanticVisitor(sc); + e.accept(v); + return v.result; +} + +// ditto, but passes alias this stop types, see trySemanticAliasThis +private Expression expressionSemantic(Expression e, Scope* sc, Type[2] aliasThisStop) +{ + if (e.expressionSemanticDone) + return e; + + scope v = new ExpressionSemanticVisitor(sc); + v.aliasThisStop = aliasThisStop; + e.accept(v); + return v.result; +} + +// ditto, but with `parent` parameter that represents the expression before rewriting. +// This way, when lowering an expression (e.g. i++ to i+=1), error messages can still +// refer to the original expression. +private Expression expressionSemanticWithParent(Expression e, Scope* sc, Expression parent) +{ + if (e.expressionSemanticDone) + return e; + scope v = new ExpressionSemanticVisitor(sc); + v.parent = parent; e.accept(v); return v.result; } private Expression dotIdSemanticPropX(DotIdExp exp, Scope* sc) { - //printf("DotIdExp::semanticX(this = %p, '%s')\n", this, toChars()); + //printf("dotIdSemanticPropX() %s\n", toChars(exp)); if (Expression ex = unaSemantic(exp, sc)) return ex; - if (!(sc.flags & SCOPE.Cfile) && exp.ident == Id._mangleof) + if (!sc.inCfile && exp.ident == Id._mangleof) { // symbol.mangleof // return mangleof as an Expression - static Expression dotMangleof(const ref Loc loc, Scope* sc, Dsymbol ds, bool hasOverloads) + static Expression dotMangleof(Loc loc, Scope* sc, Dsymbol ds, bool hasOverloads) { Expression e; @@ -14253,26 +14037,18 @@ private Expression dotIdSemanticPropX(DotIdExp exp, Scope* sc) // Template has no built-in properties except for 'stringof'. if ((exp.e1.isDotTemplateExp() || exp.e1.isTemplateExp()) && exp.ident != Id.stringof) { - error(exp.loc, "template `%s` does not have property `%s`", exp.e1.toChars(), exp.ident.toChars()); + error(exp.loc, "template `%s` does not have property `%s`", exp.e1.toErrMsg(), exp.ident.toChars()); return ErrorExp.get(); } if (!exp.e1.type) { - error(exp.loc, "expression `%s` does not have property `%s`", exp.e1.toChars(), exp.ident.toChars()); + error(exp.loc, "expression `%s` does not have property `%s`", exp.e1.toErrMsg(), exp.ident.toChars()); return ErrorExp.get(); } return exp; } -private bool checkDisabled(Dsymbol s, ref Loc loc, Scope* sc) -{ - if (auto d = s.isDeclaration()) - return d.checkDisabled(loc, sc); - - return false; -} - /****************************** * Resolve properties, i.e. `e1.ident`, without seeing UFCS. * Params: @@ -14284,11 +14060,11 @@ private bool checkDisabled(Dsymbol s, ref Loc loc, Scope* sc) */ Expression dotIdSemanticProp(DotIdExp exp, Scope* sc, bool gag) { - //printf("DotIdExp::semanticY(this = %p, '%s')\n", exp, exp.toChars()); + //printf("dotIdSemanticProp('%s')\n", exp.toChars()); //{ static int z; fflush(stdout); if (++z == 10) *(char*)0=0; } - const cfile = (sc.flags & SCOPE.Cfile) != 0; + const cfile = sc.inCfile; /* Special case: rewrite this.id and super.id * to be classtype.id and baseclasstype.id @@ -14343,13 +14119,13 @@ Expression dotIdSemanticProp(DotIdExp exp, Scope* sc, bool gag) */ if (ie.sds.isModule() && ie.sds != sc._module) flags |= SearchOpt.ignorePrivateImports; - if (sc.flags & SCOPE.ignoresymbolvisibility) + if (sc.ignoresymbolvisibility) flags |= SearchOpt.ignoreVisibility; Dsymbol s = ie.sds.search(exp.loc, exp.ident, flags); /* Check for visibility before resolving aliases because public * aliases to private symbols are public. */ - if (s && !(sc.flags & SCOPE.ignoresymbolvisibility) && !symbolIsVisible(sc._module, s)) + if (s && !sc.ignoresymbolvisibility && !symbolIsVisible(sc._module, s)) { s = null; } @@ -14367,7 +14143,8 @@ Expression dotIdSemanticProp(DotIdExp exp, Scope* sc, bool gag) s = s.toAlias(); s.checkDeprecated(exp.loc, sc); - s.checkDisabled(exp.loc, sc); + if (auto d = s.isDeclaration()) + d.checkDisabled(exp.loc, sc); if (auto em = s.isEnumMember()) { @@ -14621,7 +14398,7 @@ Expression dotIdSemanticProp(DotIdExp exp, Scope* sc, bool gag) const flag = cast(DotExpFlag) (exp.noderef * DotExpFlag.noDeref | gag * DotExpFlag.gag); - Expression e = exp.e1.type.dotExp(sc, exp.e1, exp.ident, flag); + Expression e = dotExp(exp.e1.type, sc, exp.e1, exp.ident, flag); if (e) { e = e.expressionSemantic(sc); @@ -14664,11 +14441,18 @@ Expression dotTemplateSemanticProp(DotTemplateInstanceExp exp, Scope* sc, bool g auto die = new DotIdExp(exp.loc, e1, exp.ti.name); Expression e = die.dotIdSemanticPropX(sc); + + Expression notTemplate() + { + error(exp.loc, "`%s` isn't a template", e.toErrMsg()); + return errorExp(); + } + if (e == die) { exp.e1 = die.e1; // take back Type t1b = exp.e1.type.toBasetype(); - if (t1b.ty == Tarray || t1b.ty == Tsarray || t1b.ty == Taarray || t1b.ty == Tnull || (t1b.isTypeBasic() && t1b.ty != Tvoid)) + if (t1b.isStaticOrDynamicArray() || t1b.ty == Taarray || t1b.ty == Tnull || (t1b.isTypeBasic() && t1b.ty != Tvoid)) { /* No built-in type has templatized properties, so do shortcut. * It is necessary in: 1024.max!"a < b" @@ -14708,7 +14492,7 @@ Expression dotTemplateSemanticProp(DotTemplateInstanceExp exp, Scope* sc, bool g exp.e1 = dve.e1; // pull semantic() result if (!exp.findTempDecl(sc)) - goto Lerr; + return notTemplate(); if (exp.ti.needsTypeInference(sc)) return exp; exp.ti.dsymbolSemantic(sc); @@ -14804,9 +14588,7 @@ Expression dotTemplateSemanticProp(DotTemplateInstanceExp exp, Scope* sc, bool g .expressionSemantic(sc); } -Lerr: - error(exp.loc, "`%s` isn't a template", e.toChars()); - return errorExp(); + return notTemplate(); } MATCH matchType(FuncExp funcExp, Type to, Scope* sc, FuncExp* presult, ErrorSink eSink) @@ -14926,14 +14708,14 @@ MATCH matchType(FuncExp funcExp, Type to, Scope* sc, FuncExp* presult, ErrorSink convertMatch = true; auto tfy = new TypeFunction(tfx.parameterList, tof.next, - tfx.linkage, STC.undefined_); + tfx.linkage, STC.none); tfy.mod = tfx.mod; tfy.trust = tfx.trust; - tfy.isnothrow = tfx.isnothrow; - tfy.isnogc = tfx.isnogc; + tfy.isNothrow = tfx.isNothrow; + tfy.isNogc = tfx.isNogc; tfy.purity = tfx.purity; - tfy.isproperty = tfx.isproperty; - tfy.isref = tfx.isref; + tfy.isProperty = tfx.isProperty; + tfy.isRef = tfx.isRef; tfy.isInOutParam = tfx.isInOutParam; tfy.isInOutQual = tfx.isInOutQual; tfy.deco = tfy.merge().deco; @@ -14977,47 +14759,234 @@ MATCH matchType(FuncExp funcExp, Type to, Scope* sc, FuncExp* presult, ErrorSink { auto ts = toAutoQualChars(tx, to); eSink.error(loc, "cannot implicitly convert expression `%s` of type `%s` to `%s`", - funcExp.toChars(), ts[0], ts[1]); + funcExp.toErrMsg(), ts[0], ts[1]); } return m; } -private bool checkSharedAccessBin(BinExp binExp, Scope* sc) +private bool checkScalar(Expression e) { - const r1 = binExp.e1.checkSharedAccess(sc); - const r2 = binExp.e2.checkSharedAccess(sc); - return (r1 || r2); + if (e.op == EXP.error) + return true; + if (e.type.toBasetype().ty == Terror) + return true; + if (!e.type.isScalar()) + { + error(e.loc, "`%s` is not a scalar, it is a `%s`", e.toErrMsg(), e.type.toChars()); + return true; + } + return e.checkValue(); } -/*************************************** - * If expression is shared, check that we can access it. - * Give error message if not. - * - * Params: - * e = expression to check - * sc = context - * returnRef = Whether this expression is for a `return` statement - * off a `ref` function, in which case a single level - * of dereference is allowed (e.g. `shared(int)*`). - * Returns: - * true on error - */ -bool checkSharedAccess(Expression e, Scope* sc, bool returnRef = false) +private bool checkNoBool(Expression e) { - if (global.params.noSharedAccess != FeatureState.enabled || - !sc || - sc.intypeof || - sc.flags & SCOPE.ctfe) + if (e.op == EXP.error) + return true; + if (e.type.toBasetype().ty == Terror) + return true; + if (e.type.toBasetype().ty == Tbool) { - return false; + error(e.loc, "operation not allowed on `bool` `%s`", e.toErrMsg()); + return true; } - else if (sc._module.ident == Id.atomic && sc._module.parent !is null) - { - // Allow core.internal.atomic, it is an compiler implementation for a given platform module. - // It is then exposed by other modules such as core.atomic and core.stdc.atomic. - // This is available as long as druntime is on the import path and the platform supports that operation. + return false; +} - // https://issues.dlang.org/show_bug.cgi?id=24846 +private bool checkIntegral(Expression e) +{ + if (e.op == EXP.error) + return true; + if (e.type.toBasetype().ty == Terror) + return true; + if (!e.type.isIntegral()) + { + error(e.loc, "`%s` is not of integral type, it is a `%s`", e.toErrMsg(), e.type.toChars()); + return true; + } + return e.checkValue(); +} + +private bool checkArithmetic(Expression e, EXP op) +{ + if (op == EXP.error) + return true; + if (e.type.toBasetype().ty == Terror) + return true; + if (!e.type.isIntegral() && !e.type.isFloating()) + { + // unary aggregate ops error here + const char* msg = e.type.isAggregate() ? + "operator `%s` is not defined for `%s` of type `%s`" : + "illegal operator `%s` for `%s` of type `%s`"; + error(e.loc, msg, EXPtoString(op).ptr, e.toErrMsg(), e.type.toChars()); + return true; + } + + if ((op == EXP.add || op == EXP.min) && e.isTypeExp()) + { + // @@@DEPRECATED_2.121@@@ + // Deprecated in 2.111 + // In 2.121, remove this branch to let `checkValue` raise the error + deprecation(e.loc, "type `%s` has no value", e.toChars); + if (!e.type.isOpaqueType) + deprecationSupplemental(e.loc, "perhaps use `%s.init`", e.toChars); + return false; + } + + return e.checkValue(); +} + +/******************************* + * Check whether the expression allows RMW operations, error with rmw operator diagnostic if not. + * ex is the RHS expression, or NULL if ++/-- is used (for diagnostics) + * Returns true if error occurs. + */ +private bool checkReadModifyWrite(Expression e, EXP rmwOp, Expression ex = null) +{ + //printf("Expression::checkReadModifyWrite() %s %s", toChars(), ex ? ex.toChars() : ""); + if (!e.type || !e.type.isShared() || e.type.isTypeStruct() || e.type.isTypeClass()) + return false; + + // atomicOp uses opAssign (+=/-=) rather than opOp (++/--) for the CT string literal. + switch (rmwOp) + { + case EXP.plusPlus: + case EXP.prePlusPlus: + rmwOp = EXP.addAssign; + break; + case EXP.minusMinus: + case EXP.preMinusMinus: + rmwOp = EXP.minAssign; + break; + default: + break; + } + + error(e.loc, "read-modify-write operations are not allowed for `shared` variables"); + errorSupplemental(e.loc, "Use `core.atomic.atomicOp!\"%s\"(%s, %s)` instead", + EXPtoString(rmwOp).ptr, e.toChars(), ex ? ex.toChars() : "1"); + return true; +} + +private bool checkSharedAccessBin(BinExp binExp, Scope* sc) +{ + const r1 = binExp.e1.checkSharedAccess(sc); + const r2 = binExp.e2.checkSharedAccess(sc); + return (r1 || r2); +} + +private bool checkIntegralBin(BinExp e) +{ + bool r1 = e.e1.checkIntegral(); + bool r2 = e.e2.checkIntegral(); + return (r1 || r2); +} + +private bool checkArithmeticBin(BinExp e) +{ + return (e.e1.checkArithmetic(e.op) || e.e2.checkArithmetic(e.op)); +} + +/**************************************** + * Check that the expression has a valid value. + * If not, generates an error "... has no value".` + * + * Params: + * e = expression to check + * + * Returns: + * `true` if the expression is not valid or has `void` type. + */ +bool checkValue(Expression e) +{ + if (auto te = e.isTypeExp()) + { + error(e.loc, "type `%s` has no value", e.toErrMsg()); + if (!e.type.isOpaqueType) + errorSupplemental(e.loc, "perhaps use `%s.init`", e.toChars()); + return true; + } + + if (auto dtie = e.isDotTemplateInstanceExp()) + { + if (dtie.ti.tempdecl && + dtie.ti.semantictiargsdone && + dtie.ti.semanticRun == PASS.initial) + + error(e.loc, "partial %s `%s` has no value", dtie.ti.kind(), e.toErrMsg()); + else + error(e.loc, "%s `%s` has no value", dtie.ti.kind(), dtie.ti.toChars()); + return true; + } + + if (auto se = e.isScopeExp()) + { + error(e.loc, "%s `%s` has no value", se.sds.kind(), se.sds.toChars()); + return true; + } + + if (auto te = e.isTemplateExp()) + { + error(e.loc, "%s `%s` has no value", te.td.kind(), te.toChars()); + return true; + } + + if (auto fe = e.isFuncExp()) + { + if (fe.td) + { + error(e.loc, "template lambda has no value"); + return true; + } + return false; + } + + if (auto dte = e.isDotTemplateExp()) + { + error(e.loc, "%s `%s` has no value", dte.td.kind(), e.toErrMsg()); + return true; + } + + if (e.type && e.type.toBasetype().ty == Tvoid) + { + error(e.loc, "expression `%s` is `void` and has no value", e.toErrMsg()); + //print(); assert(0); + if (!global.gag) + e.type = Type.terror; + return true; + } + return false; +} + +/*************************************** + * If expression is shared, check that we can access it. + * Give error message if not. + * + * Params: + * e = expression to check + * sc = context + * returnRef = Whether this expression is for a `return` statement + * off a `ref` function, in which case a single level + * of dereference is allowed (e.g. `shared(int)*`). + * Returns: + * true on error + */ +bool checkSharedAccess(Expression e, Scope* sc, bool returnRef = false) +{ + if (!sc || + !sc.previews.noSharedAccess || + sc.intypeof || + sc.ctfe) + { + return false; + } + else if (sc._module.ident == Id.atomic && sc._module.parent !is null) + { + // Allow core.internal.atomic, it is an compiler implementation for a given platform module. + // It is then exposed by other modules such as core.atomic and core.stdc.atomic. + // This is available as long as druntime is on the import path and the platform supports that operation. + + // https://issues.dlang.org/show_bug.cgi?id=24846 Package parent = sc._module.parent.isPackage(); if (parent !is null) @@ -15040,7 +15009,7 @@ bool checkSharedAccess(Expression e, Scope* sc, bool returnRef = false) bool sharedError(Expression e) { // https://dlang.org/phobos/core_atomic.html - error(e.loc, "direct access to shared `%s` is not allowed, see `core.atomic`", e.toChars()); + error(e.loc, "direct access to shared `%s` is not allowed, see `core.atomic`", e.toErrMsg()); return true; } @@ -15056,6 +15025,8 @@ bool checkSharedAccess(Expression e, Scope* sc, bool returnRef = false) bool visitNew(NewExp e) { + if (e.placement) + check(e.placement, false); if (e.thisexp) check(e.thisexp, false); return false; @@ -15076,10 +15047,8 @@ bool checkSharedAccess(Expression e, Scope* sc, bool returnRef = false) { if (e.var.isThisDeclaration()) return false; - else - return sharedError(e); } - else if (!allowRef && e.var.type.isShared()) + if (!allowRef && e.var.type.isShared()) return sharedError(e); return false; @@ -15183,7 +15152,7 @@ bool checkSharedAccess(Expression e, Scope* sc, bool returnRef = false) /**************************************** * Resolve __FILE__, __LINE__, __MODULE__, __FUNCTION__, __PRETTY_FUNCTION__, __FILE_FULL_PATH__ to loc. */ -Expression resolveLoc(Expression exp, const ref Loc loc, Scope* sc) +Expression resolveLoc(Expression exp, Loc loc, Scope* sc) { // Don't replace the special keywords, while we are inside a default // argument. They are replaced later when copied to the call site. @@ -15241,6 +15210,8 @@ Expression resolveLoc(Expression exp, const ref Loc loc, Scope* sc) Expression visitNew(NewExp exp) { + if (exp.placement) + exp.placement = exp.placement.resolveLoc(loc, sc); if (exp.thisexp) exp.thisexp = exp.thisexp.resolveLoc(loc, sc); if (exp.argprefix) @@ -15436,6 +15407,7 @@ Expression resolveLoc(Expression exp, const ref Loc loc, Scope* sc) */ Expression addDtorHook(Expression e, Scope* sc) { + //printf("addDtorHook() %s\n", toChars(e)); Expression visit(Expression exp) { return exp; @@ -15461,7 +15433,7 @@ Expression addDtorHook(Expression e, Scope* sc) buf[0 .. prefix.length] = prefix; buf[prefix.length .. len] = ident[0 .. len - prefix.length]; - auto tmp = copyToTemp(0, buf[0 .. len], exp); + auto tmp = copyToTemp(STC.none, buf[0 .. len], exp); Expression ae = new DeclarationExp(exp.loc, tmp); Expression e = new CommaExp(exp.loc, ae, new VarExp(exp.loc, tmp)); e = e.expressionSemantic(sc); @@ -15481,7 +15453,7 @@ Expression addDtorHook(Expression e, Scope* sc) if (auto tf = e1.type.isTypeFunction()) { - if (tf.isref) + if (tf.isRef) return exp; } @@ -15494,7 +15466,7 @@ Expression addDtorHook(Expression e, Scope* sc) /* Type needs destruction, so declare a tmp * which the back end will recognize and call dtor on */ - auto tmp = copyToTemp(0, Id.__tmpfordtor.toString(), exp); + auto tmp = copyToTemp(STC.none, Id.__tmpfordtor.toString(), exp); auto de = new DeclarationExp(exp.loc, tmp); auto ve = new VarExp(exp.loc, tmp); Expression e = new CommaExp(exp.loc, de, ve); @@ -15565,9 +15537,9 @@ private Expression toLvalueImpl(Expression _this, Scope* sc, const(char)* action if (e.op == EXP.type) error(_this.loc, "cannot %s type `%s`", action, e.type.toChars()); else if (e.op == EXP.template_) - error(_this.loc, "cannot %s template `%s`, perhaps instantiate it first", action, e.toChars()); + error(_this.loc, "cannot %s template `%s`, perhaps instantiate it first", action, e.toErrMsg()); else - error(_this.loc, "cannot %s expression `%s` because it is not an lvalue", action, e.toChars()); + error(_this.loc, "cannot %s expression `%s` because it is not an lvalue", action, e.toErrMsg()); return ErrorExp.get(); } @@ -15576,7 +15548,7 @@ private Expression toLvalueImpl(Expression _this, Scope* sc, const(char)* action { if (!_this.loc.isValid()) _this.loc = e.loc; - error(e.loc, "cannot %s constant `%s`", action, e.toChars()); + error(e.loc, "cannot %s constant `%s`", action, e.toErrMsg()); return ErrorExp.get(); } @@ -15599,7 +15571,7 @@ private Expression toLvalueImpl(Expression _this, Scope* sc, const(char)* action Expression visitStructLiteral(StructLiteralExp _this) { - if (sc.flags & SCOPE.Cfile) + if (sc.inCfile) return _this; // C struct literals are lvalues else return visit(_this); @@ -15646,7 +15618,7 @@ private Expression toLvalueImpl(Expression _this, Scope* sc, const(char)* action auto e1 = _this.e1; auto var = _this.var; //printf("DotVarExp::toLvalue(%s)\n", toChars()); - if (sc && sc.flags & SCOPE.Cfile) + if (sc && sc.inCfile) { /* C11 6.5.2.3-3: A postfix expression followed by the '.' or '->' operator * is an lvalue if the first expression is an lvalue. @@ -15692,7 +15664,7 @@ private Expression toLvalueImpl(Expression _this, Scope* sc, const(char)* action Expression visitCast(CastExp _this) { - if (sc && sc.flags & SCOPE.Cfile) + if (sc && sc.inCfile) { /* C11 6.5.4-5: A cast does not yield an lvalue. */ @@ -15703,7 +15675,7 @@ private Expression toLvalueImpl(Expression _this, Scope* sc, const(char)* action with (_this) if (!trusted && !e1.type.pointerTo().implicitConvTo(to.pointerTo())) sc.setUnsafePreview(FeatureState.default_, false, loc, - "cast from `%s` to `%s` cannot be used as an lvalue in @safe code", + "using the result of a cast from `%s` to `%s` as an lvalue", e1.type, to); return _this; @@ -16001,12 +15973,12 @@ private Expression modifiableLvalueImpl(Expression _this, Scope* sc, Expression break; if (!ff.type.isMutable) { - error(exp.loc, "cannot modify `%s` in `%s` function", exp.toChars(), MODtoChars(type.mod)); + error(exp.loc, "cannot modify `%s` in `%s` function", exp.toErrMsg(), MODtoChars(type.mod)); return ErrorExp.get(); } } } - error(exp.loc, "cannot modify `%s` expression `%s`", MODtoChars(type.mod), exp.toChars()); + error(exp.loc, "cannot modify `%s` expression `%s`", MODtoChars(type.mod), exp.toErrMsg()); return ErrorExp.get(); } else if (!type.isAssignable()) @@ -16021,7 +15993,7 @@ private Expression modifiableLvalueImpl(Expression _this, Scope* sc, Expression Expression visitString(StringExp exp) { - error(exp.loc, "cannot modify string literal `%s`", exp.toChars()); + error(exp.loc, "cannot modify string literal `%s`", exp.toErrMsg()); return ErrorExp.get(); } @@ -16030,7 +16002,7 @@ private Expression modifiableLvalueImpl(Expression _this, Scope* sc, Expression //printf("VarExp::modifiableLvalue('%s')\n", exp.var.toChars()); if (exp.var.storage_class & STC.manifest) { - error(exp.loc, "cannot modify manifest constant `%s`", exp.toChars()); + error(exp.loc, "cannot modify manifest constant `%s`", exp.toErrMsg()); return ErrorExp.get(); } // See if this expression is a modifiable lvalue (i.e. not const) @@ -16059,7 +16031,7 @@ private Expression modifiableLvalueImpl(Expression _this, Scope* sc, Expression Expression visitSlice(SliceExp exp) { - error(exp.loc, "slice expression `%s` is not a modifiable lvalue", exp.toChars()); + error(exp.loc, "slice expression `%s` is not a modifiable lvalue", exp.toErrMsg()); return exp; } @@ -16071,7 +16043,7 @@ private Expression modifiableLvalueImpl(Expression _this, Scope* sc, Expression Expression visitDelegatePtr(DelegatePtrExp exp) { - if (sc.setUnsafe(false, exp.loc, "cannot modify delegate pointer in `@safe` code `%s`", exp)) + if (sc.setUnsafe(false, exp.loc, "modifying delegate pointer `%s`", exp)) { return ErrorExp.get(); } @@ -16080,7 +16052,7 @@ private Expression modifiableLvalueImpl(Expression _this, Scope* sc, Expression Expression visitDelegateFuncptr(DelegateFuncptrExp exp) { - if (sc.setUnsafe(false, exp.loc, "cannot modify delegate function pointer in `@safe` code `%s`", exp)) + if (sc.setUnsafe(false, exp.loc, "modifying delegate function pointer `%s`", exp)) { return ErrorExp.get(); } @@ -16101,7 +16073,7 @@ private Expression modifiableLvalueImpl(Expression _this, Scope* sc, Expression { if (!exp.e1.isLvalue() && !exp.e2.isLvalue()) { - error(exp.loc, "conditional expression `%s` is not a modifiable lvalue", exp.toChars()); + error(exp.loc, "conditional expression `%s` is not a modifiable lvalue", exp.toErrMsg()); return ErrorExp.get(); } exp.e1 = exp.e1.modifiableLvalue(sc); @@ -16134,7 +16106,7 @@ private Expression modifiableLvalueImpl(Expression _this, Scope* sc, Expression * Returns: * `true` if ok, `false` for error */ -bool checkAddressVar(Scope* sc, Expression exp, VarDeclaration v) +private bool checkAddressVar(Scope* sc, Expression exp, VarDeclaration v) { //printf("checkAddressVar(exp: %s, v: %s)\n", exp.toChars(), v.toChars()); if (v is null) @@ -16142,14 +16114,14 @@ bool checkAddressVar(Scope* sc, Expression exp, VarDeclaration v) if (!v.canTakeAddressOf()) { - error(exp.loc, "cannot take address of `%s`", exp.toChars()); + error(exp.loc, "cannot take address of `%s`", exp.toErrMsg()); return false; } if (sc.func && !sc.intypeof && !v.isDataseg()) { if (sc.useDIP1000 != FeatureState.enabled && !(v.storage_class & STC.temp) && - sc.setUnsafe(false, exp.loc, "cannot take address of local `%s` in `@safe` function `%s`", v, sc.func)) + sc.setUnsafe(false, exp.loc, "taking the address of stack-allocated local variable `%s`", v)) { return false; } @@ -16195,7 +16167,7 @@ bool checkAddressable(Expression e, Scope* sc) continue; case EXP.variable: - if (sc.flags & SCOPE.Cfile) + if (sc.inCfile) { // C11 6.5.3.2: A variable that has its address taken cannot be // stored in a register. @@ -16204,9 +16176,9 @@ bool checkAddressable(Expression e, Scope* sc) if (ex.isVarExp().var.storage_class & STC.register) { if (e.isIndexExp()) - error(e.loc, "cannot index through register variable `%s`", ex.toChars()); + error(e.loc, "cannot index through register variable `%s`", ex.toErrMsg()); else - error(e.loc, "cannot take address of register variable `%s`", ex.toChars()); + error(e.loc, "cannot take address of register variable `%s`", ex.toErrMsg()); return false; } } @@ -16257,7 +16229,7 @@ private bool checkFunctionAttributes(Expression exp, Scope* sc, FuncDeclaration * Returns: * Expression representing the `this` for the var */ -Expression getThisSkipNestedFuncs(const ref Loc loc, Scope* sc, Dsymbol s, AggregateDeclaration ad, Expression e1, Type t, Dsymbol var, bool flag = false) +Expression getThisSkipNestedFuncs(Loc loc, Scope* sc, Dsymbol s, AggregateDeclaration ad, Expression e1, Type t, Dsymbol var, bool flag = false) { int n = 0; while (s && s.isFuncDeclaration()) @@ -16324,7 +16296,7 @@ Expression getThisSkipNestedFuncs(const ref Loc loc, Scope* sc, Dsymbol s, Aggre * newly created variable such that a closure is made for the variable when * the address of `fd` is taken. */ -VarDeclaration makeThis2Argument(const ref Loc loc, Scope* sc, FuncDeclaration fd) +private VarDeclaration makeThis2Argument(Loc loc, Scope* sc, FuncDeclaration fd) { Type tthis2 = Type.tvoidptr.sarrayOf(2); VarDeclaration vthis2 = new VarDeclaration(loc, tthis2, Identifier.generateId("__this"), null); @@ -16350,7 +16322,7 @@ VarDeclaration makeThis2Argument(const ref Loc loc, Scope* sc, FuncDeclaration f * Returns: * a `bool` indicating if the hook is present. */ -bool verifyHookExist(const ref Loc loc, ref Scope sc, Identifier id, string description, Identifier module_ = Id.object) +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); @@ -16374,7 +16346,7 @@ bool verifyHookExist(const ref Loc loc, ref Scope sc, Identifier id, string desc * false if any errors occur, * otherwise true and elements[] are rewritten for the output. */ -private bool fit(StructDeclaration sd, const ref Loc loc, Scope* sc, Expressions* elements, Type stype) +private bool fit(StructDeclaration sd, Loc loc, Scope* sc, Expressions* elements, Type stype) { if (!elements) return true; @@ -16428,7 +16400,7 @@ private bool fit(StructDeclaration sd, const ref Loc loc, Scope* sc, Expressions if ((!stype.alignment.isDefault() && stype.alignment.get() < target.ptrsize || (v.offset & (target.ptrsize - 1))) && (sc.setUnsafe(false, loc, - "field `%s.%s` cannot assign to misaligned pointers in `@safe` code", sd, v))) + "field `%s.%s` assigning to misaligned pointers", sd, v))) { return false; } @@ -16446,7 +16418,7 @@ private bool fit(StructDeclaration sd, const ref Loc loc, Scope* sc, Expressions Type typeb = se.type.toBasetype(); TY tynto = tb.nextOf().ty; if (!se.committed && - (typeb.ty == Tarray || typeb.ty == Tsarray) && tynto.isSomeChar && + typeb.isStaticOrDynamicArray() && tynto.isSomeChar && se.numberOfCodeUnits(tynto) < (cast(TypeSArray)tb).dim.toInteger()) { e = se.castTo(sc, t); @@ -16470,7 +16442,7 @@ private bool fit(StructDeclaration sd, const ref Loc loc, Scope* sc, Expressions if (e.op == EXP.error) return false; - (*elements)[i] = doCopyOrMove(sc, e); + (*elements)[i] = doCopyOrMove(sc, e, null, false); } return true; } @@ -16485,7 +16457,7 @@ private bool fit(StructDeclaration sd, const ref Loc loc, Scope* sc, Expressions * Returns: * VarExp referenceing `em` or ErrorExp if `em` if disabled/deprecated */ -Expression getVarExp(EnumMember em, const ref Loc loc, Scope* sc) +Expression getVarExp(EnumMember em, Loc loc, Scope* sc) { dsymbolSemantic(em, sc); if (em.errors) @@ -16503,7 +16475,7 @@ Expression getVarExp(EnumMember em, const ref Loc loc, Scope* sc) return ErrorExp.get(); Expression e = new VarExp(loc, em); e = e.expressionSemantic(sc); - if (!(sc.flags & SCOPE.Cfile) && em.isCsymbol()) + if (!sc.inCfile && em.isCsymbol()) { /* C11 types them as int. But if in D file, * type qualified names as the enum @@ -16544,7 +16516,7 @@ Expression toBoolean(Expression exp, Scope* sc) case EXP.construct: case EXP.blit: case EXP.loweredAssignExp: - if (sc.flags & SCOPE.Cfile) + if (sc.inCfile) return exp; // Things like: // if (a = b) ... @@ -16591,7 +16563,7 @@ Expression toBoolean(Expression exp, Scope* sc) /* Don't really need to check for opCast first, but by doing so we * get better error messages if it isn't there. */ - if (Dsymbol fd = search_function(ad, Id._cast)) + if (Dsymbol fd = search_function(ad, Id.opCast)) { e = new CastExp(exp.loc, e, Type.tbool); e = e.expressionSemantic(sc); @@ -16683,7 +16655,7 @@ bool evalStaticCondition(Scope* sc, Expression original, Expression e, out bool const uint nerrors = global.errors; sc = sc.startCTFE(); - sc.flags |= SCOPE.condition; + sc.condition = true; e = e.expressionSemantic(sc); e = resolveProperties(sc, e); @@ -16706,7 +16678,7 @@ bool evalStaticCondition(Scope* sc, Expression original, Expression e, out bool if (opt.isEmpty()) { if (!e.type.isTypeError()) - error(e.loc, "expression `%s` is not constant", e.toChars()); + error(e.loc, "expression `%s` is not constant", e.toErrMsg()); errors = true; return false; } @@ -16717,3 +16689,1013 @@ bool evalStaticCondition(Scope* sc, Expression original, Expression e, out bool } return impl(e); } + +/************************************ + * Check to see the aggregate type is nested and its context pointer is + * accessible from the current scope. + * Returns true if error occurs. + */ +bool checkFrameAccess(Loc loc, Scope* sc, AggregateDeclaration ad, size_t iStart = 0) +{ + Dsymbol sparent = ad.toParentLocal(); + Dsymbol sparent2 = ad.toParent2(); + Dsymbol s = sc.func; + if (ad.isNested() && s) + { + //printf("ad = %p %s [%s], parent:%p\n", ad, ad.toChars(), ad.loc.toChars(), ad.parent); + //printf("sparent = %p %s [%s], parent: %s\n", sparent, sparent.toChars(), sparent.loc.toChars(), sparent.parent,toChars()); + //printf("sparent2 = %p %s [%s], parent: %s\n", sparent2, sparent2.toChars(), sparent2.loc.toChars(), sparent2.parent,toChars()); + if (!ensureStaticLinkTo(s, sparent) || sparent != sparent2 && !ensureStaticLinkTo(s, sparent2)) + { + error(loc, "cannot access frame pointer of `%s`", ad.toPrettyChars()); + return true; + } + } + + bool result = false; + for (size_t i = iStart; i < ad.fields.length; i++) + { + VarDeclaration vd = ad.fields[i]; + Type tb = vd.type.baseElemOf(); + if (tb.ty == Tstruct) + { + result |= checkFrameAccess(loc, sc, (cast(TypeStruct)tb).sym); + } + } + return result; +} + +/// Return value for `checkModifiable` +enum Modifiable +{ + /// Not modifiable + no, + /// Modifiable (the type is mutable) + yes, + /// Modifiable because it is initialization + initialization, +} + +/** + * Specifies how the checkModify deals with certain situations + */ +enum ModifyFlags +{ + /// Issue error messages on invalid modifications of the variable + none, + /// No errors are emitted for invalid modifications + noError = 0x1, + /// The modification occurs for a subfield of the current variable + fieldAssign = 0x2, +} + +/************************************* + * Check to see if declaration can be modified in this context (sc). + * Issue error if not. + * Params: + * loc = location for error messages + * e1 = `null` or `this` expression when this declaration is a field + * sc = context + * flag = if the first bit is set it means do not issue error message for + * invalid modification; if the second bit is set, it means that + this declaration is a field and a subfield of it is modified. + * Returns: + * Modifiable.yes or Modifiable.initialization + */ +private Modifiable checkModify(Declaration d, Loc loc, Scope* sc, Expression e1, ModifyFlags flag) +{ + VarDeclaration v = d.isVarDeclaration(); + if (v && v.canassign) + return Modifiable.initialization; + + if (d.isParameter() || d.isResult()) + { + for (Scope* scx = sc; scx; scx = scx.enclosing) + { + if (scx.func == d.parent && scx.contract != Contract.none) + { + const(char)* s = d.isParameter() && d.parent.ident != Id.ensure ? "parameter" : "result"; + if (!(flag & ModifyFlags.noError)) + error(loc, "%s `%s` cannot modify %s `%s` in contract", d.kind, d.toPrettyChars, s, d.toChars()); + return Modifiable.initialization; // do not report type related errors + } + } + } + + if (e1 && e1.op == EXP.this_ && d.isField()) + { + VarDeclaration vthis = e1.isThisExp().var; + for (Scope* scx = sc; scx; scx = scx.enclosing) + { + if (scx.func == vthis.parent && scx.contract != Contract.none) + { + if (!(flag & ModifyFlags.noError)) + error(loc, "cannot modify member variable `%s` in contract", d.toPrettyChars()); + return Modifiable.initialization; // do not report type related errors + } + } + } + + if (v && (v.isCtorinit() || d.isField())) + { + // It's only modifiable if inside the right constructor + if ((d.storage_class & (STC.foreach_ | STC.ref_)) == (STC.foreach_ | STC.ref_)) + return Modifiable.initialization; + if (flag & ModifyFlags.fieldAssign) + return Modifiable.yes; + return modifyFieldVar(loc, sc, v, e1) ? Modifiable.initialization : Modifiable.yes; + } + return Modifiable.yes; +} + +/*********************************************** + * Mark variable v as modified if it is inside a constructor that var + * is a field in. + * Also used to allow immutable globals to be initialized inside a static constructor. + * Returns: + * true if it's an initialization of v + */ +private bool modifyFieldVar(Loc loc, Scope* sc, VarDeclaration var, Expression e1) +{ + //printf("modifyFieldVar(var = %s)\n", var.toChars()); + Dsymbol s = sc.func; + while (1) + { + FuncDeclaration fd = null; + if (s) + fd = s.isFuncDeclaration(); + if (fd && + ((fd.isCtorDeclaration() && var.isField()) || + ((fd.isStaticCtorDeclaration() || fd.isCrtCtor) && !var.isField())) && + fd.toParentDecl() == var.toParent2() && + (!e1 || e1.op == EXP.this_)) + { + bool result = true; + + var.ctorinit = true; + //printf("setting ctorinit\n"); + + if (var.isField() && sc.ctorflow.fieldinit.length && !sc.intypeof) + { + assert(e1); + auto mustInit = ((var.storage_class & STC.nodefaultctor) != 0 || + var.type.needsNested()); + + const dim = sc.ctorflow.fieldinit.length; + auto ad = fd.isMemberDecl(); + assert(ad); + size_t i; + for (i = 0; i < dim; i++) // same as findFieldIndexByName in ctfeexp.c ? + { + if (ad.fields[i] == var) + break; + } + assert(i < dim); + auto fieldInit = &sc.ctorflow.fieldinit[i]; + const fi = fieldInit.csx; + + if (fi & CSX.this_ctor) + { + if (var.type.isMutable() && e1.type.isMutable()) + result = false; + else + { + const(char)* modStr = !var.type.isMutable() ? MODtoChars(var.type.mod) : MODtoChars(e1.type.mod); + .error(loc, "%s field `%s` initialized multiple times", modStr, var.toChars()); + .errorSupplemental(fieldInit.loc, "Previous initialization is here."); + } + } + else if (sc.inLoop || (fi & CSX.label)) + { + if (!mustInit && var.type.isMutable() && e1.type.isMutable()) + result = false; + else + { + const(char)* modStr = !var.type.isMutable() ? MODtoChars(var.type.mod) : MODtoChars(e1.type.mod); + .error(loc, "%s field `%s` initialization is not allowed in loops or after labels", modStr, var.toChars()); + } + } + + fieldInit.csx |= CSX.this_ctor; + fieldInit.loc = e1.loc; + if (var.overlapped) // https://issues.dlang.org/show_bug.cgi?id=15258 + { + foreach (j, v; ad.fields) + { + if (v is var || !var.isOverlappedWith(v)) + continue; + v.ctorinit = true; + sc.ctorflow.fieldinit[j].csx = CSX.this_ctor; + } + } + } + else if (fd != sc.func) + { + if (var.type.isMutable()) + result = false; + else if (sc.func.fes) + { + const(char)* p = var.isField() ? "field" : var.kind(); + .error(loc, "%s %s `%s` initialization is not allowed in foreach loop", + MODtoChars(var.type.mod), p, var.toChars()); + } + else + { + const(char)* p = var.isField() ? "field" : var.kind(); + .error(loc, "%s %s `%s` initialization is not allowed in nested function `%s`", + MODtoChars(var.type.mod), p, var.toChars(), sc.func.toChars()); + } + } + else if (fd.isStaticCtorDeclaration() && !fd.isSharedStaticCtorDeclaration() && + var.type.isImmutable()) + { + .error(loc, "%s %s `%s` initialization is not allowed in `static this`", + MODtoChars(var.type.mod), var.kind(), var.toChars()); + errorSupplemental(loc, "Use `shared static this` instead."); + } + else if (fd.isStaticCtorDeclaration() && !fd.isSharedStaticCtorDeclaration() && + var.type.isConst()) + { + // @@@DEPRECATED_2.116@@@ + // Turn this into an error, merging with the branch above + .deprecation(loc, "%s %s `%s` initialization is not allowed in `static this`", + MODtoChars(var.type.mod), var.kind(), var.toChars()); + deprecationSupplemental(loc, "Use `shared static this` instead."); + } + return result; + } + else + { + if (s) + { + s = s.toParentP(var.toParent2()); + continue; + } + } + break; + } + return false; +} + +/*************************************** + * Request additional semantic analysis for TypeInfo generation. + * Params: + * sc = context + * t = type that TypeInfo is being generated for + */ +void semanticTypeInfo(Scope* sc, Type t) +{ + if (sc) + { + if (sc.intypeof) + return; + if (!sc.needsCodegen()) + return; + } + + if (!t) + return; + + void visitVector(TypeVector t) + { + semanticTypeInfo(sc, t.basetype); + } + + void visitAArray(TypeAArray t) + { + semanticTypeInfo(sc, t.index); + semanticTypeInfo(sc, t.next); + + if (global.params.useTypeInfo) + getTypeInfoType(t.loc, t, sc); + } + + void visitStruct(TypeStruct t) + { + //printf("semanticTypeInfo.visit(TypeStruct = %s)\n", t.toChars()); + StructDeclaration sd = t.sym; + + /* Step 1: create TypeInfoDeclaration + */ + if (!sc) // inline may request TypeInfo. + { + Scope scx; + scx.eSink = global.errorSink; + scx._module = sd.getModule(); + if (global.params.useTypeInfo) + { + getTypeInfoType(sd.loc, t, &scx); +version (IN_LLVM) {} else +{ + sd.requestTypeInfo = true; +} + } + } + else if (!sc.minst) + { + // don't yet have to generate TypeInfo instance if + // the typeid(T) expression exists in speculative scope. + } + else + { + getTypeInfoType(sd.loc, t, sc); +version (IN_LLVM) {} else +{ + sd.requestTypeInfo = true; +} + + // https://issues.dlang.org/show_bug.cgi?id=15149 + // if the typeid operand type comes from a + // result of auto function, it may be yet speculative. + // unSpeculative(sc, sd); + } + + /* Step 2: If the TypeInfo generation requires sd.semantic3, run it later. + * This should be done even if typeid(T) exists in speculative scope. + * Because it may appear later in non-speculative scope. + */ + if (!sd.members) + return; // opaque struct + if (!sd.xeq && !sd.xcmp && !sd.postblit && !sd.tidtor && !sd.xhash && !search_toString(sd)) + return; // none of TypeInfo-specific members + + // If the struct is in a non-root module, run semantic3 to get + // correct symbols for the member function. + if (sd.semanticRun >= PASS.semantic3) + { + // semantic3 is already done + } + else if (TemplateInstance ti = sd.isInstantiated()) + { + if (ti.minst && !ti.minst.isRoot()) + Module.addDeferredSemantic3(sd); + } + else + { + if (sd.inNonRoot()) + { + //printf("deferred sem3 for TypeInfo - sd = %s, inNonRoot = %d\n", sd.toChars(), sd.inNonRoot()); + Module.addDeferredSemantic3(sd); + } + } + } + + void visitTuple(TypeTuple t) + { + if (t.arguments) + { + foreach (arg; *t.arguments) + { + semanticTypeInfo(sc, arg.type); + } + } + } + + /* Note structural similarity of this Type walker to that in isSpeculativeType() + */ + + Type tb = t.toBasetype(); + switch (tb.ty) + { + case Tvector: visitVector(tb.isTypeVector()); break; + case Taarray: visitAArray(tb.isTypeAArray()); break; + case Tstruct: visitStruct(tb.isTypeStruct()); break; + case Ttuple: visitTuple (tb.isTypeTuple()); break; + + case Tclass: + case Tenum: break; + + default: semanticTypeInfo(sc, tb.nextOf()); break; + } +} + +/** + * Issue an error if an attempt to call a disabled method is made + * + * If the declaration is disabled but inside a disabled function, + * returns `true` but do not issue an error message. + * + * Params: + * d = Declaration to check + * loc = Location information of the call + * sc = Scope in which the call occurs + * isAliasedDeclaration = if `true` searches overload set + * + * Returns: + * `true` if this `Declaration` is `@disable`d, `false` otherwise. + */ +bool checkDisabled(Declaration d, Loc loc, Scope* sc, bool isAliasedDeclaration = false) +{ + if (!(d.storage_class & STC.disable)) + return false; + + if (sc.func && sc.func.storage_class & STC.disable) + return true; + + if (auto p = d.toParent()) + { + if (auto postblit = d.isPostBlitDeclaration()) + { + /* https://issues.dlang.org/show_bug.cgi?id=21885 + * + * If the generated postblit is disabled, it + * means that one of the fields has a disabled + * postblit. Print the first field that has + * a disabled postblit. + */ + if (postblit.isGenerated()) + { + auto sd = p.isStructDeclaration(); + assert(sd); + for (size_t i = 0; i < sd.fields.length; i++) + { + auto structField = sd.fields[i]; + if (structField.overlapped) + continue; + Type tv = structField.type.baseElemOf(); + if (tv.ty != Tstruct) + continue; + auto sdv = (cast(TypeStruct)tv).sym; + if (!sdv.postblit) + continue; + if (sdv.postblit.isDisabled()) + { + .error(loc, "%s `%s` is not copyable because field `%s` is not copyable", p.kind, p.toPrettyChars, structField.toChars()); + return true; + } + } + } + .error(loc, "%s `%s` is not copyable because it has a disabled postblit", p.kind, p.toPrettyChars); + return true; + } + } + + // if the function is @disabled, maybe there + // is an overload in the overload set that isn't + if (isAliasedDeclaration) + { + if (FuncDeclaration fd = d.isFuncDeclaration()) + { + for (FuncDeclaration ovl = fd; ovl; ovl = cast(FuncDeclaration)ovl.overnext) + if (!(ovl.storage_class & STC.disable)) + return false; + } + } + + if (auto ctor = d.isCtorDeclaration()) + { + //printf("checkDisabled() %s %s\n", ctor.toPrettyChars(), toChars(ctor.type)); + if (ctor.isCpCtor && ctor.isGenerated()) + { + .error(loc, "generating an `inout` copy constructor for `struct %s` failed, therefore instances of it are uncopyable", d.parent.toPrettyChars()); + return true; + } + } + .error(loc, "%s `%s` cannot be used because it is annotated with `@disable`", d.kind, d.toPrettyChars); + return true; +} + +/******************************************* + * Helper function for the expansion of manifest constant. + */ +private Expression expandInitializer(VarDeclaration vd, Loc loc) +{ + assert((vd.storage_class & STC.manifest) && vd._init); + + auto e = vd.getConstInitializer(); + if (!e) + { + .error(loc, "cannot make expression out of initializer for `%s`", vd.toChars()); + return ErrorExp.get(); + } + + e = e.copy(); + e.loc = loc; // for better error message + return e; +} + +/***************************************************** + * Determine if template instance is really a template function, + * and that template function needs to infer types from the function + * arguments. + * + * Like findBestMatch, iterate possible template candidates, + * but just looks only the necessity of type inference. + */ +private bool needsTypeInference(TemplateInstance ti, Scope* sc, int flag = 0) +{ + //printf("TemplateInstance.needsTypeInference() %s\n", toChars()); + if (ti.semanticRun != PASS.initial) + return false; + + const olderrs = global.errors; + Objects dedtypes; + size_t count = 0; + + 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 any of the overloaded template declarations need inference, + * then return true + */ + if (!td.onemember) + return 0; + if (auto td2 = td.onemember.isTemplateDeclaration()) + { + if (!td2.onemember || !td2.onemember.isFuncDeclaration()) + return 0; + if (ti.tiargs.length >= td.parameters.length - (td.isVariadic() ? 1 : 0)) + return 0; + return 1; + } + auto fd = td.onemember.isFuncDeclaration(); + if (!fd || fd.type.ty != Tfunction) + return 0; + + foreach (tp; *td.parameters) + { + if (tp.isTemplateThisParameter()) + return 1; + } + + /* Determine if the instance arguments, tiargs, are all that is necessary + * to instantiate the template. + */ + //printf("tp = %p, td.parameters.length = %d, tiargs.length = %d\n", tp, td.parameters.length, tiargs.length); + auto tf = fd.type.isTypeFunction(); + if (tf.parameterList.length) + { + auto tp = td.isVariadic(); + if (tp && td.parameters.length > 1) + return 1; + + if (!tp && ti.tiargs.length < td.parameters.length) + { + // Can remain tiargs be filled by default arguments? + foreach (size_t i; ti.tiargs.length .. td.parameters.length) + { + if (!(*td.parameters)[i].hasDefaultArg()) + return 1; + } + } + + foreach (i, fparam; tf.parameterList) + { + // 'auto ref' needs inference. + if (fparam.storageClass & STC.auto_) + return 1; + } + } + + if (!flag) + { + /* Calculate the need for overload resolution. + * When only one template can match with tiargs, inference is not necessary. + */ + dedtypes.setDim(td.parameters.length); + dedtypes.zero(); + if (td.semanticRun == PASS.initial) + { + if (td._scope) + { + // Try to fix forward reference. Ungag errors while doing so. + Ungag ungag = td.ungagSpeculative(); + td.dsymbolSemantic(td._scope); + } + if (td.semanticRun == PASS.initial) + { + .error(ti.loc, "%s `%s` `%s` forward references template declaration `%s`", + ti.kind, ti.toPrettyChars, ti.toChars(), td.toChars()); + return 1; + } + } + MATCH m = matchWithInstance(sc, td, ti, dedtypes, ArgumentList(), 0); + if (m == MATCH.nomatch) + return 0; + } + + /* If there is more than one function template which matches, we may + * need type inference (see https://issues.dlang.org/show_bug.cgi?id=4430) + */ + return ++count > 1 ? 1 : 0; + }); + if (r) + return true; + } + + if (olderrs != global.errors) + { + if (!global.gag) + { + errorSupplemental(ti.loc, "while looking for match for `%s`", ti.toChars()); + ti.semanticRun = PASS.semanticdone; + ti.inst = ti; + } + ti.errors = true; + } + //printf("false\n"); + return false; +} + +/*************************************** + * Fill out remainder of elements[] with default initializers for fields[]. + * Params: + * sd = struct + * loc = location + * elements = explicit arguments which given to construct object. + * ctorinit = true if the elements will be used for default initialization. + * Returns: + * false if any errors occur. + * Otherwise, returns true and the missing arguments will be pushed in elements[]. + */ +bool fill(StructDeclaration sd, Loc loc, ref Expressions elements, bool ctorinit) +{ + //printf("AggregateDeclaration::fill() %s\n", toChars()); + assert(sd.sizeok == Sizeok.done); + const nfields = sd.nonHiddenFields(); + bool errors = false; + + size_t dim = elements.length; + elements.setDim(nfields); + foreach (size_t i; dim .. nfields) + elements[i] = null; + + // Fill in missing any elements with default initializers + foreach (i; 0 .. nfields) + { + if (elements[i]) + continue; + + auto vd = sd.fields[i]; + auto vx = vd; + if (vd._init && vd._init.isVoidInitializer()) + vx = null; + + // Find overlapped fields with the hole [vd.offset .. vd.offset.size()]. + size_t fieldi = i; + foreach (j; 0 .. nfields) + { + if (i == j) + continue; + auto v2 = sd.fields[j]; + if (!vd.isOverlappedWith(v2)) + continue; + + if (elements[j]) + { + vx = null; + break; + } + if (v2._init && v2._init.isVoidInitializer()) + continue; + + version (all) + { + /* Prefer first found non-void-initialized field + * union U { int a; int b = 2; } + * U u; // Error: overlapping initialization for field a and b + */ + if (!vx) + { + vx = v2; + fieldi = j; + } + else if (v2._init) + { + .error(loc, "overlapping initialization for field `%s` and `%s`", v2.toChars(), vd.toChars()); + errors = true; + } + } + else + { + // fixes https://issues.dlang.org/show_bug.cgi?id=1432 by enabling this path always + + /* Prefer explicitly initialized field + * union U { int a; int b = 2; } + * U u; // OK (u.b == 2) + */ + if (!vx || !vx._init && v2._init) + { + vx = v2; + fieldi = j; + } + else if (vx != vd && !vx.isOverlappedWith(v2)) + { + // Both vx and v2 fills vd, but vx and v2 does not overlap + } + else if (vx._init && v2._init) + { + .error(loc, "overlapping default initialization for field `%s` and `%s`", + v2.toChars(), vd.toChars()); + errors = true; + } + else + assert(vx._init || !vx._init && !v2._init); + } + } + if (!vx) + continue; + + Expression e; + if (vx.type.size() == 0) + { + e = null; + } + else if (vx._init) + { + assert(!vx._init.isVoidInitializer()); + if (vx.inuse) // https://issues.dlang.org/show_bug.cgi?id=18057 + { + .error(loc, "%s `%s` recursive initialization of field", vx.kind(), vx.toPrettyChars()); + errors = true; + } + else + e = vx.getConstInitializer(false); + } + else + { + if ((vx.storage_class & STC.nodefaultctor) && !ctorinit) + { + .error(loc, "field `%s.%s` must be initialized because it has no default constructor", + sd.type.toChars(), vx.toChars()); + errors = true; + } + /* https://issues.dlang.org/show_bug.cgi?id=12509 + * Get the element of static array type. + */ + Type telem = vx.type; + if (telem.ty == Tsarray) + { + /* We cannot use Type::baseElemOf() here. + * If the bottom of the Tsarray is an enum type, baseElemOf() + * will return the base of the enum, and its default initializer + * would be different from the enum's. + */ + TypeSArray tsa; + while ((tsa = telem.toBasetype().isTypeSArray()) !is null) + telem = tsa.next; + if (telem.ty == Tvoid) + telem = Type.tuns8.addMod(telem.mod); + } + if (telem.needsNested() && ctorinit) + e = telem.defaultInit(loc); + else + e = telem.defaultInitLiteral(loc); + } + elements[fieldi] = e; + } + foreach (e; elements) + { + if (e && e.op == EXP.error) + return false; + } + + return !errors; +} + +/***************************************** +* Lower any aggregate that is not an array to an array using a +* regular foreach loop within CTFE. If there are multiple +* `static foreach` loop variables, an array of tuples is +* generated. In thise case, the field `needExpansion` is set to +* true to indicate that the static foreach loop expansion will +* need to expand the tuples into multiple variables. +* +* For example, `static foreach (x; range) { ... }` is lowered to: +* +* static foreach (x; { +* typeof({ +* foreach (x; range) return x; +* }())[] __res; +* foreach (x; range) __res ~= x; +* return __res; +* }()) { ... } +* +* Finally, call `lowerArrayAggregate` to turn the produced +* array into an expression tuple. +* +* Params: +* sfe = The 'static foreach'. +* sc = The current scope. +*/ +extern (C++) void lowerNonArrayAggregate(StaticForeach sfe, Scope* sc) +{ + import dmd.statement; + + auto nvars = sfe.aggrfe ? sfe.aggrfe.parameters.length : 1; + auto aloc = sfe.aggrfe ? sfe.aggrfe.aggr.loc : sfe.rangefe.lwr.loc; + // We need three sets of foreach loop variables because the + // lowering contains three foreach loops. + Parameters*[3] pparams = [new Parameters(), new Parameters(), new Parameters()]; + foreach (i; 0 .. nvars) + { + foreach (params; pparams) + { + auto p = sfe.aggrfe ? (*sfe.aggrfe.parameters)[i] : sfe.rangefe.param; + params.push(new Parameter(aloc, p.storageClass, p.type, p.ident, null, null)); + } + } + Expression[2] res; + TypeStruct tplty = null; + if (nvars == 1) // only one `static foreach` variable, generate identifiers. + { + foreach (i; 0 .. 2) + { + res[i] = new IdentifierExp(aloc, (*pparams[i])[0].ident); + } + } + else // multiple `static foreach` variables, generate tuples. + { + foreach (i; 0 .. 2) + { + auto e = new Expressions(pparams[0].length); + foreach (j, ref elem; *e) + { + auto p = (*pparams[i])[j]; + elem = new IdentifierExp(aloc, p.ident); + } + if (!tplty) + { + tplty = sfe.createTupleType(aloc, e, sc); + } + res[i] = sfe.createTuple(aloc, tplty, e); + } + sfe.needExpansion = true; // need to expand the tuples later + } + // generate remaining code for the new aggregate which is an + // array (see documentation comment). + if (sfe.rangefe) + { + sc = sc.startCTFE(); + sfe.rangefe.lwr = sfe.rangefe.lwr.expressionSemantic(sc); + sfe.rangefe.lwr = resolveProperties(sc, sfe.rangefe.lwr); + sfe.rangefe.upr = sfe.rangefe.upr.expressionSemantic(sc); + sfe.rangefe.upr = resolveProperties(sc, sfe.rangefe.upr); + sc = sc.endCTFE(); + sfe.rangefe.lwr = sfe.rangefe.lwr.optimize(WANTvalue); + sfe.rangefe.lwr = sfe.rangefe.lwr.ctfeInterpret(); + sfe.rangefe.upr = sfe.rangefe.upr.optimize(WANTvalue); + sfe.rangefe.upr = sfe.rangefe.upr.ctfeInterpret(); + } + auto s1 = new Statements(); + auto stmts = new Statements(); + if (tplty) stmts.push(new ExpStatement(sfe.loc, tplty.sym)); + stmts.push(new ReturnStatement(aloc, res[0])); + s1.push(sfe.createForeach(aloc, pparams[0], new CompoundStatement(aloc, stmts))); + s1.push(new ExpStatement(aloc, new AssertExp(aloc, IntegerExp.literal!0))); + Type ety = new TypeTypeof(aloc, sfe.wrapAndCall(aloc, new CompoundStatement(aloc, s1))); + auto aty = ety.arrayOf(); + auto idres = Identifier.generateId("__res"); + auto vard = new VarDeclaration(aloc, aty, idres, null, STC.temp); + auto s2 = new Statements(); + + // Run 'typeof' gagged to avoid duplicate errors and if it fails just create + // an empty foreach to expose them. + const olderrors = global.startGagging(); + ety = ety.typeSemantic(aloc, sc); + if (global.endGagging(olderrors)) + s2.push(sfe.createForeach(aloc, pparams[1], null)); + else + { + s2.push(new ExpStatement(aloc, vard)); + auto catass = new CatAssignExp(aloc, new IdentifierExp(aloc, idres), res[1]); + s2.push(sfe.createForeach(aloc, pparams[1], new ExpStatement(aloc, catass))); + s2.push(new ReturnStatement(aloc, new IdentifierExp(aloc, idres))); + } + + Expression aggr = void; + Type indexty = void; + + if (sfe.rangefe && (indexty = ety).isIntegral()) + { + sfe.rangefe.lwr.type = indexty; + sfe.rangefe.upr.type = indexty; + auto lwrRange = getIntRange(sfe.rangefe.lwr); + auto uprRange = getIntRange(sfe.rangefe.upr); + + const lwr = sfe.rangefe.lwr.toInteger(); + auto upr = sfe.rangefe.upr.toInteger(); + size_t length = 0; + + if (lwrRange.imin <= uprRange.imax) + length = cast(size_t) (upr - lwr); + + auto exps = new Expressions(length); + + if (sfe.rangefe.op == TOK.foreach_) + { + foreach (i; 0 .. length) + (*exps)[i] = new IntegerExp(aloc, lwr + i, indexty); + } + else + { + --upr; + foreach (i; 0 .. length) + (*exps)[i] = new IntegerExp(aloc, upr - i, indexty); + } + aggr = new ArrayLiteralExp(aloc, indexty.arrayOf(), exps); + } + else + { + aggr = sfe.wrapAndCall(aloc, new CompoundStatement(aloc, s2)); + sc = sc.startCTFE(); + aggr = aggr.expressionSemantic(sc); + aggr = resolveProperties(sc, aggr); + sc = sc.endCTFE(); + aggr = aggr.optimize(WANTvalue); + aggr = aggr.ctfeInterpret(); + } + + assert(!!sfe.aggrfe ^ !!sfe.rangefe); + sfe.aggrfe = new ForeachStatement(sfe.loc, TOK.foreach_, pparams[2], aggr, + sfe.aggrfe ? sfe.aggrfe._body : sfe.rangefe._body, + sfe.aggrfe ? sfe.aggrfe.endloc : sfe.rangefe.endloc); + sfe.rangefe = null; + sfe.lowerArrayAggregate(sc); // finally, turn generated array into expression tuple +} + +/***************************************** +* Perform `static foreach` lowerings that are necessary in order +* to finally expand the `static foreach` using +* `dmd.statementsem.makeTupleForeach`. +*/ +extern(D) void prepare(StaticForeach sfe, Scope* sc) +{ + assert(sc); + + if (sfe.aggrfe) + { + sc = sc.startCTFE(); + sfe.aggrfe.aggr = sfe.aggrfe.aggr.expressionSemantic(sc); + sc = sc.endCTFE(); + } + + if (sfe.aggrfe && sfe.aggrfe.aggr.type.toBasetype().ty == Terror) + { + return; + } + + if (!sfe.ready()) + { + if (sfe.aggrfe && sfe.aggrfe.aggr.type.toBasetype().ty == Tarray) + { + sfe.lowerArrayAggregate(sc); + } + else + { + sfe.lowerNonArrayAggregate(sc); + } + } +} + +/***************************************** + * Turn an aggregate which is an array into an expression tuple + * of its elements. I.e., lower + * static foreach (x; [1, 2, 3, 4]) { ... } + * to + * static foreach (x; AliasSeq!(1, 2, 3, 4)) { ... } + */ +extern(D) void lowerArrayAggregate(StaticForeach sfe, Scope* sc) +{ + auto aggr = sfe.aggrfe.aggr; + Expression el = new ArrayLengthExp(aggr.loc, aggr); + sc = sc.startCTFE(); + el = el.expressionSemantic(sc); + sc = sc.endCTFE(); + el = el.optimize(WANTvalue); + el = el.ctfeInterpret(); + if (el.op != EXP.int64) + { + sfe.aggrfe.aggr = ErrorExp.get(); + return; + } + + Expressions* es; + if (auto ale = aggr.isArrayLiteralExp()) + { + // Directly use the elements of the array for the TupleExp creation + es = ale.elements; + } + else + { + const length = cast(size_t)el.toInteger(); + es = new Expressions(length); + foreach (i; 0 .. length) + { + auto index = new IntegerExp(sfe.loc, i, Type.tsize_t); + auto value = new IndexExp(aggr.loc, aggr, index); + (*es)[i] = value; + } + } + sfe.aggrfe.aggr = new TupleExp(aggr.loc, es); + sfe.aggrfe.aggr = sfe.aggrfe.aggr.expressionSemantic(sc); + sfe.aggrfe.aggr = sfe.aggrfe.aggr.optimize(WANTvalue); + sfe.aggrfe.aggr = sfe.aggrfe.aggr.ctfeInterpret(); +} diff --git a/dmd/file_manager.d b/dmd/file_manager.d index 6ad3d62170..2ccb1d2549 100644 --- a/dmd/file_manager.d +++ b/dmd/file_manager.d @@ -1,11 +1,11 @@ /** * Read a file from disk and store it in memory. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/file_manager.d, _file_manager.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/file_manager.d, _file_manager.d) * Documentation: https://dlang.org/phobos/dmd_file_manager.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/file_manager.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/file_manager.d */ module dmd.file_manager; @@ -16,7 +16,6 @@ import dmd.root.stringtable : StringTable; import dmd.root.file : File, Buffer; import dmd.root.filename : FileName, isDirSeparator; import dmd.root.string : toDString; -import dmd.errors; import dmd.globals; import dmd.identifier; import dmd.location; @@ -101,8 +100,10 @@ private struct PathCache */ bool exists = true; auto st = PathStack(filespec); - while (st.up) { - if (auto cached = pathStatus.lookup(st.cur)) { + while (st.up) + { + if (auto cached = pathStatus.lookup(st.cur)) + { exists = cached.value; break; } @@ -112,7 +113,8 @@ private struct PathCache * Once a directory is found to not exist, all the directories * to the right of it do not exist */ - while (st.down) { + while (st.down) + { if (!exists) pathStatus.insert(st.cur, false); else @@ -159,16 +161,20 @@ nothrow: * Does not open the file. * Params: * filename = as supplied by the user - * paths = paths to look for filename + * pathsInfo = pathsInfo to look for filename with metadata + * whichPathFoundThis = Which path from `path` was used in determining the output path, or -1 if unknown. * Returns: * the found file name or * `null` if it is not different from filename. */ - const(char)[] lookForSourceFile(const char[] filename, const char*[] paths) + const(char)[] lookForSourceFile(const char[] filename, const ImportPathInfo[] pathsInfo, out ptrdiff_t whichPathFoundThis) { //printf("lookForSourceFile(`%.*s`)\n", cast(int)filename.length, filename.ptr); - /* Search along paths[] for .di file, then .d file. + /* Search along pathsInfo[] for .di file, then .d file. */ + + whichPathFoundThis = -1; + // see if we should check for the module locally. bool checkLocal = pathCache.pathExists(filename); const sdi = FileName.forceExt(filename, hdr_ext); @@ -177,9 +183,6 @@ nothrow: scope(exit) FileName.free(sdi.ptr); const sd = FileName.forceExt(filename, mars_ext); - // Special file name representing `stdin`, always assume its presence - if (sd == "__stdin.d") - return sd; if (checkLocal && FileName.exists(sd) == 1) return sd; scope(exit) FileName.free(sd.ptr); @@ -206,25 +209,28 @@ nothrow: if (FileName.absolute(filename)) return null; - if (!paths.length) + if (!pathsInfo.length) return null; - foreach (entry; paths) + foreach (pathIndex, entry; pathsInfo) { - const p = entry.toDString(); + const p = entry.path.toDString(); const(char)[] n = FileName.combine(p, sdi); - if (!pathCache.pathExists(n)) { + if (!pathCache.pathExists(n)) + { FileName.free(n.ptr); continue; // no need to check for anything else. } - if (FileName.exists(n) == 1) { + if (FileName.exists(n) == 1) return n; - } + FileName.free(n.ptr); n = FileName.combine(p, sd); - if (FileName.exists(n) == 1) { + if (FileName.exists(n) == 1) + { + whichPathFoundThis = pathIndex; return n; } FileName.free(n.ptr); @@ -237,10 +243,16 @@ nothrow: { const n2i = FileName.combine(n, package_di); if (FileName.exists(n2i) == 1) + { + whichPathFoundThis = pathIndex; return n2i; + } + FileName.free(n2i.ptr); const n2 = FileName.combine(n, package_d); - if (FileName.exists(n2) == 1) { + if (FileName.exists(n2) == 1) + { + whichPathFoundThis = pathIndex; return n2; } FileName.free(n2.ptr); @@ -258,18 +270,22 @@ nothrow: if (FileName.exists(sc) == 1) return sc; scope(exit) FileName.free(sc.ptr); - foreach (entry; paths) + foreach (pathIndex, entry; pathsInfo) { - const p = entry.toDString(); + const p = entry.path.toDString(); const(char)[] n = FileName.combine(p, si); - if (FileName.exists(n) == 1) { + if (FileName.exists(n) == 1) + { + whichPathFoundThis = pathIndex; return n; } FileName.free(n.ptr); n = FileName.combine(p, sc); - if (FileName.exists(n) == 1) { + if (FileName.exists(n) == 1) + { + whichPathFoundThis = pathIndex; return n; } FileName.free(n.ptr); @@ -292,20 +308,12 @@ nothrow: if (auto val = files.lookup(name)) // if `name` is cached return val.value; // return its contents - OutBuffer buf; - if (name == "__stdin.d") // special name for reading from stdin - { - if (readFromStdin(buf)) - fatal(); - } - else - { - if (FileName.exists(name) != 1) // if not an ordinary file - return null; + if (FileName.exists(name) != 1) // if not an ordinary file + return null; - if (File.read(name, buf)) - return null; // failed - } + OutBuffer buf; + if (File.read(name, buf)) + return null; // failed buf.write32(0); // terminating dchar 0 @@ -331,36 +339,3 @@ nothrow: return val == null ? null : val.value; } } - -private bool readFromStdin(ref OutBuffer sink) nothrow -{ - import core.stdc.stdio; - import dmd.errors; - - enum BufIncrement = 128 * 1024; - - for (size_t j; 1; ++j) - { - char[] buffer = sink.allocate(BufIncrement); - - // Fill up buffer - size_t filled = 0; - do - { - filled += fread(buffer.ptr + filled, 1, buffer.length - filled, stdin); - if (ferror(stdin)) - { - import core.stdc.errno; - error(Loc.initial, "cannot read from stdin, errno = %d", errno); - return true; - } - if (feof(stdin)) // successful completion - { - sink.setsize(j * BufIncrement + filled); - return false; - } - } while (filled < BufIncrement); - } - - assert(0); -} diff --git a/dmd/frontend.h b/dmd/frontend.h index 17fdcb4dc5..910dd0d037 100644 --- a/dmd/frontend.h +++ b/dmd/frontend.h @@ -374,33 +374,49 @@ enum class MessageStyle : uint8_t { digitalmars = 0u, gnu = 1u, + sarif = 2u, +}; + +struct SourceLoc final +{ + _d_dynamicArray< const char > filename; + uint32_t line; + uint32_t column; + uint32_t fileOffset; + _d_dynamicArray< const char > fileContent; + const char* toChars(bool showColumns = Loc::showColumns, MessageStyle messageStyle = Loc::messageStyle) const; + SourceLoc() : + filename(), + line(), + column(), + fileOffset(), + fileContent() + { + } }; struct Loc final { private: - uint32_t _linnum; - uint32_t _charnum; - uint32_t fileIndex; + uint32_t index; public: static bool showColumns; static MessageStyle messageStyle; static void set(bool showColumns, MessageStyle messageStyle); - Loc(const char* filename, uint32_t linnum, uint32_t charnum); + static Loc singleFilename(const char* const filename); uint32_t charnum() const; - uint32_t charnum(uint32_t num); uint32_t linnum() const; - uint32_t linnum(uint32_t num); const char* filename() const; - void filename(const char* name); const char* toChars(bool showColumns = Loc::showColumns, MessageStyle messageStyle = Loc::messageStyle) const; - bool equals(const Loc& loc) const; + SourceLoc toSourceLoc() const; + bool equals(Loc loc) const; Loc() : - _linnum(), - _charnum(), - fileIndex() + index(0u) { } + Loc(uint32_t index) : + index(index) + {} }; enum class PASS : uint8_t @@ -417,6 +433,76 @@ enum class PASS : uint8_t obj = 9u, }; +enum class DSYM : uint8_t +{ + none = 0u, + dsymbol = 1u, + linkDeclaration = 2u, + cppMangleDeclaration = 3u, + alignDeclaration = 4u, + pragmaDeclaration = 5u, + conditionalDeclaration = 6u, + staticForeachDeclaration = 7u, + userAttributeDeclaration = 8u, + labelDsymbol = 9u, + aliasThis = 10u, + package_ = 11u, + module_ = 12u, + enumMember = 13u, + templateDeclaration = 14u, + templateInstance = 15u, + templateMixin = 16u, + forwardingAttribDeclaration = 17u, + nspace = 18u, + declaration = 19u, + storageClassDeclaration = 20u, + expressionDsymbol = 21u, + aliasAssign = 22u, + thisDeclaration = 23u, + bitFieldDeclaration = 24u, + typeInfoDeclaration = 25u, + tupleDeclaration = 26u, + aliasDeclaration = 27u, + aggregateDeclaration = 28u, + funcDeclaration = 29u, + funcAliasDeclaration = 30u, + overDeclaration = 31u, + funcLiteralDeclaration = 32u, + ctorDeclaration = 33u, + postBlitDeclaration = 34u, + dtorDeclaration = 35u, + staticCtorDeclaration = 36u, + staticDtorDeclaration = 37u, + sharedStaticCtorDeclaration = 38u, + sharedStaticDtorDeclaration = 39u, + invariantDeclaration = 40u, + unitTestDeclaration = 41u, + newDeclaration = 42u, + varDeclaration = 43u, + versionSymbol = 44u, + debugSymbol = 45u, + classDeclaration = 46u, + structDeclaration = 47u, + unionDeclaration = 48u, + interfaceDeclaration = 49u, + scopeDsymbol = 50u, + forwardingScopeDsymbol = 51u, + withScopeSymbol = 52u, + arrayScopeSymbol = 53u, + import_ = 54u, + enumDeclaration = 55u, + symbolDeclaration = 56u, + attribDeclaration = 57u, + anonDeclaration = 58u, + cppNamespaceDeclaration = 59u, + visibilityDeclaration = 60u, + overloadSet = 61u, + mixinDeclaration = 62u, + staticAssert = 63u, + staticIfDeclaration = 64u, + cAsmDeclaration = 65u, +}; + struct Ungag final { uint32_t oldgag; @@ -457,17 +543,37 @@ class Dsymbol : public ASTNode Identifier* ident; Dsymbol* parent; Symbol* csym; - const Loc loc; Scope* _scope; - const char* prettystring; private: DsymbolAttributes* atts; public: - bool errors; - PASS semanticRun; + const Loc loc; uint16_t localNum; + struct BitFields final + { + bool errors; + PASS semanticRun; + BitFields() : + errors(), + semanticRun((PASS)0u) + { + } + BitFields(bool errors, PASS semanticRun = (PASS)0u) : + errors(errors), + semanticRun(semanticRun) + {} + }; + + bool errors() const; + bool errors(bool v); + PASS semanticRun() const; + PASS semanticRun(PASS v); +private: + uint8_t bitFields; +public: + DSYM dsym; static Dsymbol* create(Identifier* ident); - const char* toChars() const override; + const char* toChars() const final override; DeprecatedDeclaration* depdecl(); CPPNamespaceDeclaration* cppnamespace(); UserAttributeDeclaration* userAttribDecl(); @@ -475,8 +581,6 @@ class Dsymbol : public ASTNode CPPNamespaceDeclaration* cppnamespace(CPPNamespaceDeclaration* ns); UserAttributeDeclaration* userAttribDecl(UserAttributeDeclaration* uad); virtual const char* toPrettyCharsHelper(); - const Loc getLoc(); - const char* locToChars(); bool equals(const RootObject* const o) const override; bool isAnonymous() const; Module* getModule(); @@ -499,7 +603,7 @@ class Dsymbol : public ASTNode virtual Dsymbol* toAlias(); virtual Dsymbol* toAlias2(); virtual bool overloadInsert(Dsymbol* s); - virtual uinteger_t size(const Loc& loc); + virtual uinteger_t size(Loc loc); virtual bool isforwardRef(); virtual AggregateDeclaration* isThis(); virtual bool isExport() const; @@ -516,11 +620,8 @@ class Dsymbol : public ASTNode virtual bool needThis(); virtual Visibility visible(); virtual Dsymbol* syntaxCopy(Dsymbol* s); - virtual bool oneMember(Dsymbol*& ps, Identifier* ident); virtual bool hasPointers(); - virtual bool hasStaticCtorOrDtor(); virtual void addObjcSymbols(Array* classes, Array* categories); - virtual void checkCtorConstInit(); virtual void addComment(const char* comment); const char* comment(); void comment(const char* comment); @@ -529,61 +630,61 @@ class Dsymbol : public ASTNode bool inNonRoot(); static void deinitialize(); void accept(Visitor* v) override; - virtual Package* isPackage(); - virtual Module* isModule(); - virtual EnumMember* isEnumMember(); - virtual TemplateDeclaration* isTemplateDeclaration(); - virtual TemplateInstance* isTemplateInstance(); - virtual TemplateMixin* isTemplateMixin(); - virtual ForwardingAttribDeclaration* isForwardingAttribDeclaration(); - virtual Nspace* isNspace(); - virtual Declaration* isDeclaration(); - virtual StorageClassDeclaration* isStorageClassDeclaration(); - virtual ExpressionDsymbol* isExpressionDsymbol(); - virtual AliasAssign* isAliasAssign(); - virtual ThisDeclaration* isThisDeclaration(); - virtual BitFieldDeclaration* isBitFieldDeclaration(); - virtual TypeInfoDeclaration* isTypeInfoDeclaration(); - virtual TupleDeclaration* isTupleDeclaration(); - virtual AliasDeclaration* isAliasDeclaration(); - virtual AggregateDeclaration* isAggregateDeclaration(); - virtual FuncDeclaration* isFuncDeclaration(); - virtual FuncAliasDeclaration* isFuncAliasDeclaration(); - virtual OverDeclaration* isOverDeclaration(); - virtual FuncLiteralDeclaration* isFuncLiteralDeclaration(); - virtual CtorDeclaration* isCtorDeclaration(); - virtual PostBlitDeclaration* isPostBlitDeclaration(); - virtual DtorDeclaration* isDtorDeclaration(); - virtual StaticCtorDeclaration* isStaticCtorDeclaration(); - virtual StaticDtorDeclaration* isStaticDtorDeclaration(); - virtual SharedStaticCtorDeclaration* isSharedStaticCtorDeclaration(); - virtual SharedStaticDtorDeclaration* isSharedStaticDtorDeclaration(); - virtual InvariantDeclaration* isInvariantDeclaration(); - virtual UnitTestDeclaration* isUnitTestDeclaration(); - virtual NewDeclaration* isNewDeclaration(); - virtual VarDeclaration* isVarDeclaration(); - virtual VersionSymbol* isVersionSymbol(); - virtual DebugSymbol* isDebugSymbol(); - virtual ClassDeclaration* isClassDeclaration(); - virtual StructDeclaration* isStructDeclaration(); - virtual UnionDeclaration* isUnionDeclaration(); - virtual InterfaceDeclaration* isInterfaceDeclaration(); - virtual ScopeDsymbol* isScopeDsymbol(); - virtual ForwardingScopeDsymbol* isForwardingScopeDsymbol(); - virtual WithScopeSymbol* isWithScopeSymbol(); - virtual ArrayScopeSymbol* isArrayScopeSymbol(); - virtual Import* isImport(); - virtual EnumDeclaration* isEnumDeclaration(); - virtual SymbolDeclaration* isSymbolDeclaration(); - virtual AttribDeclaration* isAttribDeclaration(); - virtual AnonDeclaration* isAnonDeclaration(); - virtual CPPNamespaceDeclaration* isCPPNamespaceDeclaration(); - virtual VisibilityDeclaration* isVisibilityDeclaration(); - virtual OverloadSet* isOverloadSet(); - virtual MixinDeclaration* isMixinDeclaration(); - virtual StaticAssert* isStaticAssert(); - virtual StaticIfDeclaration* isStaticIfDeclaration(); - virtual CAsmDeclaration* isCAsmDeclaration(); + Package* isPackage(); + Module* isModule(); + EnumMember* isEnumMember(); + TemplateDeclaration* isTemplateDeclaration(); + TemplateInstance* isTemplateInstance(); + TemplateMixin* isTemplateMixin(); + ForwardingAttribDeclaration* isForwardingAttribDeclaration(); + Nspace* isNspace(); + Declaration* isDeclaration(); + StorageClassDeclaration* isStorageClassDeclaration(); + ExpressionDsymbol* isExpressionDsymbol(); + AliasAssign* isAliasAssign(); + ThisDeclaration* isThisDeclaration(); + BitFieldDeclaration* isBitFieldDeclaration(); + TypeInfoDeclaration* isTypeInfoDeclaration(); + TupleDeclaration* isTupleDeclaration(); + AliasDeclaration* isAliasDeclaration(); + AggregateDeclaration* isAggregateDeclaration(); + FuncDeclaration* isFuncDeclaration(); + FuncAliasDeclaration* isFuncAliasDeclaration(); + OverDeclaration* isOverDeclaration(); + FuncLiteralDeclaration* isFuncLiteralDeclaration(); + CtorDeclaration* isCtorDeclaration(); + PostBlitDeclaration* isPostBlitDeclaration(); + DtorDeclaration* isDtorDeclaration(); + StaticCtorDeclaration* isStaticCtorDeclaration(); + StaticDtorDeclaration* isStaticDtorDeclaration(); + SharedStaticCtorDeclaration* isSharedStaticCtorDeclaration(); + SharedStaticDtorDeclaration* isSharedStaticDtorDeclaration(); + InvariantDeclaration* isInvariantDeclaration(); + UnitTestDeclaration* isUnitTestDeclaration(); + NewDeclaration* isNewDeclaration(); + VarDeclaration* isVarDeclaration(); + VersionSymbol* isVersionSymbol(); + DebugSymbol* isDebugSymbol(); + ClassDeclaration* isClassDeclaration(); + StructDeclaration* isStructDeclaration(); + UnionDeclaration* isUnionDeclaration(); + InterfaceDeclaration* isInterfaceDeclaration(); + ScopeDsymbol* isScopeDsymbol(); + ForwardingScopeDsymbol* isForwardingScopeDsymbol(); + WithScopeSymbol* isWithScopeSymbol(); + ArrayScopeSymbol* isArrayScopeSymbol(); + Import* isImport(); + EnumDeclaration* isEnumDeclaration(); + SymbolDeclaration* isSymbolDeclaration(); + AttribDeclaration* isAttribDeclaration(); + AnonDeclaration* isAnonDeclaration(); + CPPNamespaceDeclaration* isCPPNamespaceDeclaration(); + VisibilityDeclaration* isVisibilityDeclaration(); + OverloadSet* isOverloadSet(); + MixinDeclaration* isMixinDeclaration(); + StaticAssert* isStaticAssert(); + StaticIfDeclaration* isStaticIfDeclaration(); + CAsmDeclaration* isCAsmDeclaration(); }; struct BitArray final @@ -625,16 +726,77 @@ class ScopeDsymbol : public Dsymbol virtual void importScope(Dsymbol* s, Visibility visibility); virtual bool isPackageAccessible(Package* p, Visibility visibility, uint32_t flags = 0u); bool isforwardRef() final override; - static void multiplyDefined(const Loc& loc, Dsymbol* s1, Dsymbol* s2); + static void multiplyDefined(Loc loc, Dsymbol* s1, Dsymbol* s2); const char* kind() const override; virtual Dsymbol* symtabInsert(Dsymbol* s); virtual Dsymbol* symtabLookup(Dsymbol* s, Identifier* id); - bool hasStaticCtorOrDtor() override; - ScopeDsymbol* isScopeDsymbol() final override; void accept(Visitor* v) override; }; -typedef uint64_t StorageClass; +enum class STC : uint64_t +{ + none = 0LLU, + static_ = 1LLU, + extern_ = 2LLU, + const_ = 4LLU, + final_ = 8LLU, + abstract_ = 16LLU, + parameter = 32LLU, + field = 64LLU, + override_ = 128LLU, + auto_ = 256LLU, + synchronized_ = 512LLU, + deprecated_ = 1024LLU, + in_ = 2048LLU, + out_ = 4096LLU, + lazy_ = 8192LLU, + foreach_ = 16384LLU, + variadic = 32768LLU, + constscoperef = 65536LLU, + templateparameter = 131072LLU, + ref_ = 262144LLU, + scope_ = 524288LLU, + scopeinferred = 2097152LLU, + return_ = 4194304LLU, + returnScope = 8388608LLU, + returninferred = 16777216LLU, + immutable_ = 33554432LLU, + manifest = 134217728LLU, + nodtor = 268435456LLU, + nothrow_ = 536870912LLU, + pure_ = 1073741824LLU, + tls = 2147483648LLU, + alias_ = 4294967296LLU, + shared_ = 8589934592LLU, + gshared = 17179869184LLU, + wild = 34359738368LLU, + property = 68719476736LLU, + safe = 137438953472LLU, + trusted = 274877906944LLU, + system = 549755813888LLU, + ctfe = 1099511627776LLU, + disable = 2199023255552LLU, + result = 4398046511104LLU, + nodefaultctor = 8796093022208LLU, + temp = 17592186044416LLU, + rvalue = 35184372088832LLU, + nogc = 70368744177664LLU, + autoref = 140737488355328LLU, + inference = 281474976710656LLU, + exptemp = 562949953421312LLU, + future = 1125899906842624LLU, + local = 2251799813685248LLU, + live = 4503599627370496LLU, + register_ = 9007199254740992LLU, + volatile_ = 18014398509481984LLU, + safeGroup = 962072674304LLU, + IOR = 333824LLU, + TYPECTOR = 42983227396LLU, + FUNCATTR = 4575000774574080LLU, + visibleStorageClasses = 7954966262857631LLU, + flowThruAggregate = 962072674304LLU, + flowThruFunction = 18446742978991225440LLU, +}; template struct Array final @@ -819,6 +981,8 @@ struct ObjcClassDeclaration final } }; +typedef uint64_t StorageClass; + enum class PKG { unknown = 0, @@ -950,6 +1114,115 @@ struct CtorFlow final {} }; +enum class Contract : uint8_t +{ + none = 0u, + invariant_ = 1u, + require = 2u, + ensure = 3u, +}; + +enum class FeatureState : uint8_t +{ + default_ = 0u, + disabled = 1u, + enabled = 2u, +}; + +struct Previews final +{ +private: + struct BitFields final + { + bool bitfields; + bool dip1000; + bool dip1008; + bool dip1021; + bool dip25; + bool fieldwise; + bool fixAliasThis; + bool fixImmutableConv; + bool in_; + bool inclusiveInContracts; + bool noSharedAccess; + bool rvalueRefParam; + bool safer; + FeatureState systemVariables; + BitFields() : + bitfields(), + dip1000(), + dip1008(), + dip1021(), + dip25(), + fieldwise(), + fixAliasThis(), + fixImmutableConv(), + in_(), + inclusiveInContracts(), + noSharedAccess(), + rvalueRefParam(), + safer() + { + } + BitFields(bool bitfields, bool dip1000 = false, bool dip1008 = false, bool dip1021 = false, bool dip25 = false, bool fieldwise = false, bool fixAliasThis = false, bool fixImmutableConv = false, bool in_ = false, bool inclusiveInContracts = false, bool noSharedAccess = false, bool rvalueRefParam = false, bool safer = false, FeatureState systemVariables = (FeatureState)0u) : + bitfields(bitfields), + dip1000(dip1000), + dip1008(dip1008), + dip1021(dip1021), + dip25(dip25), + fieldwise(fieldwise), + fixAliasThis(fixAliasThis), + fixImmutableConv(fixImmutableConv), + in_(in_), + inclusiveInContracts(inclusiveInContracts), + noSharedAccess(noSharedAccess), + rvalueRefParam(rvalueRefParam), + safer(safer), + systemVariables(systemVariables) + {} + }; + +public: + bool bitfields() const; + bool bitfields(bool v); + bool dip1000() const; + bool dip1000(bool v); + bool dip1008() const; + bool dip1008(bool v); + bool dip1021() const; + bool dip1021(bool v); + bool dip25() const; + bool dip25(bool v); + bool fieldwise() const; + bool fieldwise(bool v); + bool fixAliasThis() const; + bool fixAliasThis(bool v); + bool fixImmutableConv() const; + bool fixImmutableConv(bool v); + bool in_() const; + bool in_(bool v); + bool inclusiveInContracts() const; + bool inclusiveInContracts(bool v); + bool noSharedAccess() const; + bool noSharedAccess(bool v); + bool rvalueRefParam() const; + bool rvalueRefParam(bool v); + bool safer() const; + bool safer(bool v); + FeatureState systemVariables() const; + FeatureState systemVariables(FeatureState v); +private: + uint16_t bitFields; +public: + Previews() : + bitFields(0u) + { + } + Previews(uint16_t bitFields) : + bitFields(bitFields) + {} +}; + template struct AssocArray final { @@ -1435,11 +1708,10 @@ class TemplateParameter : public ASTNode virtual bool declareParameter(Scope* sc) = 0; virtual void print(RootObject* oarg, RootObject* oded) = 0; virtual RootObject* specialization() = 0; - virtual RootObject* defaultArg(const Loc& instLoc, Scope* sc) = 0; + virtual RootObject* defaultArg(Loc instLoc, Scope* sc) = 0; virtual bool hasDefaultArg() = 0; const char* toChars() const override; DYNCAST dyncast() const override; - virtual RootObject* dummyArg() = 0; void accept(Visitor* v) override; }; @@ -1454,9 +1726,8 @@ class TemplateAliasParameter final : public TemplateParameter bool declareParameter(Scope* sc) override; void print(RootObject* oarg, RootObject* oded) override; RootObject* specialization() override; - RootObject* defaultArg(const Loc& instLoc, Scope* sc) override; + RootObject* defaultArg(Loc instLoc, Scope* sc) override; bool hasDefaultArg() override; - RootObject* dummyArg() override; void accept(Visitor* v) override; }; @@ -1484,13 +1755,10 @@ class TemplateDeclaration final : public ScopeDsymbol Array* lastConstraintTiargs; TemplateDeclaration* syntaxCopy(Dsymbol* __param_0_) override; bool overloadInsert(Dsymbol* s) override; - bool hasStaticCtorOrDtor() override; const char* kind() const override; - const char* toChars() const override; const char* toCharsNoConstraints() const; Visibility visible() override; const char* getConstraintEvalError(const char*& tip); - TemplateDeclaration* isTemplateDeclaration() override; bool isDeprecated() const override; bool isOverloadable() const override; void accept(Visitor* v) override; @@ -1533,14 +1801,11 @@ class TemplateInstance : public ScopeDsymbol TemplateInstance* syntaxCopy(Dsymbol* s) override; Dsymbol* toAlias() final override; const char* kind() const override; - bool oneMember(Dsymbol*& ps, Identifier* ident) override; - const char* toChars() const override; const char* toPrettyCharsHelper() final override; Identifier* getIdent() final override; bool equalsx(TemplateInstance* ti); bool isDiscardable(); bool needsCodegen(); - TemplateInstance* isTemplateInstance() final override; void accept(Visitor* v) override; }; @@ -1559,10 +1824,7 @@ class TemplateMixin final : public TemplateInstance TypeQualified* tqual; TemplateInstance* syntaxCopy(Dsymbol* s) override; const char* kind() const override; - bool oneMember(Dsymbol*& ps, Identifier* ident) override; bool hasPointers() override; - const char* toChars() const override; - TemplateMixin* isTemplateMixin() override; void accept(Visitor* v) override; }; @@ -1612,9 +1874,8 @@ class TemplateTypeParameter : public TemplateParameter bool declareParameter(Scope* sc) final override; void print(RootObject* oarg, RootObject* oded) final override; RootObject* specialization() final override; - RootObject* defaultArg(const Loc& instLoc, Scope* sc) final override; + RootObject* defaultArg(Loc instLoc, Scope* sc) final override; bool hasDefaultArg() final override; - RootObject* dummyArg() final override; void accept(Visitor* v) override; }; @@ -1634,9 +1895,8 @@ class TemplateTupleParameter final : public TemplateParameter bool declareParameter(Scope* sc) override; void print(RootObject* oarg, RootObject* oded) override; RootObject* specialization() override; - RootObject* defaultArg(const Loc& instLoc, Scope* sc) override; + RootObject* defaultArg(Loc instLoc, Scope* sc) override; bool hasDefaultArg() override; - RootObject* dummyArg() override; void accept(Visitor* v) override; }; @@ -1651,9 +1911,8 @@ class TemplateValueParameter final : public TemplateParameter bool declareParameter(Scope* sc) override; void print(RootObject* oarg, RootObject* oded) override; RootObject* specialization() override; - RootObject* defaultArg(const Loc& instLoc, Scope* sc) override; + RootObject* defaultArg(Loc instLoc, Scope* sc) override; bool hasDefaultArg() override; - RootObject* dummyArg() override; void accept(Visitor* v) override; }; @@ -1733,6 +1992,10 @@ class Type : public ASTNode Type* wcto; Type* swto; Type* swcto; + Type* pto; + Type* rto; + Type* arrayof; + Identifier* typedefIdent; Mcache() : cto(), ito(), @@ -1741,10 +2004,14 @@ class Type : public ASTNode wto(), wcto(), swto(), - swcto() + swcto(), + pto(), + rto(), + arrayof(), + typedefIdent() { } - Mcache(Type* cto, Type* ito = nullptr, Type* sto = nullptr, Type* scto = nullptr, Type* wto = nullptr, Type* wcto = nullptr, Type* swto = nullptr, Type* swcto = nullptr) : + Mcache(Type* cto, Type* ito = nullptr, Type* sto = nullptr, Type* scto = nullptr, Type* wto = nullptr, Type* wcto = nullptr, Type* swto = nullptr, Type* swcto = nullptr, Type* pto = nullptr, Type* rto = nullptr, Type* arrayof = nullptr, Identifier* typedefIdent = nullptr) : cto(cto), ito(ito), sto(sto), @@ -1752,14 +2019,15 @@ class Type : public ASTNode wto(wto), wcto(wcto), swto(swto), - swcto(swcto) + swcto(swcto), + pto(pto), + rto(rto), + arrayof(arrayof), + typedefIdent(typedefIdent) {} }; Mcache* mcache; - Type* pto; - Type* rto; - Type* arrayof; TypeInfoDeclaration* vtinfo; void* ctype; static Type* tvoid; @@ -1829,14 +2097,14 @@ class Type : public ASTNode virtual uint32_t alignsize(); void modToBuffer(OutBuffer& buf) const; char* modToChars() const; - virtual bool isintegral(); - virtual bool isfloating(); - virtual bool isreal(); - virtual bool isimaginary(); - virtual bool iscomplex(); - virtual bool isscalar(); - virtual bool isunsigned(); - virtual bool isscope(); + virtual bool isIntegral(); + virtual bool isFloating(); + virtual bool isReal(); + virtual bool isImaginary(); + virtual bool isComplex(); + virtual bool isScalar(); + virtual bool isUnsigned(); + virtual bool isScopeClass(); virtual bool isString(); virtual bool isAssignable(); virtual bool isBoolean(); @@ -1861,19 +2129,16 @@ class Type : public ASTNode virtual Type* makeSharedWildConst(); virtual Type* makeMutable(); Type* toBasetype(); - virtual MATCH constConv(Type* to); virtual uint8_t deduceWild(Type* t, bool isRef); virtual ClassDeclaration* isClassHandle(); virtual structalign_t alignment(); - virtual Expression* defaultInitLiteral(const Loc& loc); - virtual bool isZeroInit(const Loc& loc); + virtual Expression* defaultInitLiteral(Loc loc); virtual int32_t hasWild() const; virtual bool hasVoidInitPointers(); virtual bool hasUnsafeBitpatterns(); virtual bool hasInvariant(); virtual Type* nextOf(); Type* baseElemOf(); - uint32_t numberOfElems(const Loc& loc); virtual bool needsDestruction(); virtual bool needsCopyOrPostblit(); virtual bool needsNested(); @@ -1921,22 +2186,16 @@ class TypeDeduced final : public Type class DebugSymbol final : public Dsymbol { public: - uint32_t level; DebugSymbol* syntaxCopy(Dsymbol* s) override; - const char* toChars() const override; const char* kind() const override; - DebugSymbol* isDebugSymbol() override; void accept(Visitor* v) override; }; class VersionSymbol final : public Dsymbol { public: - uint32_t level; VersionSymbol* syntaxCopy(Dsymbol* s) override; - const char* toChars() const override; const char* kind() const override; - VersionSymbol* isVersionSymbol() override; void accept(Visitor* v) override; }; @@ -2070,6 +2329,7 @@ enum class EXP : uint8_t _Generic_ = 125u, interval = 126u, loweredAssignExp = 127u, + rvalue = 128u, }; typedef uint64_t dinteger_t; @@ -2106,13 +2366,34 @@ class Expression : public ASTNode Type* type; Loc loc; const EXP op; - bool parens; + struct BitFields final + { + bool parens; + bool rvalue; + BitFields() : + parens(), + rvalue() + { + } + BitFields(bool parens, bool rvalue = false) : + parens(parens), + rvalue(rvalue) + {} + }; + + bool parens() const; + bool parens(bool v); + bool rvalue() const; + bool rvalue(bool v); +private: + uint8_t bitFields; +public: size_t size() const; static void _init(); static void deinitialize(); virtual Expression* syntaxCopy(); DYNCAST dyncast() const final override; - const char* toChars() const override; + const char* toChars() const final override; virtual dinteger_t toInteger(); virtual uinteger_t toUInteger(); virtual _d_real toReal(); @@ -2121,7 +2402,6 @@ class Expression : public ASTNode virtual StringExp* toStringExp(); virtual bool isLvalue(); virtual bool checkType(); - virtual bool checkValue(); Expression* addressOf(); Expression* deref(); int32_t isConst(); @@ -2159,7 +2439,7 @@ class Expression : public ASTNode TypeidExp* isTypeidExp(); TraitsExp* isTraitsExp(); HaltExp* isHaltExp(); - IsExp* isExp(); + IsExp* isIsExp(); MixinExp* isMixinExp(); ImportExp* isImportExp(); AssertExp* isAssertExp(); @@ -2251,8 +2531,6 @@ class BinExp : public Expression public: Expression* e1; Expression* e2; - Type* att1; - Type* att2; BinExp* syntaxCopy() override; void setNoderefOperands(); void accept(Visitor* v) override; @@ -2350,7 +2628,7 @@ class ArrayLiteralExp final : public Expression bool onstack; Expression* basis; Array* elements; - static ArrayLiteralExp* create(const Loc& loc, Array* elements); + static ArrayLiteralExp* create(Loc loc, Array* elements); ArrayLiteralExp* syntaxCopy() override; bool equals(const RootObject* const o) const override; Expression* getElement(size_t i); @@ -2378,7 +2656,7 @@ class AssignExp : public BinExp { public: MemorySet memset; - AssignExp(const Loc& loc, EXP tok, Expression* e1, Expression* e2); + AssignExp(Loc loc, EXP tok, Expression* e1, Expression* e2); bool isLvalue() final override; void accept(Visitor* v) override; }; @@ -2404,8 +2682,6 @@ class BlitExp final : public AssignExp class CTFEExp final : public Expression { -public: - const char* toChars() const override; }; class CallExp final : public UnaExp @@ -2419,10 +2695,10 @@ class CallExp final : public UnaExp bool ignoreAttributes; bool isUfcsRewrite; VarDeclaration* vthis2; - static CallExp* create(const Loc& loc, Expression* e, Array* exps); - static CallExp* create(const Loc& loc, Expression* e); - static CallExp* create(const Loc& loc, Expression* e, Expression* earg1); - static CallExp* create(const Loc& loc, FuncDeclaration* fd, Expression* earg1); + static CallExp* create(Loc loc, Expression* e, Array* exps); + static CallExp* create(Loc loc, Expression* e); + static CallExp* create(Loc loc, Expression* e, Expression* earg1); + static CallExp* create(Loc loc, FuncDeclaration* fd, Expression* earg1); CallExp* syntaxCopy() override; bool isLvalue() override; void accept(Visitor* v) override; @@ -2502,7 +2778,7 @@ class ComplexExp final : public Expression { public: complex_t value; - static ComplexExp* create(const Loc& loc, complex_t value, Type* type); + static ComplexExp* create(Loc loc, complex_t value, Type* type); bool equals(const RootObject* const o) const override; bool isIdentical(const Expression* const e) const override; dinteger_t toInteger() override; @@ -2597,7 +2873,7 @@ class IdentifierExp : public Expression { public: Identifier* ident; - static IdentifierExp* create(const Loc& loc, Identifier* ident); + static IdentifierExp* create(Loc loc, Identifier* ident); bool isLvalue() final override; void accept(Visitor* v) override; }; @@ -2621,7 +2897,7 @@ class DotIdExp final : public UnaExp bool noderef; bool wantsym; bool arrow; - static DotIdExp* create(const Loc& loc, Expression* e, Identifier* ident); + static DotIdExp* create(Loc loc, Expression* e, Identifier* ident); void accept(Visitor* v) override; }; @@ -2630,7 +2906,6 @@ class DotTemplateExp final : public UnaExp public: TemplateDeclaration* td; bool checkType() override; - bool checkValue() override; void accept(Visitor* v) override; }; @@ -2640,7 +2915,6 @@ class DotTemplateInstanceExp final : public UnaExp TemplateInstance* ti; DotTemplateInstanceExp* syntaxCopy() override; bool checkType() override; - bool checkValue() override; void accept(Visitor* v) override; }; @@ -2709,187 +2983,187 @@ enum class TOK : uint8_t false_ = 15u, throw_ = 16u, new_ = 17u, - delete_ = 18u, - variable = 19u, - slice = 20u, - version_ = 21u, - module_ = 22u, - dollar = 23u, - template_ = 24u, - typeof_ = 25u, - pragma_ = 26u, - typeid_ = 27u, - comment = 28u, - lessThan = 29u, - greaterThan = 30u, - lessOrEqual = 31u, - greaterOrEqual = 32u, - equal = 33u, - notEqual = 34u, - identity = 35u, - notIdentity = 36u, - is_ = 37u, - leftShift = 38u, - rightShift = 39u, - leftShiftAssign = 40u, - rightShiftAssign = 41u, - unsignedRightShift = 42u, - unsignedRightShiftAssign = 43u, - concatenateAssign = 44u, - add = 45u, - min = 46u, - addAssign = 47u, - minAssign = 48u, - mul = 49u, - div = 50u, - mod = 51u, - mulAssign = 52u, - divAssign = 53u, - modAssign = 54u, - and_ = 55u, - or_ = 56u, - xor_ = 57u, - andAssign = 58u, - orAssign = 59u, - xorAssign = 60u, - assign = 61u, - not_ = 62u, - tilde = 63u, - plusPlus = 64u, - minusMinus = 65u, - dot = 66u, - comma = 67u, - question = 68u, - andAnd = 69u, - orOr = 70u, - int32Literal = 71u, - uns32Literal = 72u, - int64Literal = 73u, - uns64Literal = 74u, - int128Literal = 75u, - uns128Literal = 76u, - float32Literal = 77u, - float64Literal = 78u, - float80Literal = 79u, - imaginary32Literal = 80u, - imaginary64Literal = 81u, - imaginary80Literal = 82u, - charLiteral = 83u, - wcharLiteral = 84u, - dcharLiteral = 85u, - identifier = 86u, - string_ = 87u, - interpolated = 88u, - hexadecimalString = 89u, - this_ = 90u, - super_ = 91u, - error = 92u, - void_ = 93u, - int8 = 94u, - uns8 = 95u, - int16 = 96u, - uns16 = 97u, - int32 = 98u, - uns32 = 99u, - int64 = 100u, - uns64 = 101u, - int128 = 102u, - uns128 = 103u, - float32 = 104u, - float64 = 105u, - float80 = 106u, - imaginary32 = 107u, - imaginary64 = 108u, - imaginary80 = 109u, - complex32 = 110u, - complex64 = 111u, - complex80 = 112u, - char_ = 113u, - wchar_ = 114u, - dchar_ = 115u, - bool_ = 116u, - struct_ = 117u, - class_ = 118u, - interface_ = 119u, - union_ = 120u, - enum_ = 121u, - import_ = 122u, - alias_ = 123u, - override_ = 124u, - delegate_ = 125u, - function_ = 126u, - mixin_ = 127u, - align_ = 128u, - extern_ = 129u, - private_ = 130u, - protected_ = 131u, - public_ = 132u, - export_ = 133u, - static_ = 134u, - final_ = 135u, - const_ = 136u, - abstract_ = 137u, - debug_ = 138u, - deprecated_ = 139u, - in_ = 140u, - out_ = 141u, - inout_ = 142u, - lazy_ = 143u, - auto_ = 144u, - package_ = 145u, - immutable_ = 146u, - if_ = 147u, - else_ = 148u, - while_ = 149u, - for_ = 150u, - do_ = 151u, - switch_ = 152u, - case_ = 153u, - default_ = 154u, - break_ = 155u, - continue_ = 156u, - with_ = 157u, - synchronized_ = 158u, - return_ = 159u, - goto_ = 160u, - try_ = 161u, - catch_ = 162u, - finally_ = 163u, - asm_ = 164u, - foreach_ = 165u, - foreach_reverse_ = 166u, - scope_ = 167u, - onScopeExit = 168u, - onScopeFailure = 169u, - onScopeSuccess = 170u, - invariant_ = 171u, - unittest_ = 172u, - argumentTypes = 173u, - ref_ = 174u, - macro_ = 175u, - parameters = 176u, - traits = 177u, - pure_ = 178u, - nothrow_ = 179u, - gshared = 180u, - line = 181u, - file = 182u, - fileFullPath = 183u, - moduleString = 184u, - functionString = 185u, - prettyFunction = 186u, - shared_ = 187u, - at = 188u, - pow = 189u, - powAssign = 190u, - goesTo = 191u, - vector = 192u, - pound = 193u, - arrow = 194u, - colonColon = 195u, - wchar_tLiteral = 196u, - endOfLine = 197u, - whitespace = 198u, + variable = 18u, + slice = 19u, + version_ = 20u, + module_ = 21u, + dollar = 22u, + template_ = 23u, + typeof_ = 24u, + pragma_ = 25u, + typeid_ = 26u, + comment = 27u, + lessThan = 28u, + greaterThan = 29u, + lessOrEqual = 30u, + greaterOrEqual = 31u, + equal = 32u, + notEqual = 33u, + identity = 34u, + notIdentity = 35u, + is_ = 36u, + leftShift = 37u, + rightShift = 38u, + leftShiftAssign = 39u, + rightShiftAssign = 40u, + unsignedRightShift = 41u, + unsignedRightShiftAssign = 42u, + concatenateAssign = 43u, + add = 44u, + min = 45u, + addAssign = 46u, + minAssign = 47u, + mul = 48u, + div = 49u, + mod = 50u, + mulAssign = 51u, + divAssign = 52u, + modAssign = 53u, + and_ = 54u, + or_ = 55u, + xor_ = 56u, + andAssign = 57u, + orAssign = 58u, + xorAssign = 59u, + assign = 60u, + not_ = 61u, + tilde = 62u, + plusPlus = 63u, + minusMinus = 64u, + dot = 65u, + comma = 66u, + question = 67u, + andAnd = 68u, + orOr = 69u, + int32Literal = 70u, + uns32Literal = 71u, + int64Literal = 72u, + uns64Literal = 73u, + int128Literal = 74u, + uns128Literal = 75u, + float32Literal = 76u, + float64Literal = 77u, + float80Literal = 78u, + imaginary32Literal = 79u, + imaginary64Literal = 80u, + imaginary80Literal = 81u, + charLiteral = 82u, + wcharLiteral = 83u, + dcharLiteral = 84u, + identifier = 85u, + string_ = 86u, + interpolated = 87u, + hexadecimalString = 88u, + this_ = 89u, + super_ = 90u, + error = 91u, + void_ = 92u, + int8 = 93u, + uns8 = 94u, + int16 = 95u, + uns16 = 96u, + int32 = 97u, + uns32 = 98u, + int64 = 99u, + uns64 = 100u, + int128 = 101u, + uns128 = 102u, + float32 = 103u, + float64 = 104u, + float80 = 105u, + imaginary32 = 106u, + imaginary64 = 107u, + imaginary80 = 108u, + complex32 = 109u, + complex64 = 110u, + complex80 = 111u, + char_ = 112u, + wchar_ = 113u, + dchar_ = 114u, + bool_ = 115u, + struct_ = 116u, + class_ = 117u, + interface_ = 118u, + union_ = 119u, + enum_ = 120u, + import_ = 121u, + alias_ = 122u, + override_ = 123u, + delegate_ = 124u, + function_ = 125u, + mixin_ = 126u, + align_ = 127u, + extern_ = 128u, + private_ = 129u, + protected_ = 130u, + public_ = 131u, + export_ = 132u, + static_ = 133u, + final_ = 134u, + const_ = 135u, + abstract_ = 136u, + debug_ = 137u, + deprecated_ = 138u, + in_ = 139u, + out_ = 140u, + inout_ = 141u, + lazy_ = 142u, + auto_ = 143u, + package_ = 144u, + immutable_ = 145u, + if_ = 146u, + else_ = 147u, + while_ = 148u, + for_ = 149u, + do_ = 150u, + switch_ = 151u, + case_ = 152u, + default_ = 153u, + break_ = 154u, + continue_ = 155u, + with_ = 156u, + synchronized_ = 157u, + return_ = 158u, + goto_ = 159u, + try_ = 160u, + catch_ = 161u, + finally_ = 162u, + asm_ = 163u, + foreach_ = 164u, + foreach_reverse_ = 165u, + scope_ = 166u, + onScopeExit = 167u, + onScopeFailure = 168u, + onScopeSuccess = 169u, + invariant_ = 170u, + unittest_ = 171u, + argumentTypes = 172u, + ref_ = 173u, + macro_ = 174u, + parameters = 175u, + traits = 176u, + pure_ = 177u, + nothrow_ = 178u, + gshared = 179u, + line = 180u, + file = 181u, + fileFullPath = 182u, + moduleString = 183u, + functionString = 184u, + prettyFunction = 185u, + shared_ = 186u, + at = 187u, + pow = 188u, + powAssign = 189u, + goesTo = 190u, + vector = 191u, + pound = 192u, + arrow = 193u, + colonColon = 194u, + wchar_tLiteral = 195u, + endOfLine = 196u, + whitespace = 197u, + rvalue = 198u, inline_ = 199u, register_ = 200u, restrict_ = 201u, @@ -2927,9 +3201,7 @@ class FuncExp final : public Expression TOK tok; bool equals(const RootObject* const o) const override; FuncExp* syntaxCopy() override; - const char* toChars() const override; bool checkType() override; - bool checkValue() override; void accept(Visitor* v) override; }; @@ -2988,7 +3260,7 @@ class IntegerExp final : public Expression { dinteger_t value; public: - static IntegerExp* create(const Loc& loc, dinteger_t value, Type* type); + static IntegerExp* create(Loc loc, dinteger_t value, Type* type); bool equals(const RootObject* const o) const override; dinteger_t toInteger() override; _d_real toReal() override; @@ -3052,7 +3324,6 @@ class LoweredAssignExp final : public AssignExp { public: Expression* lowering; - const char* toChars() const override; void accept(Visitor* v) override; }; @@ -3089,20 +3360,6 @@ class ModExp final : public BinExp void accept(Visitor* v) override; }; -enum class Modifiable -{ - no = 0, - yes = 1, - initialization = 2, -}; - -enum class ModifyFlags -{ - none = 0, - noError = 1, - fieldAssign = 2, -}; - class ModuleInitExp final : public DefaultInitExp { public: @@ -3133,6 +3390,7 @@ class NewAnonClassExp final : public Expression Expression* thisexp; ClassDeclaration* cd; Array* arguments; + Expression* placement; NewAnonClassExp* syntaxCopy() override; void accept(Visitor* v) override; }; @@ -3144,12 +3402,13 @@ class NewExp final : public Expression Type* newtype; Array* arguments; Array* names; + Expression* placement; Expression* argprefix; CtorDeclaration* member; bool onstack; bool thrownew; Expression* lowering; - static NewExp* create(const Loc& loc, Expression* thisexp, Type* newtype, Array* arguments); + static NewExp* create(Loc loc, Expression* placement, Expression* thisexp, Type* newtype, Array* arguments); NewExp* syntaxCopy() override; void accept(Visitor* v) override; }; @@ -3237,7 +3496,7 @@ class RealExp final : public Expression { public: _d_real value; - static RealExp* create(const Loc& loc, _d_real value, Type* type); + static RealExp* create(Loc loc, _d_real value, Type* type); bool equals(const RootObject* const o) const override; bool isIdentical(const Expression* const e) const override; dinteger_t toInteger() override; @@ -3261,7 +3520,6 @@ class ScopeExp final : public Expression ScopeDsymbol* sds; ScopeExp* syntaxCopy() override; bool checkType() override; - bool checkValue() override; void accept(Visitor* v) override; }; @@ -3348,8 +3606,8 @@ class StringExp final : public Expression bool hexString; enum : char { NoPostfix = 0u }; - static StringExp* create(const Loc& loc, const char* s); - static StringExp* create(const Loc& loc, const void* string, size_t len); + static StringExp* create(Loc loc, const char* s); + static StringExp* create(Loc loc, const void* string, size_t len); bool equals(const RootObject* const o) const override; size_t numberOfCodeUnits(int32_t tynto = 0) const; void writeTo(void* dest, bool zero, int32_t tyto = 0) const; @@ -3365,6 +3623,34 @@ class StringExp final : public Expression class StructLiteralExp final : public Expression { public: + struct BitFields final + { + bool useStaticInit; + bool isOriginal; + OwnedBy ownedByCtfe; + BitFields() : + useStaticInit(), + isOriginal(false), + ownedByCtfe((OwnedBy)0u) + { + } + BitFields(bool useStaticInit, bool isOriginal = false, OwnedBy ownedByCtfe = (OwnedBy)0u) : + useStaticInit(useStaticInit), + isOriginal(isOriginal), + ownedByCtfe(ownedByCtfe) + {} + }; + + bool useStaticInit() const; + bool useStaticInit(bool v); + bool isOriginal() const; + bool isOriginal(bool v); + OwnedBy ownedByCtfe() const; + OwnedBy ownedByCtfe(OwnedBy v); +private: + uint8_t bitFields; +public: + StageFlags stageflags; StructDeclaration* sd; Array* elements; Type* stype; @@ -3374,11 +3660,18 @@ class StructLiteralExp final : public Expression StructLiteralExp* inlinecopy; }; StructLiteralExp* origin; - uint8_t stageflags; - bool useStaticInit; - bool isOriginal; - OwnedBy ownedByCtfe; - static StructLiteralExp* create(const Loc& loc, StructDeclaration* sd, void* elements, Type* stype = nullptr); + enum class StageFlags : uint8_t + { + none = 0u, + scrub = 1u, + searchPointers = 2u, + optimize = 4u, + apply = 8u, + inlineScan = 16u, + toCBuffer = 32u, + }; + + 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; @@ -3388,7 +3681,7 @@ class ThisExp : public Expression { public: VarDeclaration* var; - ThisExp(const Loc& loc, const EXP tok); + ThisExp(Loc loc, const EXP tok); ThisExp* syntaxCopy() override; Optional toBool() override; bool isLvalue() final override; @@ -3425,7 +3718,6 @@ class TemplateExp final : public Expression FuncDeclaration* fd; bool isLvalue() override; bool checkType() override; - bool checkValue() override; void accept(Visitor* v) override; }; @@ -3440,7 +3732,6 @@ class ThrownExceptionExp final : public Expression { public: ClassReferenceExp* thrown; - const char* toChars() const override; void accept(Visitor* v) override; }; @@ -3458,7 +3749,7 @@ class TupleExp final : public Expression public: Expression* e0; Array* exps; - static TupleExp* create(const Loc& loc, Array* exps); + static TupleExp* create(Loc loc, Array* exps); TupleExp* syntaxCopy() override; bool equals(const RootObject* const o) const override; void accept(Visitor* v) override; @@ -3469,7 +3760,6 @@ class TypeExp final : public Expression public: TypeExp* syntaxCopy() override; bool checkType() override; - bool checkValue() override; void accept(Visitor* v) override; }; @@ -3503,7 +3793,7 @@ class VarExp final : public SymbolExp { public: bool delegateWasExtracted; - static VarExp* create(const Loc& loc, Declaration* var, bool hasOverloads = true); + 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; @@ -3522,7 +3812,7 @@ class VectorExp final : public UnaExp TypeVector* to; uint32_t dim; OwnedBy ownedByCtfe; - static VectorExp* create(const Loc& loc, Expression* e, Type* t); + static VectorExp* create(Loc loc, Expression* e, Type* t); VectorExp* syntaxCopy() override; void accept(Visitor* v) override; }; @@ -3550,40 +3840,17 @@ class XorExp final : public BinExp void accept(Visitor* v) override; }; -enum : int32_t { stageApply = 8 }; - -enum : int32_t { stageInlineScan = 16 }; - -enum : int32_t { stageOptimize = 4 }; - -enum : int32_t { stageScrub = 1 }; - -enum : int32_t { stageSearchPointers = 2 }; - -enum : int32_t { stageToCBuffer = 32 }; - struct AttributeViolation final { Loc loc; - const char* fmtStr; - RootObject* arg0; - RootObject* arg1; - RootObject* arg2; + FuncDeclaration* fd; + _d_dynamicArray< const char > action; AttributeViolation() : - loc(Loc(0u, 0u, 0u)), - fmtStr(nullptr), - arg0(nullptr), - arg1(nullptr), - arg2(nullptr) + loc(), + fd(), + action() { } - AttributeViolation(Loc loc, const char* fmtStr = nullptr, RootObject* arg0 = nullptr, RootObject* arg1 = nullptr, RootObject* arg2 = nullptr) : - loc(loc), - fmtStr(fmtStr), - arg0(arg0), - arg1(arg1), - arg2(arg2) - {} }; enum class ILS : uint8_t @@ -3629,15 +3896,14 @@ enum class VarArg : uint8_t struct ParameterList final { Array* parameters; - StorageClass stc; + STC stc; VarArg varargs; bool hasIdentifierList; - ParameterList(Array* parameters, VarArg varargs = (VarArg)0u, StorageClass stc = 0); + ParameterList(Array* parameters, VarArg varargs = (VarArg)0u, STC stc = (STC)0LLU); size_t length(); Parameter* opIndex(size_t i); ParameterList() : parameters(), - stc(), varargs((VarArg)0u), hasIdentifierList() { @@ -3673,8 +3939,7 @@ class FuncDeclaration : public Declaration ForeachStatement* fes; BaseClass* interfaceVirtual; Type* tintro; - StorageClass storage_class2; - int32_t hasReturnExp; + STC storage_class2; VarDeclaration* nrvo_var; Symbol* shidden; Array* returns; @@ -3686,6 +3951,7 @@ class FuncDeclaration : public Declaration bool requiresClosure; Array closureVars; Array outerVars; + static FuncDeclaration* lastMain; Array siblingCallers; Array* inlinedNestedCallees; AttributeViolation* safetyViolation; @@ -3700,12 +3966,12 @@ class FuncDeclaration : public Declaration bool nothrowInprocess(bool v); bool nogcInprocess() const; bool nogcInprocess(bool v); - bool returnInprocess() const; - bool returnInprocess(bool v); + bool saferD() const; + bool saferD(bool v); + bool scopeInprocess() const; + bool scopeInprocess(bool v); bool inlineScanned() const; bool inlineScanned(bool v); - bool inferScope() const; - bool inferScope(bool v); bool hasCatches() const; bool hasCatches(bool v); bool skipCodegen() const; @@ -3746,11 +4012,17 @@ class FuncDeclaration : public Declaration bool dllImport(bool v); bool dllExport() const; bool dllExport(bool v); + bool hasReturnExp() const; + bool hasReturnExp(bool v); + bool hasInlineAsm() const; + bool hasInlineAsm(bool v); + bool hasMultipleReturnExp() const; + bool hasMultipleReturnExp(bool v); private: uint32_t bitFields; public: ObjcFuncDeclaration objc; - static FuncDeclaration* create(const Loc& loc, const Loc& endloc, Identifier* id, StorageClass storage_class, Type* type, bool noreturn = false); + static FuncDeclaration* create(Loc loc, Loc endloc, Identifier* id, StorageClass storage_class, Type* type, bool noreturn = false); Array* frequires(); Array* fensures(); Statement* frequire(); @@ -3771,7 +4043,7 @@ class FuncDeclaration : public Declaration bool equals(const RootObject* const o) const final override; bool overloadInsert(Dsymbol* s) override; bool inUnittest(); - LabelDsymbol* searchLabel(Identifier* ident, const Loc& loc); + LabelDsymbol* searchLabel(Identifier* ident, Loc loc); enum : int32_t { LevelError = -2 }; const char* toPrettyChars(bool QualifyTypes = false) override; @@ -3786,10 +4058,6 @@ class FuncDeclaration : public Declaration bool isCodeseg() const final override; bool isOverloadable() const final override; bool isAbstract() final override; - void initInferAttributes(); - bool isSafe(); - bool isTrusted(); - bool isNogc(); virtual bool isNested() const; AggregateDeclaration* isThis() override; bool needThis() final override; @@ -3801,12 +4069,8 @@ class FuncDeclaration : public Declaration const char* kind() const override; bool isUnique() const; bool needsClosure(); - bool checkClosure(); bool hasNestedFrameRefs(); ParameterList getParameterList(); - static FuncDeclaration* genCfunc(Array* fparams, Type* treturn, const char* name, StorageClass stc = 0); - static FuncDeclaration* genCfunc(Array* fparams, Type* treturn, Identifier* id, StorageClass stc = 0); - FuncDeclaration* isFuncDeclaration() final override; virtual FuncDeclaration* toAliasFunc(); void accept(Visitor* v) override; }; @@ -3815,13 +4079,12 @@ class CtorDeclaration final : public FuncDeclaration { public: bool isCpCtor; + bool isMoveCtor; CtorDeclaration* syntaxCopy(Dsymbol* s) override; const char* kind() const override; - const char* toChars() const override; bool isVirtual() const override; bool addPreInvariant() override; bool addPostInvariant() override; - CtorDeclaration* isCtorDeclaration() override; void accept(Visitor* v) override; }; @@ -3830,12 +4093,10 @@ class DtorDeclaration final : public FuncDeclaration public: DtorDeclaration* syntaxCopy(Dsymbol* s) override; const char* kind() const override; - const char* toChars() const override; bool isVirtual() const override; bool addPreInvariant() override; bool addPostInvariant() override; bool overloadInsert(Dsymbol* s) override; - DtorDeclaration* isDtorDeclaration() override; void accept(Visitor* v) override; }; @@ -3844,7 +4105,6 @@ class FuncAliasDeclaration final : public FuncDeclaration public: FuncDeclaration* funcalias; bool hasOverloads; - FuncAliasDeclaration* isFuncAliasDeclaration() override; const char* kind() const override; FuncDeclaration* toAliasFunc() override; void accept(Visitor* v) override; @@ -3862,7 +4122,6 @@ class FuncLiteralDeclaration final : public FuncDeclaration bool isVirtual() const override; bool addPreInvariant() override; bool addPostInvariant() override; - FuncLiteralDeclaration* isFuncLiteralDeclaration() override; const char* kind() const override; const char* toPrettyChars(bool QualifyTypes = false) override; void accept(Visitor* v) override; @@ -3875,7 +4134,6 @@ class InvariantDeclaration final : public FuncDeclaration bool isVirtual() const override; bool addPreInvariant() override; bool addPostInvariant() override; - InvariantDeclaration* isInvariantDeclaration() override; void accept(Visitor* v) override; }; @@ -3887,7 +4145,6 @@ class NewDeclaration final : public FuncDeclaration bool isVirtual() const override; bool addPreInvariant() override; bool addPostInvariant() override; - NewDeclaration* isNewDeclaration() override; void accept(Visitor* v) override; }; @@ -3899,7 +4156,6 @@ class PostBlitDeclaration final : public FuncDeclaration bool addPreInvariant() override; bool addPostInvariant() override; bool overloadInsert(Dsymbol* s) override; - PostBlitDeclaration* isPostBlitDeclaration() override; void accept(Visitor* v) override; }; @@ -3911,8 +4167,6 @@ class StaticCtorDeclaration : public FuncDeclaration bool isVirtual() const final override; bool addPreInvariant() final override; bool addPostInvariant() final override; - bool hasStaticCtorOrDtor() final override; - StaticCtorDeclaration* isStaticCtorDeclaration() final override; void accept(Visitor* v) override; }; @@ -3921,7 +4175,6 @@ class SharedStaticCtorDeclaration final : public StaticCtorDeclaration public: bool standalone; SharedStaticCtorDeclaration* syntaxCopy(Dsymbol* s) override; - SharedStaticCtorDeclaration* isSharedStaticCtorDeclaration() override; void accept(Visitor* v) override; }; @@ -3932,10 +4185,8 @@ class StaticDtorDeclaration : public FuncDeclaration StaticDtorDeclaration* syntaxCopy(Dsymbol* s) override; AggregateDeclaration* isThis() final override; bool isVirtual() const final override; - bool hasStaticCtorOrDtor() final override; bool addPreInvariant() final override; bool addPostInvariant() final override; - StaticDtorDeclaration* isStaticDtorDeclaration() final override; void accept(Visitor* v) override; }; @@ -3943,7 +4194,6 @@ class SharedStaticDtorDeclaration final : public StaticDtorDeclaration { public: SharedStaticDtorDeclaration* syntaxCopy(Dsymbol* s) override; - SharedStaticDtorDeclaration* isSharedStaticDtorDeclaration() override; void accept(Visitor* v) override; }; @@ -3957,7 +4207,6 @@ class UnitTestDeclaration final : public FuncDeclaration bool isVirtual() const override; bool addPreInvariant() override; bool addPostInvariant() override; - UnitTestDeclaration* isUnitTestDeclaration() override; void accept(Visitor* v) override; }; @@ -3967,10 +4216,12 @@ struct HdrGenState final bool ddoc; bool fullDump; bool importcHdr; + bool inCAlias; bool doFuncBodies; bool vcg_ast; bool skipConstraints; bool showOneMember; + bool errorMsg; bool fullQual; int32_t tpltMember; int32_t autoMember; @@ -3984,10 +4235,12 @@ struct HdrGenState final ddoc(), fullDump(), importcHdr(), + inCAlias(), doFuncBodies(), vcg_ast(), skipConstraints(), showOneMember(true), + errorMsg(), fullQual(), tpltMember(), autoMember(), @@ -3998,15 +4251,17 @@ struct HdrGenState final inEnumDecl() { } - HdrGenState(bool hdrgen, bool ddoc = false, bool fullDump = false, bool importcHdr = false, bool doFuncBodies = false, bool vcg_ast = false, bool skipConstraints = false, bool showOneMember = true, bool fullQual = false, int32_t tpltMember = 0, int32_t autoMember = 0, int32_t forStmtInit = 0, int32_t insideFuncBody = 0, int32_t insideAggregate = 0, bool declstring = false, EnumDeclaration* inEnumDecl = nullptr) : + HdrGenState(bool hdrgen, bool ddoc = false, bool fullDump = false, bool importcHdr = false, bool inCAlias = false, bool doFuncBodies = false, bool vcg_ast = false, bool skipConstraints = false, bool showOneMember = true, bool errorMsg = false, bool fullQual = false, int32_t tpltMember = 0, int32_t autoMember = 0, int32_t forStmtInit = 0, int32_t insideFuncBody = 0, int32_t insideAggregate = 0, bool declstring = false, EnumDeclaration* inEnumDecl = nullptr) : hdrgen(hdrgen), ddoc(ddoc), fullDump(fullDump), importcHdr(importcHdr), + inCAlias(inCAlias), doFuncBodies(doFuncBodies), vcg_ast(vcg_ast), skipConstraints(skipConstraints), showOneMember(showOneMember), + errorMsg(errorMsg), fullQual(fullQual), tpltMember(tpltMember), autoMember(autoMember), @@ -4036,6 +4291,7 @@ class Initializer : public ASTNode public: Loc loc; InitKind kind; + bool semanticDone; DYNCAST dyncast() const override; ErrorInitializer* isErrorInitializer(); VoidInitializer* isVoidInitializer(); @@ -4054,7 +4310,6 @@ class ArrayInitializer final : public Initializer Array value; uint32_t dim; Type* type; - bool sem; bool isCarray; bool isAssociativeArray() const; void accept(Visitor* v) override; @@ -4065,7 +4320,6 @@ class CInitializer final : public Initializer public: Array initializerList; Type* type; - bool sem; void accept(Visitor* v) override; }; @@ -4135,12 +4389,12 @@ class Parameter final : public ASTNode { public: Loc loc; - StorageClass storageClass; + STC storageClass; Type* type; Identifier* ident; Expression* defaultArg; UserAttributeDeclaration* userAttribDecl; - static Parameter* create(const Loc& loc, StorageClass storageClass, Type* type, Identifier* ident, Expression* defaultArg, UserAttributeDeclaration* userAttribDecl); + static Parameter* create(Loc loc, StorageClass storageClass, Type* type, Identifier* ident, Expression* defaultArg, UserAttributeDeclaration* userAttribDecl); Parameter* syntaxCopy(); Type* isLazyArray(); bool isLazy() const; @@ -4195,7 +4449,6 @@ class TypeNext : public Type Type* makeSharedWild() final override; Type* makeSharedWildConst() final override; Type* makeMutable() final override; - MATCH constConv(Type* to) override; uint8_t deduceWild(Type* t, bool isRef) final override; void transitive(); void accept(Visitor* v) override; @@ -4215,9 +4468,7 @@ class TypeAArray final : public TypeArray static TypeAArray* create(Type* t, Type* index); const char* kind() const override; TypeAArray* syntaxCopy() override; - bool isZeroInit(const Loc& loc) override; bool isBoolean() override; - MATCH constConv(Type* to) override; void accept(Visitor* v) override; }; @@ -4229,14 +4480,13 @@ class TypeBasic final : public Type const char* kind() const override; TypeBasic* syntaxCopy() override; uint32_t alignsize() override; - bool isintegral() override; - bool isfloating() override; - bool isreal() override; - bool isimaginary() override; - bool iscomplex() override; - bool isscalar() override; - bool isunsigned() override; - bool isZeroInit(const Loc& loc) override; + bool isIntegral() override; + bool isFloating() override; + bool isReal() override; + bool isImaginary() override; + bool isComplex() override; + bool isScalar() override; + bool isUnsigned() override; bool hasUnsafeBitpatterns() override; TypeBasic* isTypeBasic() override; void accept(Visitor* v) override; @@ -4261,10 +4511,8 @@ class TypeClass final : public Type const char* kind() const override; TypeClass* syntaxCopy() override; ClassDeclaration* isClassHandle() override; - MATCH constConv(Type* to) override; uint8_t deduceWild(Type* t, bool isRef) override; - bool isZeroInit(const Loc& loc) override; - bool isscope() override; + bool isScopeClass() override; bool isBoolean() override; void accept(Visitor* v) override; }; @@ -4276,7 +4524,6 @@ class TypeDArray final : public TypeArray TypeDArray* syntaxCopy() override; uint32_t alignsize() override; bool isString() override; - bool isZeroInit(const Loc& loc) override; bool isBoolean() override; void accept(Visitor* v) override; }; @@ -4288,7 +4535,6 @@ class TypeDelegate final : public TypeNext const char* kind() const override; TypeDelegate* syntaxCopy() override; uint32_t alignsize() override; - bool isZeroInit(const Loc& loc) override; bool isBoolean() override; void accept(Visitor* v) override; }; @@ -4301,21 +4547,19 @@ class TypeEnum final : public Type TypeEnum* syntaxCopy() override; Type* memType(); uint32_t alignsize() override; - bool isintegral() override; - bool isfloating() override; - bool isreal() override; - bool isimaginary() override; - bool iscomplex() override; - bool isscalar() override; - bool isunsigned() override; + 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 isAssignable() override; bool needsDestruction() override; bool needsCopyOrPostblit() override; bool needsNested() override; - MATCH constConv(Type* to) override; - bool isZeroInit(const Loc& loc) override; bool hasVoidInitPointers() override; bool hasUnsafeBitpatterns() override; bool hasInvariant() override; @@ -4328,7 +4572,7 @@ class TypeError final : public Type public: const char* kind() const override; TypeError* syntaxCopy() override; - Expression* defaultInitLiteral(const Loc& loc) override; + Expression* defaultInitLiteral(Loc loc) override; void accept(Visitor* v) override; }; @@ -4355,84 +4599,89 @@ class TypeFunction final : public TypeNext private: struct BitFields final { - bool isnothrow; - bool isnogc; - bool isproperty; - bool isref; - bool isreturn; + bool isNothrow; + bool isNogc; + bool isProperty; + bool isRef; + bool isReturn; bool isScopeQual; - bool isreturninferred; - bool isscopeinferred; - bool islive; + bool isReturnInferred; + bool isScopeInferred; + bool isLive; bool incomplete; bool isInOutParam; bool isInOutQual; - bool isctor; - bool isreturnscope; + bool isCtor; + bool isReturnScope; + bool isRvalue; BitFields() : - isnothrow(), - isnogc(), - isproperty(), - isref(), - isreturn(), + isNothrow(), + isNogc(), + isProperty(), + isRef(), + isReturn(), isScopeQual(), - isreturninferred(), - isscopeinferred(), - islive(), + isReturnInferred(), + isScopeInferred(), + isLive(), incomplete(), isInOutParam(), isInOutQual(), - isctor(), - isreturnscope() + isCtor(), + isReturnScope(), + isRvalue() { } - BitFields(bool isnothrow, bool isnogc = false, bool isproperty = false, bool isref = false, bool isreturn = false, bool isScopeQual = false, bool isreturninferred = false, bool isscopeinferred = false, bool islive = false, bool incomplete = false, bool isInOutParam = false, bool isInOutQual = false, bool isctor = false, bool isreturnscope = false) : - isnothrow(isnothrow), - isnogc(isnogc), - isproperty(isproperty), - isref(isref), - isreturn(isreturn), + BitFields(bool isNothrow, bool isNogc = false, bool isProperty = false, bool isRef = false, bool isReturn = false, bool isScopeQual = false, bool isReturnInferred = false, bool isScopeInferred = false, bool isLive = false, bool incomplete = false, bool isInOutParam = false, bool isInOutQual = false, bool isCtor = false, bool isReturnScope = false, bool isRvalue = false) : + isNothrow(isNothrow), + isNogc(isNogc), + isProperty(isProperty), + isRef(isRef), + isReturn(isReturn), isScopeQual(isScopeQual), - isreturninferred(isreturninferred), - isscopeinferred(isscopeinferred), - islive(islive), + isReturnInferred(isReturnInferred), + isScopeInferred(isScopeInferred), + isLive(isLive), incomplete(incomplete), isInOutParam(isInOutParam), isInOutQual(isInOutQual), - isctor(isctor), - isreturnscope(isreturnscope) + isCtor(isCtor), + isReturnScope(isReturnScope), + isRvalue(isRvalue) {} }; public: - bool isnothrow() const; - bool isnothrow(bool v); - bool isnogc() const; - bool isnogc(bool v); - bool isproperty() const; - bool isproperty(bool v); - bool isref() const; - bool isref(bool v); - bool isreturn() const; - bool isreturn(bool v); + bool isNothrow() const; + bool isNothrow(bool v); + bool isNogc() const; + bool isNogc(bool v); + bool isProperty() const; + bool isProperty(bool v); + bool isRef() const; + bool isRef(bool v); + bool isReturn() const; + bool isReturn(bool v); bool isScopeQual() const; bool isScopeQual(bool v); - bool isreturninferred() const; - bool isreturninferred(bool v); - bool isscopeinferred() const; - bool isscopeinferred(bool v); - bool islive() const; - bool islive(bool v); + bool isReturnInferred() const; + bool isReturnInferred(bool v); + bool isScopeInferred() const; + bool isScopeInferred(bool v); + bool isLive() const; + bool isLive(bool v); bool incomplete() const; bool incomplete(bool v); bool isInOutParam() const; bool isInOutParam(bool v); bool isInOutQual() const; bool isInOutQual(bool v); - bool isctor() const; - bool isctor(bool v); - bool isreturnscope() const; - bool isreturnscope(bool v); + bool isCtor() const; + bool isCtor(bool v); + bool isReturnScope() const; + bool isReturnScope(bool v); + bool isRvalue() const; + bool isRvalue(bool v); private: uint16_t bitFields; public: @@ -4441,12 +4690,11 @@ class TypeFunction final : public TypeNext PURE purity; int8_t inuse; ArgumentList inferenceArguments; - static TypeFunction* create(Array* parameters, Type* treturn, uint8_t varargs, LINK linkage, StorageClass stc = 0); + static TypeFunction* create(Array* parameters, Type* treturn, uint8_t varargs, LINK linkage, StorageClass stc = static_cast(STC::none)); const char* kind() const override; TypeFunction* syntaxCopy() override; bool hasLazyParameters(); bool isDstyleVariadic() const; - MATCH constConv(Type* to) override; bool iswild() const; void accept(Visitor* v) override; }; @@ -4464,8 +4712,7 @@ class TypeIdentifier final : public TypeQualified { public: Identifier* ident; - Dsymbol* originalSymbol; - static TypeIdentifier* create(const Loc& loc, Identifier* ident); + static TypeIdentifier* create(Loc loc, Identifier* ident); const char* kind() const override; TypeIdentifier* syntaxCopy() override; void accept(Visitor* v) override; @@ -4496,7 +4743,6 @@ class TypeNoreturn final : public Type public: const char* kind() const override; TypeNoreturn* syntaxCopy() override; - MATCH constConv(Type* to) override; bool isBoolean() override; uint32_t alignsize() override; void accept(Visitor* v) override; @@ -4517,9 +4763,7 @@ class TypePointer final : public TypeNext static TypePointer* create(Type* t); const char* kind() const override; TypePointer* syntaxCopy() override; - MATCH constConv(Type* to) override; - bool isscalar() override; - bool isZeroInit(const Loc& loc) override; + bool isScalar() override; void accept(Visitor* v) override; }; @@ -4528,7 +4772,6 @@ class TypeReference final : public TypeNext public: const char* kind() const override; TypeReference* syntaxCopy() override; - bool isZeroInit(const Loc& loc) override; void accept(Visitor* v) override; }; @@ -4549,10 +4792,8 @@ class TypeSArray final : public TypeArray bool isIncomplete(); uint32_t alignsize() override; bool isString() override; - bool isZeroInit(const Loc& loc) override; structalign_t alignment() override; - MATCH constConv(Type* to) override; - Expression* defaultInitLiteral(const Loc& loc) override; + Expression* defaultInitLiteral(Loc loc) override; bool hasUnsafeBitpatterns() override; bool hasVoidInitPointers() override; bool hasInvariant() override; @@ -4583,8 +4824,7 @@ class TypeStruct final : public Type uint32_t alignsize() override; TypeStruct* syntaxCopy() override; structalign_t alignment() override; - Expression* defaultInitLiteral(const Loc& loc) override; - bool isZeroInit(const Loc& loc) override; + Expression* defaultInitLiteral(Loc loc) override; bool isAssignable() override; bool isBoolean() override; bool needsDestruction() override; @@ -4593,7 +4833,6 @@ class TypeStruct final : public Type bool hasVoidInitPointers() override; bool hasUnsafeBitpatterns() override; bool hasInvariant() override; - MATCH constConv(Type* to) override; uint8_t deduceWild(Type* t, bool isRef) override; void accept(Visitor* v) override; }; @@ -4658,14 +4897,13 @@ class TypeVector final : public Type const char* kind() const override; TypeVector* syntaxCopy() override; uint32_t alignsize() override; - bool isintegral() override; - bool isfloating() override; - bool isscalar() override; - bool isunsigned() override; + bool isIntegral() override; + bool isFloating() override; + bool isScalar() override; + bool isUnsigned() override; bool isBoolean() override; - Expression* defaultInitLiteral(const Loc& loc) override; + Expression* defaultInitLiteral(Loc loc) override; TypeBasic* elementType(); - bool isZeroInit(const Loc& loc) override; void accept(Visitor* v) override; }; @@ -4676,7 +4914,6 @@ class Nspace final : public ScopeDsymbol Nspace* syntaxCopy(Dsymbol* s) override; bool hasPointers() override; const char* kind() const override; - Nspace* isNspace() override; void accept(Visitor* v) override; }; @@ -4844,7 +5081,7 @@ class CompoundStatement : public Statement { public: Array* statements; - static CompoundStatement* create(const Loc& loc, Statement* s1, Statement* s2); + static CompoundStatement* create(Loc loc, Statement* s1, Statement* s2); CompoundStatement* syntaxCopy() override; ReturnStatement* endsWithReturnStatement() final override; Statement* last() final override; @@ -4854,7 +5091,7 @@ class CompoundStatement : public Statement class CompoundAsmStatement final : public CompoundStatement { public: - StorageClass stc; + STC stc; CompoundAsmStatement* syntaxCopy() override; void accept(Visitor* v) override; }; @@ -4917,7 +5154,7 @@ class ExpStatement : public Statement { public: Expression* exp; - static ExpStatement* create(const Loc& loc, Expression* exp); + static ExpStatement* create(Loc loc, Expression* exp); ExpStatement* syntaxCopy() override; void accept(Visitor* v) override; }; @@ -4957,7 +5194,7 @@ class ForeachRangeStatement final : public Statement { public: TOK op; - Parameter* prm; + Parameter* param; Expression* lwr; Expression* upr; Statement* _body; @@ -5000,7 +5237,7 @@ class ForwardingStatement final : public Statement class GccAsmStatement final : public AsmStatement { public: - StorageClass stc; + STC stc; Expression* insn; Array* args; uint32_t outputargs; @@ -5047,7 +5284,7 @@ class GotoStatement final : public Statement class IfStatement final : public Statement { public: - Parameter* prm; + Parameter* param; Expression* condition; Statement* ifbody; Statement* elsebody; @@ -5198,7 +5435,7 @@ class SwitchStatement final : public Statement bool hasVars; DefaultStatement* sdefault; Statement* tryBody; - TryFinallyStatement* tf; + TryFinallyStatement* tryFinally; Array gotoCases; Array* cases; VarDeclaration* lastVar; @@ -5245,7 +5482,7 @@ class TryFinallyStatement final : public Statement Statement* finalbody; Statement* tryBody; bool bodyFallsThru; - static TryFinallyStatement* create(const Loc& loc, Statement* _body, Statement* finalbody); + static TryFinallyStatement* create(Loc loc, Statement* _body, Statement* finalbody); TryFinallyStatement* syntaxCopy() override; bool hasBreak() const override; bool hasContinue() const override; @@ -5292,9 +5529,7 @@ class StaticAssert final : public Dsymbol Expression* exp; Array* msgs; StaticAssert* syntaxCopy(Dsymbol* s) override; - bool oneMember(Dsymbol*& ps, Identifier* ident) override; const char* kind() const override; - StaticAssert* isStaticAssert() override; void accept(Visitor* v) override; }; @@ -5304,23 +5539,23 @@ struct UnionExp final private: union _AnonStruct_u { - char exp[30LLU]; - char integerexp[40LLU]; - char errorexp[30LLU]; + char exp[22LLU]; + char integerexp[32LLU]; + char errorexp[22LLU]; char realexp[48LLU]; char complexexp[64LLU]; - char symoffexp[64LLU]; - char stringexp[51LLU]; - char arrayliteralexp[48LLU]; - char assocarrayliteralexp[56LLU]; - char structliteralexp[76LLU]; - char compoundliteralexp[40LLU]; - char nullexp[30LLU]; - char dotvarexp[49LLU]; - char addrexp[40LLU]; - char indexexp[74LLU]; - char sliceexp[65LLU]; - char vectorexp[53LLU]; + char symoffexp[56LLU]; + char stringexp[43LLU]; + char arrayliteralexp[40LLU]; + char assocarrayliteralexp[48LLU]; + char structliteralexp[64LLU]; + char compoundliteralexp[32LLU]; + char nullexp[22LLU]; + char dotvarexp[41LLU]; + char addrexp[32LLU]; + char indexexp[50LLU]; + char sliceexp[57LLU]; + char vectorexp[45LLU]; }; #pragma pack(pop) @@ -5344,71 +5579,6 @@ enum class MODFlags mutable_ = 16, }; -enum class STC : uint64_t -{ - undefined_ = 0LLU, - static_ = 1LLU, - extern_ = 2LLU, - const_ = 4LLU, - final_ = 8LLU, - abstract_ = 16LLU, - parameter = 32LLU, - field = 64LLU, - override_ = 128LLU, - auto_ = 256LLU, - synchronized_ = 512LLU, - deprecated_ = 1024LLU, - in_ = 2048LLU, - out_ = 4096LLU, - lazy_ = 8192LLU, - foreach_ = 16384LLU, - variadic = 32768LLU, - constscoperef = 65536LLU, - templateparameter = 131072LLU, - ref_ = 262144LLU, - scope_ = 524288LLU, - scopeinferred = 2097152LLU, - return_ = 4194304LLU, - returnScope = 8388608LLU, - returninferred = 16777216LLU, - immutable_ = 33554432LLU, - manifest = 134217728LLU, - nodtor = 268435456LLU, - nothrow_ = 536870912LLU, - pure_ = 1073741824LLU, - tls = 2147483648LLU, - alias_ = 4294967296LLU, - shared_ = 8589934592LLU, - gshared = 17179869184LLU, - wild = 34359738368LLU, - property = 68719476736LLU, - safe = 137438953472LLU, - trusted = 274877906944LLU, - system = 549755813888LLU, - ctfe = 1099511627776LLU, - disable = 2199023255552LLU, - result = 4398046511104LLU, - nodefaultctor = 8796093022208LLU, - temp = 17592186044416LLU, - rvalue = 35184372088832LLU, - nogc = 70368744177664LLU, - autoref = 140737488355328LLU, - inference = 281474976710656LLU, - exptemp = 562949953421312LLU, - future = 1125899906842624LLU, - local = 2251799813685248LLU, - live = 4503599627370496LLU, - register_ = 9007199254740992LLU, - volatile_ = 18014398509481984LLU, - safeGroup = 962072674304LLU, - IOR = 333824LLU, - TYPECTOR = 42983227396LLU, - FUNCATTR = 4575000774574080LLU, - visibleStorageClasses = 7954966262857631LLU, - flowThruAggregate = 962072674304LLU, - flowThruFunction = 18446742978991225440LLU, -}; - struct ASTCodegen final { using AggregateDeclaration = ::AggregateDeclaration; @@ -5510,6 +5680,7 @@ struct ASTCodegen final using AliasAssign = ::AliasAssign; using ArrayScopeSymbol = ::ArrayScopeSymbol; using CAsmDeclaration = ::CAsmDeclaration; + using DSYM = ::DSYM; using Dsymbol = ::Dsymbol; using DsymbolTable = ::DsymbolTable; using ExpressionDsymbol = ::ExpressionDsymbol; @@ -5612,8 +5783,6 @@ struct ASTCodegen final using MixinExp = ::MixinExp; using ModAssignExp = ::ModAssignExp; using ModExp = ::ModExp; - using Modifiable = ::Modifiable; - using ModifyFlags = ::ModifyFlags; using ModuleInitExp = ::ModuleInitExp; using MulAssignExp = ::MulAssignExp; using MulExp = ::MulExp; @@ -6047,13 +6216,10 @@ enum class CPU : uint8_t native = 12u, }; -enum class ErrorKind +enum class ErrorPrintMode : uint8_t { - warning = 0, - deprecation = 1, - error = 2, - tip = 3, - message = 4, + simpleError = 0u, + printErrorContext = 1u, }; enum class DiagnosticReporting : uint8_t @@ -6070,13 +6236,7 @@ enum class CppStdRevision : uint32_t cpp14 = 201402u, cpp17 = 201703u, cpp20 = 202002u, -}; - -enum class FeatureState : uint8_t -{ - default_ = 0u, - disabled = 1u, - enabled = 2u, + cpp23 = 202302u, }; enum class CHECKENABLE : uint8_t @@ -6121,6 +6281,7 @@ struct CompileEnv final _d_dynamicArray< const char > vendor; _d_dynamicArray< const char > timestamp; bool previewIn; + bool transitionIn; bool ddocOutput; bool masm; IdentifierCharLookup cCharLookupTable; @@ -6132,19 +6293,21 @@ struct CompileEnv final vendor(), timestamp(), previewIn(), + transitionIn(), ddocOutput(), masm(), cCharLookupTable(), dCharLookupTable() { } - CompileEnv(uint32_t versionNumber, _d_dynamicArray< const char > date = {}, _d_dynamicArray< const char > time = {}, _d_dynamicArray< const char > vendor = {}, _d_dynamicArray< const char > timestamp = {}, bool previewIn = false, bool ddocOutput = false, bool masm = false, IdentifierCharLookup cCharLookupTable = IdentifierCharLookup(), IdentifierCharLookup dCharLookupTable = IdentifierCharLookup()) : + CompileEnv(uint32_t versionNumber, _d_dynamicArray< const char > date = {}, _d_dynamicArray< const char > time = {}, _d_dynamicArray< const char > vendor = {}, _d_dynamicArray< const char > timestamp = {}, bool previewIn = false, bool transitionIn = false, bool ddocOutput = false, bool masm = false, IdentifierCharLookup cCharLookupTable = IdentifierCharLookup(), IdentifierCharLookup dCharLookupTable = IdentifierCharLookup()) : versionNumber(versionNumber), date(date), time(time), vendor(vendor), timestamp(timestamp), previewIn(previewIn), + transitionIn(transitionIn), ddocOutput(ddocOutput), masm(masm), cCharLookupTable(cCharLookupTable), @@ -6160,7 +6323,7 @@ class AggregateDeclaration : public ScopeDsymbol { public: Type* type; - StorageClass storage_class; + STC storage_class; uint32_t structsize; uint32_t alignsize; Array fields; @@ -6188,8 +6351,7 @@ class AggregateDeclaration : public ScopeDsymbol Sizeok sizeok; virtual Scope* newScope(Scope* sc); virtual void finalizeSize() = 0; - uinteger_t size(const Loc& loc) final override; - bool fill(const Loc& loc, Array& elements, bool ctorinit); + uinteger_t size(Loc loc) final override; Type* getType() final override; bool isDeprecated() const final override; bool isNested() const; @@ -6198,7 +6360,6 @@ class AggregateDeclaration : public ScopeDsymbol Type* handleType(); bool hasInvariant(); void* sinit; - AggregateDeclaration* isAggregateDeclaration() final override; void accept(Visitor* v) override; }; @@ -6253,27 +6414,17 @@ class AttribDeclaration : public Dsymbol { public: Array* decl; - virtual Array* include(Scope* sc); - virtual Scope* newScope(Scope* sc); - void addComment(const char* comment) override; const char* kind() const override; - bool oneMember(Dsymbol*& ps, Identifier* ident) override; bool hasPointers() final override; - bool hasStaticCtorOrDtor() final override; - void checkCtorConstInit() final override; void addObjcSymbols(Array* classes, Array* categories) final override; - AttribDeclaration* isAttribDeclaration() override; void accept(Visitor* v) override; }; class StorageClassDeclaration : public AttribDeclaration { public: - StorageClass stc; + STC stc; StorageClassDeclaration* syntaxCopy(Dsymbol* s) override; - Scope* newScope(Scope* sc) override; - bool oneMember(Dsymbol*& ps, Identifier* ident) final override; - StorageClassDeclaration* isStorageClassDeclaration() override; void accept(Visitor* v) override; }; @@ -6283,7 +6434,6 @@ class DeprecatedDeclaration final : public StorageClassDeclaration Expression* msg; const char* msgstr; DeprecatedDeclaration* syntaxCopy(Dsymbol* s) override; - Scope* newScope(Scope* sc) override; void accept(Visitor* v) override; }; @@ -6291,10 +6441,8 @@ class LinkDeclaration final : public AttribDeclaration { public: LINK linkage; - static LinkDeclaration* create(const Loc& loc, LINK p, Array* decl); + static LinkDeclaration* create(Loc loc, LINK p, Array* decl); LinkDeclaration* syntaxCopy(Dsymbol* s) override; - Scope* newScope(Scope* sc) override; - const char* toChars() const override; void accept(Visitor* v) override; }; @@ -6303,8 +6451,6 @@ class CPPMangleDeclaration final : public AttribDeclaration public: CPPMANGLE cppmangle; CPPMangleDeclaration* syntaxCopy(Dsymbol* s) override; - Scope* newScope(Scope* sc) override; - const char* toChars() const override; void accept(Visitor* v) override; }; @@ -6313,10 +6459,7 @@ class CPPNamespaceDeclaration final : public AttribDeclaration public: Expression* exp; CPPNamespaceDeclaration* syntaxCopy(Dsymbol* s) override; - Scope* newScope(Scope* sc) override; - const char* toChars() const override; void accept(Visitor* v) override; - CPPNamespaceDeclaration* isCPPNamespaceDeclaration() override; }; class VisibilityDeclaration final : public AttribDeclaration @@ -6325,10 +6468,8 @@ class VisibilityDeclaration final : public AttribDeclaration Visibility visibility; _d_dynamicArray< Identifier* > pkg_identifiers; VisibilityDeclaration* syntaxCopy(Dsymbol* s) override; - Scope* newScope(Scope* sc) override; const char* kind() const override; const char* toPrettyChars(bool __param_0_) override; - VisibilityDeclaration* isVisibilityDeclaration() override; void accept(Visitor* v) override; }; @@ -6338,7 +6479,6 @@ class AlignDeclaration final : public AttribDeclaration Array* exps; structalign_t salign; AlignDeclaration* syntaxCopy(Dsymbol* s) override; - Scope* newScope(Scope* sc) override; void accept(Visitor* v) override; }; @@ -6352,7 +6492,6 @@ class AnonDeclaration final : public AttribDeclaration uint32_t anonalignsize; AnonDeclaration* syntaxCopy(Dsymbol* s) override; const char* kind() const override; - AnonDeclaration* isAnonDeclaration() override; void accept(Visitor* v) override; }; @@ -6361,7 +6500,6 @@ class PragmaDeclaration final : public AttribDeclaration public: Array* args; PragmaDeclaration* syntaxCopy(Dsymbol* s) override; - Scope* newScope(Scope* sc) override; const char* kind() const override; void accept(Visitor* v) override; }; @@ -6372,9 +6510,6 @@ class ConditionalDeclaration : public AttribDeclaration Condition* condition; Array* elsedecl; ConditionalDeclaration* syntaxCopy(Dsymbol* s) override; - bool oneMember(Dsymbol*& ps, Identifier* ident) final override; - Array* include(Scope* sc) override; - void addComment(const char* comment) final override; void accept(Visitor* v) override; }; @@ -6382,14 +6517,10 @@ class StaticIfDeclaration final : public ConditionalDeclaration { public: ScopeDsymbol* scopesym; -private: bool addisdone; bool onStack; -public: StaticIfDeclaration* syntaxCopy(Dsymbol* s) override; - Array* include(Scope* sc) override; const char* kind() const override; - StaticIfDeclaration* isStaticIfDeclaration() override; void accept(Visitor* v) override; }; @@ -6402,9 +6533,6 @@ class StaticForeachDeclaration final : public AttribDeclaration bool cached; Array* cache; StaticForeachDeclaration* syntaxCopy(Dsymbol* s) override; - bool oneMember(Dsymbol*& ps, Identifier* ident) override; - Array* include(Scope* sc) override; - void addComment(const char* comment) override; const char* kind() const override; void accept(Visitor* v) override; }; @@ -6414,8 +6542,6 @@ class ForwardingAttribDeclaration final : public AttribDeclaration public: ForwardingScopeDsymbol* sym; ForwardingAttribDeclaration(Array* decl); - Scope* newScope(Scope* sc) override; - ForwardingAttribDeclaration* isForwardingAttribDeclaration() override; void accept(Visitor* v) override; }; @@ -6427,7 +6553,6 @@ class MixinDeclaration final : public AttribDeclaration bool compiled; MixinDeclaration* syntaxCopy(Dsymbol* s) override; const char* kind() const override; - MixinDeclaration* isMixinDeclaration() override; void accept(Visitor* v) override; }; @@ -6436,16 +6561,13 @@ class UserAttributeDeclaration final : public AttribDeclaration public: Array* atts; UserAttributeDeclaration* syntaxCopy(Dsymbol* s) override; - Scope* newScope(Scope* sc) override; const char* kind() const override; void accept(Visitor* v) override; - static bool isGNUABITag(Expression* e); - static void checkGNUABITag(Dsymbol* sym, LINK linkage); }; extern BUILTIN isBuiltin(FuncDeclaration* fd); -extern Expression* eval_builtin(const Loc& loc, FuncDeclaration* fd, Array* arguments); +extern Expression* eval_builtin(Loc loc, FuncDeclaration* fd, Array* arguments); extern bool includeImports; @@ -6489,7 +6611,6 @@ class StaticForeach final : public RootObject class DVCondition : public Condition { public: - uint32_t level; Identifier* ident; Module* mod; DVCondition* syntaxCopy() final override; @@ -6503,7 +6624,6 @@ class DebugCondition final : public DVCondition int32_t include(Scope* sc) override; DebugCondition* isDebugCondition() override; void accept(Visitor* v) override; - const char* toChars() const override; }; class VersionCondition final : public DVCondition @@ -6514,7 +6634,6 @@ class VersionCondition final : public DVCondition int32_t include(Scope* sc) override; VersionCondition* isVersionCondition() override; void accept(Visitor* v) override; - const char* toChars() const override; }; class StaticIfCondition final : public Condition @@ -6525,10 +6644,9 @@ class StaticIfCondition final : public Condition int32_t include(Scope* sc) override; void accept(Visitor* v) override; StaticIfCondition* isStaticIfCondition() override; - const char* toChars() const override; }; -extern DArray preprocess(FileName csrcfile, const Loc& loc, OutBuffer& defines); +extern DArray preprocess(FileName csrcfile, Loc loc, OutBuffer& defines); struct BaseClass final { @@ -6573,7 +6691,7 @@ class ClassDeclaration : public AggregateDeclaration Baseok baseok; ObjcClassDeclaration objc; Symbol* cpp_type_info_ptr_sym; - static ClassDeclaration* create(const Loc& loc, Identifier* id, Array* baseclasses, Array* members, bool inObject); + static ClassDeclaration* create(Loc loc, Identifier* id, Array* baseclasses, Array* members, bool inObject); const char* toPrettyChars(bool qualifyTypes = false) override; ClassDeclaration* syntaxCopy(Dsymbol* s) override; Scope* newScope(Scope* sc) override; @@ -6585,7 +6703,6 @@ class ClassDeclaration : public AggregateDeclaration bool isBaseInfoComplete() const; void finalizeSize() final override; bool hasMonitor(); - bool isFuncHidden(FuncDeclaration* fd); bool isCOMclass() const; virtual bool isCOMinterface() const; bool isCPPclass() const; @@ -6595,8 +6712,6 @@ class ClassDeclaration : public AggregateDeclaration const char* kind() const override; void addObjcSymbols(Array* classes, Array* categories) final override; Dsymbol* vtblsym; - Dsymbol* vtblSymbol(); - ClassDeclaration* isClassDeclaration() final override; void accept(Visitor* v) override; }; @@ -6610,7 +6725,6 @@ class InterfaceDeclaration final : public ClassDeclaration int32_t vtblOffset() const override; bool isCPPinterface() const override; bool isCOMinterface() const override; - InterfaceDeclaration* isInterfaceDeclaration() override; void accept(Visitor* v) override; }; @@ -6619,22 +6733,27 @@ class Declaration : public Dsymbol public: Type* type; Type* originalType; - StorageClass storage_class; + STC storage_class; + _d_dynamicArray< const char > mangleOverride; Visibility visibility; - LINK _linkage; int16_t inuse; - uint8_t adFlags; - enum : int32_t { wasRead = 1 }; - - enum : int32_t { ignoreRead = 2 }; - - enum : int32_t { nounderscore = 4 }; - - enum : int32_t { hidden = 8 }; - - _d_dynamicArray< const char > mangleOverride; + LINK _linkage() const; + LINK _linkage(LINK v); + bool wasRead() const; + bool wasRead(bool v); + bool ignoreRead() const; + bool ignoreRead(bool v); + bool noUnderscore() const; + bool noUnderscore(bool v); + bool hidden() const; + bool hidden(bool v); + bool nrvo() const; + bool nrvo(bool v); +private: + uint8_t bitFields; +public: const char* kind() const override; - uinteger_t size(const Loc& loc) final override; + uinteger_t size(Loc loc) final override; bool isStatic() const; LINK resolvedLinkage() const; virtual bool isDelete(); @@ -6662,7 +6781,6 @@ class Declaration : public Dsymbol bool isReference() const; bool isFuture() const; Visibility visible() final override; - Declaration* isDeclaration() final override; void accept(Visitor* v) override; }; @@ -6678,7 +6796,6 @@ class TupleDeclaration final : public Declaration Type* getType() override; Dsymbol* toAlias2() override; bool needThis() override; - TupleDeclaration* isTupleDeclaration() override; void accept(Visitor* v) override; }; @@ -6688,7 +6805,7 @@ class AliasDeclaration final : public Declaration Dsymbol* aliassym; Dsymbol* overnext; Dsymbol* _import_; - static AliasDeclaration* create(const Loc& loc, Identifier* id, Type* type); + static AliasDeclaration* create(Loc loc, Identifier* id, Type* type); AliasDeclaration* syntaxCopy(Dsymbol* s) override; bool overloadInsert(Dsymbol* s) override; const char* kind() const override; @@ -6696,7 +6813,6 @@ class AliasDeclaration final : public Declaration Dsymbol* toAlias() override; Dsymbol* toAlias2() override; bool isOverloadable() const override; - AliasDeclaration* isAliasDeclaration() override; void accept(Visitor* v) override; }; @@ -6710,7 +6826,6 @@ class OverDeclaration final : public Declaration bool overloadInsert(Dsymbol* s) override; bool isOverloadable() const override; Dsymbol* isUnique(); - OverDeclaration* isOverDeclaration() override; void accept(Visitor* v) override; }; @@ -6723,7 +6838,6 @@ class VarDeclaration : public Declaration VarDeclaration* lastVar; Expression* edtor; IntRange* range; - Array* maybes; uint32_t endlinnum; uint32_t offset; uint32_t sequenceNumber; @@ -6770,7 +6884,7 @@ class VarDeclaration : public Declaration public: int8_t canassign; uint8_t isdataseg; - static VarDeclaration* create(const Loc& loc, Type* type, Identifier* ident, Initializer* _init, StorageClass storage_class = static_cast(STC::undefined_)); + static VarDeclaration* create(Loc loc, Type* type, Identifier* ident, Initializer* _init, StorageClass storage_class = static_cast(STC::none)); VarDeclaration* syntaxCopy(Dsymbol* s) override; const char* kind() const override; AggregateDeclaration* isThis() final override; @@ -6785,9 +6899,7 @@ class VarDeclaration : public Declaration bool hasPointers() final override; bool canTakeAddressOf(); bool needsScopeDtor(); - void checkCtorConstInit() final override; Dsymbol* toAlias() final override; - VarDeclaration* isVarDeclaration() final override; void accept(Visitor* v) override; }; @@ -6798,7 +6910,6 @@ class BitFieldDeclaration : public VarDeclaration uint32_t fieldWidth; uint32_t bitOffset; BitFieldDeclaration* syntaxCopy(Dsymbol* s) override; - BitFieldDeclaration* isBitFieldDeclaration() final override; void accept(Visitor* v) override; uint64_t getMinMax(Identifier* id); }; @@ -6807,7 +6918,6 @@ class SymbolDeclaration final : public Declaration { public: AggregateDeclaration* dsym; - SymbolDeclaration* isSymbolDeclaration() override; void accept(Visitor* v) override; }; @@ -6817,8 +6927,6 @@ class TypeInfoDeclaration : public VarDeclaration Type* tinfo; static TypeInfoDeclaration* create(Type* tinfo); TypeInfoDeclaration* syntaxCopy(Dsymbol* s) final override; - const char* toChars() const final override; - TypeInfoDeclaration* isTypeInfoDeclaration() final override; void accept(Visitor* v) override; }; @@ -6867,6 +6975,7 @@ class TypeInfoStaticArrayDeclaration final : public TypeInfoDeclaration class TypeInfoAssociativeArrayDeclaration final : public TypeInfoDeclaration { public: + Type* entry; static TypeInfoAssociativeArrayDeclaration* create(Type* tinfo); void accept(Visitor* v) override; }; @@ -6938,7 +7047,6 @@ class ThisDeclaration final : public VarDeclaration { public: ThisDeclaration* syntaxCopy(Dsymbol* s) override; - ThisDeclaration* isThisDeclaration() override; void accept(Visitor* v) override; }; @@ -6962,13 +7070,11 @@ class EnumDeclaration final : public ScopeDsymbol public: Symbol* sinit; EnumDeclaration* syntaxCopy(Dsymbol* s) override; - bool oneMember(Dsymbol*& ps, Identifier* ident) override; Type* getType() override; const char* kind() const override; bool isDeprecated() const override; Visibility visible() override; bool isSpecial() const; - EnumDeclaration* isEnumDeclaration() override; void accept(Visitor* v) override; }; @@ -6981,7 +7087,6 @@ class EnumMember final : public VarDeclaration EnumDeclaration* ed; EnumMember* syntaxCopy(Dsymbol* s) override; const char* kind() const override; - EnumMember* isEnumMember() override; void accept(Visitor* v) override; }; @@ -7003,7 +7108,6 @@ class Import final : public Dsymbol Import* syntaxCopy(Dsymbol* s) override; Dsymbol* toAlias() override; bool overloadInsert(Dsymbol* s) override; - Import* isImport() override; void accept(Visitor* v) override; }; @@ -7017,7 +7121,6 @@ class Package : public ScopeDsymbol Module* mod; const char* kind() const override; bool equals(const RootObject* const o) const override; - Package* isPackage() final override; bool isAncestorPackageOf(const Package* const pkg) const; void accept(Visitor* v) override; Module* isPackageMod(); @@ -7039,8 +7142,8 @@ class Module final : public Package _d_dynamicArray< const char > arg; ModuleDeclaration* md; const FileName srcfile; - const FileName objfile; - const FileName hdrfile; + FileName objfile; + FileName hdrfile; FileName docfile; _d_dynamicArray< const uint8_t > src; uint32_t errors; @@ -7069,10 +7172,8 @@ class Module final : public Package Module* importedFrom; Array* decldefs; Array aimports; - uint32_t debuglevel; Array* debugids; Array* debugidsNot; - uint32_t versionlevel; Array* versionids; Array* versionidsNot; MacroTable macrotable; @@ -7081,12 +7182,12 @@ class Module final : public Package size_t namelen; static Module* create(const char* filename, Identifier* ident, int32_t doDocComment, int32_t doHdrGen); static const char* find(const char* filename); - static Module* load(const Loc& loc, Array* packages, Identifier* ident); + static Module* load(Loc loc, Array* packages, Identifier* ident); const char* kind() const override; - bool read(const Loc& loc); + bool read(Loc loc); Module* parse(); int32_t needModuleInfo(); - void checkImportDeprecation(const Loc& loc, Scope* sc); + void checkImportDeprecation(Loc loc, Scope* sc); bool isPackageAccessible(Package* p, Visibility visibility, uint32_t flags = 0u) override; Dsymbol* symtabInsert(Dsymbol* s) override; static void runDeferredSemantic(); @@ -7106,7 +7207,6 @@ class Module final : public Package Symbol* stest; Symbol* sfilename; void* ctfe_cov; - Module* isModule() override; void accept(Visitor* v) override; void fullyQualifiedName(OutBuffer& buf); }; @@ -7138,10 +7238,10 @@ struct Scope final VarDeclaration* varDecl; Dsymbol* parent; LabelStatement* slabel; - SwitchStatement* sw; + SwitchStatement* switchStatement; Statement* tryBody; - TryFinallyStatement* tf; - ScopeGuardStatement* os; + TryFinallyStatement* tryFinally; + ScopeGuardStatement* scopeGuard; Statement* sbreak; Statement* scontinue; ForeachStatement* fes; @@ -7163,15 +7263,45 @@ struct Scope final PragmaDeclaration* inlining; Visibility visibility; int32_t explicitVisibility; - StorageClass stc; + STC stc; DeprecatedDeclaration* depdecl; - uint32_t flags; + bool ctor() const; + bool ctor(bool v); + bool noAccessCheck() const; + bool noAccessCheck(bool v); + bool condition() const; + bool condition(bool v); + bool debug_() const; + bool debug_(bool v); + bool inTemplateConstraint() const; + bool inTemplateConstraint(bool v); + Contract contract() const; + Contract contract(Contract v); + bool ctfe() const; + bool ctfe(bool v); + bool traitsCompiles() const; + bool traitsCompiles(bool v); + bool ignoresymbolvisibility() const; + bool ignoresymbolvisibility(bool v); + bool inCfile() const; + bool inCfile(bool v); + bool canFree() const; + bool canFree(bool v); + bool fullinst() const; + bool fullinst(bool v); + bool ctfeBlock() const; + bool ctfeBlock(bool v); +private: + uint16_t bitFields; +public: + Previews previews; UserAttributeDeclaration* userAttribDecl; DocComment* lastdc; void* anchorCounts; Identifier* prevAnchor; AliasDeclaration* aliasAsg; - Dsymbol* search(const Loc& loc, Identifier* ident, Dsymbol*& pscopesym, uint32_t flags = 0u); + StructDeclaration* argStruct; + Dsymbol* search(Loc loc, Identifier* ident, Dsymbol*& pscopesym, uint32_t flags = 0u); Scope() : enclosing(), _module(), @@ -7180,10 +7310,10 @@ struct Scope final varDecl(), parent(), slabel(), - sw(), + switchStatement(), tryBody(), - tf(), - os(), + tryFinally(), + scopeGuard(), sbreak(), scontinue(), fes(), @@ -7205,16 +7335,17 @@ struct Scope final inlining(), visibility(Visibility((Visibility::Kind)5u, nullptr)), explicitVisibility(), - stc(), depdecl(), - flags(), + bitFields(0u), + previews(), userAttribDecl(), lastdc(), prevAnchor(), - aliasAsg() + aliasAsg(), + argStruct() { } - Scope(Scope* enclosing, Module* _module = nullptr, ScopeDsymbol* scopesym = nullptr, FuncDeclaration* func = nullptr, VarDeclaration* varDecl = nullptr, Dsymbol* parent = nullptr, LabelStatement* slabel = nullptr, SwitchStatement* sw = nullptr, Statement* tryBody = nullptr, TryFinallyStatement* tf = nullptr, ScopeGuardStatement* os = nullptr, Statement* sbreak = nullptr, Statement* scontinue = nullptr, ForeachStatement* fes = nullptr, Scope* callsc = nullptr, Dsymbol* inunion = nullptr, bool nofree = false, bool inLoop = false, bool inDefaultArg = false, int32_t intypeof = 0, VarDeclaration* lastVar = nullptr, ErrorSink* eSink = nullptr, Module* minst = nullptr, TemplateInstance* tinst = nullptr, CtorFlow ctorflow = CtorFlow(), AlignDeclaration* aligndecl = nullptr, CPPNamespaceDeclaration* namespace_ = nullptr, LINK linkage = (LINK)1u, CPPMANGLE cppmangle = (CPPMANGLE)0u, PragmaDeclaration* inlining = nullptr, Visibility visibility = Visibility((Visibility::Kind)5u, nullptr), int32_t explicitVisibility = 0, uint64_t stc = 0LLU, DeprecatedDeclaration* depdecl = nullptr, uint32_t flags = 0u, UserAttributeDeclaration* userAttribDecl = nullptr, DocComment* lastdc = nullptr, void* anchorCounts = nullptr, Identifier* prevAnchor = nullptr, AliasDeclaration* aliasAsg = nullptr) : + Scope(Scope* enclosing, Module* _module = nullptr, ScopeDsymbol* scopesym = nullptr, FuncDeclaration* func = nullptr, VarDeclaration* varDecl = nullptr, Dsymbol* parent = nullptr, LabelStatement* slabel = nullptr, SwitchStatement* switchStatement = nullptr, Statement* tryBody = nullptr, TryFinallyStatement* tryFinally = nullptr, ScopeGuardStatement* scopeGuard = nullptr, Statement* sbreak = nullptr, Statement* scontinue = nullptr, ForeachStatement* fes = nullptr, Scope* callsc = nullptr, Dsymbol* inunion = nullptr, bool nofree = false, bool inLoop = false, bool inDefaultArg = false, int32_t intypeof = 0, VarDeclaration* lastVar = nullptr, ErrorSink* eSink = nullptr, Module* minst = nullptr, TemplateInstance* tinst = nullptr, CtorFlow ctorflow = CtorFlow(), AlignDeclaration* aligndecl = nullptr, CPPNamespaceDeclaration* namespace_ = nullptr, LINK linkage = (LINK)1u, CPPMANGLE cppmangle = (CPPMANGLE)0u, PragmaDeclaration* inlining = nullptr, Visibility visibility = Visibility((Visibility::Kind)5u, nullptr), int32_t explicitVisibility = 0, STC stc = (STC)0LLU, DeprecatedDeclaration* depdecl = nullptr, uint16_t bitFields = 0u, Previews previews = Previews(), UserAttributeDeclaration* userAttribDecl = nullptr, DocComment* lastdc = nullptr, void* anchorCounts = nullptr, Identifier* prevAnchor = nullptr, AliasDeclaration* aliasAsg = nullptr, StructDeclaration* argStruct = nullptr) : enclosing(enclosing), _module(_module), scopesym(scopesym), @@ -7222,10 +7353,10 @@ struct Scope final varDecl(varDecl), parent(parent), slabel(slabel), - sw(sw), + switchStatement(switchStatement), tryBody(tryBody), - tf(tf), - os(os), + tryFinally(tryFinally), + scopeGuard(scopeGuard), sbreak(sbreak), scontinue(scontinue), fes(fes), @@ -7249,12 +7380,14 @@ struct Scope final explicitVisibility(explicitVisibility), stc(stc), depdecl(depdecl), - flags(flags), + bitFields(bitFields), + previews(previews), userAttribDecl(userAttribDecl), lastdc(lastdc), anchorCounts(anchorCounts), prevAnchor(prevAnchor), - aliasAsg(aliasAsg) + aliasAsg(aliasAsg), + argStruct(argStruct) {} }; @@ -7283,6 +7416,8 @@ class StructDeclaration : public AggregateDeclaration bool hasNoFields(bool v); bool hasCopyCtor() const; bool hasCopyCtor(bool v); + bool hasMoveCtor() const; + bool hasMoveCtor(bool v); bool hasPointerField() const; bool hasPointerField(bool v); bool hasVoidInitPointers() const; @@ -7298,13 +7433,12 @@ class StructDeclaration : public AggregateDeclaration private: uint16_t bitFields; public: - static StructDeclaration* create(const Loc& loc, Identifier* id, bool inObject); + static StructDeclaration* create(Loc loc, Identifier* id, bool inObject); StructDeclaration* syntaxCopy(Dsymbol* s) override; const char* kind() const override; void finalizeSize() final override; bool isPOD(); bool hasCopyConstruction(); - StructDeclaration* isStructDeclaration() final override; void accept(Visitor* v) override; uint32_t numArgTypes() const; Type* argType(uint32_t index); @@ -7316,7 +7450,6 @@ class UnionDeclaration final : public StructDeclaration public: UnionDeclaration* syntaxCopy(Dsymbol* s) override; const char* kind() const override; - UnionDeclaration* isUnionDeclaration() override; void accept(Visitor* v) override; }; @@ -7324,7 +7457,6 @@ class WithScopeSymbol final : public ScopeDsymbol { public: WithStatement* withstate; - WithScopeSymbol* isWithScopeSymbol() override; void accept(Visitor* v) override; }; @@ -7332,7 +7464,6 @@ class ArrayScopeSymbol final : public ScopeDsymbol { public: RootObject* arrayContent; - ArrayScopeSymbol* isArrayScopeSymbol() override; void accept(Visitor* v) override; }; @@ -7341,7 +7472,6 @@ class OverloadSet final : public Dsymbol public: Array a; void push(Dsymbol* s); - OverloadSet* isOverloadSet() override; const char* kind() const override; void accept(Visitor* v) override; }; @@ -7353,7 +7483,6 @@ class ForwardingScopeDsymbol final : public ScopeDsymbol Dsymbol* symtabLookup(Dsymbol* s, Identifier* id) override; void importScope(Dsymbol* s, Visibility visibility) override; const char* kind() const override; - ForwardingScopeDsymbol* isForwardingScopeDsymbol() override; }; class ExpressionDsymbol final : public Dsymbol @@ -7361,7 +7490,6 @@ class ExpressionDsymbol final : public Dsymbol public: Expression* exp; ExpressionDsymbol(Expression* exp); - ExpressionDsymbol* isExpressionDsymbol() override; }; class AliasAssign final : public Dsymbol @@ -7371,7 +7499,6 @@ class AliasAssign final : public Dsymbol Type* type; Dsymbol* aliassym; AliasAssign* syntaxCopy(Dsymbol* s) override; - AliasAssign* isAliasAssign() override; const char* kind() const override; void accept(Visitor* v) override; }; @@ -7392,7 +7519,6 @@ class CAsmDeclaration final : public Dsymbol { public: Expression* code; - CAsmDeclaration* isCAsmDeclaration() override; void accept(Visitor* v) override; }; @@ -7410,6 +7536,41 @@ class ImportAllVisitor : public Visitor void visit(StaticForeachDeclaration* _) override; }; +extern Array* include(Dsymbol* d, Scope* sc); + +class IncludeVisitor : public Visitor +{ +public: + using Visitor::visit; + Scope* sc; + Array* symbols; + IncludeVisitor(Scope* sc); + void visit(AttribDeclaration* ad) override; + void visit(ConditionalDeclaration* cdc) override; + void visit(StaticIfDeclaration* sif) override; + void visit(StaticForeachDeclaration* sfd) override; +}; + +extern void addComment(Dsymbol* d, const char* comment); + +class AddCommentVisitor : public Visitor +{ +public: + using Visitor::visit; + const char* comment; + AddCommentVisitor(const char* comment); + void visit(Dsymbol* d) override; + void visit(AttribDeclaration* atd) override; + void visit(ConditionalDeclaration* cd) override; + void visit(StaticForeachDeclaration* sfd) override; +}; + +extern bool hasStaticCtorOrDtor(Dsymbol* d); + +extern bool isFuncHidden(ClassDeclaration* cd, FuncDeclaration* fd); + +extern void lowerNonArrayAggregate(StaticForeach* sfe, Scope* sc); + class NrvoWalker final : public StatementRewriteWalker { public: @@ -7420,6 +7581,8 @@ class NrvoWalker final : public StatementRewriteWalker void visit(TryFinallyStatement* s) override; }; +extern bool onlyOneMain(FuncDeclaration* fd); + class NOGCVisitor final : public StoppableVisitor { public: @@ -7427,6 +7590,7 @@ class NOGCVisitor final : public StoppableVisitor FuncDeclaration* f; bool checkOnly; bool err; + bool nogcExceptions; void doCond(Expression* exp); void visit(Expression* e) override; void visit(DeclarationExp* e) override; @@ -7468,21 +7632,6 @@ class Objc virtual void checkTupleof(Expression* expression, TypeClass* type) const = 0; }; -template -class PermissiveVisitor : public ParseTimeVisitor -{ -public: - typedef ParseTimeVisitor visit; - virtual void visit(typename AST::Dsymbol ) override; - virtual void visit(typename AST::Parameter ) override; - virtual void visit(typename AST::Statement ) override; - virtual void visit(typename AST::Type ) override; - virtual void visit(typename AST::Expression ) override; - virtual void visit(typename AST::TemplateParameter ) override; - virtual void visit(typename AST::Condition ) override; - virtual void visit(typename AST::Initializer ) override; -}; - struct Target final { enum class OS : uint8_t @@ -7512,6 +7661,7 @@ struct Target final TargetObjC objc; _d_dynamicArray< const char > architectureName; CPU cpu; + bool isAArch64; bool isX86_64; bool isX86; bool isLP64; @@ -7550,7 +7700,7 @@ struct Target final void deinitialize(); uint32_t alignsize(Type* type); uint32_t fieldalign(Type* type); - Type* va_listType(const Loc& loc, Scope* sc); + Type* va_listType(Loc loc, Scope* sc); int32_t isVectorTypeSupported(int32_t sz, Type* type); bool isVectorOpSupported(Type* type, EXP op, Type* t2 = nullptr); LINK systemLinkage(); @@ -7568,7 +7718,7 @@ struct Target final }; public: - Expression* getTargetInfo(const char* name, const Loc& loc); + Expression* getTargetInfo(const char* name, Loc loc); bool isCalleeDestroyingArgs(TypeFunction* tf); bool libraryObjectMonitors(FuncDeclaration* fd, Statement* fbody); bool supportsLinkerDirective() const; @@ -7584,6 +7734,7 @@ struct Target final cpp(), objc(), architectureName(), + isAArch64(), isX86_64(), isX86(), isLP64(), @@ -7598,7 +7749,7 @@ struct Target final params() { } - Target(OS os, uint8_t osMajor = 0u, uint8_t ptrsize = 0u, uint8_t realsize = 0u, uint8_t realpad = 0u, uint8_t realalignsize = 0u, uint8_t classinfosize = 0u, uint64_t maxStaticDataSize = 0LLU, TargetC c = TargetC(), TargetCPP cpp = TargetCPP(), TargetObjC objc = TargetObjC(), _d_dynamicArray< const char > architectureName = {}, CPU cpu = (CPU)0u, bool isX86_64 = false, bool isX86 = false, bool isLP64 = false, _d_dynamicArray< const char > obj_ext = {}, _d_dynamicArray< const char > lib_ext = {}, _d_dynamicArray< const char > dll_ext = {}, bool run_noext = false, FPTypeProperties FloatProperties = FPTypeProperties(), FPTypeProperties DoubleProperties = FPTypeProperties(), FPTypeProperties<_d_real > RealProperties = FPTypeProperties<_d_real >(), Type* tvalist = nullptr, const Param* params = nullptr) : + Target(OS os, uint8_t osMajor = 0u, uint8_t ptrsize = 0u, uint8_t realsize = 0u, uint8_t realpad = 0u, uint8_t realalignsize = 0u, uint8_t classinfosize = 0u, uint64_t maxStaticDataSize = 0LLU, TargetC c = TargetC(), TargetCPP cpp = TargetCPP(), TargetObjC objc = TargetObjC(), _d_dynamicArray< const char > architectureName = {}, CPU cpu = (CPU)0u, bool isAArch64 = false, bool isX86_64 = false, bool isX86 = false, bool isLP64 = false, _d_dynamicArray< const char > obj_ext = {}, _d_dynamicArray< const char > lib_ext = {}, _d_dynamicArray< const char > dll_ext = {}, bool run_noext = false, FPTypeProperties FloatProperties = FPTypeProperties(), FPTypeProperties DoubleProperties = FPTypeProperties(), FPTypeProperties<_d_real > RealProperties = FPTypeProperties<_d_real >(), Type* tvalist = nullptr, const Param* params = nullptr) : os(os), osMajor(osMajor), ptrsize(ptrsize), @@ -7612,6 +7763,7 @@ struct Target final objc(objc), architectureName(architectureName), cpu(cpu), + isAArch64(isAArch64), isX86_64(isX86_64), isX86(isX86), isLP64(isLP64), @@ -7629,7 +7781,7 @@ struct Target final extern Target target; -extern Type* getTypeInfoType(const Loc& loc, Type* t, Scope* sc); +extern Type* getTypeInfoType(Loc loc, Type* t, Scope* sc); class SemanticTimeTransitiveVisitor : public SemanticTimePermissiveVisitor { @@ -7791,6 +7943,251 @@ class SemanticTimeTransitiveVisitor : public SemanticTimePermissiveVisitor void visit(LoweredAssignExp* e) override; }; +template +class PermissiveVisitor : public ParseTimeVisitor +{ +public: + typedef ParseTimeVisitor visit; + virtual void visit(typename AST::Dsymbol ) override; + virtual void visit(typename AST::Parameter ) override; + virtual void visit(typename AST::Statement ) override; + virtual void visit(typename AST::Type ) override; + virtual void visit(typename AST::Expression ) override; + virtual void visit(typename AST::TemplateParameter ) override; + virtual void visit(typename AST::Condition ) override; + virtual void visit(typename AST::Initializer ) override; +}; + +template +class StrictVisitor : public ParseTimeVisitor +{ +public: + typedef ParseTimeVisitor visit; + virtual void visit(typename AST::Dsymbol ) override; + virtual void visit(typename AST::AliasThis ) override; + virtual void visit(typename AST::Declaration ) override; + virtual void visit(typename AST::ScopeDsymbol ) override; + virtual void visit(typename AST::Import ) override; + virtual void visit(typename AST::AttribDeclaration ) override; + virtual void visit(typename AST::StaticAssert ) override; + virtual void visit(typename AST::DebugSymbol ) override; + virtual void visit(typename AST::VersionSymbol ) override; + virtual void visit(typename AST::VarDeclaration ) override; + virtual void visit(typename AST::FuncDeclaration ) override; + virtual void visit(typename AST::AliasDeclaration ) override; + virtual void visit(typename AST::AliasAssign ) override; + virtual void visit(typename AST::TupleDeclaration ) override; + virtual void visit(typename AST::FuncLiteralDeclaration ) override; + virtual void visit(typename AST::PostBlitDeclaration ) override; + virtual void visit(typename AST::CtorDeclaration ) override; + virtual void visit(typename AST::DtorDeclaration ) override; + virtual void visit(typename AST::InvariantDeclaration ) override; + virtual void visit(typename AST::UnitTestDeclaration ) override; + virtual void visit(typename AST::NewDeclaration ) override; + virtual void visit(typename AST::StaticCtorDeclaration ) override; + virtual void visit(typename AST::StaticDtorDeclaration ) override; + virtual void visit(typename AST::SharedStaticCtorDeclaration ) override; + virtual void visit(typename AST::SharedStaticDtorDeclaration ) override; + virtual void visit(typename AST::Package ) override; + virtual void visit(typename AST::EnumDeclaration ) override; + virtual void visit(typename AST::AggregateDeclaration ) override; + virtual void visit(typename AST::TemplateDeclaration ) override; + virtual void visit(typename AST::TemplateInstance ) override; + virtual void visit(typename AST::Nspace ) override; + virtual void visit(typename AST::MixinDeclaration ) override; + virtual void visit(typename AST::UserAttributeDeclaration ) override; + virtual void visit(typename AST::LinkDeclaration ) override; + virtual void visit(typename AST::AnonDeclaration ) override; + virtual void visit(typename AST::AlignDeclaration ) override; + virtual void visit(typename AST::CPPMangleDeclaration ) override; + virtual void visit(typename AST::VisibilityDeclaration ) override; + virtual void visit(typename AST::PragmaDeclaration ) override; + virtual void visit(typename AST::StorageClassDeclaration ) override; + virtual void visit(typename AST::ConditionalDeclaration ) override; + virtual void visit(typename AST::DeprecatedDeclaration ) override; + virtual void visit(typename AST::StaticIfDeclaration ) override; + virtual void visit(typename AST::EnumMember ) override; + virtual void visit(typename AST::Module ) override; + virtual void visit(typename AST::StructDeclaration ) override; + virtual void visit(typename AST::UnionDeclaration ) override; + virtual void visit(typename AST::ClassDeclaration ) override; + virtual void visit(typename AST::InterfaceDeclaration ) override; + virtual void visit(typename AST::TemplateMixin ) override; + virtual void visit(typename AST::Parameter ) override; + virtual void visit(typename AST::Statement ) override; + virtual void visit(typename AST::ImportStatement ) override; + virtual void visit(typename AST::ScopeStatement ) override; + virtual void visit(typename AST::ReturnStatement ) override; + virtual void visit(typename AST::LabelStatement ) override; + virtual void visit(typename AST::StaticAssertStatement ) override; + virtual void visit(typename AST::MixinStatement ) override; + virtual void visit(typename AST::WhileStatement ) override; + virtual void visit(typename AST::ForStatement ) override; + virtual void visit(typename AST::DoStatement ) override; + virtual void visit(typename AST::ForeachRangeStatement ) override; + virtual void visit(typename AST::ForeachStatement ) override; + virtual void visit(typename AST::IfStatement ) override; + virtual void visit(typename AST::ScopeGuardStatement ) override; + virtual void visit(typename AST::ConditionalStatement ) override; + virtual void visit(typename AST::PragmaStatement ) override; + virtual void visit(typename AST::SwitchStatement ) override; + virtual void visit(typename AST::CaseRangeStatement ) override; + virtual void visit(typename AST::CaseStatement ) override; + virtual void visit(typename AST::DefaultStatement ) override; + virtual void visit(typename AST::BreakStatement ) override; + virtual void visit(typename AST::ContinueStatement ) override; + virtual void visit(typename AST::GotoDefaultStatement ) override; + virtual void visit(typename AST::GotoCaseStatement ) override; + virtual void visit(typename AST::GotoStatement ) override; + virtual void visit(typename AST::SynchronizedStatement ) override; + virtual void visit(typename AST::WithStatement ) override; + virtual void visit(typename AST::TryCatchStatement ) override; + virtual void visit(typename AST::TryFinallyStatement ) override; + virtual void visit(typename AST::ThrowStatement ) override; + virtual void visit(typename AST::AsmStatement ) override; + virtual void visit(typename AST::ExpStatement ) override; + virtual void visit(typename AST::CompoundStatement ) override; + virtual void visit(typename AST::CompoundDeclarationStatement ) override; + virtual void visit(typename AST::CompoundAsmStatement ) override; + virtual void visit(typename AST::InlineAsmStatement ) override; + virtual void visit(typename AST::Type ) override; + virtual void visit(typename AST::TypeBasic ) override; + virtual void visit(typename AST::TypeError ) override; + virtual void visit(typename AST::TypeNull ) override; + virtual void visit(typename AST::TypeNoreturn ) override; + virtual void visit(typename AST::TypeVector ) override; + virtual void visit(typename AST::TypeEnum ) override; + virtual void visit(typename AST::TypeTuple ) override; + virtual void visit(typename AST::TypeClass ) override; + virtual void visit(typename AST::TypeStruct ) override; + virtual void visit(typename AST::TypeNext ) override; + virtual void visit(typename AST::TypeReference ) override; + virtual void visit(typename AST::TypeSlice ) override; + virtual void visit(typename AST::TypeDelegate ) override; + virtual void visit(typename AST::TypePointer ) override; + virtual void visit(typename AST::TypeFunction ) override; + virtual void visit(typename AST::TypeArray ) override; + virtual void visit(typename AST::TypeDArray ) override; + virtual void visit(typename AST::TypeAArray ) override; + virtual void visit(typename AST::TypeSArray ) override; + virtual void visit(typename AST::TypeQualified ) override; + virtual void visit(typename AST::TypeTraits ) override; + virtual void visit(typename AST::TypeMixin ) override; + virtual void visit(typename AST::TypeIdentifier ) override; + virtual void visit(typename AST::TypeReturn ) override; + virtual void visit(typename AST::TypeTypeof ) override; + virtual void visit(typename AST::TypeInstance ) override; + virtual void visit(typename AST::Expression ) override; + virtual void visit(typename AST::DeclarationExp ) override; + virtual void visit(typename AST::IntegerExp ) override; + virtual void visit(typename AST::NewAnonClassExp ) override; + virtual void visit(typename AST::IsExp ) override; + virtual void visit(typename AST::RealExp ) override; + virtual void visit(typename AST::NullExp ) override; + virtual void visit(typename AST::TypeidExp ) override; + virtual void visit(typename AST::TraitsExp ) override; + virtual void visit(typename AST::StringExp ) override; + virtual void visit(typename AST::InterpExp ) override; + virtual void visit(typename AST::NewExp ) override; + virtual void visit(typename AST::AssocArrayLiteralExp ) override; + virtual void visit(typename AST::ArrayLiteralExp ) override; + virtual void visit(typename AST::FuncExp ) override; + virtual void visit(typename AST::IntervalExp ) override; + virtual void visit(typename AST::TypeExp ) override; + virtual void visit(typename AST::ScopeExp ) override; + virtual void visit(typename AST::IdentifierExp ) override; + virtual void visit(typename AST::UnaExp ) override; + virtual void visit(typename AST::DefaultInitExp ) override; + virtual void visit(typename AST::BinExp ) override; + virtual void visit(typename AST::DsymbolExp ) override; + virtual void visit(typename AST::TemplateExp ) override; + virtual void visit(typename AST::SymbolExp ) override; + virtual void visit(typename AST::VarExp ) override; + virtual void visit(typename AST::TupleExp ) override; + virtual void visit(typename AST::DollarExp ) override; + virtual void visit(typename AST::ThisExp ) override; + virtual void visit(typename AST::SuperExp ) override; + virtual void visit(typename AST::AddrExp ) override; + virtual void visit(typename AST::PreExp ) override; + virtual void visit(typename AST::PtrExp ) override; + virtual void visit(typename AST::NegExp ) override; + virtual void visit(typename AST::UAddExp ) override; + virtual void visit(typename AST::NotExp ) override; + virtual void visit(typename AST::ComExp ) override; + virtual void visit(typename AST::DeleteExp ) override; + virtual void visit(typename AST::CastExp ) override; + virtual void visit(typename AST::CallExp ) override; + virtual void visit(typename AST::DotIdExp ) override; + virtual void visit(typename AST::AssertExp ) override; + virtual void visit(typename AST::ThrowExp ) override; + virtual void visit(typename AST::MixinExp ) override; + virtual void visit(typename AST::ImportExp ) override; + virtual void visit(typename AST::DotTemplateInstanceExp ) override; + virtual void visit(typename AST::ArrayExp ) override; + virtual void visit(typename AST::FuncInitExp ) override; + virtual void visit(typename AST::PrettyFuncInitExp ) override; + virtual void visit(typename AST::FileInitExp ) override; + virtual void visit(typename AST::LineInitExp ) override; + virtual void visit(typename AST::ModuleInitExp ) override; + virtual void visit(typename AST::CommaExp ) override; + virtual void visit(typename AST::PostExp ) override; + virtual void visit(typename AST::PowExp ) override; + virtual void visit(typename AST::MulExp ) override; + virtual void visit(typename AST::DivExp ) override; + virtual void visit(typename AST::ModExp ) override; + virtual void visit(typename AST::AddExp ) override; + virtual void visit(typename AST::MinExp ) override; + virtual void visit(typename AST::CatExp ) override; + virtual void visit(typename AST::ShlExp ) override; + virtual void visit(typename AST::ShrExp ) override; + virtual void visit(typename AST::UshrExp ) override; + virtual void visit(typename AST::EqualExp ) override; + virtual void visit(typename AST::InExp ) override; + virtual void visit(typename AST::IdentityExp ) override; + virtual void visit(typename AST::CmpExp ) override; + virtual void visit(typename AST::AndExp ) override; + virtual void visit(typename AST::XorExp ) override; + virtual void visit(typename AST::OrExp ) override; + virtual void visit(typename AST::LogicalExp ) override; + virtual void visit(typename AST::CondExp ) override; + virtual void visit(typename AST::AssignExp ) override; + virtual void visit(typename AST::BinAssignExp ) override; + virtual void visit(typename AST::AddAssignExp ) override; + virtual void visit(typename AST::MinAssignExp ) override; + virtual void visit(typename AST::MulAssignExp ) override; + virtual void visit(typename AST::DivAssignExp ) override; + virtual void visit(typename AST::ModAssignExp ) override; + virtual void visit(typename AST::PowAssignExp ) override; + virtual void visit(typename AST::AndAssignExp ) override; + virtual void visit(typename AST::OrAssignExp ) override; + virtual void visit(typename AST::XorAssignExp ) override; + virtual void visit(typename AST::ShlAssignExp ) override; + virtual void visit(typename AST::ShrAssignExp ) override; + virtual void visit(typename AST::UshrAssignExp ) override; + virtual void visit(typename AST::CatAssignExp ) override; + virtual void visit(typename AST::CatElemAssignExp ) override; + virtual void visit(typename AST::CatDcharAssignExp ) override; + virtual void visit(typename AST::GenericExp ) override; + virtual void visit(typename AST::TemplateParameter ) override; + virtual void visit(typename AST::TemplateAliasParameter ) override; + virtual void visit(typename AST::TemplateTypeParameter ) override; + virtual void visit(typename AST::TemplateTupleParameter ) override; + virtual void visit(typename AST::TemplateValueParameter ) override; + virtual void visit(typename AST::TemplateThisParameter ) override; + virtual void visit(typename AST::Condition ) override; + virtual void visit(typename AST::StaticIfCondition ) override; + virtual void visit(typename AST::DVCondition ) override; + virtual void visit(typename AST::DebugCondition ) override; + virtual void visit(typename AST::VersionCondition ) override; + virtual void visit(typename AST::Initializer ) override; + virtual void visit(typename AST::ExpInitializer ) override; + virtual void visit(typename AST::StructInitializer ) override; + virtual void visit(typename AST::ArrayInitializer ) override; + virtual void visit(typename AST::VoidInitializer ) override; + virtual void visit(typename AST::DefaultInitializer ) override; + virtual void visit(typename AST::CInitializer ) override; +}; + extern _d_real creall(complex_t x); extern _d_real cimagl(complex_t x); @@ -7847,30 +8244,28 @@ extern bool c_isxdigit(const int32_t c); extern bool c_isalnum(const int32_t c); -extern void error(const Loc& loc, const char* format, ...); +extern void error(Loc loc, const char* format, ...); extern void error(const char* filename, uint32_t linnum, uint32_t charnum, const char* format, ...); -extern void errorSupplemental(const Loc& loc, const char* format, ...); +extern void errorBackend(const char* filename, uint32_t linnum, uint32_t charnum, const char* format, ...); + +extern void errorSupplemental(Loc loc, const char* format, ...); -extern void warning(const Loc& loc, const char* format, ...); +extern void warning(Loc loc, const char* format, ...); -extern void warningSupplemental(const Loc& loc, const char* format, ...); +extern void warningSupplemental(Loc loc, const char* format, ...); -extern void deprecation(const Loc& loc, const char* format, ...); +extern void deprecation(Loc loc, const char* format, ...); -extern void deprecationSupplemental(const Loc& loc, const char* format, ...); +extern void deprecationSupplemental(Loc loc, const char* format, ...); -extern void message(const Loc& loc, const char* format, ...); +extern void message(Loc loc, const char* format, ...); extern void message(const char* format, ...); extern void tip(const char* format, ...); -extern void verrorReport(const Loc& loc, const char* format, va_list ap, ErrorKind kind, const char* p1 = nullptr, const char* p2 = nullptr); - -extern void verrorReportSupplemental(const Loc& loc, const char* format, va_list ap, ErrorKind kind); - extern void fatal(); extern void halt(); @@ -7956,10 +8351,10 @@ struct Verbose final bool complex; bool vin; bool showGaggedErrors; - bool printErrorContext; bool logo; bool color; bool cov; + ErrorPrintMode errorPrintMode; MessageStyle messageStyle; uint32_t errorLimit; uint32_t errorSupplementLimit; @@ -7975,7 +8370,6 @@ struct Verbose final complex(true), vin(), showGaggedErrors(), - printErrorContext(), logo(), color(), cov(), @@ -7984,7 +8378,7 @@ struct Verbose final errorSupplementLimit(6u) { } - Verbose(bool verbose, bool showColumns = false, bool tls = false, bool templates = false, bool templatesListInstances = false, bool gc = false, bool field = false, bool complex = true, bool vin = false, bool showGaggedErrors = false, bool printErrorContext = false, bool logo = false, bool color = false, bool cov = false, MessageStyle messageStyle = (MessageStyle)0u, uint32_t errorLimit = 20u, uint32_t errorSupplementLimit = 6u) : + Verbose(bool verbose, bool showColumns = false, bool tls = false, bool templates = false, bool templatesListInstances = false, bool gc = false, bool field = false, bool complex = true, bool vin = false, bool showGaggedErrors = false, bool logo = false, bool color = false, bool cov = false, ErrorPrintMode errorPrintMode = (ErrorPrintMode)0u, MessageStyle messageStyle = (MessageStyle)0u, uint32_t errorLimit = 20u, uint32_t errorSupplementLimit = 6u) : verbose(verbose), showColumns(showColumns), tls(tls), @@ -7995,19 +8389,32 @@ struct Verbose final complex(complex), vin(vin), showGaggedErrors(showGaggedErrors), - printErrorContext(printErrorContext), logo(logo), color(color), cov(cov), + errorPrintMode(errorPrintMode), messageStyle(messageStyle), errorLimit(errorLimit), errorSupplementLimit(errorSupplementLimit) {} }; +struct ImportPathInfo final +{ + const char* path; + ImportPathInfo() : + path() + { + } + ImportPathInfo(const char* path) : + path(path) + {} +}; + struct Param final { bool obj; + bool readStdin; bool multiobj; bool trace; bool tracegc; @@ -8017,7 +8424,7 @@ struct Param final bool useInline; bool release; bool preservePaths; - DiagnosticReporting warnings; + DiagnosticReporting useWarnings; bool cov; uint8_t covPercent; bool ctfe_cov; @@ -8040,6 +8447,7 @@ struct Param final FeatureState fieldwise; bool fixAliasThis; FeatureState rvalueRefParam; + FeatureState safer; FeatureState noSharedAccess; bool previewIn; bool inclusiveInContracts; @@ -8060,7 +8468,7 @@ struct Param final CLIIdentifierTable cIdentifierTable; _d_dynamicArray< const char > argv0; Array modFileAliasStrings; - Array imppath; + Array imppath; Array fileImppath; _d_dynamicArray< const char > objdir; _d_dynamicArray< const char > objname; @@ -8073,8 +8481,7 @@ struct Param final Output makeDeps; Output mixinOut; Output moduleDeps; - uint32_t debuglevel; - uint32_t versionlevel; + bool debugEnabled; bool run; Array runargs; Array cppswitches; @@ -8088,9 +8495,14 @@ struct Param final _d_dynamicArray< const char > resfile; _d_dynamicArray< const char > exefile; _d_dynamicArray< const char > mapfile; + bool fullyQualifiedObjectFiles; + bool timeTrace; + uint32_t timeTraceGranularityUs; + const char* timeTraceFile; bool parsingUnittestsRequired(); Param() : obj(true), + readStdin(), multiobj(), trace(), tracegc(), @@ -8100,7 +8512,7 @@ struct Param final useInline(false), release(), preservePaths(), - warnings((DiagnosticReporting)2u), + useWarnings((DiagnosticReporting)2u), cov(), covPercent(), ctfe_cov(false), @@ -8149,8 +8561,7 @@ struct Param final makeDeps(), mixinOut(), moduleDeps(), - debuglevel(), - versionlevel(), + debugEnabled(), run(), runargs(), cppswitches(), @@ -8163,11 +8574,16 @@ struct Param final deffile(), resfile(), exefile(), - mapfile() + mapfile(), + fullyQualifiedObjectFiles(), + timeTrace(false), + timeTraceGranularityUs(500u), + timeTraceFile() { } - Param(bool obj, bool multiobj = false, bool trace = false, bool tracegc = false, bool vcg_ast = false, DiagnosticReporting useDeprecated = (DiagnosticReporting)1u, bool useUnitTests = false, bool useInline = false, bool release = false, bool preservePaths = false, DiagnosticReporting warnings = (DiagnosticReporting)2u, bool cov = false, uint8_t covPercent = 0u, bool ctfe_cov = false, bool ignoreUnsupportedPragmas = true, bool useModuleInfo = true, bool useTypeInfo = true, bool useExceptions = true, bool useGC = true, bool betterC = false, bool addMain = false, bool allInst = false, bool bitfields = false, CppStdRevision cplusplus = (CppStdRevision)201103u, Help help = Help(), Verbose v = Verbose(), FeatureState useDIP25 = (FeatureState)2u, FeatureState useDIP1000 = (FeatureState)0u, bool ehnogc = false, bool useDIP1021 = false, FeatureState fieldwise = (FeatureState)0u, bool fixAliasThis = false, FeatureState rvalueRefParam = (FeatureState)0u, FeatureState noSharedAccess = (FeatureState)0u, bool previewIn = false, bool inclusiveInContracts = false, bool shortenedMethods = true, bool fixImmutableConv = false, bool fix16997 = true, FeatureState dtorFields = (FeatureState)0u, FeatureState systemVariables = (FeatureState)0u, CHECKENABLE useInvariants = (CHECKENABLE)0u, CHECKENABLE useIn = (CHECKENABLE)0u, CHECKENABLE useOut = (CHECKENABLE)0u, CHECKENABLE useArrayBounds = (CHECKENABLE)0u, CHECKENABLE useAssert = (CHECKENABLE)0u, CHECKENABLE useSwitchError = (CHECKENABLE)0u, CHECKENABLE boundscheck = (CHECKENABLE)0u, CHECKACTION checkAction = (CHECKACTION)0u, CLIIdentifierTable dIdentifierTable = (CLIIdentifierTable)0u, CLIIdentifierTable cIdentifierTable = (CLIIdentifierTable)0u, _d_dynamicArray< const char > argv0 = {}, Array modFileAliasStrings = Array(), Array imppath = Array(), Array fileImppath = Array(), _d_dynamicArray< const char > objdir = {}, _d_dynamicArray< const char > objname = {}, _d_dynamicArray< const char > libname = {}, Output ddoc = Output(), Output dihdr = Output(), Output cxxhdr = Output(), Output json = Output(), JsonFieldFlags jsonFieldFlags = (JsonFieldFlags)0u, Output makeDeps = Output(), Output mixinOut = Output(), Output moduleDeps = Output(), uint32_t debuglevel = 0u, uint32_t versionlevel = 0u, bool run = false, Array runargs = Array(), Array cppswitches = Array(), const char* cpp = nullptr, Array objfiles = Array(), Array linkswitches = Array(), Array linkswitchIsForCC = Array(), Array libfiles = Array(), Array dllfiles = Array(), _d_dynamicArray< const char > deffile = {}, _d_dynamicArray< const char > resfile = {}, _d_dynamicArray< const char > exefile = {}, _d_dynamicArray< const char > mapfile = {}) : + Param(bool obj, bool readStdin = false, bool multiobj = false, bool trace = false, bool tracegc = false, bool vcg_ast = false, DiagnosticReporting useDeprecated = (DiagnosticReporting)1u, bool useUnitTests = false, bool useInline = false, bool release = false, bool preservePaths = false, DiagnosticReporting useWarnings = (DiagnosticReporting)2u, bool cov = false, uint8_t covPercent = 0u, bool ctfe_cov = false, bool ignoreUnsupportedPragmas = true, bool useModuleInfo = true, bool useTypeInfo = true, bool useExceptions = true, bool useGC = true, bool betterC = false, bool addMain = false, bool allInst = false, bool bitfields = false, CppStdRevision cplusplus = (CppStdRevision)201103u, Help help = Help(), Verbose v = Verbose(), FeatureState useDIP25 = (FeatureState)2u, FeatureState useDIP1000 = (FeatureState)0u, bool ehnogc = false, bool useDIP1021 = false, FeatureState fieldwise = (FeatureState)0u, bool fixAliasThis = false, FeatureState rvalueRefParam = (FeatureState)0u, FeatureState safer = (FeatureState)0u, FeatureState noSharedAccess = (FeatureState)0u, bool previewIn = false, bool inclusiveInContracts = false, bool shortenedMethods = true, bool fixImmutableConv = false, bool fix16997 = true, FeatureState dtorFields = (FeatureState)0u, FeatureState systemVariables = (FeatureState)0u, CHECKENABLE useInvariants = (CHECKENABLE)0u, CHECKENABLE useIn = (CHECKENABLE)0u, CHECKENABLE useOut = (CHECKENABLE)0u, CHECKENABLE useArrayBounds = (CHECKENABLE)0u, CHECKENABLE useAssert = (CHECKENABLE)0u, CHECKENABLE useSwitchError = (CHECKENABLE)0u, CHECKENABLE boundscheck = (CHECKENABLE)0u, CHECKACTION checkAction = (CHECKACTION)0u, CLIIdentifierTable dIdentifierTable = (CLIIdentifierTable)0u, CLIIdentifierTable cIdentifierTable = (CLIIdentifierTable)0u, _d_dynamicArray< const char > argv0 = {}, Array modFileAliasStrings = Array(), Array imppath = Array(), Array fileImppath = Array(), _d_dynamicArray< const char > objdir = {}, _d_dynamicArray< const char > objname = {}, _d_dynamicArray< const char > libname = {}, Output ddoc = Output(), Output dihdr = Output(), Output cxxhdr = Output(), Output json = Output(), JsonFieldFlags jsonFieldFlags = (JsonFieldFlags)0u, Output makeDeps = Output(), Output mixinOut = Output(), Output moduleDeps = Output(), bool debugEnabled = false, bool run = false, Array runargs = Array(), Array cppswitches = Array(), const char* cpp = nullptr, Array objfiles = Array(), Array linkswitches = Array(), Array linkswitchIsForCC = Array(), Array libfiles = Array(), Array dllfiles = Array(), _d_dynamicArray< const char > deffile = {}, _d_dynamicArray< const char > resfile = {}, _d_dynamicArray< const char > exefile = {}, _d_dynamicArray< const char > mapfile = {}, bool fullyQualifiedObjectFiles = false, bool timeTrace = false, uint32_t timeTraceGranularityUs = 500u, const char* timeTraceFile = nullptr) : obj(obj), + readStdin(readStdin), multiobj(multiobj), trace(trace), tracegc(tracegc), @@ -8177,7 +8593,7 @@ struct Param final useInline(useInline), release(release), preservePaths(preservePaths), - warnings(warnings), + useWarnings(useWarnings), cov(cov), covPercent(covPercent), ctfe_cov(ctfe_cov), @@ -8200,6 +8616,7 @@ struct Param final fieldwise(fieldwise), fixAliasThis(fixAliasThis), rvalueRefParam(rvalueRefParam), + safer(safer), noSharedAccess(noSharedAccess), previewIn(previewIn), inclusiveInContracts(inclusiveInContracts), @@ -8233,8 +8650,7 @@ struct Param final makeDeps(makeDeps), mixinOut(mixinOut), moduleDeps(moduleDeps), - debuglevel(debuglevel), - versionlevel(versionlevel), + debugEnabled(debugEnabled), run(run), runargs(runargs), cppswitches(cppswitches), @@ -8247,7 +8663,11 @@ struct Param final deffile(deffile), resfile(resfile), exefile(exefile), - mapfile(mapfile) + mapfile(mapfile), + fullyQualifiedObjectFiles(fullyQualifiedObjectFiles), + timeTrace(timeTrace), + timeTraceGranularityUs(timeTraceGranularityUs), + timeTraceFile(timeTraceFile) {} }; @@ -8256,7 +8676,8 @@ struct Global final _d_dynamicArray< const char > inifilename; _d_dynamicArray< const char > copyright; _d_dynamicArray< const char > written; - Array path; + Array path; + Array importPaths; Array filePath; char datetime[26LLU]; CompileEnv compileEnv; @@ -8277,18 +8698,20 @@ struct Global final ErrorSink* errorSink; ErrorSink* errorSinkNull; - DArray(*preprocess)(FileName , const Loc& , OutBuffer& ); + DArray(*preprocess)(FileName , Loc , OutBuffer& ); uint32_t startGagging(); bool endGagging(uint32_t oldGagged); void increaseErrorCount(); void _init(); + void plugErrorSinks(); uint32_t versionNumber(); const char* const versionChars(); Global() : inifilename(), - copyright(73, "Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved"), + copyright(73, "Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved"), written(24, "written by Walter Bright"), path(), + importPaths(), filePath(), compileEnv(), params(), @@ -8309,11 +8732,12 @@ struct Global final preprocess() { } - Global(_d_dynamicArray< const char > inifilename, _d_dynamicArray< const char > copyright = { 73, "Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved" }, _d_dynamicArray< const char > written = { 24, "written by Walter Bright" }, Array path = Array(), Array filePath = Array(), CompileEnv compileEnv = CompileEnv(), Param params = Param(), uint32_t errors = 0u, uint32_t deprecations = 0u, uint32_t warnings = 0u, uint32_t gag = 0u, uint32_t gaggedErrors = 0u, uint32_t gaggedWarnings = 0u, void* console = nullptr, Array versionids = Array(), Array debugids = Array(), bool hasMainFunction = false, uint32_t varSequenceNumber = 1u, FileManager* fileManager = nullptr, ErrorSink* errorSink = nullptr, ErrorSink* errorSinkNull = nullptr, DArray(*preprocess)(FileName , const Loc& , OutBuffer& ) = nullptr) : + Global(_d_dynamicArray< const char > inifilename, _d_dynamicArray< const char > copyright = { 73, "Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved" }, _d_dynamicArray< const char > written = { 24, "written by Walter Bright" }, Array path = Array(), Array importPaths = Array(), Array filePath = Array(), CompileEnv compileEnv = CompileEnv(), Param params = Param(), uint32_t errors = 0u, uint32_t deprecations = 0u, uint32_t warnings = 0u, uint32_t gag = 0u, uint32_t gaggedErrors = 0u, uint32_t gaggedWarnings = 0u, void* console = nullptr, Array versionids = Array(), Array debugids = Array(), bool hasMainFunction = false, uint32_t varSequenceNumber = 1u, FileManager* fileManager = nullptr, ErrorSink* errorSink = nullptr, ErrorSink* errorSinkNull = nullptr, DArray(*preprocess)(FileName , Loc , OutBuffer& ) = nullptr) : inifilename(inifilename), copyright(copyright), written(written), path(path), + importPaths(importPaths), filePath(filePath), compileEnv(compileEnv), params(params), @@ -8423,6 +8847,7 @@ struct Id final static Identifier* xopEquals; static Identifier* xopCmp; static Identifier* xtoHash; + static Identifier* Entry; static Identifier* LINE; static Identifier* FILE; static Identifier* MODULE; @@ -8466,60 +8891,15 @@ struct Id final static Identifier* system; static Identifier* disable; static Identifier* _dollar; - static Identifier* uadd; - static Identifier* neg; - static Identifier* com; - static Identifier* add; - static Identifier* add_r; - static Identifier* sub; - static Identifier* sub_r; - static Identifier* mul; - static Identifier* mul_r; - static Identifier* div; - static Identifier* div_r; - static Identifier* mod; - static Identifier* mod_r; - static Identifier* eq; - static Identifier* cmp; - static Identifier* iand; - static Identifier* iand_r; - static Identifier* ior; - static Identifier* ior_r; - static Identifier* ixor; - static Identifier* ixor_r; - static Identifier* shl; - static Identifier* shl_r; - static Identifier* shr; - static Identifier* shr_r; - static Identifier* ushr; - static Identifier* ushr_r; - static Identifier* cat; - static Identifier* cat_r; - static Identifier* assign; - static Identifier* addass; - static Identifier* subass; - static Identifier* mulass; - static Identifier* divass; - static Identifier* modass; - static Identifier* andass; - static Identifier* orass; - static Identifier* xorass; - static Identifier* shlass; - static Identifier* shrass; - static Identifier* ushrass; - static Identifier* catass; - static Identifier* postinc; - static Identifier* postdec; - static Identifier* index; - static Identifier* indexass; - static Identifier* slice; - static Identifier* sliceass; - static Identifier* call; - static Identifier* _cast; - static Identifier* opIn; - static Identifier* opIn_r; - static Identifier* opStar; - static Identifier* opDot; + static Identifier* opEquals; + static Identifier* opCmp; + static Identifier* opAssign; + static Identifier* opIndex; + static Identifier* opIndexAssign; + static Identifier* opSlice; + static Identifier* opSliceAssign; + static Identifier* opCall; + static Identifier* opCast; static Identifier* opDispatch; static Identifier* opDollar; static Identifier* opUnary; @@ -8530,9 +8910,6 @@ struct Id final static Identifier* opOpAssign; static Identifier* opIndexOpAssign; static Identifier* opSliceOpAssign; - static Identifier* pow; - static Identifier* pow_r; - static Identifier* powass; static Identifier* classNew; static Identifier* classDelete; static Identifier* apply; @@ -8696,6 +9073,8 @@ struct Id final static Identifier* hasMember; static Identifier* identifier; static Identifier* fullyQualifiedName; + static Identifier* getBitfieldOffset; + static Identifier* getBitfieldWidth; static Identifier* getProtection; static Identifier* getVisibility; static Identifier* parent; @@ -8727,6 +9106,7 @@ struct Id final static Identifier* getLocation; static Identifier* hasPostblit; static Identifier* hasCopyConstructor; + static Identifier* hasMoveConstructor; static Identifier* isCopyable; static Identifier* toType; static Identifier* parameters; diff --git a/dmd/func.d b/dmd/func.d index ab60d28f71..c0b48bc69e 100644 --- a/dmd/func.d +++ b/dmd/func.d @@ -8,12 +8,12 @@ * - `invariant` * - `unittest` * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/func.d, _func.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/func.d, _func.d) * Documentation: https://dlang.org/phobos/dmd_func.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/func.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/func.d */ module dmd.func; @@ -29,15 +29,14 @@ import dmd.dcast; import dmd.dclass; import dmd.declaration; import dmd.delegatize; -import dmd.dinterpret; import dmd.dmodule; import dmd.dscope; import dmd.dstruct; import dmd.dsymbol; import dmd.dtemplate; -import dmd.errors; import dmd.escape; import dmd.expression; +import dmd.funcsem : overloadApply; import dmd.globals; import dmd.hdrgen; import dmd.id; @@ -46,17 +45,12 @@ import dmd.init; import dmd.location; import dmd.mtype; import dmd.objc; -import dmd.root.aav; import dmd.common.outbuffer; import dmd.rootobject; import dmd.root.string; import dmd.root.stringtable; -import dmd.semantic2; -import dmd.semantic3; -import dmd.statement_rewrite_walker; import dmd.statement; import dmd.tokens; -import dmd.typesem; import dmd.visitor; version (IN_GCC) {} @@ -149,9 +143,9 @@ private struct FUNCFLAG bool safetyInprocess; /// working on determining safety bool nothrowInprocess; /// working on determining nothrow bool nogcInprocess; /// working on determining @nogc - bool returnInprocess; /// working on inferring 'return' for parameters + bool saferD; /// do -preview=safer checks if this function has default safety + bool scopeInprocess; /// infer `return` and `scope` for parameters bool inlineScanned; /// function has been scanned for inline possibilities - bool inferScope; /// infer 'scope' for parameters bool hasCatches; /// function has try-catch statements bool skipCodegen; /// do not generate code for this function. bool printf; /// is a printf-like function @@ -174,6 +168,10 @@ private struct FUNCFLAG bool computedEscapingSiblings; /// `hasEscapingSiblings` has been computed bool dllImport; /// __declspec(dllimport) bool dllExport; /// __declspec(dllexport) + + bool hasReturnExp; /// Has return exp; statement + bool hasInlineAsm; /// Has asm{} statement + bool hasMultipleReturnExp; /// Has multiple return exp; statements } /*********************************************************** @@ -195,15 +193,12 @@ extern (C++) struct Ensure */ static Ensures* arraySyntaxCopy(Ensures* a) { - Ensures* b = null; - if (a) - { - b = a.copy(); - foreach (i, e; *a) - { - (*b)[i] = e.syntaxCopy(); - } - } + if (!a) + return null; + + Ensures* b = a.copy(); + foreach (i, e; *a) + (*b)[i] = e.syntaxCopy(); return b; } @@ -285,17 +280,10 @@ version (IN_LLVM) */ Type tintro; - StorageClass storage_class2; /// storage class for template onemember's + STC storage_class2; /// storage class for template onemember's // Things that should really go into Scope - /// 1 if there's a return exp; statement - /// 2 if there's a throw statement - /// 4 if there's an assert(0) - /// 8 if there's inline asm - /// 16 if there are multiple return statements - int hasReturnExp; - VarDeclaration nrvo_var; /// variable to replace with shidden version (IN_LLVM) {} else { @@ -330,10 +318,14 @@ version (IN_LLVM) {} else */ VarDeclarations outerVars; + // Most recent encountered `main` (`WinMain` or `DllMain`) function. + // Track it to give error messages for multiple entrypoints + __gshared FuncDeclaration lastMain; + /// Sibling nested functions which called this one FuncDeclarations siblingCallers; - FuncDeclarations *inlinedNestedCallees; + FuncDeclarations* inlinedNestedCallees; /// In case of failed `@safe` inference, store the error that made the function `@system` for /// better diagnostics @@ -352,9 +344,9 @@ version (IN_LLVM) {} else */ ObjcFuncDeclaration objc; - extern (D) this(const ref Loc loc, const ref Loc endloc, Identifier ident, StorageClass storage_class, Type type, bool noreturn = false) + extern (D) this(Loc loc, Loc endloc, Identifier ident, STC storage_class, Type type, bool noreturn = false) { - super(loc, ident); + super(DSYM.funcDeclaration, loc, ident); //.printf("FuncDeclaration(id = '%s', type = %s)\n", ident.toChars(), type.toChars()); //.printf("storage_class = x%llx\n", storage_class); this.storage_class = storage_class; @@ -376,9 +368,9 @@ version (IN_LLVM) {} else this.inferRetType = true; } - static FuncDeclaration create(const ref Loc loc, const ref Loc endloc, Identifier id, StorageClass storage_class, Type type, bool noreturn = false) + static FuncDeclaration create(Loc loc, Loc endloc, Identifier id, StorageClass storage_class, Type type, bool noreturn = false) { - return new FuncDeclaration(loc, endloc, id, storage_class, type, noreturn); + return new FuncDeclaration(loc, endloc, id, cast(STC) storage_class, type, noreturn); } final nothrow pure @safe @@ -439,40 +431,38 @@ version (IN_LLVM) if (this == o) return true; - if (auto s = isDsymbol(o)) - { - auto fd1 = this; - auto fd2 = s.isFuncDeclaration(); - if (!fd2) - return false; + auto s = isDsymbol(o); + if (!s) + return false; - auto fa1 = fd1.isFuncAliasDeclaration(); - auto faf1 = fa1 ? fa1.toAliasFunc() : fd1; + auto fd1 = this; + auto fd2 = s.isFuncDeclaration(); + if (!fd2) + return false; - auto fa2 = fd2.isFuncAliasDeclaration(); - auto faf2 = fa2 ? fa2.toAliasFunc() : fd2; + auto fa1 = fd1.isFuncAliasDeclaration(); + auto faf1 = fa1 ? fa1.toAliasFunc() : fd1; - if (fa1 && fa2) - { - return faf1.equals(faf2) && fa1.hasOverloads == fa2.hasOverloads; - } + auto fa2 = fd2.isFuncAliasDeclaration(); + auto faf2 = fa2 ? fa2.toAliasFunc() : fd2; - bool b1 = fa1 !is null; - if (b1 && faf1.isUnique() && !fa1.hasOverloads) - b1 = false; + if (fa1 && fa2) + return faf1.equals(faf2) && fa1.hasOverloads == fa2.hasOverloads; - bool b2 = fa2 !is null; - if (b2 && faf2.isUnique() && !fa2.hasOverloads) - b2 = false; + bool b1 = fa1 !is null; + if (b1 && faf1.isUnique() && !fa1.hasOverloads) + b1 = false; - if (b1 != b2) - return false; + bool b2 = fa2 !is null; + if (b2 && faf2.isUnique() && !fa2.hasOverloads) + b2 = false; - return faf1.toParent().equals(faf2.toParent()) && - faf1.ident.equals(faf2.ident) && - faf1.type.equals(faf2.type); - } - return false; + if (b1 != b2) + return false; + + return faf1.toParent().equals(faf2.toParent()) && + faf1.ident.equals(faf2.ident) && + faf1.type.equals(faf2.type); } /**************************************************** @@ -483,8 +473,7 @@ version (IN_LLVM) { //printf("FuncDeclaration::overloadInsert(s = %s) this = %s\n", s.toChars(), toChars()); assert(s != this); - AliasDeclaration ad = s.isAliasDeclaration(); - if (ad) + if (AliasDeclaration ad = s.isAliasDeclaration()) { if (overnext) return overnext.overloadInsert(ad); @@ -511,26 +500,6 @@ version (IN_LLVM) if (!fd) return false; - version (none) - { - /* Disable this check because: - * const void foo(); - * semantic() isn't run yet on foo(), so the const hasn't been - * applied yet. - */ - if (type) - { - printf("type = %s\n", type.toChars()); - printf("fd.type = %s\n", fd.type.toChars()); - } - // fd.type can be NULL for overloaded constructors - if (type && fd.type && fd.type.covariant(type) && fd.type.mod == type.mod && !isFuncAliasDeclaration()) - { - //printf("\tfalse: conflict %s\n", kind()); - return false; - } - } - if (overnext) { td = overnext.isTemplateDeclaration(); @@ -553,8 +522,7 @@ version (IN_LLVM) while (f && f.overnext) { //printf("f.overnext = %p %s\n", f.overnext, f.overnext.toChars()); - TemplateDeclaration td = f.overnext.isTemplateDeclaration(); - if (td) + if (TemplateDeclaration td = f.overnext.isTemplateDeclaration()) return td; f = f.overnext.isFuncDeclaration(); } @@ -588,7 +556,7 @@ version (IN_LLVM) * * Returns: the `LabelDsymbol` for `ident` */ - final LabelDsymbol searchLabel(Identifier ident, const ref Loc loc) + final LabelDsymbol searchLabel(Identifier ident, Loc loc) { Dsymbol s; if (!labtab) @@ -665,8 +633,7 @@ version (IN_LLVM) { if (isMain()) return "D main"; - else - return Dsymbol.toPrettyChars(QualifyTypes); + return Dsymbol.toPrettyChars(QualifyTypes); } /** for diagnostics, e.g. 'int foo(int x, int y) pure' */ @@ -757,186 +724,31 @@ version (IN_LLVM) return false; } - /***************************************** - * Initialize for inferring the attributes of this function. - */ - final void initInferAttributes() - { - //printf("initInferAttributes() for %s (%s)\n", toPrettyChars(), ident.toChars()); - TypeFunction tf = type.toTypeFunction(); - if (tf.purity == PURE.impure) // purity not specified - purityInprocess = true; - - if (tf.trust == TRUST.default_) - safetyInprocess = true; - - if (!tf.isnothrow) - nothrowInprocess = true; - - if (!tf.isnogc) - nogcInprocess = true; - - if (!isVirtual() || this.isIntroducing()) - returnInprocess = true; - - // Initialize for inferring STC.scope_ - inferScope = true; - } - - extern (D) final uint flags() + extern (D) final uint saveFlags() { return bitFields; } - extern (D) final uint flags(uint f) + extern (D) final uint restoreFlags(uint f) { bitFields = f; return bitFields; } - final bool isSafe() - { - if (safetyInprocess) - setUnsafe(); - return type.toTypeFunction().trust == TRUST.safe; - } - - extern (D) final bool isSafeBypassingInference() - { - return !(safetyInprocess) && isSafe(); - } - - final bool isTrusted() - { - if (safetyInprocess) - setUnsafe(); - return type.toTypeFunction().trust == TRUST.trusted; - } - - /************************************** - * The function is doing something unsafe, so mark it as unsafe. - * - * Params: - * gag = surpress error message (used in escape.d) - * loc = location of error - * fmt = printf-style format string - * arg0 = (optional) argument for first %s format specifier - * arg1 = (optional) argument for second %s format specifier - * arg2 = (optional) argument for third %s format specifier - * Returns: whether there's a safe error - */ - extern (D) final bool setUnsafe( - bool gag = false, Loc loc = Loc.init, const(char)* fmt = null, - RootObject arg0 = null, RootObject arg1 = null, RootObject arg2 = null) - { - if (safetyInprocess) - { - safetyInprocess = false; - type.toTypeFunction().trust = TRUST.system; - if (fmt || arg0) - safetyViolation = new AttributeViolation(loc, fmt, arg0, arg1, arg2); - - if (fes) - fes.func.setUnsafe(); - } - else if (isSafe()) - { - if (!gag && fmt) - .error(loc, fmt, arg0 ? arg0.toChars() : "", arg1 ? arg1.toChars() : "", arg2 ? arg2.toChars() : ""); - - return true; - } - return false; - } - - /************************************** - * The function is calling `@system` function `f`, so mark it as unsafe. - * - * Params: - * f = function being called (needed for diagnostic of inferred functions) - * Returns: whether there's a safe error - */ - extern (D) final bool setUnsafeCall(FuncDeclaration f) - { - return setUnsafe(false, f.loc, null, f, null); - } - - final bool isNogc() - { - //printf("isNogc() %s, inprocess: %d\n", toChars(), !!(flags & FUNCFLAG.nogcInprocess)); - if (nogcInprocess) - setGC(loc, null); - return type.toTypeFunction().isnogc; - } - - extern (D) final bool isNogcBypassingInference() - { - return !nogcInprocess && isNogc(); - } - - /************************************** - * The function is doing something that may allocate with the GC, - * so mark it as not nogc (not no-how). - * - * Params: - * loc = location of impure action - * fmt = format string for error message. Must include "%s `%s`" for the function kind and name. - * arg0 = (optional) argument to format string - * - * Returns: - * true if function is marked as @nogc, meaning a user error occurred - */ - extern (D) final bool setGC(Loc loc, const(char)* fmt, RootObject arg0 = null) - { - //printf("setGC() %s\n", toChars()); - if (nogcInprocess && semanticRun < PASS.semantic3 && _scope) - { - this.semantic2(_scope); - this.semantic3(_scope); - } - - if (nogcInprocess) - { - nogcInprocess = false; - if (fmt) - nogcViolation = new AttributeViolation(loc, fmt, this, arg0); // action that requires GC - else if (arg0) - nogcViolation = new AttributeViolation(loc, fmt, arg0); // call to non-@nogc function - - type.toTypeFunction().isnogc = false; - if (fes) - fes.func.setGC(Loc.init, null, null); - } - else if (isNogc()) - return true; - return false; - } - - /************************************** - * The function calls non-`@nogc` function f, mark it as not nogc. - * Params: - * f = function being called - * Returns: - * true if function is marked as @nogc, meaning a user error occurred - */ - extern (D) final bool setGCCall(FuncDeclaration f) - { - return setGC(loc, null, f); - } /************************************** * The function is doing something that may throw an exception, register that in case nothrow is being inferred * * Params: * loc = location of action - * fmt = format string for error message - * arg0 = (optional) argument to format string + * format = format string for error message + * args = arguments to format string */ - extern (D) final void setThrow(Loc loc, const(char)* fmt, RootObject arg0 = null) + extern (D) final void setThrow(Loc loc, const(char)* format, RootObject[] args...) { if (nothrowInprocess && !nothrowViolation) { - nothrowViolation = new AttributeViolation(loc, fmt, arg0); // action that requires GC + nothrowViolation = new AttributeViolation(loc, format, args); // action that requires GC } } @@ -944,22 +756,13 @@ version (IN_LLVM) * The function calls non-`nothrow` function f, register that in case nothrow is being inferred * Params: * loc = location of call - * f = function being called + * fd = function being called */ - extern (D) final void setThrowCall(Loc loc, FuncDeclaration f) + extern (D) final void setThrowCall(Loc loc, FuncDeclaration fd) { - return setThrow(loc, null, f); - } - - extern (D) final void printGCUsage(const ref Loc loc, const(char)* warn) - { - if (!global.params.v.gc) - return; - - Module m = getModule(); - if (m && m.isRoot() && !inUnittest()) + if (nothrowInprocess && !nothrowViolation) { - message(loc, "vgc: %s", warn); + nothrowViolation = new AttributeViolation(loc, fd); // action that requires GC } } @@ -1205,75 +1008,6 @@ version (IN_LLVM) return true; } - /*********************************************** - * Check that the function contains any closure. - * If it's @nogc, report suitable errors. - * This is mostly consistent with FuncDeclaration::needsClosure(). - * - * Returns: - * true if any errors occur. - */ - extern (C++) final bool checkClosure() - { - //printf("checkClosure() %s\n", toPrettyChars()); - if (!needsClosure()) - return false; - - if (setGC(loc, "%s `%s` is `@nogc` yet allocates closure for `%s()` with the GC", this)) - { - .error(loc, "%s `%s` is `@nogc` yet allocates closure for `%s()` with the GC", kind, toPrettyChars, toChars()); - if (global.gag) // need not report supplemental errors - return true; - } - else if (!global.params.useGC) - { - .error(loc, "%s `%s` is `-betterC` yet allocates closure for `%s()` with the GC", kind, toPrettyChars, toChars()); - if (global.gag) // need not report supplemental errors - return true; - } - else - { - printGCUsage(loc, "using closure causes GC allocation"); - return false; - } - - FuncDeclarations a; - foreach (v; closureVars) - { - foreach (f; v.nestedrefs) - { - assert(f !is this); - - LcheckAncestorsOfANestedRef: - for (Dsymbol s = f; s && s !is this; s = s.toParentP(this)) - { - auto fx = s.isFuncDeclaration(); - if (!fx) - continue; - if (fx.isThis() || - fx.tookAddressOf || - checkEscapingSiblings(fx, this)) - { - foreach (f2; a) - { - if (f2 == f) - break LcheckAncestorsOfANestedRef; - } - a.push(f); - .errorSupplemental(f.loc, "%s `%s` closes over variable `%s`", - f.kind, f.toPrettyChars(), v.toChars()); - if (v.ident != Id.This) - .errorSupplemental(v.loc, "`%s` declared here", v.toChars()); - - break LcheckAncestorsOfANestedRef; - } - } - } - } - - return true; - } - /*********************************************** * Determine if function's variables are referenced by a function * nested within it. @@ -1314,8 +1048,7 @@ version (IN_LLVM) { if (type) { - TypeFunction fdtype = type.isTypeFunction(); - if (fdtype) // Could also be TypeError + if (TypeFunction fdtype = type.isTypeFunction()) // Could also be TypeError return fdtype.parameterList; } @@ -1325,12 +1058,12 @@ version (IN_LLVM) /********************************** * Generate a FuncDeclaration for a runtime library function. */ - static FuncDeclaration genCfunc(Parameters* fparams, Type treturn, const(char)* name, StorageClass stc = 0) + 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); } - static FuncDeclaration genCfunc(Parameters* fparams, Type treturn, Identifier id, StorageClass stc = 0) + extern(D) static FuncDeclaration genCfunc(Parameters* fparams, Type treturn, Identifier id, STC stc = STC.none) { FuncDeclaration fd; TypeFunction tf; @@ -1362,11 +1095,6 @@ version (IN_LLVM) return fd; } - override final inout(FuncDeclaration) isFuncDeclaration() inout - { - return this; - } - inout(FuncDeclaration) toAliasFunc() inout @safe { return this; @@ -1378,118 +1106,6 @@ version (IN_LLVM) } } -/*************************************************** - * Visit each overloaded function/template in turn, and call dg(s) on it. - * Exit when no more, or dg(s) returns nonzero. - * - * Params: - * fstart = symbol to start from - * dg = the delegate to be called on the overload - * sc = context used to check if symbol is accessible (and therefore visible), - * can be null - * - * Returns: - * ==0 continue - * !=0 done (and the return value from the last dg() call) - */ -extern (D) int overloadApply(Dsymbol fstart, scope int delegate(Dsymbol) dg, Scope* sc = null) -{ - Dsymbols visited; - - 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) - { - import dmd.access : checkSymbolAccess; - if (auto od = d.isOverDeclaration()) - { - /* The scope is needed here to check whether a function in - an overload set was added by means of a private alias (or a - selective import). If the scope where the alias is created - is imported somewhere, the overload set is visible, but the private - alias is not. - */ - if (sc) - { - if (checkSymbolAccess(sc, od)) - { - if (int r = overloadApplyRecurse(od.aliassym, dg, sc)) - return r; - } - } - else if (int r = overloadApplyRecurse(od.aliassym, dg, sc)) - return r; - next = od.overnext; - } - else if (auto fa = d.isFuncAliasDeclaration()) - { - if (fa.hasOverloads) - { - if (int r = overloadApplyRecurse(fa.funcalias, dg, sc)) - return r; - } - else if (auto fd = fa.toAliasFunc()) - { - if (int r = dg(fd)) - return r; - } - else - { - .error(d.loc, "%s `%s` is aliased to a function", d.kind, d.toPrettyChars); - break; - } - next = fa.overnext; - } - else if (auto ad = d.isAliasDeclaration()) - { - if (sc) - { - if (checkSymbolAccess(sc, ad)) - next = ad.toAlias(); - } - else - next = ad.toAlias(); - if (next == ad) - break; - if (next == fstart) - break; - } - else if (auto td = d.isTemplateDeclaration()) - { - if (int r = dg(td)) - return r; - next = td.overnext; - } - else if (auto fd = d.isFuncDeclaration()) - { - if (int r = dg(fd)) - return r; - next = fd.overnext; - } - else if (auto os = d.isOverloadSet()) - { - foreach (ds; os.a) - if (int r = dg(ds)) - return r; - } - else - { - .error(d.loc, "%s `%s` is aliased to a function", d.kind, d.toPrettyChars); - break; - // BUG: should print error message? - } - } - return 0; - } - return overloadApplyRecurse(fstart, dg, sc); -} - /** Checks for mismatching modifiers between `lhsMod` and `rhsMod` and prints the mismatching modifiers to `buf`. @@ -1603,7 +1219,7 @@ private void markAsNeedingClosure(Dsymbol f, FuncDeclaration outerFunc) * Returns: * true if any closures were needed */ -private bool checkEscapingSiblings(FuncDeclaration f, FuncDeclaration outerFunc, void* p = null) +bool checkEscapingSiblings(FuncDeclaration f, FuncDeclaration outerFunc, void* p = null) { static struct PrevSibling { @@ -1674,6 +1290,7 @@ extern (C++) final class FuncAliasDeclaration : FuncDeclaration super(funcalias.loc, funcalias.endloc, ident, funcalias.storage_class, funcalias.type); assert(funcalias != this); this.funcalias = funcalias; + this.dsym = DSYM.funcAliasDeclaration; this.hasOverloads = hasOverloads; if (hasOverloads) @@ -1690,11 +1307,6 @@ extern (C++) final class FuncAliasDeclaration : FuncDeclaration userAttribDecl = funcalias.userAttribDecl; } - override inout(FuncAliasDeclaration) isFuncAliasDeclaration() inout - { - return this; - } - override const(char)* kind() const { return "function alias"; @@ -1721,15 +1333,16 @@ extern (C++) final class FuncLiteralDeclaration : FuncDeclaration // backend bool deferToObj; - extern (D) this(const ref Loc loc, const ref Loc endloc, Type type, TOK tok, ForeachStatement fes, Identifier id = null, StorageClass storage_class = STC.undefined_) + extern (D) this(Loc loc, Loc endloc, Type type, TOK tok, ForeachStatement fes, Identifier id = null, STC storage_class = STC.none) { super(loc, endloc, null, storage_class, type); + this.dsym = DSYM.funcLiteralDeclaration; this.ident = id ? id : Id.empty; this.tok = tok; this.fes = fes; // Always infer scope for function literals // See https://issues.dlang.org/show_bug.cgi?id=20362 - this.inferScope = true; + this.scopeInprocess = true; //printf("FuncLiteralDeclaration() id = '%s', type = '%s'\n", this.ident.toChars(), type.toChars()); } @@ -1769,11 +1382,6 @@ extern (C++) final class FuncLiteralDeclaration : FuncDeclaration return false; } - override inout(FuncLiteralDeclaration) isFuncLiteralDeclaration() inout - { - return this; - } - override const(char)* kind() const { // GCC requires the (char*) casts @@ -1784,8 +1392,7 @@ extern (C++) final class FuncLiteralDeclaration : FuncDeclaration { if (parent) { - TemplateInstance ti = parent.isTemplateInstance(); - if (ti) + if (TemplateInstance ti = parent.isTemplateInstance()) return ti.tempdecl.toPrettyChars(QualifyTypes); } return Dsymbol.toPrettyChars(QualifyTypes); @@ -1801,11 +1408,12 @@ extern (C++) final class FuncLiteralDeclaration : FuncDeclaration */ extern (C++) final class CtorDeclaration : FuncDeclaration { - bool isCpCtor; - extern (D) this(const ref Loc loc, const ref Loc endloc, StorageClass stc, Type type, bool isCpCtor = false) + bool isCpCtor; // copy constructor + bool isMoveCtor; // move constructor (aka rvalue constructor) + extern (D) this(Loc loc, Loc endloc, STC stc, Type type) { super(loc, endloc, Id.ctor, stc, type); - this.isCpCtor = isCpCtor; + this.dsym = DSYM.ctorDeclaration; //printf("CtorDeclaration(loc = %s) %s %p\n", loc.toChars(), toChars(), this); } @@ -1822,11 +1430,6 @@ extern (C++) final class CtorDeclaration : FuncDeclaration return isCpCtor ? "copy constructor" : "constructor"; } - override const(char)* toChars() const - { - return "this"; - } - override bool isVirtual() const { return false; @@ -1842,11 +1445,6 @@ extern (C++) final class CtorDeclaration : FuncDeclaration return (isThis() && vthis && global.params.useInvariants == CHECKENABLE.on); } - override inout(CtorDeclaration) isCtorDeclaration() inout - { - return this; - } - override void accept(Visitor v) { v.visit(this); @@ -1857,9 +1455,10 @@ extern (C++) final class CtorDeclaration : FuncDeclaration */ extern (C++) final class PostBlitDeclaration : FuncDeclaration { - extern (D) this(const ref Loc loc, const ref Loc endloc, StorageClass stc, Identifier id) + extern (D) this(Loc loc, Loc endloc, STC stc, Identifier id) { super(loc, endloc, id, stc, null); + this.dsym = DSYM.postBlitDeclaration; } override PostBlitDeclaration syntaxCopy(Dsymbol s) @@ -1890,11 +1489,6 @@ extern (C++) final class PostBlitDeclaration : FuncDeclaration return false; // cannot overload postblits } - override inout(PostBlitDeclaration) isPostBlitDeclaration() inout - { - return this; - } - override void accept(Visitor v) { v.visit(this); @@ -1905,14 +1499,16 @@ extern (C++) final class PostBlitDeclaration : FuncDeclaration */ extern (C++) final class DtorDeclaration : FuncDeclaration { - extern (D) this(const ref Loc loc, const ref Loc endloc) + extern (D) this(Loc loc, Loc endloc) { - super(loc, endloc, Id.dtor, STC.undefined_, null); + super(loc, endloc, Id.dtor, STC.none, null); + this.dsym = DSYM.dtorDeclaration; } - extern (D) this(const ref Loc loc, const ref Loc endloc, StorageClass stc, Identifier id) + extern (D) this(Loc loc, Loc endloc, STC stc, Identifier id) { super(loc, endloc, id, stc, null); + this.dsym = DSYM.dtorDeclaration; } override DtorDeclaration syntaxCopy(Dsymbol s) @@ -1928,11 +1524,6 @@ extern (C++) final class DtorDeclaration : FuncDeclaration return "destructor"; } - override const(char)* toChars() const - { - return "~this"; - } - override bool isVirtual() const { // D dtor's don't get put into the vtbl[] @@ -1955,11 +1546,6 @@ extern (C++) final class DtorDeclaration : FuncDeclaration return false; // cannot overload destructors } - override inout(DtorDeclaration) isDtorDeclaration() inout - { - return this; - } - override void accept(Visitor v) { v.visit(this); @@ -1970,14 +1556,16 @@ extern (C++) final class DtorDeclaration : FuncDeclaration */ extern (C++) class StaticCtorDeclaration : FuncDeclaration { - extern (D) this(const ref Loc loc, const ref Loc endloc, StorageClass stc) + extern (D) this(Loc loc, Loc endloc, STC stc) { super(loc, endloc, Identifier.generateIdWithLoc("_staticCtor", loc), STC.static_ | stc, null); + this.dsym = DSYM.staticCtorDeclaration; } - extern (D) this(const ref Loc loc, const ref Loc endloc, string name, StorageClass stc) + extern (D) this(Loc loc, Loc endloc, string name, STC stc) { super(loc, endloc, Identifier.generateIdWithLoc(name, loc), STC.static_ | stc, null); + this.dsym = DSYM.staticCtorDeclaration; } override StaticCtorDeclaration syntaxCopy(Dsymbol s) @@ -2008,16 +1596,6 @@ extern (C++) class StaticCtorDeclaration : FuncDeclaration return false; } - override final bool hasStaticCtorOrDtor() @nogc nothrow pure @safe - { - return true; - } - - override final inout(StaticCtorDeclaration) isStaticCtorDeclaration() inout @nogc nothrow pure @safe - { - return this; - } - override void accept(Visitor v) { v.visit(this); @@ -2031,9 +1609,10 @@ extern (C++) final class SharedStaticCtorDeclaration : StaticCtorDeclaration /// Exclude this constructor from cyclic dependency check bool standalone; - extern (D) this(const ref Loc loc, const ref Loc endloc, StorageClass stc) + extern (D) this(Loc loc, Loc endloc, STC stc) { super(loc, endloc, "_sharedStaticCtor", stc); + this.dsym = DSYM.sharedStaticCtorDeclaration; } override SharedStaticCtorDeclaration syntaxCopy(Dsymbol s) @@ -2044,11 +1623,6 @@ extern (C++) final class SharedStaticCtorDeclaration : StaticCtorDeclaration return scd; } - override inout(SharedStaticCtorDeclaration) isSharedStaticCtorDeclaration() inout - { - return this; - } - override void accept(Visitor v) { v.visit(this); @@ -2061,14 +1635,16 @@ extern (C++) class StaticDtorDeclaration : FuncDeclaration { VarDeclaration vgate; // 'gate' variable - extern (D) this(const ref Loc loc, const ref Loc endloc, StorageClass stc) + extern (D) this(Loc loc, Loc endloc, STC stc) { super(loc, endloc, Identifier.generateIdWithLoc("_staticDtor", loc), STC.static_ | stc, null); + this.dsym = DSYM.staticDtorDeclaration; } - extern (D) this(const ref Loc loc, const ref Loc endloc, string name, StorageClass stc) + extern (D) this(Loc loc, Loc endloc, string name, STC stc) { super(loc, endloc, Identifier.generateIdWithLoc(name, loc), STC.static_ | stc, null); + this.dsym = DSYM.staticDtorDeclaration; } override StaticDtorDeclaration syntaxCopy(Dsymbol s) @@ -2089,11 +1665,6 @@ extern (C++) class StaticDtorDeclaration : FuncDeclaration return false; } - override final bool hasStaticCtorOrDtor() - { - return true; - } - override final bool addPreInvariant() { return false; @@ -2104,11 +1675,6 @@ extern (C++) class StaticDtorDeclaration : FuncDeclaration return false; } - override final inout(StaticDtorDeclaration) isStaticDtorDeclaration() inout - { - return this; - } - override void accept(Visitor v) { v.visit(this); @@ -2119,9 +1685,10 @@ extern (C++) class StaticDtorDeclaration : FuncDeclaration */ extern (C++) final class SharedStaticDtorDeclaration : StaticDtorDeclaration { - extern (D) this(const ref Loc loc, const ref Loc endloc, StorageClass stc) + extern (D) this(Loc loc, Loc endloc, STC stc) { super(loc, endloc, "_sharedStaticDtor", stc); + this.dsym = DSYM.sharedStaticDtorDeclaration; } override SharedStaticDtorDeclaration syntaxCopy(Dsymbol s) @@ -2132,11 +1699,6 @@ extern (C++) final class SharedStaticDtorDeclaration : StaticDtorDeclaration return sdd; } - override inout(SharedStaticDtorDeclaration) isSharedStaticDtorDeclaration() inout - { - return this; - } - override void accept(Visitor v) { v.visit(this); @@ -2147,11 +1709,12 @@ extern (C++) final class SharedStaticDtorDeclaration : StaticDtorDeclaration */ extern (C++) final class InvariantDeclaration : FuncDeclaration { - extern (D) this(const ref Loc loc, const ref Loc endloc, StorageClass stc, Identifier id, Statement fbody) + extern (D) this(Loc loc, Loc endloc, STC stc, Identifier id, Statement fbody) { // Make a unique invariant for now; we'll fix it up as we add it to the aggregate invariant list. super(loc, endloc, id ? id : Identifier.generateId("__invariant"), stc, null); this.fbody = fbody; + this.dsym = DSYM.invariantDeclaration; } override InvariantDeclaration syntaxCopy(Dsymbol s) @@ -2177,11 +1740,6 @@ extern (C++) final class InvariantDeclaration : FuncDeclaration return false; } - override inout(InvariantDeclaration) isInvariantDeclaration() inout - { - return this; - } - override void accept(Visitor v) { v.visit(this); @@ -2207,10 +1765,11 @@ extern (C++) final class UnitTestDeclaration : FuncDeclaration // toObjFile() these nested functions after this one FuncDeclarations deferredNested; - extern (D) this(const ref Loc loc, const ref Loc endloc, StorageClass stc, char* codedoc) + extern (D) this(Loc loc, Loc endloc, STC stc, char* codedoc) { super(loc, endloc, Identifier.generateIdWithLoc("__unittest", loc), stc, null); this.codedoc = codedoc; + this.dsym = DSYM.unitTestDeclaration; } override UnitTestDeclaration syntaxCopy(Dsymbol s) @@ -2241,11 +1800,6 @@ extern (C++) final class UnitTestDeclaration : FuncDeclaration return false; } - override inout(UnitTestDeclaration) isUnitTestDeclaration() inout - { - return this; - } - override void accept(Visitor v) { v.visit(this); @@ -2256,9 +1810,10 @@ extern (C++) final class UnitTestDeclaration : FuncDeclaration */ extern (C++) final class NewDeclaration : FuncDeclaration { - extern (D) this(const ref Loc loc, StorageClass stc) + extern (D) this(Loc loc, STC stc) { super(loc, Loc.initial, Id.classNew, STC.static_ | stc, null); + this.dsym = DSYM.newDeclaration; } override NewDeclaration syntaxCopy(Dsymbol s) @@ -2289,11 +1844,6 @@ extern (C++) final class NewDeclaration : FuncDeclaration return false; } - override inout(NewDeclaration) isNewDeclaration() inout - { - return this; - } - override void accept(Visitor v) { v.visit(this); @@ -2303,86 +1853,33 @@ extern (C++) final class NewDeclaration : FuncDeclaration /// Stores a reason why a function failed to infer a function attribute like `@safe` or `pure` /// /// Has two modes: -/// - a regular safety error, stored in (fmtStr, arg0, arg1) +/// - a regular safety error, stored in `action` /// - a call to a function without the attribute, which is a special case, because in that case, /// that function might recursively also have a `AttributeViolation`. This way, in case /// of a big call stack, the error can go down all the way to the root cause. -/// The `FunctionDeclaration` is then stored in `arg0` and `fmtStr` must be `null`. struct AttributeViolation { - /// location of error - Loc loc = Loc.init; - /// printf-style format string - const(char)* fmtStr = null; - /// Arguments for up to two `%s` format specifiers in format string - RootObject arg0 = null; - /// ditto - RootObject arg1 = null; - /// ditto - RootObject arg2 = null; -} + Loc loc; /// location of error -/// Print the reason why `fd` was inferred `@system` as a supplemental error -/// Params: -/// fd = function to check -/// maxDepth = up to how many functions deep to report errors -/// deprecation = print deprecations instead of errors -/// stc = storage class of attribute to check -void errorSupplementalInferredAttr(FuncDeclaration fd, int maxDepth, bool deprecation, STC stc) -{ - auto errorFunc = deprecation ? &deprecationSupplemental : &errorSupplemental; + FuncDeclaration fd; /// function is the focus of the violation - AttributeViolation* s; - const(char)* attr; - if (stc & STC.safe) - { - s = fd.safetyViolation; - attr = "@safe"; - } - else if (stc & STC.pure_) - { - s = fd.pureViolation; - attr = "pure"; - } - else if (stc & STC.nothrow_) - { - s = fd.nothrowViolation; - attr = "nothrow"; - } - else if (stc & STC.nogc) - { - s = fd.nogcViolation; - attr = "@nogc"; - } + // -- OR -- + + string action; /// Action that made the attribute fail to get inferred + + this(Loc loc, FuncDeclaration fd) { this.loc = loc; this.fd = fd; } - if (s) + this(Loc loc, const(char)* fmt, RootObject[] args) { - if (s.fmtStr) - { - errorFunc(s.loc, deprecation ? - "which wouldn't be `%s` because of:" : - "which wasn't inferred `%s` because of:", attr); - if (stc == STC.nogc || stc == STC.pure_) - { - auto f = (cast(Dsymbol) s.arg0).isFuncDeclaration(); - errorFunc(s.loc, s.fmtStr, f.kind(), f.toPrettyChars(), s.arg1 ? s.arg1.toChars() : ""); - } - else - { - errorFunc(s.loc, s.fmtStr, - s.arg0 ? s.arg0.toChars() : "", s.arg1 ? s.arg1.toChars() : "", s.arg2 ? s.arg2.toChars() : ""); - } - } - else if (auto sa = s.arg0.isDsymbol()) - { - if (FuncDeclaration fd2 = sa.isFuncDeclaration()) - { - if (maxDepth > 0) - { - errorFunc(s.loc, "which calls `%s`", fd2.toPrettyChars()); - errorSupplementalInferredAttr(fd2, maxDepth - 1, deprecation, stc); - } - } - } + this.loc = loc; + assert(args.length <= 4); // expand if necessary + OutBuffer buf; + buf.printf(fmt, + args.length > 0 && args[0] ? args[0].toErrMsg() : "", + args.length > 1 && args[1] ? args[1].toErrMsg() : "", + args.length > 2 && args[2] ? args[2].toErrMsg() : "", + args.length > 3 && args[3] ? args[3].toErrMsg() : "", + ); + this.action = buf.extractSlice(); } } diff --git a/dmd/funcsem.d b/dmd/funcsem.d index bc7cf5f234..b3cb79b74e 100644 --- a/dmd/funcsem.d +++ b/dmd/funcsem.d @@ -3,12 +3,12 @@ * * Specification: $(LINK2 https://dlang.org/spec/function.html, Functions) * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/funcsem.d, _funcsem.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/funcsem.d, _funcsem.d) * Documentation: https://dlang.org/phobos/dmd_funcsem.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/funcsem.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/funcsem.d */ module dmd.funcsem; @@ -54,9 +54,9 @@ import dmd.rootobject; import dmd.root.filename; import dmd.root.string; import dmd.root.stringtable; +import dmd.safe; import dmd.semantic2; import dmd.semantic3; -import dmd.statement_rewrite_walker; import dmd.statement; import dmd.statementsem; import dmd.target; @@ -64,6 +64,7 @@ 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) {} @@ -153,6 +154,28 @@ public: } } +/**************************************** + * Only one entry point function is allowed. Print error if more than one. + * Params: + * fd = a "main" function + * Returns: + * true if haven't seen "main" before + */ +extern (C++) bool onlyOneMain(FuncDeclaration fd) +{ + if (auto lastMain = FuncDeclaration.lastMain) + { + const format = (target.os == Target.OS.Windows) + ? "only one entry point `main`, `WinMain` or `DllMain` is allowed" + : "only one entry point `main` is allowed"; + error(fd.loc, format.ptr); + errorSupplemental(lastMain.loc, "previously found `%s` here", lastMain.toFullSignature()); + return false; + } + FuncDeclaration.lastMain = fd; + return true; +} + /********************************** * Main semantic routine for functions. */ @@ -214,11 +237,11 @@ void funcDeclarationSemantic(Scope* sc, FuncDeclaration funcdecl) //printf("function storage_class = x%llx, sc.stc = x%llx, %x\n", storage_class, sc.stc, Declaration.isFinal()); - if (sc.flags & SCOPE.compile) + if (sc.traitsCompiles) funcdecl.skipCodegen = true; funcdecl._linkage = sc.linkage; - if (sc.flags & SCOPE.Cfile && funcdecl.isFuncLiteralDeclaration()) + if (sc.inCfile && funcdecl.isFuncLiteralDeclaration()) funcdecl._linkage = LINK.d; // so they are uniquely mangled if (auto fld = funcdecl.isFuncLiteralDeclaration()) @@ -243,7 +266,7 @@ void funcDeclarationSemantic(Scope* sc, FuncDeclaration funcdecl) funcdecl.visibility = sc.visibility; funcdecl.userAttribDecl = sc.userAttribDecl; - UserAttributeDeclaration.checkGNUABITag(funcdecl, funcdecl._linkage); + checkGNUABITag(funcdecl, funcdecl._linkage); checkMustUseReserved(funcdecl); version (IN_LLVM) @@ -268,7 +291,7 @@ version (IN_LLVM) return null; } - if (sc.flags & SCOPE.Cfile) + if (sc.inCfile) { /* C11 allows a function to be declared with a typedef, D does not. */ @@ -337,15 +360,15 @@ version (IN_LLVM) } } - if (tf.isref) + if (tf.isRef) sc.stc |= STC.ref_; if (tf.isScopeQual) sc.stc |= STC.scope_; - if (tf.isnothrow) + if (tf.isNothrow) sc.stc |= STC.nothrow_; - if (tf.isnogc) + if (tf.isNogc) sc.stc |= STC.nogc; - if (tf.isproperty) + if (tf.isProperty) sc.stc |= STC.property; if (tf.purity == PURE.fwdref) sc.stc |= STC.pure_; @@ -363,7 +386,7 @@ version (IN_LLVM) if (funcdecl.isCtorDeclaration()) { - tf.isctor = true; + tf.isCtor = true; Type tret = ad.handleType(); assert(tret); tret = tret.addStorageClass(funcdecl.storage_class | sc.stc); @@ -374,7 +397,7 @@ version (IN_LLVM) } // 'return' on a non-static class member function implies 'scope' as well - if (ad && ad.isClassDeclaration() && (tf.isreturn || sc.stc & STC.return_) && !(sc.stc & STC.static_)) + if (ad && ad.isClassDeclaration() && (tf.isReturn || sc.stc & STC.return_) && !(sc.stc & STC.static_)) sc.stc |= STC.scope_; // If 'this' has no pointers, remove 'scope' as it has no meaning @@ -385,11 +408,11 @@ version (IN_LLVM) { sc.stc &= ~STC.scope_; tf.isScopeQual = false; - if (tf.isreturnscope) + if (tf.isReturnScope) { sc.stc &= ~(STC.return_ | STC.returnScope); - tf.isreturn = false; - tf.isreturnscope = false; + tf.isReturn = false; + tf.isReturnScope = false; } } @@ -435,12 +458,12 @@ version (IN_LLVM) TypeFunction tfo = funcdecl.originalType.toTypeFunction(); tfo.mod = f.mod; tfo.isScopeQual = f.isScopeQual; - tfo.isreturninferred = f.isreturninferred; - tfo.isscopeinferred = f.isscopeinferred; - tfo.isref = f.isref; - tfo.isnothrow = f.isnothrow; - tfo.isnogc = f.isnogc; - tfo.isproperty = f.isproperty; + tfo.isReturnInferred = f.isReturnInferred; + tfo.isScopeInferred = f.isScopeInferred; + tfo.isRef = f.isRef; + tfo.isNothrow = f.isNothrow; + tfo.isNogc = f.isNogc; + tfo.isProperty = f.isProperty; tfo.purity = f.purity; tfo.trust = f.trust; @@ -477,10 +500,10 @@ version (IN_LLVM) funcdecl.overnext = null; // don't overload the redeclarations } - if ((funcdecl.storage_class & STC.auto_) && !f.isref && !funcdecl.inferRetType) + if ((funcdecl.storage_class & STC.auto_) && !f.isRef && !funcdecl.inferRetType) .error(funcdecl.loc, "%s `%s` storage class `auto` has no effect if return type is not inferred", funcdecl.kind, funcdecl.toPrettyChars); - if (f.isreturn && !funcdecl.needThis() && !funcdecl.isNested()) + if (f.isReturn && !funcdecl.needThis() && !funcdecl.isNested()) { /* Non-static nested functions have a hidden 'this' pointer to which * the 'return' applies @@ -1052,8 +1075,7 @@ Ldone: } // If it's a member template - ClassDeclaration cd = ti.tempdecl.isClassMember(); - if (cd) + if (ClassDeclaration cd = ti.tempdecl.isClassMember()) { .error(funcdecl.loc, "%s `%s` cannot use template to add virtual function to class `%s`", funcdecl.kind, funcdecl.toPrettyChars, cd.toChars()); } @@ -1088,7 +1110,7 @@ Ldone: { printedMain = true; auto name = mod.srcfile.toChars(); - auto path = FileName.searchPath(global.path, name, true); + auto path = FileName.searchPath(global.importPaths, name, true); message("entry %-10s\t%s", type, path ? path : name); } } @@ -1131,6 +1153,28 @@ Ldone: } } +/***************************************** + * Initialize for inferring the attributes of this function. + */ +private void initInferAttributes(FuncDeclaration fd) +{ + //printf("initInferAttributes() for %s (%s)\n", toPrettyChars(), ident.toChars()); + TypeFunction tf = fd.type.toTypeFunction(); + if (tf.purity == PURE.impure) // purity not specified + fd.purityInprocess = true; + + if (tf.trust == TRUST.default_) + fd.safetyInprocess = true; + + if (!tf.isNothrow) + fd.nothrowInprocess = true; + + if (!tf.isNogc) + fd.nogcInprocess = true; + + // Initialize for inferring STC.scope_ + fd.scopeInprocess = true; +} /**************************************************** * Resolve forward reference of function signature - @@ -1152,8 +1196,8 @@ bool functionSemantic(FuncDeclaration fd) if (!fd.originalType) // semantic not yet run { TemplateInstance spec = fd.isSpeculative(); - uint olderrs = global.errors; - uint oldgag = global.gag; + const olderrs = global.errors; + const oldgag = global.gag; if (global.gag && !spec) global.gag = 0; dsymbolSemantic(fd, fd._scope); @@ -1208,8 +1252,8 @@ bool functionSemantic3(FuncDeclaration fd) * we need to temporarily ungag errors. */ TemplateInstance spec = fd.isSpeculative(); - uint olderrs = global.errors; - uint oldgag = global.gag; + const olderrs = global.errors; + const oldgag = global.gag; if (global.gag && !spec && (!IN_LLVM || !global.gaggedForInlining)) global.gag = 0; semantic3(fd, fd._scope); @@ -1238,6 +1282,7 @@ extern (D) void declareThis(FuncDeclaration fd, Scope* sc) const bool dualCtx = (fd.toParent2() != fd.toParentLocal()); if (dualCtx) fd.hasDualContext = true; + auto ad = fd.isThis(); if (!dualCtx && !ad && !fd.isNested()) { @@ -1276,11 +1321,11 @@ extern (D) void declareThis(FuncDeclaration fd, Scope* sc) if (auto tf = fd.type.isTypeFunction()) { - if (tf.isreturn) + if (tf.isReturn) fd.vthis.storage_class |= STC.return_; if (tf.isScopeQual) fd.vthis.storage_class |= STC.scope_; - if (tf.isreturnscope) + if (tf.isReturnScope) fd.vthis.storage_class |= STC.returnScope; } @@ -1296,7 +1341,7 @@ extern (D) void declareThis(FuncDeclaration fd, Scope* sc) * Check that this function type is properly resolved. * If not, report "forward reference error" and return true. */ -extern (D) bool checkForwardRef(FuncDeclaration fd, const ref Loc loc) +extern (D) bool checkForwardRef(FuncDeclaration fd, Loc loc) { if (!functionSemantic(fd)) return true; @@ -1332,66 +1377,66 @@ int findVtblIndex(FuncDeclaration fd, Dsymbol[] vtbl) import dmd.typesem : covariant; FuncDeclaration mismatch = null; - StorageClass mismatchstc = 0; + STC mismatchstc = STC.none; int mismatchvi = -1; int exactvi = -1; int bestvi = -1; for (int vi = 0; vi < cast(int)vtbl.length; vi++) { FuncDeclaration fdv = vtbl[vi].isFuncDeclaration(); - if (fdv && fdv.ident == fd.ident) + if (!fdv || fdv.ident != fd.ident) + continue; + + if (fd.type.equals(fdv.type)) // if exact match { - if (fd.type.equals(fdv.type)) // if exact match + if (fdv.parent.isClassDeclaration()) { - if (fdv.parent.isClassDeclaration()) - { - if (fdv.isFuture()) - { - bestvi = vi; - continue; // keep looking - } - return vi; // no need to look further - } - - if (exactvi >= 0) + if (fdv.isFuture()) { - .error(fd.loc, "%s `%s` cannot determine overridden function", fd.kind, fd.toPrettyChars); - return exactvi; + bestvi = vi; + continue; // keep looking } - exactvi = vi; - bestvi = vi; - continue; + return vi; // no need to look further } - StorageClass stc = 0; - const cov = fd.type.covariant(fdv.type, &stc); - //printf("\tbaseclass cov = %d\n", cov); - final switch (cov) + if (exactvi >= 0) { - case Covariant.distinct: - // types are distinct - break; + .error(fd.loc, "%s `%s` cannot determine overridden function", fd.kind, fd.toPrettyChars); + return exactvi; + } + exactvi = vi; + bestvi = vi; + continue; + } - case Covariant.yes: - bestvi = vi; // covariant, but not identical - break; - // keep looking for an exact match + STC stc = STC.none; + const cov = fd.type.covariant(fdv.type, &stc); + //printf("\tbaseclass cov = %d\n", cov); + final switch (cov) + { + case Covariant.distinct: + // types are distinct + break; - case Covariant.no: - mismatchvi = vi; - mismatchstc = stc; - mismatch = fdv; // overrides, but is not covariant - break; - // keep looking for an exact match + case Covariant.yes: + bestvi = vi; // covariant, but not identical + break; + // keep looking for an exact match - case Covariant.fwdref: - return -2; // forward references - } + case Covariant.no: + mismatchvi = vi; + mismatchstc = stc; + mismatch = fdv; // overrides, but is not covariant + break; + // keep looking for an exact match + + case Covariant.fwdref: + return -2; // forward references } } if (fd._linkage == LINK.cpp && bestvi != -1) { - StorageClass stc = 0; + STC stc = STC.none; FuncDeclaration fdv = vtbl[bestvi].isFuncDeclaration(); assert(fdv && fdv.ident == fd.ident); if (fd.type.covariant(fdv.type, &stc, /*cppCovariant=*/true) == Covariant.no) @@ -1489,16 +1534,17 @@ enum FuncResolveFlag : ubyte * Returns: * if match is found, then function symbol, else null */ -FuncDeclaration resolveFuncCall(const ref Loc loc, Scope* sc, Dsymbol s, +FuncDeclaration resolveFuncCall(Loc loc, Scope* sc, Dsymbol s, Objects* tiargs, Type tthis, ArgumentList argumentList, FuncResolveFlag flags) { + //printf("resolveFuncCall() %s\n", s.toChars()); auto fargs = argumentList.arguments; if (!s) return null; // no match version (none) { - printf("resolveFuncCall('%s')\n", s.toChars()); + printf("resolveFuncCall() %s)\n", s.toChars()); if (tthis) printf("\tthis: %s\n", tthis.toChars()); if (fargs) @@ -1510,7 +1556,6 @@ FuncDeclaration resolveFuncCall(const ref Loc loc, Scope* sc, Dsymbol s, printf("\t%s: %s\n", arg.toChars(), arg.type.toChars()); } } - printf("\tfnames: %s\n", fnames ? fnames.toChars() : "null"); } if (tiargs && arrayObjectIsError(*tiargs)) @@ -1578,9 +1623,26 @@ FuncDeclaration resolveFuncCall(const ref Loc loc, Scope* sc, Dsymbol s, const(char)* lastprms = parametersTypeToChars(tf1.parameterList); const(char)* nextprms = parametersTypeToChars(tf2.parameterList); - .error(loc, "`%s.%s` called with argument types `%s` matches both:\n%s: `%s%s%s`\nand:\n%s: `%s%s%s`", + string match = ""; + final switch (m.last) + { + case MATCH.convert: + match = "after implicit conversions"; + break; + case MATCH.constant: + match = "after qualifier conversion"; + break; + case MATCH.exact: + match = "exactly"; + break; + case MATCH.nomatch: + assert(0); + } + + .error(loc, "`%s.%s` called with argument types `%s` matches multiple overloads %.*s:\n%s: `%s%s%s`\nand:\n%s: `%s%s%s`", s.parent.toPrettyChars(), s.ident.toChars(), fargsBuf.peekChars(), + match.fTuple.expand, m.lastf.loc.toChars(), m.lastf.toPrettyChars(), lastprms, tf1.modToChars(), m.nextf.loc.toChars(), m.nextf.toPrettyChars(), nextprms, tf2.modToChars()); return null; @@ -1629,9 +1691,12 @@ FuncDeclaration resolveFuncCall(const ref Loc loc, Scope* sc, Dsymbol s, { .error(loc, "none of the overloads of `%s` are callable using argument types `!(%s)%s`", od.ident.toChars(), tiargsBuf.peekChars(), fargsBuf.peekChars()); + if (!global.gag || global.params.v.showGaggedErrors) + printCandidates(loc, od, sc.isDeprecated()); return null; } + import dmd.expressionsem : checkDisabled; // remove when deprecation period of class allocators and deallocators is over if (fd.isNewDeclaration() && fd.checkDisabled(loc, sc)) return null; @@ -1708,39 +1773,40 @@ FuncDeclaration resolveFuncCall(const ref Loc loc, Scope* sc, Dsymbol s, fd.kind(), fd.toPrettyChars(), parametersTypeToChars(tf.parameterList), tf.modToChars(), fargsBuf.peekChars()); + if (global.gag && !global.params.v.showGaggedErrors) + return null; + // re-resolve to check for supplemental message - if (!global.gag || global.params.v.showGaggedErrors) + if (tthis) { - if (tthis) + if (auto classType = tthis.isTypeClass()) { - if (auto classType = tthis.isTypeClass()) + if (auto baseClass = classType.sym.baseClass) { - if (auto baseClass = classType.sym.baseClass) + if (auto baseFunction = baseClass.search(baseClass.loc, fd.ident)) { - if (auto baseFunction = baseClass.search(baseClass.loc, fd.ident)) + MatchAccumulator mErr; + functionResolve(mErr, baseFunction, loc, sc, tiargs, baseClass.type, argumentList); + if (mErr.last > MATCH.nomatch && mErr.lastf) { - MatchAccumulator mErr; - functionResolve(mErr, baseFunction, loc, sc, tiargs, baseClass.type, argumentList); - if (mErr.last > MATCH.nomatch && mErr.lastf) - { - errorSupplemental(loc, "%s `%s` hides base class function `%s`", - fd.kind, fd.toPrettyChars(), mErr.lastf.toPrettyChars()); - errorSupplemental(loc, "add `alias %s = %s` to `%s`'s body to merge the overload sets", - fd.toChars(), mErr.lastf.toPrettyChars(), tthis.toChars()); - return null; - } + errorSupplemental(loc, "%s `%s` hides base class function `%s`", + fd.kind, fd.toPrettyChars(), mErr.lastf.toPrettyChars()); + errorSupplemental(loc, "add `alias %s = %s` to `%s`'s body to merge the overload sets", + fd.toChars(), mErr.lastf.toPrettyChars(), tthis.toChars()); + return null; } } } } + } - void errorHelper2(const(char)* failMessage) scope - { - errorSupplemental(loc, failMessage); - } - - functionResolve(m, orig_s, loc, sc, tiargs, tthis, argumentList, &errorHelper2); + void errorHelper2(const(char)* failMessage) scope + { + errorSupplemental(loc, failMessage); } + + functionResolve(m, orig_s, loc, sc, tiargs, tthis, argumentList, &errorHelper2); + return null; } @@ -1751,8 +1817,7 @@ FuncDeclaration resolveFuncCall(const ref Loc loc, Scope* sc, Dsymbol s, * declaration = the declaration to print overload candidates for * showDeprecated = If `false`, `deprecated` function won't be shown */ -private void printCandidates(Decl)(const ref Loc loc, Decl declaration, bool showDeprecated) -if (is(Decl == TemplateDeclaration) || is(Decl == FuncDeclaration)) +private void printCandidates(Decl)(Loc loc, Decl declaration, bool showDeprecated) { // max num of overloads to print (-v or -verror-supplements overrides this). const uint DisplayLimit = global.params.v.errorSupplementCount(); @@ -1806,13 +1871,16 @@ if (is(Decl == TemplateDeclaration) || is(Decl == FuncDeclaration)) if (!print) return true; + // if td.onemember is a function, toCharsMaybeConstraints can print it + // without us recursing, otherwise we have to handle it. // td.onemember may not have overloads set // (see fail_compilation/onemember_overloads.d) // assume if more than one member it is overloaded internally - bool recurse = td.onemember && td.members.length > 1; + bool recurse = td.onemember && (!td.onemember.isFuncDeclaration || + td.members.length > 1); OutBuffer buf; HdrGenState hgs; - hgs.skipConstraints = true; + hgs.skipConstraints = true; // failing constraint should get printed below hgs.showOneMember = !recurse; toCharsMaybeConstraints(td, buf, hgs); const tmsg = buf.peekChars(); @@ -1896,32 +1964,32 @@ Expression addInvariant(AggregateDeclaration ad, VarDeclaration vthis) break; inv = cd.inv; } - if (inv) + if (!inv) + return e; + + version (all) { - version (all) - { - // Workaround for https://issues.dlang.org/show_bug.cgi?id=13394 - // For the correct mangling, - // run attribute inference on inv if needed. - functionSemantic(inv); - } + // Workaround for https://issues.dlang.org/show_bug.cgi?id=13394 + // For the correct mangling, + // run attribute inference on inv if needed. + functionSemantic(inv); + } - //e = new DsymbolExp(Loc.initial, inv); - //e = new CallExp(Loc.initial, e); - //e = e.semantic(sc2); + //e = new DsymbolExp(Loc.initial, inv); + //e = new CallExp(Loc.initial, e); + //e = e.semantic(sc2); - /* https://issues.dlang.org/show_bug.cgi?id=13113 - * Currently virtual invariant calls completely - * bypass attribute enforcement. - * Change the behavior of pre-invariant call by following it. - */ - e = new ThisExp(Loc.initial); - e.type = ad.type.addMod(vthis.type.mod); - e = new DotVarExp(Loc.initial, e, inv, false); - e.type = inv.type; - e = new CallExp(Loc.initial, e); - e.type = Type.tvoid; - } + /* https://issues.dlang.org/show_bug.cgi?id=13113 + * Currently virtual invariant calls completely + * bypass attribute enforcement. + * Change the behavior of pre-invariant call by following it. + */ + e = new ThisExp(Loc.initial); + e.type = ad.type.addMod(vthis.type.mod); + e = new DotVarExp(Loc.initial, e, inv, false); + e.type = inv.type; + e = new CallExp(Loc.initial, e); + e.type = Type.tvoid; return e; } @@ -1968,19 +2036,19 @@ FuncDeclaration overloadExactMatch(FuncDeclaration thisfd, Type t) */ int overrides(FuncDeclaration fd1, FuncDeclaration fd2) { - int result = 0; - if (fd1.ident == fd2.ident) - { - const cov = fd1.type.covariant(fd2.type); - if (cov != Covariant.distinct) - { - ClassDeclaration cd1 = fd1.toParent().isClassDeclaration(); - ClassDeclaration cd2 = fd2.toParent().isClassDeclaration(); - if (cd1 && cd2 && cd2.isBaseOf(cd1, null)) - result = 1; - } - } - return result; + if (fd1.ident != fd2.ident) + return 0; + + const cov = fd1.type.covariant(fd2.type); + if (cov == Covariant.distinct) + return 0; + + ClassDeclaration cd1 = fd1.toParent().isClassDeclaration(); + ClassDeclaration cd2 = fd2.toParent().isClassDeclaration(); + + if (cd1 && cd2 && cd2.isBaseOf(cd1, null)) + return 1; + return 0; } /************************************* @@ -2045,7 +2113,7 @@ MATCH leastAsSpecialized(FuncDeclaration f, FuncDeclaration g, Identifiers* name args.push(e); } - MATCH m = tg.callMatch(null, ArgumentList(&args, names), 1); + MATCH m = callMatch(g, tg, null, ArgumentList(&args, names), 1); if (m > MATCH.nomatch) { /* A variadic parameter list is less specialized than a @@ -2086,7 +2154,7 @@ L1: * 4. If there's no candidates, it's "no match" and returns null with error report. * e.g. If 'tthis' is const but there's no const methods. */ -FuncDeclaration overloadModMatch(FuncDeclaration thisfd, const ref Loc loc, Type tthis, ref bool hasOverloads) +FuncDeclaration overloadModMatch(FuncDeclaration thisfd, Loc loc, Type tthis, ref bool hasOverloads) { //printf("FuncDeclaration::overloadModMatch('%s')\n", toChars()); MatchAccumulator m; @@ -2098,6 +2166,26 @@ FuncDeclaration overloadModMatch(FuncDeclaration thisfd, const ref Loc loc, Type auto tf = f.type.toTypeFunction(); //printf("tf = %s\n", tf.toChars()); MATCH match; + int lastIsBetter() + { + //printf("\tlastbetter\n"); + m.count++; // count up + return 0; + } + int currIsBetter() + { + //printf("\tisbetter\n"); + if (m.last <= MATCH.convert) + { + // clear last secondary matching + m.nextf = null; + m.count = 0; + } + m.last = match; + m.lastf = f; + m.count++; // count up + return 0; + } if (tthis) // non-static functions are preferred than static ones { if (f.needThis()) @@ -2114,31 +2202,15 @@ FuncDeclaration overloadModMatch(FuncDeclaration thisfd, const ref Loc loc, Type } if (match == MATCH.nomatch) return 0; - if (match > m.last) goto LcurrIsBetter; - if (match < m.last) goto LlastIsBetter; + if (match > m.last) return currIsBetter(); + if (match < m.last) return lastIsBetter(); // See if one of the matches overrides the other. - if (m.lastf.overrides(f)) goto LlastIsBetter; - if (f.overrides(m.lastf)) goto LcurrIsBetter; + if (m.lastf.overrides(f)) return lastIsBetter(); + if (f.overrides(m.lastf)) return currIsBetter(); //printf("\tambiguous\n"); m.nextf = f; m.count++; return 0; - LlastIsBetter: - //printf("\tlastbetter\n"); - m.count++; // count up - return 0; - LcurrIsBetter: - //printf("\tisbetter\n"); - if (m.last <= MATCH.convert) - { - // clear last secondary matching - m.nextf = null; - m.count = 0; - } - m.last = match; - m.lastf = f; - m.count++; // count up - return 0; }); if (m.count == 1) // exact match { @@ -2182,14 +2254,14 @@ FuncDeclaration overloadModMatch(FuncDeclaration thisfd, const ref Loc loc, Type * -1 increase nesting by 1 (`target` is nested within 'fd') * LevelError error */ -int getLevelAndCheck(FuncDeclaration fd, const ref Loc loc, Scope* sc, FuncDeclaration target, +int getLevelAndCheck(FuncDeclaration fd, Loc loc, Scope* sc, FuncDeclaration target, Declaration decl) { int level = fd.getLevel(target, sc.intypeof); if (level != fd.LevelError) return level; // Don't give error if in template constraint - if (!(sc.flags & SCOPE.constraint)) + if (!sc.inTemplateConstraint) { const(char)* xstatic = fd.isStatic() ? "`static` " : ""; // better diagnostics for static functions @@ -2205,6 +2277,9 @@ int getLevelAndCheck(FuncDeclaration fd, const ref Loc loc, Scope* sc, FuncDecla /********************************** * Decide if attributes for this function can be inferred from examining * the function body. + * Params: + * fd = function to infer attributes for + * sc = context * Returns: * true if can */ @@ -2226,7 +2301,8 @@ bool canInferAttributes(FuncDeclaration fd, Scope* sc) (!fd.isMember() || sc.func.isSafeBypassingInference() && !fd.isInstantiated())) return true; if (fd.isFuncLiteralDeclaration() || // externs are not possible with literals - (fd.storage_class & STC.inference) || // do attribute inference + (fd.storage_class & STC.inference) || // do attribute inference + fd.isGenerated || // compiler generated function (fd.inferRetType && !fd.isCtorDeclaration())) return true; if (fd.isInstantiated()) @@ -2239,17 +2315,17 @@ bool canInferAttributes(FuncDeclaration fd, Scope* sc) } /********************************************* - * In the current function, we are calling 'this' function. - * 1. Check to see if the current function can call 'this' function, issue error if not. - * 2. If the current function is not the parent of 'this' function, then add - * the current function to the list of siblings of 'this' function. + * In the current function 'sc.func', we are calling 'fd'. + * 1. Check to see if the current function can call 'fd' , issue error if not. + * 2. If the current function is not the parent of 'fd' , then add + * the current function to the list of siblings of 'fd' . * 3. If the current function is a literal, and it's accessing an uplevel scope, * then mark it as a delegate. * Returns true if error occurs. */ -bool checkNestedReference(FuncDeclaration fd, Scope* sc, const ref Loc loc) +bool checkNestedFuncReference(FuncDeclaration fd, Scope* sc, Loc loc) { - //printf("FuncDeclaration::checkNestedReference() %s\n", toPrettyChars()); + //printf("FuncDeclaration::checkNestedFuncReference() %s\n", toPrettyChars()); if (auto fld = fd.isFuncLiteralDeclaration()) { if (fld.tok == TOK.reserved) @@ -2274,51 +2350,51 @@ bool checkNestedReference(FuncDeclaration fd, Scope* sc, const ref Loc loc) ensureStaticLinkTo(fdthis, p); if (p != p2) ensureStaticLinkTo(fdthis, p2); - if (fd.isNested()) + if (!fd.isNested()) + return false; + + // The function that this function is in + bool checkEnclosing(FuncDeclaration fdv) { - // The function that this function is in - bool checkEnclosing(FuncDeclaration fdv) - { - if (!fdv) - return false; - if (fdv == fdthis) - return false; - //printf("this = %s in [%s]\n", this.toChars(), this.loc.toChars()); - //printf("fdv = %s in [%s]\n", fdv .toChars(), fdv .loc.toChars()); - //printf("fdthis = %s in [%s]\n", fdthis.toChars(), fdthis.loc.toChars()); - // Add this function to the list of those which called us - if (fdthis != fd) + if (!fdv) + return false; + if (fdv == fdthis) + return false; + //printf("this = %s in [%s]\n", this.toChars(), this.loc.toChars()); + //printf("fdv = %s in [%s]\n", fdv .toChars(), fdv .loc.toChars()); + //printf("fdthis = %s in [%s]\n", fdthis.toChars(), fdthis.loc.toChars()); + // Add this function to the list of those which called us + if (fdthis != fd) + { + bool found = false; + for (size_t i = 0; i < fd.siblingCallers.length; ++i) { - bool found = false; - for (size_t i = 0; i < fd.siblingCallers.length; ++i) - { - if (fd.siblingCallers[i] == fdthis) - found = true; - } - if (!found) + if (fd.siblingCallers[i] == fdthis) + found = true; + } + if (!found) + { + //printf("\tadding sibling %s to %s\n", fdthis.toPrettyChars(), toPrettyChars()); + if (!sc.intypeof && !sc.traitsCompiles) { - //printf("\tadding sibling %s to %s\n", fdthis.toPrettyChars(), toPrettyChars()); - if (!sc.intypeof && !(sc.flags & SCOPE.compile)) - { - fd.siblingCallers.push(fdthis); - fd.computedEscapingSiblings = false; - } + fd.siblingCallers.push(fdthis); + fd.computedEscapingSiblings = false; } } - const lv = fdthis.getLevelAndCheck(loc, sc, fdv, fd); - if (lv == fd.LevelError) - return true; // error - if (lv == -1) - return false; // downlevel call - if (lv == 0) - return false; // same level call - return false; // Uplevel call - } - if (checkEnclosing(p.isFuncDeclaration())) - return true; - if (checkEnclosing(p == p2 ? null : p2.isFuncDeclaration())) - return true; + } + const lv = fdthis.getLevelAndCheck(loc, sc, fdv, fd); + if (lv == fd.LevelError) + return true; // error + if (lv == -1) + return false; // downlevel call + if (lv == 0) + return false; // same level call + return false; // Uplevel call } + if (checkEnclosing(p.isFuncDeclaration())) + return true; + if (checkEnclosing(p == p2 ? null : p2.isFuncDeclaration())) + return true; return false; } @@ -2357,7 +2433,7 @@ void buildResultVar(FuncDeclaration fd, Scope* sc, Type tret) if (sc && fd.vresult.semanticRun == PASS.initial) { TypeFunction tf = fd.type.toTypeFunction(); - if (tf.isref) + if (tf.isRef) fd.vresult.storage_class |= STC.ref_; fd.vresult.type = tret; fd.vresult.dsymbolSemantic(sc); @@ -2457,33 +2533,31 @@ Statement mergeFrequireInclusivePreview(FuncDeclaration fd, Statement sf, Expres sc.pop(); } sf = fdv.mergeFrequireInclusivePreview(sf, params); - if (sf && fdv.fdrequire) - { - const loc = fd.fdrequire.loc; - //printf("fdv.frequire: %s\n", fdv.frequire.toChars()); - /* Make the call: - * try { frequire; } - * catch (Throwable thr) { __require(params); assert(false, "Logic error: " ~ thr.msg); } - */ - Identifier id = Identifier.generateId("thr"); - params = Expression.arraySyntaxCopy(params); - Expression e = new CallExp(loc, new VarExp(loc, fdv.fdrequire, false), params); - Statement s2 = new ExpStatement(loc, e); - // assert(false, ...) - // TODO make this a runtime helper to allow: - // - chaining the original expression - // - nogc concatenation - Expression msg = new StringExp(loc, "Logic error: in-contract was tighter than parent in-contract"); - Statement fail = new ExpStatement(loc, new AssertExp(loc, IntegerExp.literal!0, msg)); - Statement s3 = new CompoundStatement(loc, s2, fail); - auto c = new Catch(loc, getThrowable(), id, s3); - c.internalCatch = true; - auto catches = new Catches(); - catches.push(c); - sf = new TryCatchStatement(loc, sf, catches); - } - else + if (!sf || !fdv.fdrequire) return null; + + const loc = fd.fdrequire.loc; + //printf("fdv.frequire: %s\n", fdv.frequire.toChars()); + /* Make the call: + * try { frequire; } + * catch (Throwable thr) { __require(params); assert(false, "Logic error: " ~ thr.msg); } + */ + Identifier id = Identifier.generateId("thr"); + params = Expression.arraySyntaxCopy(params); + Expression e = new CallExp(loc, new VarExp(loc, fdv.fdrequire, false), params); + Statement s2 = new ExpStatement(loc, e); + // assert(false, ...) + // TODO make this a runtime helper to allow: + // - chaining the original expression + // - nogc concatenation + Expression msg = new StringExp(loc, "Logic error: in-contract was tighter than parent in-contract"); + Statement fail = new ExpStatement(loc, new AssertExp(loc, IntegerExp.literal!0, msg)); + Statement s3 = new CompoundStatement(loc, s2, fail); + auto c = new Catch(loc, getThrowable(), id, s3); + c.internalCatch = true; + auto catches = new Catches(); + catches.push(c); + sf = new TryCatchStatement(loc, sf, catches); } return sf; } @@ -2578,11 +2652,11 @@ void buildEnsureRequire(FuncDeclaration thisfd) auto fo = cast(TypeFunction)(thisfd.originalType ? thisfd.originalType : f); auto fparams = toRefCopy(fo.parameterList); auto tf = new TypeFunction(ParameterList(fparams), Type.tvoid, LINK.d); - tf.isnothrow = f.isnothrow; - tf.isnogc = f.isnogc; + tf.isNothrow = f.isNothrow; + tf.isNogc = f.isNogc; tf.purity = f.purity; tf.trust = f.trust; - auto fd = new FuncDeclaration(loc, loc, Id.require, STC.undefined_, tf); + auto fd = new FuncDeclaration(loc, loc, Id.require, STC.none, tf); fd.fbody = thisfd.frequire; Statement s1 = new ExpStatement(loc, fd); Expression e = new CallExp(loc, new VarExp(loc, fd, false), thisfd.fdrequireParams); @@ -2619,11 +2693,11 @@ void buildEnsureRequire(FuncDeclaration thisfd) auto fo = cast(TypeFunction)(thisfd.originalType ? thisfd.originalType : f); fparams.pushSlice((*toRefCopy(fo.parameterList))[]); auto tf = new TypeFunction(ParameterList(fparams), Type.tvoid, LINK.d); - tf.isnothrow = f.isnothrow; - tf.isnogc = f.isnogc; + tf.isNothrow = f.isNothrow; + tf.isNogc = f.isNogc; tf.purity = f.purity; tf.trust = f.trust; - auto fd = new FuncDeclaration(loc, loc, Id.ensure, STC.undefined_, tf); + auto fd = new FuncDeclaration(loc, loc, Id.ensure, STC.none, tf); fd.fbody = thisfd.fensure; Statement s1 = new ExpStatement(loc, fd); Expression e = new CallExp(loc, new VarExp(loc, fd, false), thisfd.fdensureParams); @@ -2685,40 +2759,40 @@ Statement mergeFensure(FuncDeclaration fd, Statement sf, Identifier oid, Express sc.pop(); } sf = fdv.mergeFensure(sf, oid, params); - if (fdv.fdensure) + if (!fdv.fdensure) + continue; + + //printf("fdv.fensure: %s\n", fdv.fensure.toChars()); + // Make the call: __ensure(result, params) + params = Expression.arraySyntaxCopy(params); + if (fd.canBuildResultVar()) { - //printf("fdv.fensure: %s\n", fdv.fensure.toChars()); - // Make the call: __ensure(result, params) - params = Expression.arraySyntaxCopy(params); - if (fd.canBuildResultVar()) + Type t1 = fdv.type.nextOf().toBasetype(); + Type t2 = fd.type.nextOf().toBasetype(); + if (t1.isBaseOf(t2, null)) { - Type t1 = fdv.type.nextOf().toBasetype(); - Type t2 = fd.type.nextOf().toBasetype(); - if (t1.isBaseOf(t2, null)) - { - /* Making temporary reference variable is necessary - * in covariant return. - * https://issues.dlang.org/show_bug.cgi?id=5204 - * https://issues.dlang.org/show_bug.cgi?id=10479 - */ - Expression* eresult = &(*params)[0]; - auto ei = new ExpInitializer(Loc.initial, *eresult); - auto v = new VarDeclaration(Loc.initial, t1, Identifier.generateId("__covres"), ei); - v.storage_class |= STC.temp; - auto de = new DeclarationExp(Loc.initial, v); - auto ve = new VarExp(Loc.initial, v); - *eresult = new CommaExp(Loc.initial, de, ve); - } - } - Expression e = new CallExp(fd.loc, new VarExp(fd.loc, fdv.fdensure, false), params); - Statement s2 = new ExpStatement(fd.loc, e); - if (sf) - { - sf = new CompoundStatement(sf.loc, s2, sf); + /* Making temporary reference variable is necessary + * in covariant return. + * https://issues.dlang.org/show_bug.cgi?id=5204 + * https://issues.dlang.org/show_bug.cgi?id=10479 + */ + Expression* eresult = &(*params)[0]; + auto ei = new ExpInitializer(Loc.initial, *eresult); + auto v = new VarDeclaration(Loc.initial, t1, Identifier.generateId("__covres"), ei); + v.storage_class |= STC.temp; + auto de = new DeclarationExp(Loc.initial, v); + auto ve = new VarExp(Loc.initial, v); + *eresult = new CommaExp(Loc.initial, de, ve); } - else - sf = s2; } + Expression e = new CallExp(fd.loc, new VarExp(fd.loc, fdv.fdensure, false), params); + Statement s2 = new ExpStatement(fd.loc, e); + if (sf) + { + sf = new CompoundStatement(sf.loc, s2, sf); + } + else + sf = s2; } return sf; } @@ -2736,7 +2810,6 @@ Statement mergeFensure(FuncDeclaration fd, Statement sf, Identifier oid, Express */ void modifyReturns(FuncLiteralDeclaration fld, Scope* sc, Type tret) { - import dmd.statement_rewrite_walker; extern (C++) final class RetWalker : StatementRewriteWalker { alias visit = typeof(super).visit; @@ -2782,119 +2855,9 @@ void modifyReturns(FuncLiteralDeclaration fld, Scope* sc, Type tret) * Returns: `true` if the provided scope is the root * of the traits compiles list of scopes. */ -bool isRootTraitsCompilesScope(Scope* sc) +bool isRootTraitsCompilesScope(Scope* sc) @safe { - return (sc.flags & SCOPE.compile) && !(sc.func.flags & SCOPE.compile); -} - -/************************************** - * A statement / expression in this scope is not `@safe`, - * so mark the enclosing function as `@system` - * - * Params: - * sc = scope that the unsafe statement / expression is in - * gag = surpress error message (used in escape.d) - * loc = location of error - * fmt = printf-style format string - * arg0 = (optional) argument for first %s format specifier - * arg1 = (optional) argument for second %s format specifier - * arg2 = (optional) argument for third %s format specifier - * Returns: whether there's a safe error - */ -bool setUnsafe(Scope* sc, - bool gag = false, Loc loc = Loc.init, const(char)* fmt = null, - RootObject arg0 = null, RootObject arg1 = null, RootObject arg2 = null) -{ - if (sc.intypeof) - return false; // typeof(cast(int*)0) is safe - - if (sc.flags & SCOPE.debug_) // debug {} scopes are permissive - return false; - - if (!sc.func) - { - if (sc.varDecl) - { - if (sc.varDecl.storage_class & STC.safe) - { - .error(loc, fmt, arg0 ? arg0.toChars() : "", arg1 ? arg1.toChars() : "", arg2 ? arg2.toChars() : ""); - return true; - } - else if (!(sc.varDecl.storage_class & STC.trusted)) - { - sc.varDecl.storage_class |= STC.system; - sc.varDecl.systemInferred = true; - } - } - return false; - } - - - if (isRootTraitsCompilesScope(sc)) // __traits(compiles, x) - { - if (sc.func.isSafeBypassingInference()) - { - // Message wil be gagged, but still call error() to update global.errors and for - // -verrors=spec - .error(loc, fmt, arg0 ? arg0.toChars() : "", arg1 ? arg1.toChars() : "", arg2 ? arg2.toChars() : ""); - return true; - } - return false; - } - - return sc.func.setUnsafe(gag, loc, fmt, arg0, arg1, arg2); -} - -/*************************************** - * Like `setUnsafe`, but for safety errors still behind preview switches - * - * Given a `FeatureState fs`, for example dip1000 / dip25 / systemVariables, - * the behavior changes based on the setting: - * - * - In case of `-revert=fs`, it does nothing. - * - In case of `-preview=fs`, it's the same as `setUnsafe` - * - By default, print a deprecation in `@safe` functions, or store an attribute violation in inferred functions. - * - * Params: - * sc = used to find affected function/variable, and for checking whether we are in a deprecated / speculative scope - * fs = feature state from the preview flag - * gag = surpress error message - * loc = location of error - * msg = printf-style format string - * arg0 = (optional) argument for first %s format specifier - * arg1 = (optional) argument for second %s format specifier - * arg2 = (optional) argument for third %s format specifier - * Returns: whether an actual safe error (not deprecation) occured - */ -bool setUnsafePreview(Scope* sc, FeatureState fs, bool gag, Loc loc, const(char)* msg, - RootObject arg0 = null, RootObject arg1 = null, RootObject arg2 = null) -{ - //printf("setUnsafePreview() fs:%d %s\n", fs, msg); - with (FeatureState) final switch (fs) - { - case disabled: - return false; - - case enabled: - return sc.setUnsafe(gag, loc, msg, arg0, arg1, arg2); - - case default_: - if (!sc.func) - return false; - if (sc.func.isSafeBypassingInference()) - { - if (!gag && !sc.isDeprecated()) - { - deprecation(loc, msg, arg0 ? arg0.toChars() : "", arg1 ? arg1.toChars() : "", arg2 ? arg2.toChars() : ""); - } - } - else if (!sc.func.safetyViolation) - { - import dmd.func : AttributeViolation; - sc.func.safetyViolation = new AttributeViolation(loc, msg, arg0, arg1, arg2); - } - return false; - } + return (sc.traitsCompiles) && !sc.func.skipCodegen; } /+ @@ -3027,11 +2990,12 @@ extern (D) void checkMain(FuncDeclaration fd) */ extern (D) bool checkNRVO(FuncDeclaration fd) { + //printf("checkNRVO*() %s\n", fd.ident.toChars()); if (!fd.isNRVO() || fd.returns is null) return false; auto tf = fd.type.toTypeFunction(); - if (tf.isref) + if (tf.isRef) return false; foreach (rs; *fd.returns) @@ -3041,7 +3005,7 @@ extern (D) bool checkNRVO(FuncDeclaration fd) auto v = ve.var.isVarDeclaration(); if (!v || v.isReference()) return false; - else if (fd.nrvo_var is null) + if (fd.nrvo_var is null) { // Variables in the data segment (e.g. globals, TLS or not), // parameters and closure variables cannot be NRVOed. @@ -3076,23 +3040,31 @@ extern (D) bool checkNRVO(FuncDeclaration fd) * Params: * fd = function declaration to mark * loc = location of impure action - * fmt = format string for error message. Must include "%s `%s`" for the function kind and name. - * arg0 = (optional) argument to format string + * fmt = format string for error message + * args = argument to format string * * Returns: `true` if there's a purity error */ -extern (D) bool setImpure(FuncDeclaration fd, Loc loc = Loc.init, const(char)* fmt = null, RootObject arg0 = null) +extern (D) bool setImpure(FuncDeclaration fd, Loc loc, const(char)* fmt, RootObject[] args...) { if (fd.purityInprocess) { fd.purityInprocess = false; if (fmt) - fd.pureViolation = new AttributeViolation(loc, fmt, fd, arg0); // impure action - else if (arg0) - fd.pureViolation = new AttributeViolation(loc, fmt, arg0); // call to impure function + fd.pureViolation = new AttributeViolation(loc, fmt, args); // impure action + else if (args.length > 0) + { + if (auto sa = args[0].isDsymbol()) + { + if (FuncDeclaration fd2 = sa.isFuncDeclaration()) + { + fd.pureViolation = new AttributeViolation(loc, fd2); // call to impure function + } + } + } if (fd.fes) - fd.fes.func.setImpure(loc, fmt, arg0); + fd.fes.func.setImpure(loc, fmt, args); } else if (fd.isPure()) return true; @@ -3106,7 +3078,7 @@ PURE isPure(FuncDeclaration fd) TypeFunction tf = fd.type.toTypeFunction(); if (fd.purityInprocess) - fd.setImpure(); + fd.setImpure(Loc.initial, null); if (tf.purity == PURE.fwdref) tf.purityLevel(); PURE purity = tf.purity; @@ -3350,7 +3322,7 @@ extern (D) bool isReturnIsolated(FuncDeclaration fd) assert(tf.next); Type treti = tf.next; - if (tf.isref) + if (tf.isRef) return fd.isTypeIsolatedIndirect(treti); // check influence from parameters return fd.isTypeIsolated(treti); @@ -3428,3 +3400,176 @@ extern (D) bool isTypeIsolated(FuncDeclaration fd, Type t, ref StringTable!Type return true; } } + +/** + * Check signature of `pragma(printf)` function, print error if invalid. + * + * printf/scanf-like functions must be of the form: + * extern (C/C++) T printf([parameters...], const(char)* format, ...); + * or: + * extern (C/C++) T vprintf([parameters...], const(char)* format, va_list); + * + * Params: + * funcdecl = function to check + * f = function type + * sc = scope + */ +private void checkPrintfScanfSignature(FuncDeclaration funcdecl, TypeFunction f, Scope* sc) +{ + static bool isPointerToChar(Parameter p) + { + if (auto tptr = p.type.isTypePointer()) + { + return tptr.next.ty == Tchar; + } + return false; + } + + bool isVa_list(Parameter p) + { + return p.type.equals(target.va_listType(funcdecl.loc, sc)); + } + + const nparams = f.parameterList.length; + const p = (funcdecl.printf ? Id.printf : Id.scanf).toChars(); + if (!(f.linkage == LINK.c || f.linkage == LINK.cpp)) + { + .error(funcdecl.loc, "`pragma(%s)` function `%s` must have `extern(C)` or `extern(C++)` linkage," + ~" not `extern(%s)`", + p, funcdecl.toChars(), f.linkage.linkageToChars()); + } + if (f.parameterList.varargs == VarArg.variadic) + { + if (!(nparams >= 1 && isPointerToChar(f.parameterList[nparams - 1]))) + { + .error(funcdecl.loc, "`pragma(%s)` function `%s` must have" + ~ " signature `%s %s([parameters...], const(char)*, ...)` not `%s`", + p, funcdecl.toChars(), f.next.toChars(), funcdecl.toChars(), funcdecl.type.toChars()); + } + } + else if (f.parameterList.varargs == VarArg.none) + { + if(!(nparams >= 2 && isPointerToChar(f.parameterList[nparams - 2]) && + isVa_list(f.parameterList[nparams - 1]))) + .error(funcdecl.loc, "`pragma(%s)` function `%s` must have"~ + " signature `%s %s([parameters...], const(char)*, va_list)`", + p, funcdecl.toChars(), f.next.toChars(), funcdecl.toChars()); + } + else + { + .error(funcdecl.loc, "`pragma(%s)` function `%s` must have C-style variadic `...` or `va_list` parameter", + p, funcdecl.toChars()); + } +} + +/*************************************************** + * Visit each overloaded function/template in turn, and call dg(s) on it. + * Exit when no more, or dg(s) returns nonzero. + * + * Params: + * fstart = symbol to start from + * dg = the delegate to be called on the overload + * sc = context used to check if symbol is accessible (and therefore visible), + * can be null + * + * Returns: + * ==0 continue + * !=0 done (and the return value from the last dg() call) + */ +extern (D) int overloadApply(Dsymbol fstart, scope int delegate(Dsymbol) dg, Scope* sc = null) +{ + Dsymbols visited; + + 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) + { + import dmd.access : checkSymbolAccess; + if (auto od = d.isOverDeclaration()) + { + /* The scope is needed here to check whether a function in + an overload set was added by means of a private alias (or a + selective import). If the scope where the alias is created + is imported somewhere, the overload set is visible, but the private + alias is not. + */ + if (sc) + { + if (checkSymbolAccess(sc, od)) + { + if (int r = overloadApplyRecurse(od.aliassym, dg, sc)) + return r; + } + } + else if (int r = overloadApplyRecurse(od.aliassym, dg, sc)) + return r; + next = od.overnext; + } + else if (auto fa = d.isFuncAliasDeclaration()) + { + if (fa.hasOverloads) + { + if (int r = overloadApplyRecurse(fa.funcalias, dg, sc)) + return r; + } + else if (auto fd = fa.toAliasFunc()) + { + if (int r = dg(fd)) + return r; + } + else + { + .error(d.loc, "%s `%s` is aliased to a function", d.kind, d.toPrettyChars); + break; + } + next = fa.overnext; + } + else if (auto ad = d.isAliasDeclaration()) + { + if (sc) + { + if (checkSymbolAccess(sc, ad)) + next = ad.toAlias(); + } + else + next = ad.toAlias(); + if (next == ad) + break; + if (next == fstart) + break; + } + else if (auto td = d.isTemplateDeclaration()) + { + if (int r = dg(td)) + return r; + next = td.overnext; + } + else if (auto fd = d.isFuncDeclaration()) + { + if (int r = dg(fd)) + return r; + next = fd.overnext; + } + else if (auto os = d.isOverloadSet()) + { + foreach (ds; os.a) + if (int r = dg(ds)) + return r; + } + else + { + .error(d.loc, "%s `%s` is aliased to a function", d.kind, d.toPrettyChars); + break; + // BUG: should print error message? + } + } + return 0; + } + return overloadApplyRecurse(fstart, dg, sc); +} diff --git a/dmd/globals.d b/dmd/globals.d index e1b9c46606..b973db0d05 100644 --- a/dmd/globals.d +++ b/dmd/globals.d @@ -1,12 +1,12 @@ /** * Stores command line options and contains other miscellaneous declarations. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/globals.d, _globals.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/globals.d, _globals.d) * Documentation: https://dlang.org/phobos/dmd_globals.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/globals.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/globals.d */ module dmd.globals; @@ -79,6 +79,7 @@ enum CppStdRevision : uint cpp14 = 2014_02, cpp17 = 2017_03, cpp20 = 2020_02, + cpp23 = 2023_02, } /// Trivalent boolean to represent the state of a `revert`able change @@ -99,6 +100,13 @@ enum CLIIdentifierTable : ubyte All = 4, /// The least restrictive set of all other tables } +/// Specifies the mode for error printing +enum ErrorPrintMode : ubyte +{ + simpleError, // Print errors without squiggles and carets + printErrorContext, // Print errors with context (source line and caret) +} + version (IN_LLVM) { enum LinkonceTemplates : byte @@ -160,15 +168,15 @@ extern(C++) struct Verbose bool complex = true; // identify complex/imaginary type usage bool vin; // identify 'in' parameters bool showGaggedErrors; // print gagged errors anyway - bool printErrorContext; // print errors with the error context (the error line in the source file) bool logo; // print compiler logo bool color; // use ANSI colors in console output bool cov; // generate code coverage data + ErrorPrintMode errorPrintMode; // enum for error printing mode MessageStyle messageStyle = MessageStyle.digitalmars; // style of file/line annotations on messages uint errorLimit = 20; uint errorSupplementLimit = 6; // Limit the number of supplemental messages for each error (0 means unlimited) - uint errorSupplementCount() + uint errorSupplementCount() @safe { if (verbose) return uint.max; @@ -178,10 +186,15 @@ extern(C++) struct Verbose } } +extern (C++) struct ImportPathInfo { + const(char)* path; // char*'s of where to look for import modules +} + /// Put command line switches in here extern (C++) struct Param { bool obj = true; // write object file + bool readStdin; // saw "-" on command line, read source file from stdin bool multiobj; // break one object file into multiple ones bool trace; // insert profiling hooks bool tracegc; // instrument calls to 'new' @@ -191,7 +204,7 @@ extern (C++) struct Param bool useInline = false; // inline expand functions bool release; // build release version bool preservePaths; // true means don't strip path from source file - DiagnosticReporting warnings = DiagnosticReporting.off; // how compiler warnings are handled + DiagnosticReporting useWarnings = DiagnosticReporting.off; // how compiler warnings are handled bool cov; // generate code coverage data ubyte covPercent; // 0..100 code coverage percentage required bool ctfe_cov = false; // generate coverage data for ctfe @@ -222,6 +235,8 @@ extern (C++) struct Param // https://gist.github.com/andralex/e5405a5d773f07f73196c05f8339435a // https://digitalmars.com/d/archives/digitalmars/D/Binding_rvalues_to_ref_parameters_redux_325087.html // Implementation: https://github.com/dlang/dmd/pull/9817 + FeatureState safer; // safer by default (more @safe checks in unattributed code) + // https://github.com/WalterBright/documents/blob/38f0a846726b571f8108f6e63e5e217b91421c86/safer.md FeatureState noSharedAccess; // read/write access to shared memory objects bool previewIn; // `in` means `[ref] scope const`, accepts rvalues bool inclusiveInContracts; // 'in' contracts of overridden methods must be a superset of parent contract @@ -248,7 +263,7 @@ extern (C++) struct Param const(char)[] argv0; // program name Array!(const(char)*) modFileAliasStrings; // array of char*'s of -I module filename alias strings - Array!(const(char)*) imppath; // array of char*'s of where to look for import modules + Array!(ImportPathInfo) imppath; // array of import path information of where to look for import modules Array!(const(char)*) fileImppath; // array of char*'s of where to look for file import modules const(char)[] objdir; // .obj/.lib file output directory const(char)[] objname; // .obj file output name @@ -263,8 +278,7 @@ extern (C++) struct Param Output mixinOut; // write expanded mixins for debugging Output moduleDeps; // Generate `.deps` module dependencies - uint debuglevel; // debug level - uint versionlevel; // version level + bool debugEnabled; // Global -debug flag (no -debug=XXX) is active bool run; // run resulting executable Strings runargs; // arguments for executable @@ -282,6 +296,13 @@ extern (C++) struct Param const(char)[] exefile; const(char)[] mapfile; + bool fullyQualifiedObjectFiles; // prepend module names to object files to prevent name conflicts with -od + + // Time tracing + bool timeTrace = false; /// Whether profiling of compile time is enabled + uint timeTraceGranularityUs = 500; /// In microseconds, minimum event size to report + const(char)* timeTraceFile; /// File path of output file + version (IN_LLVM) { // stuff which was extracted upstream into `driverParams` global: @@ -301,7 +322,6 @@ version (IN_LLVM) OUTPUTFLAG output_o; bool useInlineAsm; bool verbose_cg; - bool fullyQualifiedObjectFiles; bool cleanupObjectFiles; // Profile-guided optimization: @@ -328,7 +348,7 @@ version (IN_LLVM) } // IN_LLVM /// - bool parsingUnittestsRequired() + bool parsingUnittestsRequired() @safe { return useUnitTests || ddoc.doOutput || dihdr.doOutput; } @@ -363,10 +383,11 @@ extern (C++) struct Global { const(char)[] inifilename; /// filename of configuration file as given by `-conf=`, or default value - string copyright = "Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved"; + string copyright = "Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved"; string written = "written by Walter Bright"; - Array!(const(char)*) path; /// Array of char*'s which form the import lookup path + Array!(ImportPathInfo) path; /// Array of path informations which form the import lookup path + Array!(const(char)*) importPaths; /// Array of char*'s which form the import lookup path without metadata Array!(const(char)*) filePath; /// Array of char*'s which form the file import lookup path char[26] datetime; /// string returned by ctime() @@ -414,7 +435,7 @@ version (IN_LLVM) } else { - extern (C++) DArray!ubyte function(FileName, ref const Loc, ref OutBuffer) preprocess; + extern (C++) DArray!ubyte function(FileName, Loc, ref OutBuffer) preprocess; } nothrow: @@ -493,14 +514,8 @@ else params.v.color = detectTerminal(); } -version (IN_LLVM) -{ + params.v.errorPrintMode = ErrorPrintMode.printErrorContext; // Enable error context globally by default compileEnv.versionNumber = parseVersionNumber(versionString()); -} -else -{ - compileEnv.versionNumber = parseVersionNumber(_version); -} /* Initialize date, time, and timestamp */ @@ -576,6 +591,17 @@ else return major * 1000 + minor; } + /** + * Indicate to stateful error sinks that no more errors can be produced. + * This is to support error sinks that collect information to produce a + * single (say) report. + */ + extern(C++) void plugErrorSinks() + { + global.errorSink.plugSink(); + global.errorSinkNull.plugSink(); + } + /** Returns: the version as the number that would be returned for __VERSION__ */ diff --git a/dmd/globals.h b/dmd/globals.h index 42127a8409..c630dd304f 100644 --- a/dmd/globals.h +++ b/dmd/globals.h @@ -1,6 +1,6 @@ /* Compiler implementation of the D programming language - * Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * written by Walter Bright * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. @@ -50,7 +50,8 @@ enum enum class MessageStyle : unsigned char { digitalmars, // file(line,column): message - gnu // file:line:column: message + gnu, // file:line:column: message + sarif // JSON SARIF output, see https://docs.oasis-open.org/sarif/sarif/v2.1.0/sarif-v2.1.0.html }; // The state of array bounds checking @@ -87,7 +88,8 @@ enum CppStdRevision CppStdRevisionCpp11 = 201103, CppStdRevisionCpp14 = 201402, CppStdRevisionCpp17 = 201703, - CppStdRevisionCpp20 = 202002 + CppStdRevisionCpp20 = 202002, + CppStdRevisionCpp23 = 202302, }; /// Trivalent boolean to represent the state of a `revert`able change @@ -108,6 +110,13 @@ enum class CLIIdentifierTable : unsigned char All = 4, /// The least restrictive set of all other tables }; +/// Specifies the mode for error printing +enum class ErrorPrintMode : unsigned char +{ + simpleError, // Print errors without squiggles and carets + printErrorContext, // Print errors with the error line and caret +}; + #if IN_LLVM enum class LinkonceTemplates : char { @@ -168,20 +177,29 @@ struct Verbose d_bool complex = true; // identify complex/imaginary type usage d_bool vin; // identify 'in' parameters d_bool showGaggedErrors; // print gagged errors anyway - d_bool printErrorContext; // print errors with the error context (the error line in the source file) d_bool logo; // print compiler logo d_bool color; // use ANSI colors in console output d_bool cov; // generate code coverage data + ErrorPrintMode errorPrintMode; // enum for error printing mode MessageStyle messageStyle; // style of file/line annotations on messages unsigned errorLimit; unsigned errorSupplementLimit; // Limit the number of supplemental messages for each error (0 means unlimited) unsigned errorSupplementCount(); }; +struct ImportPathInfo +{ + const char* path; + + ImportPathInfo() : path(NULL) { } + ImportPathInfo(const char* p) : path(p) { } +}; + // Put command line switches in here struct Param { d_bool obj; // write object file + d_bool readStdin; // read source file from stdin d_bool multiobj; // break one object file into multiple ones d_bool trace; // insert profiling hooks d_bool tracegc; // instrument calls to 'new' @@ -191,7 +209,7 @@ struct Param d_bool useInline; // inline expand functions d_bool release; // build release version d_bool preservePaths; // true means don't strip path from source file - Diagnostic warnings; + Diagnostic useWarnings; d_bool cov; // generate code coverage data unsigned char covPercent; // 0..100 code coverage percentage required d_bool ctfe_cov; // generate coverage data for ctfe @@ -221,6 +239,9 @@ struct Param // https://gist.github.com/andralex/e5405a5d773f07f73196c05f8339435a // https://digitalmars.com/d/archives/digitalmars/D/Binding_rvalues_to_ref_parameters_redux_325087.html // Implementation: https://github.com/dlang/dmd/pull/9817 + FeatureState safer; // safer by default (more @safe checks in unattributed code) + // https://github.com/WalterBright/documents/blob/38f0a846726b571f8108f6e63e5e217b91421c86/safer.md + FeatureState noSharedAccess; // read/write access to shared memory objects d_bool previewIn; // `in` means `[ref] scope const`, accepts rvalues d_bool inclusiveInContracts; // 'in' contracts of overridden methods must be a superset of parent contract @@ -247,7 +268,7 @@ struct Param DString argv0; // program name Array modFileAliasStrings; // array of char*'s of -I module filename alias strings - Array imppath; // array of char*'s of where to look for import modules + Array imppath; // array of import path information of where to look for import modules Array fileImppath; // array of char*'s of where to look for file import modules DString objdir; // .obj/.lib file output directory DString objname; // .obj file output name @@ -262,8 +283,7 @@ struct Param Output mixinOut; // write expanded mixins for debugging Output moduleDeps; // Generate `.deps` module dependencies - unsigned debuglevel; // debug level - unsigned versionlevel; // version level + d_bool debugEnabled; // -debug flag is passed d_bool run; // run resulting executable Strings runargs; // arguments for executable @@ -281,6 +301,10 @@ struct Param DString resfile; DString exefile; DString mapfile; + bool fullyQualifiedObjectFiles; + bool timeTrace; + uint32_t timeTraceGranularityUs; + const char* timeTraceFile; #if IN_LLVM // stuff which was extracted upstream into `driverParams` global: @@ -300,7 +324,6 @@ struct Param OUTPUTFLAG output_o; bool useInlineAsm; bool verbose_cg; - bool fullyQualifiedObjectFiles; bool cleanupObjectFiles; // Profile-guided optimization: @@ -323,7 +346,7 @@ struct Param // Windows-specific: bool dllexport; // dllexport ~all defined symbols? DLLImport dllimport; // dllimport data symbols not defined in any root module? -#endif +#endif // IN_LLVM }; struct structalign_t @@ -369,6 +392,7 @@ struct CompileEnv DString vendor; DString timestamp; d_bool previewIn; + d_bool transitionIn; d_bool ddocOutput; d_bool masm; IdentifierCharLookup cCharLookupTable; @@ -381,8 +405,9 @@ struct Global const DString copyright; const DString written; - Array path; // Array of char*'s which form the import lookup path - Array filePath; // Array of char*'s which form the file import lookup path + Array path; // Array of path informations which form the import lookup path + Array importPaths; // Array of char*'s which form the import lookup path without metadata + Array filePath; // Array of char*'s which form the file import lookup path char datetime[26]; /// string returned by ctime() CompileEnv compileEnv; @@ -421,10 +446,9 @@ struct Global #if IN_LLVM FileName (*preprocess)(FileName, const Loc&, OutBuffer&); #else - DArray (*preprocess)(FileName, const Loc&, OutBuffer&); + DArray (*preprocess)(FileName, Loc, OutBuffer&); #endif - /* Start gagging. Return the current number of gagged errors */ unsigned startGagging(); @@ -442,6 +466,13 @@ struct Global void _init(); + /** + * Indicate to stateful error sinks that no more errors can be produced. + * This is to support error sinks that collect information to produce a + * single (say) report. + */ + void plugErrorSinks(); + /** Returns: the version as the number that would be returned for __VERSION__ */ @@ -475,43 +506,46 @@ typedef unsigned long long uinteger_t; #endif // file location +struct SourceLoc +{ + DString filename; + uint32_t line; + uint32_t column; + uint32_t fileOffset; + DString fileContent; +}; + struct Loc { private: - unsigned _linnum; - unsigned _charnum; - unsigned fileIndex; + + unsigned int index; + +#if MARS && defined(__linux__) && defined(__i386__) + unsigned int dummy; +#endif + public: static void set(bool showColumns, MessageStyle messageStyle); + static Loc singleFilename(const char* const filename); static bool showColumns; static MessageStyle messageStyle; Loc() { - _linnum = 0; - _charnum = 0; - fileIndex = 0; - } - - Loc(const char *filename, unsigned linnum, unsigned charnum) - { - this->linnum(linnum); - this->charnum(charnum); - this->filename(filename); + index = 0; } uint32_t charnum() const; - uint32_t charnum(uint32_t num); uint32_t linnum() const; - uint32_t linnum(uint32_t num); const char *filename() const; - void filename(const char *name); + SourceLoc toSourceLoc() const; const char *toChars( bool showColumns = Loc::showColumns, MessageStyle messageStyle = Loc::messageStyle) const; - bool equals(const Loc& loc) const; + bool equals(Loc loc) const; }; enum class LINK : uint8_t diff --git a/dmd/gluelayer.d b/dmd/gluelayer.d index 450c007041..af075b2d42 100644 --- a/dmd/gluelayer.d +++ b/dmd/gluelayer.d @@ -3,12 +3,12 @@ * * This 'glues' either the DMC or GCC back-end to the front-end. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/gluelayer.d, _gluelayer.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/gluelayer.d, _gluelayer.d) * Documentation: https://dlang.org/phobos/dmd_gluelayer.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/gluelayer.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/gluelayer.d */ module dmd.gluelayer; diff --git a/dmd/hdrgen.d b/dmd/hdrgen.d index d68d1aa279..bb9632ba87 100644 --- a/dmd/hdrgen.d +++ b/dmd/hdrgen.d @@ -3,12 +3,12 @@ * * Also used to convert AST nodes to D code in general, e.g. for error messages or `printf` debugging. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/hdrgen.d, _hdrgen.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/hdrgen.d, _hdrgen.d) * Documentation: https://dlang.org/phobos/dmd_hdrgen.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/hdrgen.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/hdrgen.d */ module dmd.hdrgen; @@ -59,10 +59,12 @@ struct HdrGenState bool ddoc; /// true if generating Ddoc file bool fullDump; /// true if generating a full AST dump file bool importcHdr; /// true if generating a .di file from an ImportC file + bool inCAlias; /// Set to prevent ImportC translating typedefs as `alias X = X` bool doFuncBodies; /// include function bodies in output bool vcg_ast; /// write out codegen-ast bool skipConstraints; // skip constraints when doing templates bool showOneMember = true; + bool errorMsg; /// true if formatting for inside an error message bool fullQual; /// fully qualify types when printing int tpltMember; @@ -102,6 +104,87 @@ version (IN_LLVM) toCBuffer(m, buf, hgs); } +/** + * Convert `o` to a string for error messages. + * Params: + * e = object to convert + * Returns: string representation of `e` + */ +const(char)* toErrMsg(const RootObject o) +{ + if (auto e = o.isExpression()) + return toErrMsg(e); + if (auto d = o.isDsymbol()) + return toErrMsg(d); + if (auto t = o.isType()) + return t.toChars(); + if (auto id = o.isIdentifier()) + return id.toChars(); + assert(0); +} + +/// ditto +const(char)* toErrMsg(const Expression e) +{ + HdrGenState hgs; + hgs.errorMsg = true; + OutBuffer buf; + toCBuffer(e, buf, hgs); + truncateForError(buf, 60); + + return buf.extractChars(); +} + +/// ditto +const(char)* toErrMsg(const Dsymbol d) +{ + if (d.isFuncDeclaration() || d.isTemplateInstance()) + { + if (d.ident && d.ident.toString.startsWith("__") && !d.isCtorDeclaration()) + { + HdrGenState hgs; + hgs.errorMsg = true; + OutBuffer buf; + toCBuffer(cast() d, buf, hgs); + truncateForError(buf, 80); + return buf.extractChars(); + } + } + + return d.toChars(); +} + +/** + * Make the content of `buf` fit inline for an error message. + * Params: + * buf = buffer with text to modify + * maxLength = truncate text when it exceeds this length + */ +private void truncateForError(ref OutBuffer buf, size_t maxLength) +{ + // Remove newlines, escape backticks ` by doubling them + for (size_t i = 0; i < buf.length; i++) + { + if (buf[i] == '\r') + buf.remove(i, 1); + if (buf[i] == '\n') + buf.peekSlice[i] = ' '; + if (buf[i] == '`') + i = buf.insert(i, "`"); + } + + // Strip trailing whitespace + while (buf.length && buf[$-1] == ' ') + buf.setsize(buf.length - 1); + + // Truncate + if (buf.length > maxLength) + { + buf.setsize(maxLength - 3); + buf.writestring("..."); + } +} + /*************************************** * Turn a Statement into a string suitable for printf. * Leaks memory. @@ -146,6 +229,42 @@ public const(char)* toChars(const Type t) return buf.extractChars(); } +public const(char)* toChars(const Dsymbol d) +{ + if (auto td = d.isTemplateDeclaration()) + { + HdrGenState hgs; + OutBuffer buf; + toCharsMaybeConstraints(td, buf, hgs); + return buf.extractChars(); + } + + if (auto ti = d.isTemplateInstance()) + { + OutBuffer buf; + toCBufferInstance(ti, buf); + return buf.extractChars(); + } + + if (auto tm = d.isTemplateMixin()) + { + OutBuffer buf; + toCBufferInstance(tm, buf); + return buf.extractChars(); + } + + if (auto tid = d.isTypeInfoDeclaration()) + { + OutBuffer buf; + buf.writestring("typeid("); + buf.writestring(tid.tinfo.toChars()); + buf.writeByte(')'); + return buf.extractChars(); + } + + return d.ident ? d.ident.toHChars2() : "__anonymous"; +} + public const(char)[] toString(const Initializer i) { OutBuffer buf; @@ -311,13 +430,12 @@ private void statementToBuffer(Statement s, ref OutBuffer buf, ref HdrGenState h buf.writenl(); } - void visitWhile(WhileStatement s) + void printConditionAssignment(Parameter p, Expression condition) { - buf.writestring("while ("); - if (auto p = s.param) + if (p) { // Print condition assignment - StorageClass stc = p.storageClass; + STC stc = p.storageClass; if (!p.type && !stc) stc = STC.auto_; if (stcToBuffer(buf, stc)) @@ -328,7 +446,13 @@ private void statementToBuffer(Statement s, ref OutBuffer buf, ref HdrGenState h buf.writestring(p.ident.toString()); buf.writestring(" = "); } - s.condition.expressionToBuffer(buf, hgs); + condition.expressionToBuffer(buf, hgs); + } + + void visitWhile(WhileStatement s) + { + buf.writestring("while ("); + printConditionAssignment(s.param, s.condition); buf.writeByte(')'); buf.writenl(); if (s._body) @@ -419,10 +543,10 @@ private void statementToBuffer(Statement s, ref OutBuffer buf, ref HdrGenState h { buf.writestring(Token.toString(s.op)); buf.writestring(" ("); - if (s.prm.type) - typeToBuffer(s.prm.type, s.prm.ident, buf, hgs); + if (s.param.type) + typeToBuffer(s.param.type, s.param.ident, buf, hgs); else - buf.writestring(s.prm.ident.toString()); + buf.writestring(s.param.ident.toString()); buf.writestring("; "); s.lwr.expressionToBuffer(buf, hgs); buf.writestring(" .. "); @@ -466,20 +590,7 @@ private void statementToBuffer(Statement s, ref OutBuffer buf, ref HdrGenState h void visitIf(IfStatement s) { buf.writestring("if ("); - if (Parameter p = s.prm) - { - StorageClass stc = p.storageClass; - if (!p.type && !stc) - stc = STC.auto_; - if (stcToBuffer(buf, stc)) - buf.writeByte(' '); - if (p.type) - typeToBuffer(p.type, p.ident, buf, hgs); - else - buf.writestring(p.ident.toString()); - buf.writestring(" = "); - } - s.condition.expressionToBuffer(buf, hgs); + printConditionAssignment(s.param, s.condition); buf.writeByte(')'); buf.writenl(); if (s.ifbody.isScopeStatement()) @@ -578,21 +689,7 @@ private void statementToBuffer(Statement s, ref OutBuffer buf, ref HdrGenState h void visitSwitch(SwitchStatement s) { buf.writestring(s.isFinal ? "final switch (" : "switch ("); - if (auto p = s.param) - { - // Print condition assignment - StorageClass stc = p.storageClass; - if (!p.type && !stc) - stc = STC.auto_; - if (stcToBuffer(buf, stc)) - buf.writeByte(' '); - if (p.type) - typeToBuffer(p.type, p.ident, buf, hgs); - else - buf.writestring(p.ident.toString()); - buf.writestring(" = "); - } - s.condition.expressionToBuffer(buf, hgs); + printConditionAssignment(s.param, s.condition); buf.writeByte(')'); buf.writenl(); if (s._body) @@ -908,10 +1005,7 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs) void visitDebugSymbol(DebugSymbol s) { buf.writestring("debug = "); - if (s.ident) - buf.writestring(s.ident.toString()); - else - buf.print(s.level); + buf.writestring(s.ident.toString()); buf.writeByte(';'); buf.writenl(); } @@ -919,10 +1013,7 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs) void visitVersionSymbol(VersionSymbol s) { buf.writestring("version = "); - if (s.ident) - buf.writestring(s.ident.toString()); - else - buf.print(s.level); + buf.writestring(s.ident.toString()); buf.writeByte(';'); buf.writenl(); } @@ -1196,14 +1287,14 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs) void foreachRangeWithoutBody(ForeachRangeStatement s) { - /* s.op ( prm ; lwr .. upr ) + /* s.op ( param ; lwr .. upr ) */ buf.writestring(Token.toString(s.op)); buf.writestring(" ("); - if (s.prm.type) - typeToBuffer(s.prm.type, s.prm.ident, buf, hgs); + if (s.param.type) + typeToBuffer(s.param.type, s.param.ident, buf, hgs); else - buf.writestring(s.prm.ident.toString()); + buf.writestring(s.param.ident.toString()); buf.writestring("; "); s.lwr.expressionToBuffer(buf, hgs); buf.writestring(" .. "); @@ -1656,7 +1747,7 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs) { if (d.storage_class & STC.local) return; - if (d.adFlags & d.hidden) + if (d.hidden) return; buf.writestring("alias "); if (d.aliassym) @@ -1693,7 +1784,9 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs) buf.writestring(" = "); if (stcToBuffer(buf, d.storage_class)) buf.writeByte(' '); + hgs.inCAlias = hgs.importcHdr; typeToBuffer(d.type, null, buf, hgs); + hgs.inCAlias = false; hgs.declstring = false; } buf.writeByte(';'); @@ -1724,7 +1817,10 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs) void visitFuncDeclaration(FuncDeclaration f) { //printf("FuncDeclaration::toCBuffer() '%s'\n", f.toChars()); - if (stcToBuffer(buf, f.storage_class)) + + // https://issues.dlang.org/show_bug.cgi?id=24891 + // return/scope storage classes are printed as part of function type + if (stcToBuffer(buf, f.storage_class & ~(STC.scope_ | STC.return_ | STC.returnScope))) buf.writeByte(' '); typeToBuffer(f.type, f.ident, buf, hgs); auto tf = f.type.isTypeFunction(); @@ -1767,7 +1863,7 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs) buf.writestring("__error"); return; } - if (f.tok != TOK.reserved) + if (f.tok != TOK.reserved && !hgs.errorMsg) { buf.writestring(f.kind()); buf.writeByte(' '); @@ -1784,8 +1880,9 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs) buf.writeByte(' '); buf.writestring(str); } - tf.attributesApply(&printAttribute); + if (!hgs.errorMsg) + tf.attributesApply(&printAttribute); CompoundStatement cs = f.fbody.isCompoundStatement(); Statement s1; @@ -2262,9 +2359,20 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt buf.writestring(e.ident.toString()); } - void visitDsymbol(DsymbolExp e) + void visitDsymbol(Dsymbol s) + { + // For -vcg-ast, print internal names such as __invariant, __ctor etc. + // This condition is a bit kludge, and can be cleaned up if the + // mutual dependency `AST.toChars <> hdrgen.d` gets refactored + if (hgs.vcg_ast && s.ident && !s.isTemplateInstance() && !s.isTemplateDeclaration()) + buf.writestring(s.ident.toChars()); + else + buf.writestring(s.toChars()); + } + + void visitDsymbolExp(DsymbolExp e) { - buf.writestring(e.s.toChars()); + visitDsymbol(e.s); } void visitThis(ThisExp e) @@ -2369,12 +2477,12 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt // to themselves, need to avoid infinite recursion: // struct S { this(int){ this.s = &this; } S* s; } // const foo = new S(0); - if (e.stageflags & stageToCBuffer) + if (e.stageflags & StructLiteralExp.StageFlags.toCBuffer) buf.writestring(""); else { const old = e.stageflags; - e.stageflags |= stageToCBuffer; + e.stageflags |= StructLiteralExp.StageFlags.toCBuffer; argsToBuffer(e.elements, buf, hgs); e.stageflags = old; } @@ -2429,6 +2537,13 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt buf.writeByte('.'); } buf.writestring("new "); + if (e.placement) + { + buf.writeByte('('); + expToBuffer(e.placement, PREC.assign, buf, hgs); + buf.writeByte(')'); + buf.writeByte(' '); + } typeToBuffer(e.newtype, null, buf, hgs); if (e.arguments && e.arguments.length) { @@ -2446,6 +2561,13 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt buf.writeByte('.'); } buf.writestring("new"); + if (e.placement) + { + buf.writeByte(' '); + buf.writeByte('('); + expToBuffer(e.placement, PREC.assign, buf, hgs); + buf.writeByte(')'); + } buf.writestring(" class "); if (e.arguments && e.arguments.length) { @@ -2460,7 +2582,7 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt void visitSymOff(SymOffExp e) { if (e.offset) - buf.printf("(& %s%+lld)", e.var.toChars(), e.offset); + buf.printf("(& %s + %llu)", e.var.toChars(), e.offset); else if (e.var.isTypeInfoDeclaration()) buf.writestring(e.var.toChars()); else @@ -2469,7 +2591,7 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt void visitVar(VarExp e) { - buf.writestring(e.var.toChars()); + visitDsymbol(e.var); } void visitOver(OverExp e) @@ -2615,9 +2737,9 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt // the `sideeffect.copyToTemp` function. auto ve = e.e2.isVarExp(); - // not a CommaExp introduced for temporaries, go on - // the old path - if (!ve || !(ve.var.storage_class & STC.temp)) + // Not a CommaExp introduced for temporaries, or -vcg-ast, + // print the full comma + if (!ve || !(ve.var.storage_class & STC.temp) || hgs.vcg_ast) { visitBin(cast(BinExp)e); return; @@ -2688,10 +2810,7 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt void visitDotId(DotIdExp e) { expToBuffer(e.e1, PREC.primary, buf, hgs); - if (e.arrow) - buf.writestring("->"); - else - buf.writeByte('.'); + buf.writeByte('.'); buf.writestring(e.ident.toString()); } @@ -2706,7 +2825,7 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt { expToBuffer(e.e1, PREC.primary, buf, hgs); buf.writeByte('.'); - buf.writestring(e.var.toChars()); + visitDsymbol(e.var); } void visitDotTemplateInstance(DotTemplateInstanceExp e) @@ -2897,14 +3016,21 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt buf.writestring(e.value.toChars()); } + if (e.rvalue) + buf.writestring("__rvalue("); + + scope (exit) + if (e.rvalue) + buf.writeByte(')'); + switch (e.op) { default: if (auto be = e.isBinExp()) return visitBin(be); - else if (auto ue = e.isUnaExp()) + if (auto ue = e.isUnaExp()) return visitUna(ue); - else if (auto de = e.isDefaultInitExp()) + if (auto de = e.isDefaultInitExp()) return visitDefaultInit(e.isDefaultInitExp()); return visit(e); @@ -2914,7 +3040,7 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt case EXP.float64: return visitReal(e.isRealExp()); case EXP.complex80: return visitComplex(e.isComplexExp()); case EXP.identifier: return visitIdentifier(e.isIdentifierExp()); - case EXP.dSymbol: return visitDsymbol(e.isDsymbolExp()); + case EXP.dSymbol: return visitDsymbolExp(e.isDsymbolExp()); case EXP.this_: return visitThis(e.isThisExp()); case EXP.super_: return visitSuper(e.isSuperExp()); case EXP.null_: return visitNull(e.isNullExp()); @@ -2938,7 +3064,7 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt case EXP.typeid_: return visitTypeid(e.isTypeidExp()); case EXP.traits: return visitTraits(e.isTraitsExp()); case EXP.halt: return visitHalt(e.isHaltExp()); - case EXP.is_: return visitIs(e.isExp()); + case EXP.is_: return visitIs(e.isIsExp()); case EXP.comma: return visitComma(e.isCommaExp()); case EXP.mixin_: return visitMixin(e.isMixinExp()); case EXP.import_: return visitImport(e.isImportExp()); @@ -3025,7 +3151,7 @@ void floatToBuffer(Type type, const real_t value, ref OutBuffer buf, const bool default: break; } - if (t.isimaginary()) + if (t.isImaginary()) buf.writeByte('i'); } } @@ -3133,20 +3259,14 @@ public: override void visit(DebugCondition c) { buf.writestring("debug ("); - if (c.ident) - buf.writestring(c.ident.toString()); - else - buf.print(c.level); + buf.writestring(c.ident.toString()); buf.writeByte(')'); } override void visit(VersionCondition c) { buf.writestring("version ("); - if (c.ident) - buf.writestring(c.ident.toString()); - else - buf.print(c.level); + buf.writestring(c.ident.toString()); buf.writeByte(')'); } @@ -3183,7 +3303,7 @@ void toCBuffer(const Initializer iz, ref OutBuffer buf, ref HdrGenState hgs) initializerToBuffer(cast() iz, buf, hgs); } -bool stcToBuffer(ref OutBuffer buf, StorageClass stc) @safe +bool stcToBuffer(ref OutBuffer buf, STC stc) @safe { //printf("stc: %llx\n", stc); bool result = false; @@ -3199,6 +3319,12 @@ bool stcToBuffer(ref OutBuffer buf, StorageClass stc) @safe stc &= ~(STC.return_ | STC.returninferred); } + // ensure `auto ref` keywords are (almost) adjacent + if (stc & STC.auto_) + { + buf.writestring("auto "); + stc &= ~STC.auto_; + } /* Put scope ref return into a standard order */ string rrs; @@ -3208,12 +3334,12 @@ bool stcToBuffer(ref OutBuffer buf, StorageClass stc) @safe { case ScopeRef.None: case ScopeRef.Scope: - case ScopeRef.Ref: case ScopeRef.Return: break; case ScopeRef.ReturnScope: rrs = "return scope"; goto L1; case ScopeRef.ReturnRef: rrs = isout ? "return out" : "return ref"; goto L1; + case ScopeRef.Ref: rrs = isout ? "out" : "ref"; goto L1; case ScopeRef.RefScope: rrs = isout ? "out scope" : "ref scope"; goto L1; case ScopeRef.ReturnRef_Scope: rrs = isout ? "return out scope" : "return ref scope"; goto L1; case ScopeRef.Ref_ReturnScope: rrs = isout ? "out return scope" : "ref return scope"; goto L1; @@ -3243,11 +3369,11 @@ bool stcToBuffer(ref OutBuffer buf, StorageClass stc) @safe * and return a string representation of it. * stc is reduced by the one picked. */ -string stcToString(ref StorageClass stc) @safe +string stcToString(ref STC stc) @safe { static struct SCstring { - StorageClass stc; + STC stc; string id; } @@ -3290,7 +3416,7 @@ string stcToString(ref StorageClass stc) @safe ]; foreach (ref entry; table) { - const StorageClass tbl = entry.stc; + const STC tbl = entry.stc; assert(tbl & STC.visibleStorageClasses); if (stc & tbl) { @@ -3523,7 +3649,7 @@ private void parameterToBuffer(Parameter p, ref OutBuffer buf, ref HdrGenState h if (p.storageClass & STC.auto_) buf.writestring("auto "); - StorageClass stc = p.storageClass; + STC stc = p.storageClass; if (p.storageClass & STC.in_) { buf.writestring("in "); @@ -3799,7 +3925,8 @@ private void tiargsToBuffer(TemplateInstance ti, ref OutBuffer buf, ref HdrGenSt } else if (Expression e = isExpression(oarg)) { - if (e.op == EXP.int64 || e.op == EXP.float64 || e.op == EXP.null_ || e.op == EXP.string_ || e.op == EXP.this_) + if (!(e.type && e.type.isTypeEnum()) && e.op == EXP.int64 || e.op == EXP.float64 || + e.op == EXP.null_ || e.op == EXP.string_ || e.op == EXP.this_) { toCBuffer(e, buf, hgs); return; @@ -3933,7 +4060,7 @@ private void visitFuncIdentWithPrefix(TypeFunction t, const Identifier ident, Te /* Use 'storage class' (prefix) style for attributes */ - if (t.mod) + if (t.mod && !(hgs.ddoc || hgs.hdrgen)) { MODtoBuffer(buf, t.mod); buf.writeByte(' '); @@ -3983,15 +4110,21 @@ private void visitFuncIdentWithPrefix(TypeFunction t, const Identifier ident, Te buf.writeByte(')'); } parametersToBuffer(t.parameterList, buf, hgs); - if (t.isreturnscope && !t.isreturninferred) + // postfix this attributes are more readable + if (t.mod && (hgs.ddoc || hgs.hdrgen)) + { + buf.writeByte(' '); + MODtoBuffer(buf, t.mod); + } + if (t.isReturnScope && !t.isReturnInferred) { buf.writestring(" return scope"); } - else if (t.isScopeQual && !t.isscopeinferred) + else if (t.isScopeQual && !t.isScopeInferred) { buf.writestring(" scope"); } - if (t.isreturn && !t.isreturnscope && !t.isreturninferred) + if (t.isReturn && !t.isReturnScope && !t.isReturnInferred) { buf.writestring(" return"); } @@ -4181,7 +4314,7 @@ private void typeToBufferx(Type t, ref OutBuffer buf, ref HdrGenState hgs) void visitFunction(TypeFunction t) { - //printf("TypeFunction::toCBuffer2() t = %p, ref = %d\n", t, t.isref); + //printf("TypeFunction::toCBuffer2() t = %p, ref = %d\n", t, t.isRef); visitFuncIdentWithPostfix(t, null, buf, hgs, false); } @@ -4279,13 +4412,23 @@ private void typeToBufferx(Type t, ref OutBuffer buf, ref HdrGenState hgs) void visitTag(TypeTag t) { - if (t.mod & MODFlags.const_) - buf.writestring("const "); if (hgs.importcHdr && t.id) { + // https://issues.dlang.org/show_bug.cgi?id=24670 + // `const` must be parenthesized because it can be a return type + if (t.mod & MODFlags.const_) + buf.writestring("const("); + + // For C to D translation, `struct S` or `enum S` simply becomes `S` buf.writestring(t.id.toString()); + + if (t.mod & MODFlags.const_) + buf.writestring(")"); return; } + // The following produces something like "const enum E : short" + if (t.mod & MODFlags.const_) + buf.writestring("const "); buf.writestring(Token.toString(t.tok)); buf.writeByte(' '); if (t.id) @@ -4329,6 +4472,11 @@ private void typeToBufferx(Type t, ref OutBuffer buf, ref HdrGenState hgs) buf.writestring("noreturn"); } + if (hgs.importcHdr && !hgs.inCAlias && t.mcache && t.mcache.typedefIdent) + { + buf.writestring(t.mcache.typedefIdent.toString()); + return; + } switch (t.ty) { @@ -4503,7 +4651,15 @@ string EXPtoString(EXP op) EXP.declaration : "declaration", EXP.interval : "interval", - EXP.loweredAssignExp : "=" + EXP.loweredAssignExp : "=", + + EXP.thrownException : "CTFE ThrownException", + EXP.cantExpression : "", + EXP.voidExpression : "cast(void)0", + EXP.showCtfeContext : "", + EXP.break_ : "", + EXP.continue_ : "", + EXP.goto_ : "", ]; const p = strings[op]; if (!p) diff --git a/dmd/hdrgen.h b/dmd/hdrgen.h index 14793ad23a..2104b98f75 100644 --- a/dmd/hdrgen.h +++ b/dmd/hdrgen.h @@ -1,6 +1,6 @@ /* Compiler implementation of the D programming language - * Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * written by Dave Fladebo * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. diff --git a/dmd/iasmgcc.d b/dmd/iasmgcc.d index 4b1b2e78c6..3d6e6ab5b5 100644 --- a/dmd/iasmgcc.d +++ b/dmd/iasmgcc.d @@ -1,12 +1,12 @@ /** * Inline assembler for the GCC D compiler. * - * Copyright (C) 2018-2024 by The D Language Foundation, All Rights Reserved + * Copyright (C) 2018-2025 by The D Language Foundation, All Rights Reserved * Authors: Iain Buclaw * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/iasmgcc.d, _iasmgcc.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/iasmgcc.d, _iasmgcc.d) * Documentation: https://dlang.org/phobos/dmd_iasmgcc.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/iasmgcc.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/iasmgcc.d */ module dmd.iasmgcc; @@ -29,6 +29,118 @@ import dmd.tokens; import dmd.statement; import dmd.statementsem; +/*********************************** + * Parse and run semantic analysis on a GccAsmStatement. + * Params: + * s = gcc asm statement being parsed + * sc = the scope where the asm statement is located + * Returns: + * the completed gcc asm statement, or null if errors occurred + */ +public Statement gccAsmSemantic(GccAsmStatement s, Scope* sc) +{ + //printf("GccAsmStatement.semantic()\n"); + const bool doUnittests = global.params.parsingUnittestsRequired(); + scope p = new Parser!ASTCodegen(sc._module, ";", false, global.errorSink, &global.compileEnv, doUnittests); + + // Make a safe copy of the token list before parsing. + Token* toklist = null; + Token **ptoklist = &toklist; + + for (Token* token = s.tokens; token; token = token.next) + { + *ptoklist = p.allocateToken(); + memcpy(*ptoklist, token, Token.sizeof); + ptoklist = &(*ptoklist).next; + *ptoklist = null; + } + p.token = *toklist; + p.baseLoc.startLine = s.loc.linnum; + p.linnum = s.loc.linnum; + + // Parse the gcc asm statement. + const errors = global.errors; + s = p.parseGccAsm(s); + if (errors != global.errors) + return null; + s.stc = sc.stc; + + // Fold the instruction template string. + s.insn = semanticString(sc, s.insn, "asm instruction template"); + + if (s.labels && s.outputargs) + error(s.loc, "extended asm statements with labels cannot have output constraints"); + + // Analyse all input and output operands. + if (s.args) + { + foreach (i; 0 .. s.args.length) + { + Expression e = (*s.args)[i]; + e = e.expressionSemantic(sc); + // Check argument is a valid lvalue/rvalue. + if (i < s.outputargs) + e = e.modifiableLvalue(sc); + else if (e.checkValue()) + e = ErrorExp.get(); + (*s.args)[i] = e; + + e = (*s.constraints)[i]; + e = e.expressionSemantic(sc); + assert(e.op == EXP.string_ && (cast(StringExp) e).sz == 1); + (*s.constraints)[i] = e; + } + } + + // Analyse all clobbers. + if (s.clobbers) + { + foreach (i; 0 .. s.clobbers.length) + { + Expression e = (*s.clobbers)[i]; + e = e.expressionSemantic(sc); + assert(e.op == EXP.string_ && (cast(StringExp) e).sz == 1); + (*s.clobbers)[i] = e; + } + } + + // Analyse all goto labels. + if (s.labels) + { + foreach (i; 0 .. s.labels.length) + { + Identifier ident = (*s.labels)[i]; + GotoStatement gs = new GotoStatement(s.loc, ident); + if (!s.gotos) + s.gotos = new GotoStatements(); + s.gotos.push(gs); + gs.statementSemantic(sc); + } + } + + return s; +} + +/*********************************** + * Run semantic analysis on an CAsmDeclaration. + * Params: + * ad = asm declaration + * sc = the scope where the asm declaration is located + */ +public void gccAsmSemantic(CAsmDeclaration ad, Scope* sc) +{ + import dmd.typesem : pointerTo; + ad.code = semanticString(sc, ad.code, "asm definition"); + ad.code.type = ad.code.type.nextOf().pointerTo(); + + // Asm definition always needs emitting into the root module. + import dmd.dmodule : Module; + if (sc._module && sc._module.isRoot()) + return; + if (Module m = Module.rootModule) + m.members.push(ad); +} + private: /*********************************** @@ -143,9 +255,9 @@ Lerror: * Returns: * array of parsed clobber expressions */ -Expressions *parseExtAsmClobbers(Parser)(Parser p) +Expressions* parseExtAsmClobbers(Parser)(Parser p) { - Expressions *clobbers; + Expressions* clobbers; while (1) { @@ -194,9 +306,9 @@ Lerror: * Returns: * array of parsed goto labels */ -Identifiers *parseExtAsmGotoLabels(Parser)(Parser p) +Identifiers* parseExtAsmGotoLabels(Parser)(Parser p) { - Identifiers *labels; + Identifiers* labels; while (1) { @@ -292,117 +404,6 @@ Ldone: return s; } -/*********************************** - * Parse and run semantic analysis on a GccAsmStatement. - * Params: - * s = gcc asm statement being parsed - * sc = the scope where the asm statement is located - * Returns: - * the completed gcc asm statement, or null if errors occurred - */ -public Statement gccAsmSemantic(GccAsmStatement s, Scope *sc) -{ - //printf("GccAsmStatement.semantic()\n"); - const bool doUnittests = global.params.parsingUnittestsRequired(); - scope p = new Parser!ASTCodegen(sc._module, ";", false, global.errorSink, &global.compileEnv, doUnittests); - - // Make a safe copy of the token list before parsing. - Token *toklist = null; - Token **ptoklist = &toklist; - - for (Token *token = s.tokens; token; token = token.next) - { - *ptoklist = p.allocateToken(); - memcpy(*ptoklist, token, Token.sizeof); - ptoklist = &(*ptoklist).next; - *ptoklist = null; - } - p.token = *toklist; - p.scanloc = s.loc; - - // Parse the gcc asm statement. - const errors = global.errors; - s = p.parseGccAsm(s); - if (errors != global.errors) - return null; - s.stc = sc.stc; - - // Fold the instruction template string. - s.insn = semanticString(sc, s.insn, "asm instruction template"); - - if (s.labels && s.outputargs) - error(s.loc, "extended asm statements with labels cannot have output constraints"); - - // Analyse all input and output operands. - if (s.args) - { - foreach (i; 0 .. s.args.length) - { - Expression e = (*s.args)[i]; - e = e.expressionSemantic(sc); - // Check argument is a valid lvalue/rvalue. - if (i < s.outputargs) - e = e.modifiableLvalue(sc); - else if (e.checkValue()) - e = ErrorExp.get(); - (*s.args)[i] = e; - - e = (*s.constraints)[i]; - e = e.expressionSemantic(sc); - assert(e.op == EXP.string_ && (cast(StringExp) e).sz == 1); - (*s.constraints)[i] = e; - } - } - - // Analyse all clobbers. - if (s.clobbers) - { - foreach (i; 0 .. s.clobbers.length) - { - Expression e = (*s.clobbers)[i]; - e = e.expressionSemantic(sc); - assert(e.op == EXP.string_ && (cast(StringExp) e).sz == 1); - (*s.clobbers)[i] = e; - } - } - - // Analyse all goto labels. - if (s.labels) - { - foreach (i; 0 .. s.labels.length) - { - Identifier ident = (*s.labels)[i]; - GotoStatement gs = new GotoStatement(s.loc, ident); - if (!s.gotos) - s.gotos = new GotoStatements(); - s.gotos.push(gs); - gs.statementSemantic(sc); - } - } - - return s; -} - -/*********************************** - * Run semantic analysis on an CAsmDeclaration. - * Params: - * ad = asm declaration - * sc = the scope where the asm declaration is located - */ -public void gccAsmSemantic(CAsmDeclaration ad, Scope *sc) -{ - import dmd.typesem : pointerTo; - ad.code = semanticString(sc, ad.code, "asm definition"); - ad.code.type = ad.code.type.nextOf().pointerTo(); - - // Asm definition always needs emitting into the root module. - import dmd.dmodule : Module; - if (sc._module && sc._module.isRoot()) - return; - if (Module m = Module.rootModule) - m.members.push(ad); -} - unittest { import dmd.mtype : TypeBasic; @@ -410,7 +411,7 @@ unittest if (!global.errorSink) global.errorSink = new ErrorSinkCompiler; - uint errors = global.startGagging(); + const errors = global.startGagging(); scope(exit) global.endGagging(errors); // If this check fails, then Type._init() was called before reaching here, diff --git a/dmd/id.d b/dmd/id.d index dc18b03cc9..9985d2c196 100644 --- a/dmd/id.d +++ b/dmd/id.d @@ -1,12 +1,12 @@ /** * Contains the `Id` struct with a list of predefined symbols the compiler knows about. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/id.d, _id.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/id.d, _id.d) * Documentation: https://dlang.org/phobos/dmd_id.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/id.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/id.d */ module dmd.id; @@ -166,6 +166,7 @@ immutable Msgtable[] msgtable = { "xopCmp", "__xopCmp" }, { "xtoHash", "__xtoHash" }, { "__tmpfordtor" }, + { "Entry" }, { "LINE", "__LINE__" }, { "FILE", "__FILE__" }, @@ -223,60 +224,15 @@ immutable Msgtable[] msgtable = { "__LOCAL_SIZE" }, // For operator overloads - { "uadd", "opPos" }, - { "neg", "opNeg" }, - { "com", "opCom" }, - { "add", "opAdd" }, - { "add_r", "opAdd_r" }, - { "sub", "opSub" }, - { "sub_r", "opSub_r" }, - { "mul", "opMul" }, - { "mul_r", "opMul_r" }, - { "div", "opDiv" }, - { "div_r", "opDiv_r" }, - { "mod", "opMod" }, - { "mod_r", "opMod_r" }, - { "eq", "opEquals" }, - { "cmp", "opCmp" }, - { "iand", "opAnd" }, - { "iand_r", "opAnd_r" }, - { "ior", "opOr" }, - { "ior_r", "opOr_r" }, - { "ixor", "opXor" }, - { "ixor_r", "opXor_r" }, - { "shl", "opShl" }, - { "shl_r", "opShl_r" }, - { "shr", "opShr" }, - { "shr_r", "opShr_r" }, - { "ushr", "opUShr" }, - { "ushr_r", "opUShr_r" }, - { "cat", "opCat" }, - { "cat_r", "opCat_r" }, - { "assign", "opAssign" }, - { "addass", "opAddAssign" }, - { "subass", "opSubAssign" }, - { "mulass", "opMulAssign" }, - { "divass", "opDivAssign" }, - { "modass", "opModAssign" }, - { "andass", "opAndAssign" }, - { "orass", "opOrAssign" }, - { "xorass", "opXorAssign" }, - { "shlass", "opShlAssign" }, - { "shrass", "opShrAssign" }, - { "ushrass", "opUShrAssign" }, - { "catass", "opCatAssign" }, - { "postinc", "opPostInc" }, - { "postdec", "opPostDec" }, - { "index", "opIndex" }, - { "indexass", "opIndexAssign" }, - { "slice", "opSlice" }, - { "sliceass", "opSliceAssign" }, - { "call", "opCall" }, - { "_cast", "opCast" }, - { "opIn" }, - { "opIn_r" }, - { "opStar" }, - { "opDot" }, + { "opEquals" }, + { "opCmp" }, + { "opAssign" }, + { "opIndex" }, + { "opIndexAssign" }, + { "opSlice" }, + { "opSliceAssign" }, + { "opCall" }, + { "opCast" }, { "opDispatch" }, { "opDollar" }, { "opUnary" }, @@ -287,9 +243,6 @@ immutable Msgtable[] msgtable = { "opOpAssign" }, { "opIndexOpAssign" }, { "opSliceOpAssign" }, - { "pow", "opPow" }, - { "pow_r", "opPow_r" }, - { "powass", "opPowAssign" }, { "classNew", "new" }, { "classDelete", "delete" }, @@ -486,6 +439,8 @@ immutable Msgtable[] msgtable = { "hasMember" }, { "identifier" }, { "fullyQualifiedName" }, + { "getBitfieldOffset" }, + { "getBitfieldWidth" }, { "getProtection" }, { "getVisibility" }, { "parent" }, @@ -517,6 +472,7 @@ immutable Msgtable[] msgtable = { "getLocation" }, { "hasPostblit" }, { "hasCopyConstructor" }, + { "hasMoveConstructor" }, { "isCopyable" }, { "toType" }, { "parameters" }, diff --git a/dmd/id.h b/dmd/id.h index 03e46a6317..d465b6acb8 100644 --- a/dmd/id.h +++ b/dmd/id.h @@ -1,6 +1,6 @@ /* Compiler implementation of the D programming language - * Copyright (C) 2017-2024 by The D Language Foundation, All Rights Reserved + * Copyright (C) 2017-2025 by The D Language Foundation, All Rights Reserved * written by Walter Bright * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. diff --git a/dmd/identifier.d b/dmd/identifier.d index 74be1beb29..c213597eb0 100644 --- a/dmd/identifier.d +++ b/dmd/identifier.d @@ -1,12 +1,12 @@ /** * Defines an identifier, which is the name of a `Dsymbol`. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/identifier.d, _identifier.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/identifier.d, _identifier.d) * Documentation: https://dlang.org/phobos/dmd_identifier.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/identifier.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/identifier.d */ module dmd.identifier; @@ -108,7 +108,8 @@ nothrow: const(char)* p = null; if (this == Id.ctor) p = "this"; - else if (this == Id.dtor) + else if (this == Id.dtor || this == Id.__xdtor || this == Id.__fieldDtor || + this == Id.__aggrDtor || this == Id.cppdtor || this == Id.ticppdtor) p = "~this"; else if (this == Id.unitTest) p = "unittest"; @@ -120,6 +121,8 @@ nothrow: p = "result"; else if (this == Id.returnLabel) p = "return"; + else if (this == Id.postblit) + p = "this(this)"; else { p = toChars(); @@ -218,15 +221,16 @@ nothrow: * Identifier (inside Identifier.idPool) with deterministic name based * on the source location. */ - extern (D) static Identifier generateIdWithLoc(string prefix, const ref Loc loc, string parent = "") + extern (D) static Identifier generateIdWithLoc(string prefix, Loc loc, string parent = "") { // generate `_L_C` + auto sl = SourceLoc(loc); OutBuffer idBuf; idBuf.writestring(prefix); idBuf.writestring("_L"); - idBuf.print(loc.linnum); + idBuf.print(sl.line); idBuf.writestring("_C"); - idBuf.print(loc.charnum); + idBuf.print(sl.column); /** * Make sure the identifiers are unique per filename, i.e., per module/mixin @@ -244,13 +248,14 @@ nothrow: * directly, but that would unnecessary lengthen symbols names. See issue: * https://issues.dlang.org/show_bug.cgi?id=23722 */ - static struct Key { Loc loc; string prefix; string parent; } + static struct Key { string locKey; string prefix; string parent; } __gshared uint[Key] counters; + string locKey = cast(string) (sl.filename ~ idBuf[]); static if (__traits(compiles, counters.update(Key.init, () => 0u, (ref uint a) => 0u))) { // 2.082+ - counters.update(Key(loc, prefix, parent), + counters.update(Key(locKey, prefix, parent), () => 1u, // insertion (ref uint counter) // update { @@ -262,7 +267,7 @@ nothrow: } else { - const key = Key(loc, prefix, parent); + const key = Key(locKey, prefix, parent); if (auto pCounter = key in counters) { idBuf.writestring("_"); diff --git a/dmd/identifier.h b/dmd/identifier.h index 4f26801356..eb06c25c72 100644 --- a/dmd/identifier.h +++ b/dmd/identifier.h @@ -1,6 +1,6 @@ /* Compiler implementation of the D programming language - * Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * written by Walter Bright * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. diff --git a/dmd/impcnvtab.d b/dmd/impcnvtab.d index b899f810aa..b2ab919f1b 100644 --- a/dmd/impcnvtab.d +++ b/dmd/impcnvtab.d @@ -6,12 +6,12 @@ * Specification: $(LINK2 https://dlang.org/spec/type.html#integer-promotions, Integer Promotions), * $(LINK2 https://dlang.org/spec/type.html#usual-arithmetic-conversions, Usual Arithmetic Conversions). * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/impcnvtab.d, _impcnvtab.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/impcnvtab.d, _impcnvtab.d) * Documentation: https://dlang.org/phobos/dmd_impcnvtab.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/impcnvtab.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/impcnvtab.d */ module dmd.impcnvtab; diff --git a/dmd/imphint.d b/dmd/imphint.d index ea2f13d917..382a0f3786 100644 --- a/dmd/imphint.d +++ b/dmd/imphint.d @@ -3,12 +3,12 @@ * * For example, prompt to `import std.stdio` when using `writeln`. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/imphint.d, _imphint.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/imphint.d, _imphint.d) * Documentation: https://dlang.org/phobos/dmd_imphint.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/imphint.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/imphint.d */ module dmd.imphint; @@ -80,6 +80,8 @@ shared static this() "writeln": "std.stdio", "__va_argsave_t": "core.stdc.stdarg", "__va_list_tag": "core.stdc.stdarg", + "InterpolationHeader": "core.interpolation", + "InterpolationFooter": "core.interpolation", ]; } diff --git a/dmd/import.h b/dmd/import.h index bfbb551013..14bd889bd6 100644 --- a/dmd/import.h +++ b/dmd/import.h @@ -1,6 +1,6 @@ /* Compiler implementation of the D programming language - * Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * written by Walter Bright * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. @@ -44,6 +44,5 @@ class Import final : public Dsymbol Dsymbol *toAlias() override; bool overloadInsert(Dsymbol *s) override; - Import *isImport() override { return this; } void accept(Visitor *v) override { v->visit(this); } }; diff --git a/dmd/importc.d b/dmd/importc.d index ece56c8d1b..2f88a21c04 100644 --- a/dmd/importc.d +++ b/dmd/importc.d @@ -3,12 +3,12 @@ * * Specification: C11 * - * Copyright: Copyright (C) 2021-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 2021-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/src/dmd/importc.d, _importc.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/importc.d, _importc.d) * Documentation: https://dlang.org/phobos/dmd_importc.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/importc.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/importc.d */ module dmd.importc; @@ -41,7 +41,7 @@ import dmd.typesem; */ Type cAdjustParamType(Type t, Scope* sc) { - if (!(sc.flags & SCOPE.Cfile)) + if (!sc.inCfile) return t; Type tb = t.toBasetype(); @@ -77,7 +77,7 @@ Type cAdjustParamType(Type t, Scope* sc) Expression arrayFuncConv(Expression e, Scope* sc) { //printf("arrayFuncConv() %s\n", e.toChars()); - if (!(sc.flags & SCOPE.Cfile)) + if (!sc.inCfile) return e; auto t = e.type.toBasetype(); @@ -121,7 +121,6 @@ Expression fieldLookup(Expression e, Scope* sc, Identifier id, bool arrow) if (e.isErrorExp()) return e; - Dsymbol s; auto t = e.type; if (t.isTypePointer()) { @@ -131,6 +130,7 @@ Expression fieldLookup(Expression e, Scope* sc, Identifier id, bool arrow) error(e.loc, "since `%s` is a pointer, use `%s->%s` instead of `%s.%s`", pe, pe, id.toChars(), pe, id.toChars()); e = new PtrExp(e.loc, e); } + Dsymbol s; if (auto ts = t.isTypeStruct()) s = ts.sym.search(e.loc, id, 0); if (!s) @@ -154,7 +154,7 @@ Expression fieldLookup(Expression e, Scope* sc, Identifier id, bool arrow) */ Expression carraySemantic(ArrayExp ae, Scope* sc) { - if (!(sc.flags & SCOPE.Cfile)) + if (!sc.inCfile) return null; auto e1 = ae.e1.expressionSemantic(sc); @@ -166,7 +166,7 @@ Expression carraySemantic(ArrayExp ae, Scope* sc) * So, rewrite as an IndexExp if we can. */ auto t1 = e1.type.toBasetype(); - if (t1.isTypeDArray() || t1.isTypeSArray()) + if (t1.isStaticOrDynamicArray()) { e2 = e2.expressionSemantic(sc).arrayFuncConv(sc); // C doesn't do array bounds checking, so `true` turns it off @@ -176,7 +176,7 @@ Expression carraySemantic(ArrayExp ae, Scope* sc) e1 = e1.arrayFuncConv(sc); // e1 might still be a function call e2 = e2.expressionSemantic(sc); auto t2 = e2.type.toBasetype(); - if (t2.isTypeDArray() || t2.isTypeSArray()) + if (t2.isStaticOrDynamicArray()) { return new IndexExp(ae.loc, e2, e1, true).expressionSemantic(sc); // swap operands } diff --git a/dmd/init.d b/dmd/init.d index 7d9e3e6033..55fb6f3485 100644 --- a/dmd/init.d +++ b/dmd/init.d @@ -1,12 +1,12 @@ /** * Defines initializers of variables, e.g. the array literal in `int[3] x = [0, 1, 2]`. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/init.d, _init.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/init.d, _init.d) * Documentation: https://dlang.org/phobos/dmd_init.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/init.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/init.d */ module dmd.init; @@ -38,6 +38,7 @@ extern (C++) class Initializer : ASTNode { Loc loc; InitKind kind; + bool semanticDone = false; /// initializerSemantic has been run on this override DYNCAST dyncast() const { @@ -45,7 +46,7 @@ extern (C++) class Initializer : ASTNode } - extern (D) this(const ref Loc loc, InitKind kind) @safe + extern (D) this(Loc loc, InitKind kind) @safe { this.loc = loc; this.kind = kind; @@ -99,7 +100,7 @@ extern (C++) final class VoidInitializer : Initializer { Type type; // type that this will initialize to - extern (D) this(const ref Loc loc) @safe + extern (D) this(Loc loc) @safe { super(loc, InitKind.void_); } @@ -117,7 +118,7 @@ extern (C++) final class DefaultInitializer : Initializer { Type type; // type that this will initialize to - extern (D) this(const ref Loc loc) @safe + extern (D) this(Loc loc) @safe { super(loc, InitKind.default_); } @@ -150,7 +151,7 @@ extern (C++) final class StructInitializer : Initializer Identifiers field; // of Identifier *'s Initializers value; // parallel array of Initializer *'s - extern (D) this(const ref Loc loc) + extern (D) this(Loc loc) { super(loc, InitKind.struct_); } @@ -176,10 +177,9 @@ extern (C++) final class ArrayInitializer : Initializer Initializers value; // of Initializer *'s uint dim; // length of array being initialized Type type; // type that array will be used to initialize - bool sem; // true if semantic() is run bool isCarray; // C array semantics - extern (D) this(const ref Loc loc) + extern (D) this(Loc loc) { super(loc, InitKind.array); } @@ -215,7 +215,7 @@ extern (C++) final class ExpInitializer : Initializer bool expandTuples; Expression exp; - extern (D) this(const ref Loc loc, Expression exp) @safe + extern (D) this(Loc loc, Expression exp) @safe { super(loc, InitKind.exp); this.exp = exp; @@ -256,9 +256,8 @@ extern (C++) final class CInitializer : Initializer { DesigInits initializerList; /// initializer-list Type type; /// type that array will be used to initialize - bool sem; /// true if semantic() is run - extern (D) this(const ref Loc loc) + extern (D) this(Loc loc) { super(loc, InitKind.C_); } diff --git a/dmd/init.h b/dmd/init.h index 2ee69f622b..a832b9ed4d 100644 --- a/dmd/init.h +++ b/dmd/init.h @@ -1,6 +1,6 @@ /* Compiler implementation of the D programming language - * Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * written by Walter Bright * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. @@ -33,6 +33,7 @@ class Initializer : public ASTNode public: Loc loc; unsigned char kind; + d_bool semanticDone; DYNCAST dyncast() const override { return DYNCAST_INITIALIZER; } @@ -85,7 +86,6 @@ class ArrayInitializer final : public Initializer Initializers value; // of Initializer *'s unsigned dim; // length of array being initialized Type *type; // type that array will be used to initialize - d_bool sem; // true if semantic() is run d_bool isCarray; // C array semantics bool isAssociativeArray() const; @@ -119,7 +119,6 @@ class CInitializer final : public Initializer public: DesigInits initializerList; Type *type; // type that array will be used to initialize - d_bool sem; // true if semantic() is run void accept(Visitor *v) override { v->visit(this); } }; diff --git a/dmd/initsem.d b/dmd/initsem.d index 8faad30f53..1ebccf77ee 100644 --- a/dmd/initsem.d +++ b/dmd/initsem.d @@ -1,12 +1,12 @@ /** * Semantic analysis of initializers. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/initsem.d, _initsem.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/initsem.d, _initsem.d) * Documentation: https://dlang.org/phobos/dmd_initsem.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/initsem.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/initsem.d */ module dmd.initsem; @@ -41,6 +41,7 @@ import dmd.location; import dmd.mtype; import dmd.opover; import dmd.optimize; +import dmd.safe : setUnsafe; import dmd.statement; import dmd.target; import dmd.tokens; @@ -106,6 +107,9 @@ Expression toAssocArrayLiteral(ArrayInitializer ai) Initializer initializerSemantic(Initializer init, Scope* sc, ref Type tx, NeedInterpret needInterpret) { //printf("initializerSemantic() tx: %p %s\n", tx, tx.toChars()); + if (init.semanticDone) + return init; + Type t = tx; static Initializer err() @@ -161,7 +165,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.flags & SCOPE.Cfile) != 0); + auto ex = iz.initializerToExpression(null, sc.inCfile); if (ex.op != EXP.error) i.value[j] = iz; return ex; @@ -203,11 +207,6 @@ Initializer initializerSemantic(Initializer init, Scope* sc, ref Type tx, NeedIn const(uint) amax = 0x80000000; bool errors = false; //printf("ArrayInitializer::semantic(%s), ai: %s\n", t.toChars(), toChars(i)); - if (i.sem) // if semantic() already run - { - return i; - } - i.sem = true; t = t.toBasetype(); switch (t.ty) { @@ -305,7 +304,7 @@ Initializer initializerSemantic(Initializer init, Scope* sc, ref Type tx, NeedIn } if (auto tsa = t.isTypeSArray()) { - if (sc.flags & SCOPE.Cfile && tsa.isIncomplete()) + if (sc.inCfile && tsa.isIncomplete()) { // Change to array of known length auto tn = tsa.next.toBasetype(); @@ -369,7 +368,7 @@ Initializer initializerSemantic(Initializer init, Scope* sc, ref Type tx, NeedIn // If the result will be implicitly cast, move the cast into CTFE // to avoid premature truncation of polysemous types. // eg real [] x = [1.1, 2.2]; should use real precision. - if (i.exp.implicitConvTo(t) && !(sc.flags & SCOPE.Cfile)) + if (i.exp.implicitConvTo(t) && !sc.inCfile) { i.exp = i.exp.implicitCastTo(sc, t); } @@ -377,7 +376,7 @@ Initializer initializerSemantic(Initializer init, Scope* sc, ref Type tx, NeedIn { return i; } - if (sc.flags & SCOPE.Cfile) + if (sc.inCfile) { /* the interpreter turns (char*)"string" into &"string"[0] which then * it cannot interpret. Resolve that case by doing optimize() first @@ -440,7 +439,7 @@ Initializer initializerSemantic(Initializer init, Scope* sc, ref Type tx, NeedIn Type typeb = se.type.toBasetype(); TY tynto = tb.nextOf().ty; if (!se.committed && - (typeb.ty == Tarray || typeb.ty == Tsarray) && tynto.isSomeChar && + typeb.isStaticOrDynamicArray() && tynto.isSomeChar && se.numberOfCodeUnits(tynto) < tb.isTypeSArray().dim.toInteger()) { i.exp = se.castTo(sc, t); @@ -450,7 +449,7 @@ Initializer initializerSemantic(Initializer init, Scope* sc, ref Type tx, NeedIn /* Lop off terminating 0 of initializer for: * static char s[5] = "hello"; */ - if (sc.flags & SCOPE.Cfile && + if (sc.inCfile && typeb.ty == Tsarray && tynto.isSomeChar && tb.isTypeSArray().dim.toInteger() + 1 == typeb.isTypeSArray().dim.toInteger()) @@ -463,7 +462,7 @@ Initializer initializerSemantic(Initializer init, Scope* sc, ref Type tx, NeedIn * Initialize an array of unknown size with a string. * Change to static array of known size */ - if (sc.flags & SCOPE.Cfile && i.exp.isStringExp() && + if (sc.inCfile && i.exp.isStringExp() && tb.isTypeSArray() && tb.isTypeSArray().isIncomplete()) { StringExp se = i.exp.isStringExp(); @@ -490,7 +489,7 @@ Initializer initializerSemantic(Initializer init, Scope* sc, ref Type tx, NeedIn else i.exp = e.optimize(WANTvalue); } - else if (search_function(sd, Id.call)) + else if (search_function(sd, Id.opCall)) { /* https://issues.dlang.org/show_bug.cgi?id=1547 * @@ -500,7 +499,7 @@ Initializer initializerSemantic(Initializer init, Scope* sc, ref Type tx, NeedIn * i.exp = typeof(sd).opCall(arguments) */ - Expression e = typeDotIdExp(i.loc, sd.type, Id.call); + Expression e = typeDotIdExp(i.loc, sd.type, Id.opCall); e = new CallExp(i.loc, e, i.exp); e = e.expressionSemantic(sc); e = resolveProperties(sc, e); @@ -549,7 +548,7 @@ Initializer initializerSemantic(Initializer init, Scope* sc, ref Type tx, NeedIn { i.exp = i.exp.implicitCastTo(sc, t); } - else if (sc.flags & SCOPE.Cfile && i.exp.isStringExp() && + else if (sc.inCfile && i.exp.isStringExp() && tta && (tta.next.ty == Tint8 || tta.next.ty == Tuns8) && ti.ty == Tsarray && ti.nextOf().ty == Tchar) { @@ -586,7 +585,7 @@ Initializer initializerSemantic(Initializer init, Scope* sc, ref Type tx, NeedIn const errors = global.startGagging(); i.exp = i.exp.implicitCastTo(sc, t); if (global.endGagging(errors)) - error(currExp.loc, "cannot implicitly convert expression `%s` of type `%s` to `%s`", currExp.toChars(), et.toChars(), t.toChars()); + error(currExp.loc, "cannot implicitly convert expression `%s` of type `%s` to `%s`", currExp.toErrMsg(), et.toChars(), t.toChars()); } } L1: @@ -692,51 +691,49 @@ Initializer initializerSemantic(Initializer init, Scope* sc, ref Type tx, NeedIn auto di = ci.initializerList[index]; if (di.designatorList && fieldi != 0) break; // back to top level - else + + VarDeclaration field; + while (1) // skip field if it overlaps with previously seen fields { - VarDeclaration field; - while (1) // skip field if it overlaps with previously seen fields - { - field = sd.fields[fieldi]; - ++fieldi; - if (!overlaps(field, sd.fields[], si)) - break; - if (fieldi == nfields) - break; - } - auto tn = field.type.toBasetype(); - auto tnsa = tn.isTypeSArray(); - auto tns = tn.isTypeStruct(); - auto ix = di.initializer; - if (tnsa && ix.isExpInitializer()) - { - ExpInitializer ei = ix.isExpInitializer(); - if (ei.exp.isStringExp() && tnsa.nextOf().isintegral()) - { - si.addInit(field.ident, ei); - ++index; - } - else - si.addInit(field.ident, subArray(tnsa, index)); // fwd ref of subArray is why subStruct is a template - } - else if (tns && ix.isExpInitializer()) + field = sd.fields[fieldi]; + ++fieldi; + if (!overlaps(field, sd.fields[], si)) + break; + if (fieldi == nfields) + break; + } + auto tn = field.type.toBasetype(); + auto tnsa = tn.isTypeSArray(); + auto tns = tn.isTypeStruct(); + auto ix = di.initializer; + if (tnsa && ix.isExpInitializer()) + { + ExpInitializer ei = ix.isExpInitializer(); + if (ei.exp.isStringExp() && tnsa.nextOf().isIntegral()) { - /* Disambiguate between an exp representing the entire - * struct, and an exp representing the first field of the struct - */ - if (representsStruct(ix.isExpInitializer(), tns)) // initializer represents the entire struct - { - si.addInit(field.ident, initializerSemantic(ix, sc, tn, needInterpret)); - ++index; - } - else // field initializers for struct - si.addInit(field.ident, subStruct(tns, index)); // the first field + si.addInit(field.ident, ei); + ++index; } else + si.addInit(field.ident, subArray(tnsa, index)); // fwd ref of subArray is why subStruct is a template + } + else if (tns && ix.isExpInitializer()) + { + /* Disambiguate between an exp representing the entire + * struct, and an exp representing the first field of the struct + */ + if (representsStruct(ix.isExpInitializer(), tns)) // initializer represents the entire struct { - si.addInit(field.ident, ix); + si.addInit(field.ident, initializerSemantic(ix, sc, tn, needInterpret)); ++index; } + else // field initializers for struct + si.addInit(field.ident, subStruct(tns, index)); // the first field + } + else + { + si.addInit(field.ident, ix); + ++index; } } //printf("subStruct() returns ai: %s, index: %d\n", si.toChars(), cast(int)index); @@ -774,7 +771,7 @@ Initializer initializerSemantic(Initializer init, Scope* sc, ref Type tx, NeedIn else if (tnsa && di.initializer.isExpInitializer()) { ExpInitializer ei = di.initializer.isExpInitializer(); - if (ei.exp.isStringExp() && tnsa.nextOf().isintegral()) + if (ei.exp.isStringExp() && tnsa.nextOf().isIntegral()) { ai.addInit(null, ei); ++index; @@ -837,112 +834,110 @@ Initializer initializerSemantic(Initializer init, Scope* sc, ref Type tx, NeedIn error(ci.loc, "`.%s` is not a field of `%s`\n", id.toChars(), sd.toChars()); return err(); } - else - { - if (fieldi == nfields) - break; - auto ix = di.initializer; + if (fieldi == nfields) + break; - /* If a C initializer is wrapped in a C initializer, with no designators, - * peel off the outer one - */ - if (ix.isCInitializer()) + auto ix = di.initializer; + + /* If a C initializer is wrapped in a C initializer, with no designators, + * peel off the outer one + */ + if (ix.isCInitializer()) + { + CInitializer cix = ix.isCInitializer(); + if (cix.initializerList.length == 1) { - CInitializer cix = ix.isCInitializer(); - if (cix.initializerList.length == 1) + DesigInit dix = cix.initializerList[0]; + if (!dix.designatorList) { - DesigInit dix = cix.initializerList[0]; - if (!dix.designatorList) - { - Initializer inix = dix.initializer; - if (inix.isCInitializer()) - ix = inix; - } + Initializer inix = dix.initializer; + if (inix.isCInitializer()) + ix = inix; } } + } - if (auto cix = ix.isCInitializer()) + if (auto cix = ix.isCInitializer()) + { + /* ImportC loses the structure from anonymous structs, but this is retained + * by the initializer syntax. if a CInitializer has a Designator, it is probably + * a nested anonymous struct + */ + int found; + foreach (dix; cix.initializerList) { - /* ImportC loses the structure from anonymous structs, but this is retained - * by the initializer syntax. if a CInitializer has a Designator, it is probably - * a nested anonymous struct - */ - int found; - foreach (dix; cix.initializerList) + Designators* dlistx = dix.designatorList; + if (!dlistx) + continue; + if ((*dlistx).length == 1 && (*dlistx)[0].ident) { - Designators* dlistx = dix.designatorList; - if (!dlistx) - continue; - if ((*dlistx).length == 1 && (*dlistx)[0].ident) + auto id = (*dlistx)[0].ident; + foreach (k, f; sd.fields[]) // linear search for now { - auto id = (*dlistx)[0].ident; - foreach (k, f; sd.fields[]) // linear search for now + if (f.ident == id) { - if (f.ident == id) - { - fieldi = k; - si.addInit(id, dix.initializer); - ++fieldi; - ++index; - ++found; - break; - } + fieldi = k; + si.addInit(id, dix.initializer); + ++fieldi; + ++index; + ++found; + break; } } - else { - error(ci.loc, "only 1 designator currently allowed for C struct field initializer `%s`", toChars(ci)); - } } - - if (found == cix.initializerList.length) - continue Loop1; + else { + error(ci.loc, "only 1 designator currently allowed for C struct field initializer `%s`", toChars(ci)); + } } - VarDeclaration field; - while (1) // skip field if it overlaps with previously seen fields - { - field = sd.fields[fieldi]; - ++fieldi; - if (!overlaps(field, sd.fields[], si)) - break; - if (fieldi == nfields) - break; - } + if (found == cix.initializerList.length) + continue Loop1; + } - auto tn = field.type.toBasetype(); - auto tnsa = tn.isTypeSArray(); - auto tns = tn.isTypeStruct(); + VarDeclaration field; + while (1) // skip field if it overlaps with previously seen fields + { + field = sd.fields[fieldi]; + ++fieldi; + if (!overlaps(field, sd.fields[], si)) + break; + if (fieldi == nfields) + break; + } - if (tnsa && ix.isExpInitializer()) - { - ExpInitializer ei = ix.isExpInitializer(); - if (ei.exp.isStringExp() && tnsa.nextOf().isintegral()) - { - si.addInit(field.ident, ei); - ++index; - } - else - si.addInit(field.ident, subArray(tnsa, index)); - } - else if (tns && ix.isExpInitializer()) + auto tn = field.type.toBasetype(); + auto tnsa = tn.isTypeSArray(); + auto tns = tn.isTypeStruct(); + + if (tnsa && ix.isExpInitializer()) + { + ExpInitializer ei = ix.isExpInitializer(); + if (ei.exp.isStringExp() && tnsa.nextOf().isIntegral()) { - /* Disambiguate between an exp representing the entire - * struct, and an exp representing the first field of the struct - */ - if (representsStruct(ix.isExpInitializer(), tns)) // initializer represents the entire struct - { - si.addInit(field.ident, initializerSemantic(ix, sc, tn, needInterpret)); - ++index; - } - else // field initializers for struct - si.addInit(field.ident, subStruct(tns, index)); // the first field + si.addInit(field.ident, ei); + ++index; } else + si.addInit(field.ident, subArray(tnsa, index)); + } + else if (tns && ix.isExpInitializer()) + { + /* Disambiguate between an exp representing the entire + * struct, and an exp representing the first field of the struct + */ + if (representsStruct(ix.isExpInitializer(), tns)) // initializer represents the entire struct { - si.addInit(field.ident, di.initializer); + si.addInit(field.ident, initializerSemantic(ix, sc, tn, needInterpret)); ++index; } + else // field initializers for struct + si.addInit(field.ident, subStruct(tns, index)); // the first field + } + else + { + si.addInit(field.ident, di.initializer); + ++index; } } return initializerSemantic(si, sc, t, needInterpret); @@ -954,7 +949,7 @@ Initializer initializerSemantic(Initializer init, Scope* sc, ref Type tx, NeedIn /* If it's an array of integral being initialized by `{ string }` * replace with `string` */ - if (tn.isintegral()) + if (tn.isIntegral()) { if (ExpInitializer ei = isBraceExpression()) { @@ -1017,7 +1012,7 @@ Initializer initializerSemantic(Initializer init, Scope* sc, ref Type tx, NeedIn else if (tnsa && di.initializer.isExpInitializer()) { ExpInitializer ei = di.initializer.isExpInitializer(); - if (ei.exp.isStringExp() && tnsa.nextOf().isintegral()) + if (ei.exp.isStringExp() && tnsa.nextOf().isIntegral()) { ai.addInit(null, ei); ++index; @@ -1059,6 +1054,7 @@ Initializer initializerSemantic(Initializer init, Scope* sc, ref Type tx, NeedIn mixin VisitInitializer!Initializer visit; auto result = visit.VisitInitializer(init); + result.semanticDone = true; return (result !is null) ? result : new ErrorInitializer(); } @@ -1099,64 +1095,50 @@ Initializer inferType(Initializer init, Scope* sc) { //printf("ArrayInitializer::inferType() %s\n", toChars()); Expressions* keys = null; - Expressions* values; - if (init.isAssociativeArray()) + Expressions* values = new Expressions(init.value.length); + Initializer no() { + if (keys) + error(init.loc, "not an associative array initializer"); + else + error(init.loc, "cannot infer type from array initializer"); + return new ErrorInitializer(); + } + const bool isAssoc = init.isAssociativeArray(); + if (isAssoc) keys = new Expressions(init.value.length); - values = new Expressions(init.value.length); - for (size_t i = 0; i < init.value.length; i++) + else + values.zero(); + + for (size_t i = 0; i < init.value.length; i++) + { + if (isAssoc) { Expression e = init.index[i]; if (!e) - goto Lno; + return no(); (*keys)[i] = e; - Initializer iz = init.value[i]; - if (!iz) - goto Lno; - iz = iz.inferType(sc); - if (iz.isErrorInitializer()) - { - return iz; - } - (*values)[i] = iz.isExpInitializer().exp; - assert(!(*values)[i].isErrorExp()); } - Expression e = new AssocArrayLiteralExp(init.loc, keys, values); - auto ei = new ExpInitializer(init.loc, e); - return ei.inferType(sc); - } - else - { - auto elements = new Expressions(init.value.length); - elements.zero(); - for (size_t i = 0; i < init.value.length; i++) - { + else assert(!init.index[i]); // already asserted by isAssociativeArray() - Initializer iz = init.value[i]; - if (!iz) - goto Lno; - iz = iz.inferType(sc); - if (iz.isErrorInitializer()) - { - return iz; - } - (*elements)[i] = iz.isExpInitializer().exp; - assert(!(*elements)[i].isErrorExp()); + Initializer iz = init.value[i]; + if (!iz) + return no(); + iz = iz.inferType(sc); + if (iz.isErrorInitializer()) + { + return iz; } - Expression e = new ArrayLiteralExp(init.loc, null, elements); - auto ei = new ExpInitializer(init.loc, e); - return ei.inferType(sc); + (*values)[i] = iz.isExpInitializer().exp; + assert(!(*values)[i].isErrorExp()); } - Lno: - if (keys) - { - error(init.loc, "not an associative array initializer"); - } - else - { - error(init.loc, "cannot infer type from array initializer"); - } - return new ErrorInitializer(); + + Expression e; + e = isAssoc + ? new AssocArrayLiteralExp(init.loc, keys, values) + : new ArrayLiteralExp(init.loc, null, values); + auto ei = new ExpInitializer(init.loc, e); + return ei.inferType(sc); } Initializer visitExp(ExpInitializer init) @@ -1486,10 +1468,10 @@ private bool hasNonConstPointers(Expression e) } if (auto se = ae.e1.isStructLiteralExp()) { - if (!(se.stageflags & stageSearchPointers)) + if (!(se.stageflags & StructLiteralExp.StageFlags.searchPointers)) { const old = se.stageflags; - se.stageflags |= stageSearchPointers; + se.stageflags |= StructLiteralExp.StageFlags.searchPointers; bool ret = checkArray(se.elements); se.stageflags = old; return ret; @@ -1610,7 +1592,7 @@ Expressions* resolveStructLiteralNamedArgs(StructDeclaration sd, Type t, Scope* (vd.offset & (target.ptrsize - 1)))) { if (sc.setUnsafe(false, argLoc, - "field `%s.%s` cannot assign to misaligned pointers in `@safe` code", sd, vd)) + "field `%s.%s` assigning to misaligned pointers", sd, vd)) { errors = true; elems[fieldi] = ErrorExp.get(); // for better diagnostics on multiple errors @@ -1648,7 +1630,7 @@ Expressions* resolveStructLiteralNamedArgs(StructDeclaration sd, Type t, Scope* continue; } - elems[fieldi] = doCopyOrMove(sc, ex); + elems[fieldi] = doCopyOrMove(sc, ex, null, false); ++fieldi; } if (errors) diff --git a/dmd/inline.d b/dmd/inline.d index 1d4b937049..27cf7366d7 100644 --- a/dmd/inline.d +++ b/dmd/inline.d @@ -4,12 +4,12 @@ * The AST is traversed, and every function call is considered for inlining using `inlinecost.d`. * The function call is then inlined if this cost is below a threshold. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/inline.d, _inline.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/inline.d, _inline.d) * Documentation: https://dlang.org/phobos/dmd_inline.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/inline.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/inline.d */ module dmd.inline; @@ -26,12 +26,15 @@ import dmd.dmodule; import dmd.dscope; import dmd.dstruct; import dmd.dsymbol; +import dmd.dsymbolsem : include; import dmd.dtemplate; import dmd.expression; +import dmd.expressionsem : semanticTypeInfo; import dmd.errors; import dmd.func; import dmd.funcsem; import dmd.globals; +import dmd.hdrgen; import dmd.id; import dmd.identifier; import dmd.init; @@ -40,11 +43,11 @@ import dmd.location; import dmd.mtype; import dmd.opover; import dmd.printast; -import dmd.postordervisitor; import dmd.statement; import dmd.tokens; import dmd.typesem : pointerTo, sarrayOf; import dmd.visitor; +import dmd.visitor.postorder; import dmd.inlinecost; /*********************************************************** @@ -105,7 +108,7 @@ public Expression inlineCopy(Expression e, Scope* sc) return de.copy(); } } - int cost = inlineCostExpression(e); + const cost = inlineCostExpression(e); if (cost >= COST_MAX) { error(e.loc, "cannot inline default argument `%s`", e.toChars()); @@ -311,7 +314,7 @@ public: override void visit(IfStatement s) { - assert(!s.prm); + assert(!s.param); auto econd = doInlineAs!Expression(s.condition, ids); assert(econd); @@ -323,7 +326,7 @@ public: static if (asStatements) { - result = new IfStatement(s.loc, s.prm, econd, ifbody, elsebody, s.endloc); + result = new IfStatement(s.loc, s.param, econd, ifbody, elsebody, s.endloc); } else { @@ -737,6 +740,7 @@ version (IN_LLVM) {} else goto LhasLowering; } + ne.placement = doInlineAs!Expression(e.placement, ids); ne.thisexp = doInlineAs!Expression(e.thisexp, ids); ne.argprefix = doInlineAs!Expression(e.argprefix, ids); ne.arguments = arrayExpressionDoInline(e.arguments); @@ -823,7 +827,7 @@ version (IN_LLVM) {} else visit(cast(BinExp)e); Type t1 = e.e1.type.toBasetype(); - if (t1.ty == Tarray || t1.ty == Tsarray) + if (t1.isStaticOrDynamicArray()) { Type t = t1.nextOf().toBasetype(); while (t.toBasetype().nextOf()) @@ -1008,7 +1012,7 @@ public: { static if (LOG) { - printf("ExpStatement.inlineScan(%s)\n", s.toChars()); + printf("ExpStatement.inlineScan(%s)\n", toChars(s)); } if (!s.exp) return; @@ -1248,11 +1252,9 @@ public: void scanVar(Dsymbol s) { //printf("scanVar(%s %s)\n", s.kind(), s.toPrettyChars()); - VarDeclaration vd = s.isVarDeclaration(); - if (vd) + if (VarDeclaration vd = s.isVarDeclaration()) { - TupleDeclaration td = vd.toAlias().isTupleDeclaration(); - if (td) + if (TupleDeclaration td = vd.toAlias().isTupleDeclaration()) { td.foreachVar((s) { @@ -1564,10 +1566,10 @@ public: override void visit(StructLiteralExp e) { //printf("StructLiteralExp.inlineScan()\n"); - if (e.stageflags & stageInlineScan) + if (e.stageflags & StructLiteralExp.StageFlags.inlineScan) return; const old = e.stageflags; - e.stageflags |= stageInlineScan; + e.stageflags |= StructLiteralExp.StageFlags.inlineScan; arrayInlineScan(e.elements); e.stageflags = old; } @@ -1828,7 +1830,7 @@ private bool canInline(FuncDeclaration fd, bool hasthis, bool hdrscan, bool stat /* for the isTypeSArray() case see https://github.com/dlang/dmd/pull/16145#issuecomment-1932776873 */ if (tfnext.ty != Tvoid && - (!(fd.hasReturnExp & 1) || + (!fd.hasReturnExp || hasDtor(tfnext) && (statementsToo || tfnext.isTypeSArray())) && !hdrscan) { @@ -1874,7 +1876,7 @@ private bool canInline(FuncDeclaration fd, bool hasthis, bool hdrscan, bool stat // cannot inline functions as statement if they have multiple // return statements - if ((fd.hasReturnExp & 16) && statementsToo) + if (fd.hasMultipleReturnExp && statementsToo) { static if (CANINLINE_LOG) { @@ -1933,7 +1935,7 @@ private bool canInline(FuncDeclaration fd, bool hasthis, bool hdrscan, bool stat return true; Lno: - if (fd.inlining == PINLINE.always && global.params.warnings == DiagnosticReporting.inform) + if (fd.inlining == PINLINE.always && global.params.useWarnings == DiagnosticReporting.inform) warning(fd.loc, "cannot inline function `%s`", fd.toPrettyChars()); if (!hdrscan) // Don't modify inlineStatus for header content scan @@ -2029,7 +2031,7 @@ private void expandInline(Loc callLoc, FuncDeclaration fd, FuncDeclaration paren { auto tmp = Identifier.generateId("__retvar"); vret = new VarDeclaration(fd.loc, fd.nrvo_var.type, tmp, new VoidInitializer(fd.loc)); - assert(!tf.isref); + assert(!tf.isRef); vret.storage_class = STC.temp | STC.rvalue; vret._linkage = tf.linkage; vret.parent = parent; @@ -2211,7 +2213,7 @@ private void expandInline(Loc callLoc, FuncDeclaration fd, FuncDeclaration paren static if (EXPANDINLINE_LOG) printf("\n[%s] %s expandInline sresult =\n%s\n", - callLoc.toChars(), fd.toPrettyChars(), sresult.toChars()); + callLoc.toChars(), fd.toPrettyChars(), toChars(sresult)); } else { @@ -2225,7 +2227,7 @@ private void expandInline(Loc callLoc, FuncDeclaration fd, FuncDeclaration paren import dmd.expressionsem : toLvalue; // https://issues.dlang.org/show_bug.cgi?id=11322 - if (tf.isref) + if (tf.isRef) e = e.toLvalue(null, "`ref` return"); /* If the inlined function returns a copy of a struct, @@ -2252,7 +2254,7 @@ private void expandInline(Loc callLoc, FuncDeclaration fd, FuncDeclaration paren auto ei = new ExpInitializer(callLoc, e); auto tmp = Identifier.generateId("__inlineretval"); auto vd = new VarDeclaration(callLoc, tf.next, tmp, ei); - vd.storage_class = STC.temp | (tf.isref ? STC.ref_ : STC.rvalue); + vd.storage_class = STC.temp | (tf.isRef ? STC.ref_ : STC.rvalue); vd._linkage = tf.linkage; vd.parent = parent; @@ -2426,7 +2428,7 @@ private bool expNeedsDtor(Expression exp) s = s.toAlias(); if (s != vd) return Dsymbol_needsDtor(s); - else if (vd.isStatic() || vd.storage_class & (STC.extern_ | STC.gshared | STC.manifest)) + if (vd.isStatic() || vd.storage_class & (STC.extern_ | STC.gshared | STC.manifest)) return; if (vd.needsScopeDtor()) { diff --git a/dmd/inlinecost.d b/dmd/inlinecost.d index 90334c8ca5..ad04beadbb 100644 --- a/dmd/inlinecost.d +++ b/dmd/inlinecost.d @@ -1,12 +1,12 @@ /** * Compute the cost of inlining a function call by counting expressions. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/inlinecost.d, _inlinecost.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/inlinecost.d, _inlinecost.d) * Documentation: https://dlang.org/phobos/dmd_inlinecost.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/inlinecost.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/inlinecost.d */ module dmd.inlinecost; @@ -32,10 +32,10 @@ import dmd.identifier; import dmd.init; import dmd.mtype; import dmd.opover; -import dmd.postordervisitor; import dmd.statement; import dmd.tokens; import dmd.visitor; +import dmd.visitor.postorder; enum COST_MAX = 250; @@ -213,7 +213,7 @@ public: s3.endsWithReturnStatement() ) { - if (ifs.prm) // if variables are declared + if (ifs.param) // if variables are declared { cost = COST_MAX; return; @@ -258,7 +258,7 @@ public: /* Can't declare variables inside ?: expressions, so * we cannot inline if a variable is declared. */ - if (s.prm) + if (s.param) { cost = COST_MAX; return; @@ -430,7 +430,7 @@ public: { //printf("NewExp.inlineCost3() %s\n", e.toChars()); AggregateDeclaration ad = isAggregate(e.newtype); - if (ad && ad.isNested()) + if (ad && ad.isNested() || e.placement) cost = COST_MAX; else cost++; diff --git a/dmd/intrange.d b/dmd/intrange.d index 29c8b505cd..f68e302f74 100644 --- a/dmd/intrange.d +++ b/dmd/intrange.d @@ -1,22 +1,21 @@ /** * Implement $(LINK2 https://digitalmars.com/articles/b62.html, Value Range Propagation). * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/intrange.d, _intrange.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/intrange.d, _intrange.d) * Documentation: https://dlang.org/phobos/dmd_intrange.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/intrange.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/intrange.d */ module dmd.intrange; import core.stdc.stdio; -import dmd.astenums; -import dmd.mtype; -import dmd.expression; -import dmd.globals; +import dmd.astenums : Tdchar; +import dmd.mtype : Type; +import dmd.globals : uinteger_t; private uinteger_t copySign(uinteger_t x, bool sign) @safe { @@ -70,7 +69,7 @@ struct SignExtendedNumber } if (value < a.value) return -1; - else if (value > a.value) + if (value > a.value) return 1; else return 0; @@ -122,10 +121,10 @@ struct SignExtendedNumber SignExtendedNumber opBinary(string op : "+")(SignExtendedNumber rhs) { uinteger_t sum = value + rhs.value; - bool carry = sum < value && sum < rhs.value; + const carry = sum < value && sum < rhs.value; if (negative != rhs.negative) return SignExtendedNumber(sum, !carry); - else if (negative) + if (negative) return SignExtendedNumber(carry ? sum : 0, true); else return SignExtendedNumber(carry ? ulong.max : sum, false); @@ -142,7 +141,7 @@ struct SignExtendedNumber SignExtendedNumber opBinary(string op : "*")(SignExtendedNumber rhs) { - // perform *saturated* multiplication, otherwise we may get bogus ranges + // perform* saturated* multiplication, otherwise we may get bogus ranges // like 0x10 * 0x10 == 0x100 == 0. /* Special handling for zeros: @@ -155,7 +154,7 @@ struct SignExtendedNumber { if (!negative) return this; - else if (rhs.negative) + if (rhs.negative) return max(); else return rhs.value == 0 ? rhs : this; @@ -249,7 +248,7 @@ struct SignExtendedNumber // shifts will give huge result. if (value == 0) return this; - else if (rhs.negative) + if (rhs.negative) return extreme(negative); uinteger_t v = copySign(value, negative); @@ -278,7 +277,7 @@ struct SignExtendedNumber { if (rhs.negative || rhs.value > 63) return negative ? SignExtendedNumber(-1, true) : SignExtendedNumber(0); - else if (isMinimum()) + if (isMinimum()) return rhs.value == 0 ? this : SignExtendedNumber(-1UL << (64 - rhs.value), true); uinteger_t x = value ^ -cast(int)negative; @@ -317,12 +316,12 @@ struct IntRange static IntRange fromType(Type type) { - return fromType(type, type.isunsigned()); + return fromType(type, type.isUnsigned()); } static IntRange fromType(Type type, bool isUnsigned) { - if (!type.isintegral() || type.toBasetype().ty == Tvector) + if (!type.isIntegral() || type.toBasetype().isTypeVector()) return widest(); uinteger_t mask = type.sizemask(); @@ -444,24 +443,22 @@ struct IntRange IntRange _cast(Type type) { - if (!type.isintegral() || type.toBasetype().ty == Tvector) + if (!type.isIntegral() || type.toBasetype().isTypeVector()) return this; - else if (!type.isunsigned()) + if (!type.isUnsigned()) return castSigned(type.sizemask()); - else if (type.toBasetype().ty == Tdchar) + if (type.toBasetype().ty == Tdchar) return castDchar(); - else return castUnsigned(type.sizemask()); } IntRange castUnsigned(Type type) { - if (!type.isintegral() || type.toBasetype().ty == Tvector) + if (!type.isIntegral() || type.toBasetype().isTypeVector()) return castUnsigned(ulong.max); - else if (type.toBasetype().ty == Tdchar) + if (type.toBasetype().ty == Tdchar) return castDchar(); - else - return castUnsigned(type.sizemask()); + return castUnsigned(type.sizemask()); } bool contains(IntRange a) @safe @@ -479,14 +476,11 @@ struct IntRange { if (imax.negative) return this; - else if (!imin.negative) + if (!imin.negative) return IntRange(-imax, -imin); - else - { - SignExtendedNumber imaxAbsNeg = -imax; - return IntRange(imaxAbsNeg < imin ? imaxAbsNeg : imin, - SignExtendedNumber(0)); - } + SignExtendedNumber imaxAbsNeg = -imax; + return IntRange(imaxAbsNeg < imin ? imaxAbsNeg : imin, + SignExtendedNumber(0)); } IntRange unionWith(const ref IntRange other) const @safe @@ -504,7 +498,7 @@ struct IntRange union_ = true; } - ref const(IntRange) dump(const(char)* funcName, Expression e) const return + ref const(IntRange) dump(Exp)(const(char)* funcName, Exp e) const return { printf("[(%c)%#018llx, (%c)%#018llx] @ %s ::: %s\n", imin.negative?'-':'+', cast(ulong)imin.value, @@ -574,13 +568,13 @@ struct IntRange swap(l, r); // r spans [-1,0] } - auto minAndNeg = minAnd(l, IntRange(r.imin, SignExtendedNumber(-1))); - auto minAndPos = minAnd(l, IntRange(SignExtendedNumber(0), r.imax)); - auto maxAndNeg = maxAnd(l, IntRange(r.imin, SignExtendedNumber(-1))); - auto maxAndPos = maxAnd(l, IntRange(SignExtendedNumber(0), r.imax)); + const minAndNeg = minAnd(l, IntRange(r.imin, SignExtendedNumber(-1))); + const minAndPos = minAnd(l, IntRange(SignExtendedNumber(0), r.imax)); + const maxAndNeg = maxAnd(l, IntRange(r.imin, SignExtendedNumber(-1))); + const maxAndPos = maxAnd(l, IntRange(SignExtendedNumber(0), r.imax)); - auto min = minAndNeg < minAndPos ? minAndNeg : minAndPos; - auto max = maxAndNeg > maxAndPos ? maxAndNeg : maxAndPos; + const min = minAndNeg < minAndPos ? minAndNeg : minAndPos; + const max = maxAndNeg > maxAndPos ? maxAndNeg : maxAndPos; auto range = IntRange(min, max); return range; @@ -668,7 +662,7 @@ struct IntRange return widest(); // Don't treat the whole range as divide by 0 if only one end of a range is 0. - // Issue 15289 + // https://issues.dlang.org/show_bug.cgi?id=15289 if (rhs.imax.value == 0) { rhs.imax.value--; @@ -682,17 +676,19 @@ struct IntRange { return IntRange(imin / rhs.imax, imax / rhs.imin); } - else + if (rhs.imin.negative && !rhs.imax.negative) // divisor spans [-1, 0, 1] { - // [a,b] / [c,d] = [min (a/c, a/d, b/c, b/d), max (a/c, a/d, b/c, b/d)] - SignExtendedNumber[4] bdy; - bdy[0] = imin / rhs.imin; - bdy[1] = imin / rhs.imax; - bdy[2] = imax / rhs.imin; - bdy[3] = imax / rhs.imax; - + SignExtendedNumber[4] bdy = [-imin, imin, -imax, imax]; return IntRange.fromNumbers4(bdy.ptr); } + // [a,b] / [c,d] = [min (a/c, a/d, b/c, b/d), max (a/c, a/d, b/c, b/d)] + SignExtendedNumber[4] bdy; + bdy[0] = imin / rhs.imin; + bdy[1] = imin / rhs.imax; + bdy[2] = imax / rhs.imin; + bdy[3] = imax / rhs.imax; + + return IntRange.fromNumbers4(bdy.ptr); } IntRange opBinary(string op : "%")(IntRange rhs) diff --git a/dmd/json.d b/dmd/json.d index f20b3d4b38..080870aa47 100644 --- a/dmd/json.d +++ b/dmd/json.d @@ -1,12 +1,12 @@ /** * Code for generating .json descriptions of the module when passing the `-X` flag to dmd. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/json.d, _json.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/json.d, _json.d) * Documentation: https://dlang.org/phobos/dmd_json.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/json.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/json.d */ module dmd.json; @@ -24,6 +24,7 @@ import dmd.denum; import dmd.dimport; import dmd.dmodule; import dmd.dsymbol; +import dmd.dsymbolsem : include; import dmd.dtemplate; import dmd.errors; import dmd.expression; @@ -40,9 +41,13 @@ import dmd.root.string; import dmd.target; import dmd.visitor; -version(Windows) { - extern (C) char* getcwd(char* buffer, size_t maxlen); -} else { +version(Windows) +{ + extern (C) char* _getcwd(char* buffer, size_t maxlen); + alias getcwd = _getcwd; +} +else +{ import core.sys.posix.unistd : getcwd; } @@ -328,7 +333,7 @@ public: } } - extern(D) void propertyStorageClass(const char[] name, StorageClass stc) + extern(D) void propertyStorageClass(const char[] name, STC stc) { stc &= STC.visibleStorageClasses; if (stc) @@ -345,23 +350,22 @@ public: } } - extern(D) void property(const char[] linename, const char[] charname, const ref Loc loc) + extern(D) void property(const char[] linename, const char[] charname, Loc loc) { if (loc.isValid()) { - if (auto filename = loc.filename.toDString) + SourceLoc sl = SourceLoc(loc); + if (sl.filename.length > 0 && sl.filename != this.filename) { - if (filename != this.filename) - { - this.filename = filename; - property("file", filename); - } + this.filename = sl.filename; + property("file", sl.filename); } - if (loc.linnum) + + if (sl.linnum) { - property(linename, loc.linnum); - if (loc.charnum) - property(charname, loc.charnum); + property(linename, sl.linnum); + if (sl.charnum) + property(charname, sl.charnum); } } } @@ -904,7 +908,7 @@ public: arrayStart(); foreach (importPath; global.params.imppath[]) { - item(importPath.toDString); + item(importPath.path.toDString); } arrayEnd(); @@ -990,35 +994,34 @@ void json_generate(ref Modules modules, ref OutBuffer buf) // of modules representing their syntax. json.generateModules(modules); json.removeComma(); + return; } - else - { - // Generate the new format which is an object where each - // output option is its own field. - json.objectStart(); - if (global.params.jsonFieldFlags & JsonFieldFlags.compilerInfo) - { - json.propertyStart("compilerInfo"); - json.generateCompilerInfo(); - } - if (global.params.jsonFieldFlags & JsonFieldFlags.buildInfo) - { - json.propertyStart("buildInfo"); - json.generateBuildInfo(); - } - if (global.params.jsonFieldFlags & JsonFieldFlags.modules) - { - json.propertyStart("modules"); - json.generateModules(modules); - } - if (global.params.jsonFieldFlags & JsonFieldFlags.semantics) - { - json.propertyStart("semantics"); - json.generateSemantics(); - } - json.objectEnd(); + // Generate the new format which is an object where each + // output option is its own field. + + json.objectStart(); + if (global.params.jsonFieldFlags & JsonFieldFlags.compilerInfo) + { + json.propertyStart("compilerInfo"); + json.generateCompilerInfo(); + } + if (global.params.jsonFieldFlags & JsonFieldFlags.buildInfo) + { + json.propertyStart("buildInfo"); + json.generateBuildInfo(); + } + if (global.params.jsonFieldFlags & JsonFieldFlags.modules) + { + json.propertyStart("modules"); + json.generateModules(modules); + } + if (global.params.jsonFieldFlags & JsonFieldFlags.semantics) + { + json.propertyStart("semantics"); + json.generateSemantics(); } + json.objectEnd(); } /** diff --git a/dmd/json.h b/dmd/json.h index b119c9ede4..821150935a 100644 --- a/dmd/json.h +++ b/dmd/json.h @@ -1,6 +1,6 @@ /* Compiler implementation of the D programming language - * Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * written by Walter Bright * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. diff --git a/dmd/lambdacomp.d b/dmd/lambdacomp.d index a1db8d5254..9f9fd77b7e 100644 --- a/dmd/lambdacomp.d +++ b/dmd/lambdacomp.d @@ -5,12 +5,12 @@ * The serialization is a string which contains the type of the parameters and the string * represantation of the lambda expression. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/lamdbacomp.d, _lambdacomp.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/lambdacomp.d, _lambdacomp.d) * Documentation: https://dlang.org/phobos/dmd_lambdacomp.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/lambdacomp.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/lambdacomp.d */ module dmd.lambdacomp; @@ -26,8 +26,8 @@ import dmd.dsymbolsem; import dmd.dtemplate; import dmd.expression; import dmd.func; -import dmd.dmangle; import dmd.hdrgen; +import dmd.mangle; import dmd.mtype; import dmd.common.outbuffer; import dmd.root.rmem; @@ -240,39 +240,39 @@ public: auto id = exp.ident.toChars(); // If it's not an argument - if (!checkArgument(id)) + if (checkArgument(id)) + return; + + // we must check what the identifier expression is. + Dsymbol scopesym; + Dsymbol s = sc.search(exp.loc, exp.ident, scopesym); + + // If it's an unknown symbol, consider the function incomparable + if (!s) { - // we must check what the identifier expression is. - Dsymbol scopesym; - Dsymbol s = sc.search(exp.loc, exp.ident, scopesym); - if (s) - { - auto v = s.isVarDeclaration(); - // If it's a VarDeclaration, it must be a manifest constant - if (v && (v.storage_class & STC.manifest)) - { - v.getConstInitializer.accept(this); - } - else if (auto em = s.isEnumDeclaration()) - { - d = em; - et = ExpType.EnumDecl; - } - else if (auto fd = s.isFuncDeclaration()) - { - writeMangledName(fd); - } - // For anything else, the function is deemed uncomparable - else - { - buf.setsize(0); - } - } - // If it's an unknown symbol, consider the function incomparable - else - { - buf.setsize(0); - } + buf.setsize(0); + return; + } + + auto v = s.isVarDeclaration(); + // If it's a VarDeclaration, it must be a manifest constant + if (v && (v.storage_class & STC.manifest)) + { + v.getConstInitializer.accept(this); + } + else if (auto em = s.isEnumDeclaration()) + { + d = em; + et = ExpType.EnumDecl; + } + else if (auto fd = s.isFuncDeclaration()) + { + writeMangledName(fd); + } + // For anything else, the function is deemed uncomparable + else + { + buf.setsize(0); } } @@ -445,26 +445,28 @@ public: visitType(p.type); } - override void visit(StructLiteralExp e) { + override void visit(StructLiteralExp e) + { static if (LOG) printf("StructLiteralExp: %s\n", e.toChars); auto ty = cast(TypeStruct)e.stype; - if (ty) + if (!ty) { - writeMangledName(ty.sym); - auto dim = e.elements.length; - foreach (i; 0..dim) - { - auto elem = (*e.elements)[i]; - if (elem) - elem.accept(this); - else - buf.writestring("null_"); - } - } - else buf.setsize(0); + return; + } + + writeMangledName(ty.sym); + auto dim = e.elements.length; + foreach (i; 0..dim) + { + auto elem = (*e.elements)[i]; + if (elem) + elem.accept(this); + else + buf.writestring("null_"); + } } override void visit(ArrayLiteralExp) { buf.setsize(0); } diff --git a/dmd/lexer.d b/dmd/lexer.d index a783e0a508..981002e68b 100644 --- a/dmd/lexer.d +++ b/dmd/lexer.d @@ -3,12 +3,12 @@ * * Specification: $(LINK2 https://dlang.org/spec/lex.html, Lexical) * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/lexer.d, _lexer.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/lexer.d, _lexer.d) * Documentation: https://dlang.org/phobos/dmd_lexer.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/lexer.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/lexer.d */ module dmd.lexer; @@ -34,11 +34,6 @@ import dmd.tokens; nothrow: -version (DMDLIB) -{ - version = LocOffset; -} - /*********************************************************** * Values to use for various magic identifiers */ @@ -53,6 +48,7 @@ struct CompileEnv const(char)[] timestamp; /// __TIMESTAMP__ bool previewIn; /// `in` means `[ref] scope const`, accepts rvalues + bool transitionIn; /// `-transition=in` is active, `in` parameters are listed bool ddocOutput; /// collect embedded documentation comments bool masm; /// use MASM inline asm syntax @@ -67,8 +63,10 @@ class Lexer { private __gshared OutBuffer stringbuffer; + BaseLoc* baseLoc; // Used to generate `scanloc`, which is just an index into this data structure Loc scanloc; // for error messages Loc prevloc; // location of token before current + int linnum; // current line number const(char)* p; // current character @@ -131,10 +129,11 @@ class Lexer ErrorSink errorSink, const CompileEnv* compileEnv) scope { - scanloc = Loc(filename, 1, 1); // debug printf("Lexer::Lexer(%p)\n", base); // debug printf("lexer.filename = %s\n", filename); token = Token.init; + this.baseLoc = newBaseLoc(filename, base[0 .. endoffset]); + this.linnum = 1; this.base = base; this.end = base + endoffset; p = base + begoffset; @@ -224,7 +223,9 @@ class Lexer tokenizeNewlines = true; inTokenStringConstant = 0; lastDocLine = 0; - scanloc = Loc("#defines", 1, 1); + + baseLoc = newBaseLoc("#defines", slice); + scanloc = baseLoc.getLoc(0); } /********************************** @@ -318,7 +319,7 @@ class Lexer */ final void scan(Token* t) { - const lastLine = scanloc.linnum; + const lastLine = linnum; Loc startLoc; t.blockComment = null; t.lineComment = null; @@ -499,7 +500,7 @@ class Lexer clexerCharConstant(*t, c); return; } - else if (p[1] == '\"') // C wide string literal + if (p[1] == '\"') // C wide string literal { const c = *p; ++p; @@ -509,7 +510,7 @@ class Lexer 'd'; return; } - else if (p[1] == '8' && p[2] == '\"') // C UTF-8 string literal + if (p[1] == '8' && p[2] == '\"') // C UTF-8 string literal { p += 2; escapeStringConstant(t); @@ -542,14 +543,13 @@ class Lexer delimitedStringConstant(t); return; } - else if (p[1] == '{') + if (p[1] == '{') { p++; tokenStringConstant(t); return; } - else - goto case_ident; + goto case_ident; case 'i': if (Ccompile) goto case_ident; @@ -559,20 +559,19 @@ class Lexer escapeStringConstant(t, true); return; } - else if (p[1] == '`') + if (p[1] == '`') { p++; // skip the i wysiwygStringConstant(t, true); return; } - else if (p[1] == 'q' && p[2] == '{') + if (p[1] == 'q' && p[2] == '{') { p += 2; // skip the i and q tokenStringConstant(t, true); return; } - else - goto case_ident; + goto case_ident; case '"': escapeStringConstant(t); return; @@ -644,7 +643,7 @@ class Lexer if (isidchar(c)) continue; - else if (c & 0x80) + if (c & 0x80) { const s = p; const u = decodeUTF(); @@ -868,7 +867,7 @@ class Lexer case 0: case 0x1A: error(t.loc, "unterminated /* */ comment"); - p = end; + //p = end; t.loc = loc(); t.value = TOK.endOfFile; return; @@ -894,11 +893,11 @@ class Lexer t.value = TOK.comment; return; } - else if (doDocComment && t.ptr[2] == '*' && p - 4 != t.ptr) + if (doDocComment && t.ptr[2] == '*' && p - 4 != t.ptr) { // if /** but not /**/ getDocComment(t, lastLine == startLoc.linnum, startLoc.linnum - lastDocLine > 1); - lastDocLine = scanloc.linnum; + lastDocLine = linnum; } continue; case '/': // do // style comments @@ -926,9 +925,9 @@ class Lexer if (doDocComment && t.ptr[2] == '/') { getDocComment(t, lastLine == startLoc.linnum, startLoc.linnum - lastDocLine > 1); - lastDocLine = scanloc.linnum; + lastDocLine = linnum; } - p = end; + //p = end; t.loc = loc(); t.value = TOK.endOfFile; return; @@ -958,7 +957,7 @@ class Lexer if (doDocComment && t.ptr[2] == '/') { getDocComment(t, lastLine == startLoc.linnum, startLoc.linnum - lastDocLine > 1); - lastDocLine = scanloc.linnum; + lastDocLine = linnum; } p++; endOfLine(); @@ -1004,7 +1003,7 @@ class Lexer case 0: case 0x1A: error(t.loc, "unterminated /+ +/ comment"); - p = end; + //p = end; t.loc = loc(); t.value = TOK.endOfFile; return; @@ -1030,7 +1029,7 @@ class Lexer { // if /++ but not /++/ getDocComment(t, lastLine == startLoc.linnum, startLoc.linnum - lastDocLine > 1); - lastDocLine = scanloc.linnum; + lastDocLine = linnum; } continue; } @@ -1465,7 +1464,7 @@ class Lexer * Returns: * the escape sequence as a single character */ - private dchar escapeSequence(const ref Loc loc, ref const(char)* sequence, bool Ccompile, out dchar c2) + private dchar escapeSequence(Loc loc, ref const(char)* sequence, bool Ccompile, out dchar c2) { const(char)* p = sequence; // cache sequence reference on stack scope(exit) sequence = p; @@ -2572,19 +2571,19 @@ class Lexer Ldone: if (errorDigit) { - error(token.loc, "%s digit expected, not `%c`", base == 2 ? "binary".ptr : + error(scanloc, "%s digit expected, not `%c`", base == 2 ? "binary".ptr : base == 8 ? "octal".ptr : "decimal".ptr, errorDigit); err = true; } if (overflow && !err) { - error("integer overflow"); + error(scanloc, "integer overflow"); err = true; } if ((base == 2 && !anyBinaryDigitsNoSingleUS) || (base == 16 && !anyHexDigitsNoSingleUS)) - error(token.loc, "`%.*s` isn't a valid integer literal, use `%.*s0` instead", cast(int)(p - start), start, 2, start); + error(scanloc, "`%.*s` isn't a valid integer literal, use `%.*s0` instead", cast(int)(p - start), start, 2, start); t.unsvalue = n; @@ -2613,7 +2612,7 @@ class Lexer goto L1; case 'l': f = FLAGS.long_; - error("lower case integer suffix 'l' is not allowed. Please use 'L' instead"); + error(scanloc, "lower case integer suffix 'l' is not allowed. Please use 'L' instead"); goto L1; case 'L': f = FLAGS.long_; @@ -2621,7 +2620,7 @@ class Lexer p++; if ((flags & f) && !err) { - error("repeated integer suffix `%c`", p[-1]); + error(scanloc, "repeated integer suffix `%c`", p[-1]); err = true; } flags = cast(FLAGS)(flags | f); @@ -2635,9 +2634,9 @@ class Lexer { if (err) // can't translate invalid octal value, just show a generic message - error("octal literals larger than 7 are no longer supported"); + error(scanloc, "octal literals larger than 7 are no longer supported"); else - error(token.loc, "octal literals `0%llo%.*s` are no longer supported, use `std.conv.octal!\"%llo%.*s\"` instead", + error(scanloc, "octal literals `0%llo%.*s` are no longer supported, use `std.conv.octal!\"%llo%.*s\"` instead", n, cast(int)(p - psuffix), psuffix, n, cast(int)(p - psuffix), psuffix); } TOK result; @@ -3158,9 +3157,7 @@ version (IN_LLVM) { /* *always* map C `long double` literals to D `real` ones */ final Loc loc() @nogc { - scanloc.charnum = cast(ushort)(1 + p - line); - version (LocOffset) - scanloc.fileOffset = cast(uint)(p - base); + scanloc = baseLoc.getLoc(cast(uint) (p - base)); return scanloc; } @@ -3169,7 +3166,7 @@ version (IN_LLVM) { /* *always* map C `long double` literals to D `real` ones */ eSink.error(token.loc, format, args); } - void error(T...)(const ref Loc loc, const(char)* format, T args) + void error(T...)(Loc loc, const(char)* format, T args) { eSink.error(loc, format, args); } @@ -3179,12 +3176,12 @@ version (IN_LLVM) { /* *always* map C `long double` literals to D `real` ones */ eSink.errorSupplemental(token.loc, format, args); } - void deprecation(T...)(const ref Loc loc, const(char)* format, T args) + void deprecation(T...)(Loc loc, const(char)* format, T args) { eSink.deprecation(loc, format, args); } - void warning(T...)(const ref Loc loc, const(char)* format, T args) + void warning(T...)(Loc loc, const(char)* format, T args) { eSink.warning(loc, format, args); } @@ -3255,7 +3252,6 @@ version (IN_LLVM) { /* *always* map C `long double` literals to D `real` ones */ */ final void poundLine(ref Token tok, bool linemarker) { - auto linnum = this.scanloc.linnum; const(char)* filespec = null; bool flags; @@ -3292,9 +3288,7 @@ version (IN_LLVM) { /* *always* map C `long double` literals to D `real` ones */ case TOK.endOfLine: if (!inTokenStringConstant) { - this.scanloc.linnum = linnum; - if (filespec) - this.scanloc.filename = filespec; + baseLoc.addSubstitution(cast(uint) (p - base), filespec, linnum); } return; case TOK.file: @@ -3445,12 +3439,12 @@ version (IN_LLVM) { /* *always* map C `long double` literals to D `real` ones */ if (*q != ct) break; } - /* Remove leading spaces until start of the comment + /* Remove leading line feed or space */ int linestart = 0; if (ct == '/') { - while (q < qend && (*q == ' ' || *q == '\t')) + if (q < qend && *q == ' ') ++q; } else if (q < qend) @@ -3592,10 +3586,11 @@ version (IN_LLVM) { /* *always* map C `long double` literals to D `real` ones */ /************************** * `p` should be at start of next line */ - private void endOfLine() @nogc @safe + private void endOfLine() @safe { - scanloc.linnum = scanloc.linnum + 1; + linnum += 1; line = p; + baseLoc.newLine(cast(uint)(p - base)); } /**************************** @@ -3685,18 +3680,15 @@ unittest string expectedSupplemental; bool gotError; - void error(const ref Loc loc, const(char)* format, ...) + void verror(Loc loc, const(char)* format, va_list ap) { gotError = true; char[100] buffer = void; - va_list ap; - va_start(ap, format); auto actual = buffer[0 .. vsnprintf(buffer.ptr, buffer.length, format, ap)]; - va_end(ap); assert(expected == actual); } - void errorSupplemental(const ref Loc loc, const(char)* format, ...) + void errorSupplemental(Loc loc, const(char)* format, ...) { gotError = true; char[128] buffer = void; diff --git a/dmd/location.d b/dmd/location.d index ca6805ee0b..393ffb8a92 100644 --- a/dmd/location.d +++ b/dmd/location.d @@ -1,12 +1,12 @@ /** * Encapsulates file/line/column locations. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/location.d, _location.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/location.d, _location.d) * Documentation: https://dlang.org/phobos/dmd_location.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/location.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/location.d */ module dmd.location; @@ -16,19 +16,15 @@ import core.stdc.stdio; import dmd.common.outbuffer; import dmd.root.array; import dmd.root.filename; - -version (DMDLIB) -{ - version = LocOffset; -} +import dmd.root.string: toDString; /// How code locations are formatted for diagnostic reporting enum MessageStyle : ubyte { digitalmars, /// filename.d(line): message gnu, /// filename.d:line: message, see https://www.gnu.org/prep/standards/html_node/Errors.html + sarif /// JSON SARIF output, see https://docs.oasis-open.org/sarif/sarif/v2.1.0/sarif-v2.1.0.html } - /** A source code location @@ -37,19 +33,19 @@ debug info etc. */ struct Loc { - private uint _linnum; - private uint _charnum; - private uint fileIndex; // index into filenames[], starting from 1 (0 means no filename) - version (LocOffset) - uint fileOffset; /// utf8 code unit index relative to start of file, starting from 0 + private uint index = 0; // offset into lineTable[] + + // FIXME: This arbitrary size increase is needed to prevent segfault in + // runnable/test42.d on Ubuntu x86 when DMD was built with DMD 2.105 .. 2.110 + // https://github.com/dlang/dmd/pull/20777#issuecomment-2614128849 + version (DigitalMars) version (linux) version (X86) + private uint dummy; - static immutable Loc initial; /// use for default initialization of const ref Loc's + static immutable Loc initial; /// use for default initialization of Loc's extern (C++) __gshared bool showColumns; extern (C++) __gshared MessageStyle messageStyle; - __gshared Array!(const(char)*) filenames; - nothrow: /******************************* @@ -64,35 +60,34 @@ nothrow: this.messageStyle = messageStyle; } - extern (C++) this(const(char)* filename, uint linnum, uint charnum) @safe + /// Returns: a Loc that simply holds a filename, with no line / column info + extern (C++) static Loc singleFilename(const char* filename) { - this._linnum = linnum; - this._charnum = charnum; - this.filename = filename; + Loc result; + locFileTable ~= new BaseLoc(filename.toDString, null, locIndex, 0, [0]); + result.index = locIndex++; + return result; } /// utf8 code unit index relative to start of line, starting from 1 extern (C++) uint charnum() const @nogc @safe { - return _charnum; - } - - /// ditto - extern (C++) uint charnum(uint num) @nogc @safe - { - return _charnum = num; + return SourceLoc(this).column; } /// line number, starting from 1 - extern (C++) uint linnum() const @nogc @safe + extern (C++) uint linnum() const @nogc @trusted { - return _linnum; + return SourceLoc(this).line; } - /// ditto - extern (C++) uint linnum(uint num) @nogc @safe + /// Advance this location to the first column of the next line + void nextLine() { - return _linnum = num; + const i = fileTableIndex(this.index); + const j = locFileTable[i].getLineIndex(this.index - locFileTable[i].startIndex); + if (j + 1 < locFileTable[i].lines.length) + index = locFileTable[i].startIndex + locFileTable[i].lines[j + 1]; } /*** @@ -100,62 +95,39 @@ nothrow: */ extern (C++) const(char)* filename() const @nogc { - return fileIndex ? filenames[fileIndex - 1] : null; - } + if (this.index == 0) + return null; - /*** - * Set file name for this location - * Params: - * name = file name for location, null for no file name - */ - extern (C++) void filename(const(char)* name) @trusted - { - if (name) + const i = fileTableIndex(this.index); + if (locFileTable[i].substitutions.length > 0) { - //printf("setting %s\n", name); - filenames.push(name); - fileIndex = cast(uint)filenames.length; - assert(fileIndex, "internal compiler error: file name index overflow"); + const si = locFileTable[i].getSubstitutionIndex(this.index - locFileTable[i].startIndex); + const fname = locFileTable[i].substitutions[si].filename; + if (fname.length > 0) + return fname.ptr; } - else - fileIndex = 0; + + return locFileTable[i].filename.ptr; } extern (C++) const(char)* toChars( bool showColumns = Loc.showColumns, MessageStyle messageStyle = Loc.messageStyle) const nothrow { - OutBuffer buf; - if (fileIndex) - { - buf.writestring(filename); - } - if (linnum) - { - final switch (messageStyle) - { - case MessageStyle.digitalmars: - buf.writeByte('('); - buf.print(linnum); - if (showColumns && charnum) - { - buf.writeByte(','); - buf.print(charnum); - } - buf.writeByte(')'); - break; - case MessageStyle.gnu: // https://www.gnu.org/prep/standards/html_node/Errors.html - buf.writeByte(':'); - buf.print(linnum); - if (showColumns && charnum) - { - buf.writeByte(':'); - buf.print(charnum); - } - break; - } - } - return buf.extractChars(); + return SourceLoc(this).toChars(showColumns, messageStyle); + } + + /// Returns: byte offset into source file + uint fileOffset() const + { + const i = fileTableIndex(this.index); + return this.index - locFileTable[i].startIndex; + } + + /// Returns: this location as a SourceLoc + extern (C++) SourceLoc toSourceLoc() const @nogc @safe + { + return SourceLoc(this); } /** @@ -165,11 +137,13 @@ nothrow: * - Uses case-insensitive comparison on Windows * - Ignores `charnum` if `Columns` is false. */ - extern (C++) bool equals(ref const(Loc) loc) const + extern (C++) bool equals(Loc loc) const { - return (!showColumns || charnum == loc.charnum) && - linnum == loc.linnum && - FileName.equals(filename, loc.filename); + SourceLoc lhs = SourceLoc(this); + SourceLoc rhs = SourceLoc(loc); + return (!showColumns || lhs.column == rhs.column) && + lhs.line == rhs.line && + FileName.equals(lhs.filename, rhs.filename); } /** @@ -182,23 +156,13 @@ nothrow: */ extern (D) bool opEquals(ref const(Loc) loc) const @trusted nothrow @nogc { - import core.stdc.string : strcmp; - - return charnum == loc.charnum && - linnum == loc.linnum && - (filename == loc.filename || - (filename && loc.filename && strcmp(filename, loc.filename) == 0)); + return this.index == loc.index; } /// ditto extern (D) size_t toHash() const @trusted nothrow { - import dmd.root.string : toDString; - - auto hash = hashOf(linnum); - hash = hashOf(charnum, hash); - hash = hashOf(filename.toDString, hash); - return hash; + return hashOf(this.index); } /****************** @@ -207,6 +171,302 @@ nothrow: */ bool isValid() const pure @safe { - return fileIndex != 0; + return this.index != 0; + } +} + +/** + * Format a source location for error messages + * + * Params: + * buf = buffer to write string into + * loc = source location to write + * showColumns = include column number in message + * messageStyle = select error message format + */ +void writeSourceLoc(ref OutBuffer buf, + SourceLoc loc, + bool showColumns, + MessageStyle messageStyle) nothrow +{ + if (loc.filename.length == 0) + return; + buf.writestring(loc.filename); + if (loc.line == 0) + return; + + final switch (messageStyle) + { + case MessageStyle.digitalmars: + buf.writeByte('('); + buf.print(loc.line); + if (showColumns && loc.column) + { + buf.writeByte(','); + buf.print(loc.column); + } + buf.writeByte(')'); + break; + case MessageStyle.gnu: // https://www.gnu.org/prep/standards/html_node/Errors.html + buf.writeByte(':'); + buf.print(loc.line); + if (showColumns && loc.column) + { + buf.writeByte(':'); + buf.print(loc.column); + } + break; + case MessageStyle.sarif: // https://docs.oasis-open.org/sarif/sarif/v2.1.0/sarif-v2.1.0.html + // No formatting needed here for SARIF + break; } } + +/** + * Describes a location in the source code as a file + line number + column number + * + * While `Loc` is a compact opaque location meant to be stored in the AST, + * this struct has simple modifiable fields and is used for printing. + */ +struct SourceLoc +{ + const(char)[] filename; /// name of source file + uint line; /// line number (starts at 1) + uint column; /// column number (starts at 1) + uint fileOffset; /// byte index into file + + /// Index `fileOffset` into this to to obtain source code context of this location + const(char)[] fileContent; + + // aliases for backwards compatibility + alias linnum = line; + alias charnum = column; + + this(const(char)[] filename, uint line, uint column, uint fileOffset = 0, const(char)[] fileContent = null) nothrow @nogc pure @safe + { + this.filename = filename; + this.line = line; + this.column = column; + this.fileOffset = fileOffset; + this.fileContent = fileContent; + } + + this(Loc loc) nothrow @nogc @trusted + { + if (loc.index == 0 || locFileTable.length == 0) + return; + + const i = fileTableIndex(loc.index); + this = locFileTable[i].getSourceLoc(loc.index - locFileTable[i].startIndex); + } + + extern (C++) const(char)* toChars( + bool showColumns = Loc.showColumns, + MessageStyle messageStyle = Loc.messageStyle) const nothrow + { + OutBuffer buf; + writeSourceLoc(buf, this, showColumns, messageStyle); + return buf.extractChars(); + } + + bool opEquals(SourceLoc other) const nothrow + { + return this.filename == other.filename && this.line == other.line && this.column == other.column; + } + +} + +/// Given the `index` of a `Loc`, find the index in `locFileTable` of the corresponding `BaseLoc` +private size_t fileTableIndex(uint index) nothrow @nogc +{ + // To speed up linear find, we cache the last hit and compare that first, + // since usually we stay in the same file for some time when resolving source locations. + // If it's a different file now, either scan forwards / backwards + __gshared size_t lastI = 0; // index of last found hit + + size_t i = lastI; + if (index >= locFileTable[i].startIndex) + { + while (i + 1 < locFileTable.length && index >= locFileTable[i+1].startIndex) + i++; + } + else + { + while (index < locFileTable[i].startIndex) + i--; + } + + lastI = i; + return i; +} + +/** + * Create a new source location map for a file + * Params: + * filename = source file name + * fileContent = content of source file + * Returns: new BaseLoc + */ +BaseLoc* newBaseLoc(const(char)* filename, const(char)[] fileContent) nothrow +{ + locFileTable ~= new BaseLoc(filename.toDString, fileContent, locIndex, 1, [0]); + // Careful: the endloc of a FuncDeclaration can + // point to 1 past the very last byte in the file, so account for that + locIndex += fileContent.length + 1; + return locFileTable[$ - 1]; +} + +/** +Mapping from byte offset into source file to line/column numbers + +Consider this 4-line 24 byte source file: + +--- +app.d +1 struct S +2 { +3 int y; +4 } +--- + +Loc(0) is reserved for null locations, so the first `BaseLoc` gets `startIndex = 1` +and reserves 25 possible positions. Loc(1) represents the very start of this source +file, and every next byte gets the next `Loc`, up to Loc(25) which represents the +location right past the very last `}` character (hence it's 1 more than the file +size of 24, classic fence post problem!). + +The next source file will get `Loc(26) .. Loc(26 + fileSize + 1)` etc. + +Now say we know that `int y` has a `Loc(20)` and we want to know the line and column number. + +First we find the corresponding `BaseLoc` in `locFileTable`. Since 20 < 26, the first `BaseLoc` +contains this location. Since `startIndex = 1`, we subtract that to get a file offset 19. + +To get the line number from the file offset, we binary search into the `lines` array, +which contains file offsets where each line starts: + +`locFileTable[0].lines == [0, 9, 11, 22, 24]` + +We see 14 would be inserted right after `11` at `lines[2]`, so it's line 3 (+1 for 1-indexing). +Since line 3 starts at file offset 11, and `14 - 11 = 3`, it's column 4 (again, accounting for 1-indexing) + +#line and #file directives are handled with a separate array `substitutions` because they're rare, +and we don't want to penalize memory usage in their absence. +*/ +struct BaseLoc +{ +@safe nothrow: + + const(char)[] filename; /// Source file name + const(char)[] fileContents; /// Source file contents + uint startIndex; /// Subtract this from Loc.index to get file offset + int startLine = 1; /// Line number at index 0 + uint[] lines; /// For each line, the file offset at which it starts. At index 0 there's always a 0 entry. + BaseLoc[] substitutions; /// Substitutions from #line / #file directives + + /// Register that a new line starts at `offset` bytes from the start of the source file + void newLine(uint offset) + { + lines ~= offset; + } + + /// Construct a `Loc` entry for the start of the source file + `offset` bytes + Loc getLoc(uint offset) @nogc + { + Loc result; + result.index = startIndex + offset; + return result; + } + + /** + * Register a new file/line mapping from #file and #line directives + * Params: + * offset = byte offset in the source file at which the substitution starts + * filename = new filename from this point on (null = unchanged) + * line = line number from this point on + */ + void addSubstitution(uint offset, const(char)* filename, uint line) @system + { + auto fname = filename.toDString; + if (substitutions.length == 0) + substitutions ~= BaseLoc(this.filename, null, 0, 0); + + if (fname.length == 0) + fname = substitutions[$ - 1].filename; + substitutions ~= BaseLoc(fname, null, offset, cast(int) (line - lines.length + startLine - 2)); + } + + /// Returns: `loc` modified by substitutions from #file / #line directives + SourceLoc substitute(SourceLoc loc) @nogc + { + if (substitutions.length == 0) + return loc; + + const i = getSubstitutionIndex(loc.fileOffset); + if (substitutions[i].filename.length > 0) + loc.filename = substitutions[i].filename; + loc.linnum += substitutions[i].startLine; + return loc; + } + + /// Resolve an offset into this file to a filename + line + column + private SourceLoc getSourceLoc(uint offset) @nogc + { + const i = getLineIndex(offset); + const sl = SourceLoc(filename, cast(int) (i + startLine), cast(int) (1 + offset - lines[i]), offset, fileContents); + return substitute(sl); + } + + private size_t getSubstitutionIndex(uint offset) @nogc + { + size_t lo = 0; + size_t hi = substitutions.length + -1; + size_t mid = 0; + while (lo <= hi) + { + mid = lo + (hi - lo) / 2; + if (substitutions[mid].startIndex <= offset) + { + if (mid == substitutions.length - 1 || substitutions[mid + 1].startIndex > offset) + return mid; + + lo = mid + 1; + } + else + { + hi = mid - 1; + } + } + assert(0); + } + + /// Binary search the index in `this.lines` corresponding to `offset` + private size_t getLineIndex(uint offset) @nogc + { + size_t lo = 0; + size_t hi = lines.length + -1; + size_t mid = 0; + while (lo <= hi) + { + mid = lo + (hi - lo) / 2; + if (lines[mid] <= offset) + { + if (mid == lines.length - 1 || lines[mid + 1] > offset) + return mid; + + lo = mid + 1; + } + else + { + hi = mid - 1; + } + } + assert(0); + } +} + +// Whenever a new source file is parsed, start the `Loc` from this index (0 is reserved for Loc.init) +private __gshared uint locIndex = 1; + +// Global mapping of Loc indices to source file offset/line/column, see `BaseLoc` +private __gshared BaseLoc*[] locFileTable; diff --git a/dmd/main.d b/dmd/main.d index b1588b8a0c..43c5a621df 100644 --- a/dmd/main.d +++ b/dmd/main.d @@ -6,12 +6,12 @@ * utilities needed for arguments parsing, path manipulation, etc... * This file is not shared with other compilers which use the DMD front-end. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/main.d, _main.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/main.d, _main.d) * Documentation: https://dlang.org/phobos/dmd_main.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/main.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/main.d */ module dmd.main; @@ -30,6 +30,7 @@ import dmd.compiler; import dmd.cond; import dmd.console; // IN_LLVM import dmd.cpreprocess; +import dmd.deps; // IN_LLVM import dmd.dinifile; import dmd.dinterpret; // IN_LLVM import dmd.dmdparams; @@ -63,9 +64,11 @@ import dmd.root.man; import dmd.root.rmem; import dmd.root.string; import dmd.root.stringtable; +import dmd.root.array; import dmd.semantic2; import dmd.semantic3; import dmd.target; +import dmd.timetrace; import dmd.utils; import dmd.vsoptions; @@ -79,10 +82,8 @@ version (IN_LLVM) void codegenModules(ref Modules modules); // in driver/archiver.cpp int createStaticLibrary(); - const(char)* getPathToProducedStaticLibrary(); // in driver/linker.cpp int linkObjToBinary(); - const(char)* getPathToProducedBinary(); void deleteExeFile(); int runProgram(); } @@ -153,7 +154,9 @@ extern (C) int _Dmain(char[][]) dmd_coverDestPath(sourcePath); dmd_coverSetMerge(true); } - scope(failure) stderr.printInternalFailure; + version (D_Exceptions) + scope(failure) stderr.printInternalFailure; + auto args = Runtime.cArgs(); return tryMain(args.argc, cast(const(char)**)args.argv, global.params); } @@ -181,6 +184,8 @@ private: extern (C++) int mars_tryMain(ref Param params, ref Strings files) { import dmd.common.charactertables; + import dmd.sarif; + import core.stdc.stdarg; version (IN_LLVM) { @@ -191,6 +196,22 @@ else Strings files; Strings libmodules; global._init(); +} + + scope(exit) + { + // If we are here then compilation has ended + // gracefully as opposed to with `fatal` + global.plugErrorSinks(); + + if (global.errors == 0 && global.params.v.messageStyle == MessageStyle.sarif) + { + generateSarifReport(true); + } + } + +version (IN_LLVM) {} else +{ target.setTargetBuildDefaults(); if (parseCommandlineAndConfig(argc, argv, params, files)) @@ -198,6 +219,7 @@ else } global.compileEnv.previewIn = global.params.previewIn; + global.compileEnv.transitionIn = global.params.v.vin; global.compileEnv.ddocOutput = global.params.ddoc.doOutput; final switch(global.params.cIdentifierTable) @@ -378,7 +400,7 @@ version (IN_LLVM) {} else { fatal(); } - if (files.length == 0) + if (files.length == 0 && !params.readStdin) { if (params.jsonFieldFlags) { @@ -448,7 +470,30 @@ else // Build import search path - static void buildPath(ref Strings imppath, ref Strings result) + static void buildImportPath(ref Array!ImportPathInfo imppath, ref Array!ImportPathInfo result, ref Strings pathsOnlyResult) + { + Array!ImportPathInfo array; + Strings pathsOnlyArray; + + foreach (entry; imppath) + { + int sink(const(char)* p) nothrow + { + ImportPathInfo temp = entry; + temp.path = p; + array.push(temp); + return 0; + } + + FileName.splitPath(&sink, entry.path); + FileName.appendSplitPath(entry.path, pathsOnlyArray); + } + + result.append(&array); + pathsOnlyResult.append(&pathsOnlyArray); + } + + static void buildFileImportPath(ref Strings imppath, ref Strings result) { Strings array; foreach (const path; imppath) @@ -464,13 +509,26 @@ else atexit(&flushMixins); // see comment for flushMixins } scope(exit) flushMixins(); - buildPath(params.imppath, global.path); - buildPath(params.fileImppath, global.filePath); + buildImportPath(params.imppath, global.path, global.importPaths); + buildFileImportPath(params.fileImppath, global.filePath); + + if (params.timeTrace) + { + import dmd.timetrace; +version (IN_LLVM) +{ + initializeTimeTrace(params.timeTraceGranularityUs, params.argv0.toCString.ptr); +} +else +{ + initializeTimeTrace(params.timeTraceGranularityUs, argv[0]); +} + } // Create Modules Modules modules; modules.reserve(files.length); - if (createModules(files, libmodules, target, global.errorSink, modules)) + if (createModules(files, libmodules, params, target, global.errorSink, modules)) fatal(); // Read files @@ -485,7 +543,7 @@ else /* Read ddoc macro files named by the DDOCFILE environment variable and command line * and concatenate the text into ddocbuf */ - void readDdocFiles(ref const Loc loc, ref const Strings ddocfiles, ref OutBuffer ddocbuf) + void readDdocFiles(Loc loc, ref const Strings ddocfiles, ref OutBuffer ddocbuf) { foreach (file; ddocfiles) { @@ -497,9 +555,12 @@ else ddocbufIsRead = true; } - // Parse files bool anydocfiles = false; OutBuffer ddocOutputText; + { + // Parse files + timeTraceBeginEvent(TimeTraceEventType.parseGeneral); + scope (exit) timeTraceEndEvent(TimeTraceEventType.parseGeneral); size_t filecount = modules.length; for (size_t filei = 0, modi = 0; filei < filecount; filei++, modi++) { @@ -517,8 +578,6 @@ version (IN_LLVM) {} else m.parse(); -version (IN_LLVM) -{ // Finalize output filenames. Update if `-oq` was specified (only feasible after parsing). if (params.fullyQualifiedObjectFiles && m.md) { @@ -529,6 +588,8 @@ version (IN_LLVM) m.hdrfile = m.setOutfilename(params.dihdr.name, params.dihdr.dir, m.arg, hdr_ext); } +version (IN_LLVM) +{ // Set object filename in params.objfiles. for (size_t j = 0; j < params.objfiles.length; j++) { @@ -587,6 +648,7 @@ version (IN_LLVM) driverParams.link = false; } } + } if (anydocfiles && modules.length && (driverParams.oneobj || params.objname)) { @@ -620,6 +682,10 @@ version (IN_LLVM) if (global.errors) removeHdrFilesAndFail(params, modules); + { + timeTraceBeginEvent(TimeTraceEventType.semaGeneral); + scope (exit) timeTraceEndEvent(TimeTraceEventType.semaGeneral); + // load all unconditional imports for better symbol resolving foreach (m; modules) { @@ -741,6 +807,7 @@ version (IN_LLVM) else printf("%.*s", cast(int)data.length, data.ptr); } + } printCtfePerformanceStats(); printTemplateStats(global.params.v.templatesListInstances, global.errorSink); @@ -822,11 +889,15 @@ version (IN_LLVM) codegenModules(modules); } -else +else // !IN_LLVM { - generateCodeAndWrite(modules[], libmodules[], params.libname, params.objdir, - driverParams.lib, params.obj, driverParams.oneobj, params.multiobj, - params.v.verbose); + { + timeTraceBeginEvent(TimeTraceEventType.codegenGlobal); + scope (exit) timeTraceEndEvent(TimeTraceEventType.codegenGlobal); + generateCodeAndWrite(modules[], libmodules[], params.libname, params.objdir, + driverParams.lib, params.obj, driverParams.oneobj, params.multiobj, + params.v.verbose); + } backend_term(); } // !IN_LLVM @@ -863,7 +934,11 @@ version (IN_LLVM) else // !IN_LLVM { if (driverParams.link) + { + timeTraceBeginEvent(TimeTraceEventType.link); + scope (exit) timeTraceEndEvent(TimeTraceEventType.link); status = runLINK(global.params.v.verbose, global.errorSink); + } } if (params.run) { @@ -893,9 +968,59 @@ else } } + if (params.timeTrace) + { + import dmd.timetrace; + auto fileName = params.timeTraceFile.toDString(); + if (!fileName) + { + if (global.params.objfiles.length) + { + fileName = global.params.objfiles[0].toDString() ~ ".time-trace"; + } + else + { + fileName = "out.time-trace"; + } + } + + OutBuffer buf; + timeTraceProfiler.writeToBuffer(buf); + if (fileName == "-") + { + // Write to stdout + import core.stdc.stdio : fwrite, stdout; + + size_t n = fwrite(buf[].ptr, 1, buf.length, stdout); + if (n != buf.length) + { + error(Loc.initial, "Error writing -ftime-trace profile to stdout"); + } + } + else if (!File.write(fileName, buf[])) + { + error(Loc.initial, + "Error writing -ftime-trace profile: could not open '%*.s'", + cast(int) fileName.length, fileName.ptr); + } + + deinitializeTimeTrace(); + } + // Output the makefile dependencies if (params.makeDeps.doOutput) - emitMakeDeps(params); + { + OutBuffer buf; + writeMakeDeps(buf, params, driverParams.link, driverParams.lib, target.lib_ext); + const data = buf[]; + if (params.makeDeps.name) + { + if (!writeFile(Loc.initial, params.makeDeps.name, data)) + fatal(); + } + else + printf("%.*s", cast(int) data.length, data.ptr); + } if (global.warnings) errorOnWarning(); @@ -1017,7 +1142,7 @@ bool parseCommandlineAndConfig(size_t argc, const(char)** argv, ref Param params if (char* p = getenv("DDOCFILE")) global.params.ddoc.files.shift(p); - if (target.isX86_64 != isX86_64) + if (target.isX86_64 != isX86_64 && !target.isAArch64) error(Loc.initial, "the architecture must not be changed in the %s section of %.*s", envsection.ptr, cast(int)global.inifilename.length, global.inifilename.ptr); @@ -1025,73 +1150,6 @@ bool parseCommandlineAndConfig(size_t argc, const(char)** argv, ref Param params return false; } -/// Emit the makefile dependencies for the -makedeps switch -void emitMakeDeps(ref Param params) -{ - assert(params.makeDeps.doOutput); - - OutBuffer buf; - - // start by resolving and writing the target (which is sometimes resolved during link phase) - if (IN_LLVM && driverParams.link) - { - buf.writeEscapedMakePath(getPathToProducedBinary()); - } - else if (IN_LLVM && driverParams.lib) - { - buf.writeEscapedMakePath(getPathToProducedStaticLibrary()); - } - /* IN_LLVM: handled above - else if (driverParams.link && params.exefile) - { - buf.writeEscapedMakePath(¶ms.exefile[0]); - } - else if (driverParams.lib) - { - const(char)[] libname = params.libname ? params.libname : FileName.name(params.objfiles[0].toDString); - libname = FileName.forceExt(libname,target.lib_ext); - - buf.writeEscapedMakePath(&libname[0]); - } - */ - else if (params.objname) - { - buf.writeEscapedMakePath(¶ms.objname[0]); - } - else if (params.objfiles.length) - { - buf.writeEscapedMakePath(params.objfiles[0]); - foreach (of; params.objfiles[1 .. $]) - { - buf.writestring(" "); - buf.writeEscapedMakePath(of); - } - } - else - { - assert(false, "cannot resolve makedeps target"); - } - - buf.writestring(":"); - - // then output every dependency - foreach (dep; params.makeDeps.files) - { - buf.writestringln(" \\"); - buf.writestring(" "); - buf.writeEscapedMakePath(dep); - } - buf.writenl(); - - const data = buf[]; - if (params.makeDeps.name) - { - if (!writeFile(Loc.initial, params.makeDeps.name, data)) - fatal(); - } - else - printf("%.*s", cast(int) data.length, data.ptr); -} // in druntime: alias MainFunc = extern(C) int function(char[][] args); diff --git a/dmd/mangle.h b/dmd/mangle.h index de6fa55f77..97875c5ddd 100644 --- a/dmd/mangle.h +++ b/dmd/mangle.h @@ -1,6 +1,6 @@ /* Compiler implementation of the D programming language - * Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * written by Walter Bright * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. diff --git a/dmd/basicmangle.d b/dmd/mangle/basic.d similarity index 92% rename from dmd/basicmangle.d rename to dmd/mangle/basic.d index 52534fa352..263dd5ed46 100644 --- a/dmd/basicmangle.d +++ b/dmd/mangle/basic.d @@ -1,13 +1,13 @@ /** * Defines the building blocks for creating the mangled names for basic types. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/basicmangle.d, _basicmangle.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/mangle/basic.d, _basicmangle.d) * Documentation: https://dlang.org/phobos/dmd_basicmangle.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/basicmangle.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/mangle/basic.d */ -module dmd.basicmangle; +module dmd.mangle.basic; import dmd.astenums; import dmd.common.outbuffer : OutBuffer; diff --git a/dmd/cppmangle.d b/dmd/mangle/cpp.d similarity index 97% rename from dmd/cppmangle.d rename to dmd/mangle/cpp.d index 0609778e4c..7e9f0205d1 100644 --- a/dmd/cppmangle.d +++ b/dmd/mangle/cpp.d @@ -4,24 +4,20 @@ * This is the POSIX side of the implementation. * It exports two functions to C++, `toCppMangleItanium` and `cppTypeInfoMangleItanium`. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: Walter Bright, https://www.digitalmars.com * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/cppmangle.d, _cppmangle.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/mangle/cpp.d, _cppmangle.d) * Documentation: https://dlang.org/phobos/dmd_cppmangle.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/cppmangle.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/mangle/cpp.d * * References: * Follows Itanium C++ ABI 1.86 section 5.1 * http://refspecs.linux-foundation.org/cxxabi-1.86.html#mangling * which is where the grammar comments come from. - * - * Bugs: - * https://issues.dlang.org/query.cgi - * enter `C++, mangling` as the keywords. */ -module dmd.cppmangle; +module dmd.mangle.cpp; import core.stdc.stdio; @@ -30,6 +26,7 @@ import dmd.astenums; import dmd.attrib; import dmd.declaration; import dmd.dsymbol; +import dmd.dsymbolsem : isGNUABITag; import dmd.dtemplate; import dmd.errors; import dmd.expression; @@ -63,11 +60,11 @@ package CppOperator isCppOperator(const scope Identifier id) { with (Id) with (CppOperator) { - return (id == _cast) ? Cast : - (id == assign) ? Assign : - (id == eq) ? Eq : - (id == index) ? Index : - (id == call) ? Call : + return (id == opCast) ? Cast : + (id == opAssign) ? Assign : + (id == opEquals) ? Eq : + (id == opIndex) ? Index : + (id == opCall) ? Call : (id == opUnary) ? Unary : (id == opBinary) ? Binary : (id == opOpAssign) ? OpAssign : @@ -234,7 +231,7 @@ private final class CppMangleVisitor : Visitor // if so, just pick up the type from the instance if (!rt) rt = tf.nextOf(); - if (tf.isref) + if (tf.isRef) rt = rt.referenceTo(); auto prev = this.context.push(tf.nextOf()); scope (exit) this.context.pop(prev); @@ -438,10 +435,10 @@ private final class CppMangleVisitor : Visitor // 4. null pointer: std::nullptr_t (since C++11) if (t.ty == Tvoid || t.ty == Tbool) return true; - else if (t.ty == Tnull && global.params.cplusplus >= CppStdRevision.cpp11) + if (t.ty == Tnull && global.params.cplusplus >= CppStdRevision.cpp11) return true; - else - return t.isTypeBasic() && (t.isintegral() || t.isreal()); + + return t.isTypeBasic() && (t.isIntegral() || t.isReal()); } /****************************** @@ -484,14 +481,14 @@ private final class CppMangleVisitor : Visitor else if (TemplateValueParameter tv = tp.isTemplateValueParameter()) { // ::= L E # integer literal - if (tv.valType.isintegral()) + if (tv.valType.isIntegral()) { Expression e = isExpression(o); assert(e); buf.writeByte('L'); tv.valType.accept(this); auto val = e.toUInteger(); - if (!tv.valType.isunsigned() && cast(sinteger_t)val < 0) + if (!tv.valType.isUnsigned() && cast(sinteger_t)val < 0) { val = -val; buf.writeByte('n'); @@ -725,8 +722,7 @@ private final class CppMangleVisitor : Visitor */ static Dsymbol getInstance(Dsymbol s) { - Dsymbol p = s.toParent(); - if (p) + if (Dsymbol p = s.toParent()) { if (TemplateInstance ti = p.isTemplateInstance()) return ti; @@ -827,8 +823,7 @@ private final class CppMangleVisitor : Visitor return buf.writestring("St"); auto si = getInstance(s); - Dsymbol p = getQualifier(si); - if (p) + if (Dsymbol p = getQualifier(si)) { if (isStd(p)) { @@ -1107,13 +1102,13 @@ private final class CppMangleVisitor : Visitor buf.writestring(ctor.isCpCtor ? "C2" : "C1"); else if (d.isAggregateDtor()) buf.writestring("D1"); - else if (d.ident && d.ident == Id.assign) + else if (d.ident && d.ident == Id.opAssign) buf.writestring("aS"); - else if (d.ident && d.ident == Id.eq) + else if (d.ident && d.ident == Id.opEquals) buf.writestring("eq"); - else if (d.ident && d.ident == Id.index) + else if (d.ident && d.ident == Id.opIndex) buf.writestring("ix"); - else if (d.ident && d.ident == Id.call) + else if (d.ident && d.ident == Id.opCall) buf.writestring("cl"); else source_name(d, true); @@ -1751,7 +1746,7 @@ extern(C++): * Ds char16_t * u # vendor extended type */ - if (t.isimaginary() || t.iscomplex()) + if (t.isImaginary() || t.isComplex()) { // https://issues.dlang.org/show_bug.cgi?id=22806 // Complex and imaginary types are represented in the same way as @@ -1762,7 +1757,7 @@ extern(C++): append(t); CV_qualifiers(t); - if (t.isimaginary()) + if (t.isImaginary()) buf.writeByte('G'); // 'G' means imaginary else buf.writeByte('C'); // 'C' means complex @@ -1917,7 +1912,7 @@ extern(C++): if (t.linkage == LINK.c) buf.writeByte('Y'); Type tn = t.next; - if (t.isref) + if (t.isRef) tn = tn.referenceTo(); tn.accept(this); mangleFunctionParameters(t.parameterList); @@ -1944,21 +1939,21 @@ extern(C++): //printf("enum id = '%s'\n", id.toChars()); if (id == Id.__c_long) return writeBasicType(t, 0, 'l'); - else if (id == Id.__c_ulong) + if (id == Id.__c_ulong) return writeBasicType(t, 0, 'm'); - else if (id == Id.__c_char) + if (id == Id.__c_char) return writeBasicType(t, 0, 'c'); - else if (id == Id.__c_wchar_t) + if (id == Id.__c_wchar_t) return writeBasicType(t, 0, 'w'); - else if (id == Id.__c_longlong) + if (id == Id.__c_longlong) return writeBasicType(t, 0, 'x'); - else if (id == Id.__c_ulonglong) + if (id == Id.__c_ulonglong) return writeBasicType(t, 0, 'y'); - else if (id == Id.__c_complex_float) + if (id == Id.__c_complex_float) return Type.tcomplex32.accept(this); - else if (id == Id.__c_complex_double) + if (id == Id.__c_complex_double) return Type.tcomplex64.accept(this); - else if (id == Id.__c_complex_real) + if (id == Id.__c_complex_real) return Type.tcomplex80.accept(this); doSymbol(t); @@ -2367,8 +2362,7 @@ private bool isNamespaceEqual (CPPNamespaceDeclaration a, Nspace b, size_t idx = // We need to see if there's more ident enclosing if (auto pb = b.toParent().isNspace()) return isNamespaceEqual(a.cppnamespace, pb); - else - return a.cppnamespace is null; + return a.cppnamespace is null; } /// Returns: @@ -2435,7 +2429,7 @@ private struct ABITagContainer foreach (exp; *s.userAttribDecl.atts) { - if (UserAttributeDeclaration.isGNUABITag(exp)) + if (isGNUABITag(exp)) return (*exp.isStructLiteralExp().elements)[0] .isArrayLiteralExp(); } diff --git a/dmd/cppmanglewin.d b/dmd/mangle/cppwin.d similarity index 98% rename from dmd/cppmanglewin.d rename to dmd/mangle/cppwin.d index 196e886982..7688ef2de8 100644 --- a/dmd/cppmanglewin.d +++ b/dmd/mangle/cppwin.d @@ -1,21 +1,20 @@ /** * Do mangling for C++ linkage for Digital Mars C++ and Microsoft Visual C++. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: Walter Bright, https://www.digitalmars.com * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/cppmanglewin.d, _cppmanglewin.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/mangle/cppwin.d, _cppmanglewin.d) * Documentation: https://dlang.org/phobos/dmd_cppmanglewin.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/cppmanglewin.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/mangle/cppwin.d */ -module dmd.cppmanglewin; +module dmd.mangle.cppwin; import core.stdc.stdio; import dmd.arraytypes; import dmd.astenums; -import dmd.cppmangle : isAggregateDtor, isCppOperator, CppOperator; import dmd.dclass; import dmd.declaration; import dmd.denum : isSpecialEnumIdent; @@ -31,6 +30,7 @@ import dmd.globals; import dmd.id; import dmd.identifier; import dmd.location; +import dmd.mangle.cpp : isAggregateDtor, isCppOperator, CppOperator; import dmd.mtype; import dmd.common.outbuffer; import dmd.rootobject; @@ -570,7 +570,7 @@ extern(D): */ void mangleTemplateValue(RootObject o, TemplateValueParameter tv, Dsymbol sym) { - if (!tv.valType.isintegral()) + if (!tv.valType.isIntegral()) { eSink.error(sym.loc, "%s `%s` internal compiler error: C++ %s template value parameter is not supported", sym.kind, sym.toPrettyChars, tv.valType.toChars()); errors = true; @@ -580,7 +580,7 @@ extern(D): buf.writeByte('0'); Expression e = isExpression(o); assert(e); - if (tv.valType.isunsigned()) + if (tv.valType.isUnsigned()) { mangleNumber(buf, e.toUInteger()); } @@ -1012,7 +1012,7 @@ extern(D): else { Type rettype = type.next; - if (type.isref) + if (type.isRef) rettype = rettype.referenceTo(); ignoreConst = false; if (rettype.ty == Tstruct) @@ -1105,13 +1105,13 @@ string mangleSpecialName(Dsymbol sym) mangle = "?1"; else if (!sym.ident) return null; - else if (sym.ident == Id.assign) + else if (sym.ident == Id.opAssign) mangle = "?4"; - else if (sym.ident == Id.eq) + else if (sym.ident == Id.opEquals) mangle = "?8"; - else if (sym.ident == Id.index) + else if (sym.ident == Id.opIndex) mangle = "?A"; - else if (sym.ident == Id.call) + else if (sym.ident == Id.opCall) mangle = "?R"; else if (sym.ident == Id.cppdtor) mangle = "?_G"; diff --git a/dmd/dmangle.d b/dmd/mangle/package.d similarity index 98% rename from dmd/dmangle.d rename to dmd/mangle/package.d index d4eecb94a9..3ad2c7d579 100644 --- a/dmd/dmangle.d +++ b/dmd/mangle/package.d @@ -3,16 +3,16 @@ * * Specification: $(LINK2 https://dlang.org/spec/abi.html#name_mangling, Name Mangling) * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: Walter Bright, https://www.digitalmars.com * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/dmangle.d, _dmangle.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/mangle/package.d, _dmangle.d) * Documentation: https://dlang.org/phobos/dmd_dmangle.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/dmangle.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/mangle/package.d * References: https://dlang.org/blog/2017/12/20/ds-newfangled-name-mangling/ */ -module dmd.dmangle; +module dmd.mangle; /****************************************************************************** @@ -70,7 +70,7 @@ void mangleToBuffer(TemplateInstance ti, ref OutBuffer buf) } /// Returns: `true` if the given character is a valid mangled character -package bool isValidMangling(dchar c) nothrow +package(dmd) bool isValidMangling(dchar c) nothrow { import dmd.common.charactertables; @@ -139,7 +139,7 @@ import core.stdc.string; import dmd.aggregate; import dmd.arraytypes; import dmd.astenums; -import dmd.basicmangle; +import dmd.mangle.basic; import dmd.dclass; import dmd.declaration; import dmd.dinterpret; @@ -358,31 +358,31 @@ void mangleFuncType(TypeFunction t, TypeFunction ta, ubyte modMask, Type tret, r if (ta.purity) buf.writestring("Na"); - if (ta.isnothrow) + if (ta.isNothrow) buf.writestring("Nb"); - if (ta.isref) + if (ta.isRef) buf.writestring("Nc"); - if (ta.isproperty) + if (ta.isProperty) buf.writestring("Nd"); - if (ta.isnogc) + if (ta.isNogc) buf.writestring("Ni"); // `return scope` must be in that order - if (ta.isreturnscope && !ta.isreturninferred) + if (ta.isReturnScope && !ta.isReturnInferred) { buf.writestring("NjNl"); } else { // when return ref, the order is `scope return` - if (ta.isScopeQual && !ta.isscopeinferred) + if (ta.isScopeQual && !ta.isScopeInferred) buf.writestring("Nl"); - if (ta.isreturn && !ta.isreturninferred) + if (ta.isReturn && !ta.isReturnInferred) buf.writestring("Nj"); } - if (ta.islive) + if (ta.isLive) buf.writestring("Nm"); switch (ta.trust) @@ -451,7 +451,7 @@ void mangleParameter(Parameter p, ref OutBuffer buf, ref Backref backref) switch (stc & ((STC.IOR | STC.lazy_) & ~STC.constscoperef)) { - case 0: + case STC.none: break; case STC.in_: buf.writeByte('I'); @@ -830,7 +830,7 @@ public: continue; } // Now that we know it is not an alias, we MUST obtain a value - uint olderr = global.errors; + const olderr = global.errors; ea = ea.ctfeInterpret(); if (ea.op == EXP.error || olderr != global.errors) continue; diff --git a/dmd/mars.d b/dmd/mars.d index 3398888556..281198aa96 100644 --- a/dmd/mars.d +++ b/dmd/mars.d @@ -4,12 +4,12 @@ * utilities needed for arguments parsing, path manipulation, etc... * This file is not shared with other compilers which use the DMD front-end. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/mars.d, _mars.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/mars.d, _mars.d) * Documentation: https://dlang.org/phobos/dmd_mars.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/mars.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/mars.d */ module dmd.mars; @@ -59,6 +59,13 @@ import dmd.semantic3; import dmd.target; import dmd.utils; +version (Windows) + import core.sys.windows.winbase : getpid = GetCurrentProcessId; +else version (Posix) + import core.sys.posix.unistd : getpid; +else + static assert(0); + version (IN_LLVM) { // DMD defines a `driverParams` global (of type DMDParams); @@ -91,7 +98,7 @@ void printInternalFailure(FILE* stream) { fputs(("---\n" ~ "ERROR: This is a compiler bug.\n" ~ - "Please report it via https://issues.dlang.org/enter_bug.cgi\n" ~ + "Please report it via https://github.com/dlang/dmd/issues\n" ~ "with, preferably, a reduced, reproducible example and the information below.\n" ~ "DustMite (https://github.com/CyberShadow/DustMite/wiki) can help with the reduction.\n" ~ "---\n").ptr, stream); @@ -349,9 +356,8 @@ const(char)[] parse_conf_arg(Strings* args) const(char)[] arg = p.toDString; if (arg.length && arg[0] == '-') { - if(arg.length >= 6 && arg[1 .. 6] == "conf="){ + if(arg.length >= 6 && arg[1 .. 6] == "conf=") conf = arg[6 .. $]; - } else if (arg[1 .. $] == "run") break; } @@ -961,6 +967,25 @@ bool parseCommandLine(const ref Strings arguments, const size_t argc, ref Param { driverParams.pic = PIC.pie; } + else if (arg == "-ftime-trace") + params.timeTrace = true; + else if (startsWith(p + 1, "ftime-trace-granularity=")) + { + enum len = "-ftime-trace-granularity=".length; + if (arg.length < len || !params.timeTraceGranularityUs.parseDigits(arg[len .. $])) + { + error("`-ftime-trace-granularity` requires a positive number of microseconds", p); + return false; + } + } + else if (startsWith(p + 1, "ftime-trace-file=")) + { + enum l = "-ftime-trace-file=".length; + auto tmp = p + l; + if (!tmp[0]) + goto Lnoarg; + params.timeTraceFile = mem.xstrdup(tmp); + } else if (arg == "-map") // https://dlang.org/dmd.html#switch-map driverParams.map = true; else if (arg == "-multiobj") @@ -1010,18 +1035,27 @@ bool parseCommandLine(const ref Strings arguments, const size_t argc, ref Param { continue; // skip druntime options, e.g. used to configure the GC } + else if (arg == "-arm") // https://dlang.org/dmd.html#switch-arm + { + target.isAArch64 = true; + target.isX86 = false; + target.isX86_64 = false; + } else if (arg == "-m32") // https://dlang.org/dmd.html#switch-m32 { + target.isAArch64 = false; target.isX86 = true; target.isX86_64 = false; } else if (arg == "-m64") // https://dlang.org/dmd.html#switch-m64 { + target.isAArch64 = false; target.isX86 = false; target.isX86_64 = true; } else if (arg == "-m32mscoff") // https://dlang.org/dmd.html#switch-m32mscoff { + target.isAArch64 = false; target.isX86 = true; target.isX86_64 = false; } @@ -1088,13 +1122,17 @@ bool parseCommandLine(const ref Strings arguments, const size_t argc, ref Param { params.v.showGaggedErrors = true; } + else if (startsWith(p + 9, "simple")) + { + params.v.errorPrintMode = ErrorPrintMode.simpleError; + } else if (startsWith(p + 9, "context")) { - params.v.printErrorContext = true; + params.v.errorPrintMode = ErrorPrintMode.printErrorContext; } else if (!params.v.errorLimit.parseDigits(p.toDString()[9 .. $])) { - errorInvalidSwitch(p, "Only number, `spec`, or `context` are allowed for `-verrors`"); + errorInvalidSwitch(p, "Only a number, `spec`, `simple`, or `context` are allowed for `-verrors`"); return true; } } @@ -1118,8 +1156,11 @@ bool parseCommandLine(const ref Strings arguments, const size_t argc, ref Param case "gnu": params.v.messageStyle = MessageStyle.gnu; break; + case "sarif": + params.v.messageStyle = MessageStyle.sarif; + break; default: - error("unknown error style '%.*s', must be 'digitalmars' or 'gnu'", cast(int) style.length, style.ptr); + error("unknown error style '%.*s', must be 'digitalmars', 'gnu', or 'sarif'", cast(int) style.length, style.ptr); } } else if (startsWith(p + 1, "target")) @@ -1221,6 +1262,9 @@ bool parseCommandLine(const ref Strings arguments, const size_t argc, ref Param case "c++20": params.cplusplus = CppStdRevision.cpp20; break; + case "c++23": + params.cplusplus = CppStdRevision.cpp23; + break; default: error("switch `%s` is invalid", p); params.help.externStd = true; @@ -1326,9 +1370,9 @@ bool parseCommandLine(const ref Strings arguments, const size_t argc, ref Param } } else if (arg == "-w") // https://dlang.org/dmd.html#switch-w - params.warnings = DiagnosticReporting.error; + params.useWarnings = DiagnosticReporting.error; else if (arg == "-wi") // https://dlang.org/dmd.html#switch-wi - params.warnings = DiagnosticReporting.inform; + params.useWarnings = DiagnosticReporting.inform; else if (arg == "-wo") // https://dlang.org/dmd.html#switch-wo { // Obsolete features has been obsoleted until a DIP for "editions" @@ -1370,6 +1414,11 @@ bool parseCommandLine(const ref Strings arguments, const size_t argc, ref Param goto Lerror; params.preservePaths = true; break; + case 'q': + if (p[3]) + goto Lerror; + params.fullyQualifiedObjectFiles = true; + break; case 0: error("-o no longer supported, use -of or -od"); break; @@ -1605,6 +1654,9 @@ bool parseCommandLine(const ref Strings arguments, const size_t argc, ref Param } else if (arg == "-noboundscheck") // https://dlang.org/dmd.html#switch-noboundscheck { + /// @@@DEPRECATED_2.113@@@ + // Deprecated since forever, deprecation message added in 2.111. Remove in 2.113 + eSink.deprecation(Loc.initial, "`-noboundscheck` is deprecated. Use `-boundscheck=off` instead"); params.boundscheck = CHECKENABLE.off; } else if (startsWith(p + 1, "boundscheck")) // https://dlang.org/dmd.html#switch-boundscheck @@ -1641,7 +1693,7 @@ bool parseCommandLine(const ref Strings arguments, const size_t argc, ref Param params.useUnitTests = true; else if (p[1] == 'I') // https://dlang.org/dmd.html#switch-I { - params.imppath.push(p + 2 + (p[2] == '=')); + params.imppath.push(ImportPathInfo(p + 2 + (p[2] == '='))); } else if (p[1] == 'm' && p[2] == 'v' && p[3] == '=') // https://dlang.org/dmd.html#switch-mv { @@ -1660,20 +1712,10 @@ bool parseCommandLine(const ref Strings arguments, const size_t argc, ref Param { // Parse: // -debug - // -debug=number // -debug=identifier if (p[6] == '=') { - if (isdigit(cast(char)p[7])) - { - if (!params.debuglevel.parseDigits(p.toDString()[7 .. $])) - goto Lerror; - - // @@@DEPRECATED_2.111@@@ - // Deprecated in 2.101, remove in 2.111 - eSink.deprecation(Loc.initial, "`-debug=number` is deprecated, use debug identifiers instead"); - } - else if (Identifier.isValidIdentifier(p + 7)) + if (Identifier.isValidIdentifier(p + 7)) { DebugCondition.addGlobalIdent((p + 7).toDString()); } @@ -1683,25 +1725,15 @@ bool parseCommandLine(const ref Strings arguments, const size_t argc, ref Param else if (p[6]) goto Lerror; else - params.debuglevel = 1; + params.debugEnabled = true; } else if (startsWith(p + 1, "version")) // https://dlang.org/dmd.html#switch-version { // Parse: - // -version=number // -version=identifier if (p[8] == '=') { - if (isdigit(cast(char)p[9])) - { - if (!params.versionlevel.parseDigits(p.toDString()[9 .. $])) - goto Lerror; - - // @@@DEPRECATED_2.111@@@ - // Deprecated in 2.101, remove in 2.111 - eSink.deprecation(Loc.initial, "`-version=number` is deprecated, use version identifiers instead"); - } - else if (Identifier.isValidIdentifier(p + 9)) + if (Identifier.isValidIdentifier(p + 9)) { VersionCondition.addGlobalIdent((p+9).toDString()); } @@ -1821,7 +1853,7 @@ bool parseCommandLine(const ref Strings arguments, const size_t argc, ref Param break; } if (runarg == "-") - files.push("__stdin.d"); + params.readStdin = true; else files.push(arguments[i + 1]); params.runargs.setDim(length - 1); @@ -1838,7 +1870,7 @@ bool parseCommandLine(const ref Strings arguments, const size_t argc, ref Param } } else if (p[1] == '\0') - files.push("__stdin.d"); + params.readStdin = true; else { Lerror: @@ -1878,13 +1910,15 @@ Params: file = File name to dispatch libmodules = Array to which binaries (shared/static libs and object files) will be appended + params = command line params target = target system m = created Module Returns: true on error */ private -bool createModule(const(char)* file, ref Strings libmodules, const ref Target target, ErrorSink eSink, out Module m) +bool createModule(const(char)* file, ref Strings libmodules, ref Param params, const ref Target target, + ErrorSink eSink, out Module m) { version (IN_LLVM) {} else { @@ -1895,6 +1929,7 @@ version (IN_LLVM) {} else } const(char)[] p = FileName.name(file.toDString()); // strip path const(char)[] ext = FileName.ext(p); + Loc loc = Loc.singleFilename(file); if (!ext) { if (!p.length) @@ -1903,15 +1938,15 @@ version (IN_LLVM) {} else return true; } auto id = Identifier.idPool(p); - m = new Module(file.toDString, id, global.params.ddoc.doOutput, global.params.dihdr.doOutput); + m = new Module(loc, file.toDString, id, params.ddoc.doOutput, params.dihdr.doOutput); return false; } /* Deduce what to do with a file based on its extension */ - if (FileName.equals(ext, target.obj_ext)) + if (FileName.equals(ext, "obj") || FileName.equals(ext, "o")) { - global.params.objfiles.push(file); + params.objfiles.push(file); libmodules.push(file); return false; } @@ -1923,7 +1958,7 @@ version (IN_LLVM) {} else } if (FileName.equals(ext, target.lib_ext)) { - global.params.libfiles.push(file); + params.libfiles.push(file); libmodules.push(file); return false; } @@ -1932,37 +1967,37 @@ version (IN_LLVM) {} else { if (FileName.equals(ext, target.dll_ext)) { - global.params.dllfiles.push(file); + params.dllfiles.push(file); libmodules.push(file); return false; } } if (FileName.equals(ext, ddoc_ext)) { - global.params.ddoc.files.push(file); + params.ddoc.files.push(file); return false; } if (FileName.equals(ext, json_ext)) { - global.params.json.doOutput = true; - global.params.json.name = file.toDString; + params.json.doOutput = true; + params.json.name = file.toDString; return false; } if (FileName.equals(ext, map_ext)) { - global.params.mapfile = file.toDString; + params.mapfile = file.toDString; return false; } if (target.os == Target.OS.Windows) { if (FileName.equals(ext, "res")) { - global.params.resfile = file.toDString; + params.resfile = file.toDString; return false; } if (FileName.equals(ext, "def")) { - global.params.deffile = file.toDString; + params.deffile = file.toDString; return false; } if (FileName.equals(ext, "exe")) @@ -1990,8 +2025,7 @@ version (IN_LLVM) {} else * its path and extension. */ auto id = Identifier.idPool(name); - - m = new Module(file.toDString, id, global.params.ddoc.doOutput, global.params.dihdr.doOutput); + m = new Module(loc, file.toDString, id, params.ddoc.doOutput, params.dihdr.doOutput); return false; } eSink.error(Loc.initial, "unrecognized file extension %.*s", cast(int)ext.length, ext.ptr); @@ -2010,6 +2044,7 @@ Params: files = File names to dispatch libmodules = Array to which binaries (shared/static libs and object files) will be appended + params = command line params target = target system eSink = error message sink modules = empty array of modules to be filled in @@ -2017,7 +2052,8 @@ Params: Returns: true on error */ -bool createModules(ref Strings files, ref Strings libmodules, const ref Target target, ErrorSink eSink, ref Modules modules) +bool createModules(ref Strings files, ref Strings libmodules, ref Param params, const ref Target target, + ErrorSink eSink, ref Modules modules) { version (IN_LLVM) { @@ -2030,7 +2066,7 @@ else foreach(file; files) { Module m; - if (createModule(file, libmodules, target, eSink, m)) + if (createModule(file, libmodules, params, target, eSink, m)) return true; if (m is null) @@ -2050,29 +2086,73 @@ else { if (firstmodule) { - global.params.objfiles.push(m.objfile.toChars()); + params.objfiles.push(m.objfile.toChars()); firstmodule = false; } } } + version (IN_LLVM) { - // When compiling to a single object file, move that object file to the - // beginning of the object files list. - if (driverParams.oneobj && modules.length > 0 && firstModuleObjectFileIndex != 0) + scope(exit) { - auto fn = global.params.objfiles[firstModuleObjectFileIndex]; - global.params.objfiles.remove(firstModuleObjectFileIndex); - global.params.objfiles.insert(0, fn); + // When compiling to a single object file, move that object file to the + // beginning of the object files list. + if (driverParams.oneobj && modules.length > 0 && firstModuleObjectFileIndex != 0) + { + auto fn = global.params.objfiles[firstModuleObjectFileIndex]; + global.params.objfiles.remove(firstModuleObjectFileIndex); + global.params.objfiles.insert(0, fn); + } } } + + // Special module representing `stdin` + if (params.readStdin) + { + Module m; + if (createModule("__stdin.d", libmodules, params, target, eSink, m)) + return true; + if (m is null) + return false; + + modules.push(m); + + // Set the source file contents of the module + OutBuffer buf; + buf.readFromStdin(); + m.src = cast(ubyte[])buf.extractSlice(); + + // Give unique outfile name + OutBuffer namebuf; + namebuf.printf("__stdin_%d", getpid()); + + auto filename = FileName.forceExt(namebuf.extractSlice(), target.obj_ext); + m.objfile = FileName(filename); + +version (IN_LLVM) +{ + if (!driverParams.oneobj || firstModuleObjectFileIndex == size_t.max) + { + global.params.objfiles.push(cast(const(char)*)m); // defer to a later stage after parsing + if (firstModuleObjectFileIndex == size_t.max) + firstModuleObjectFileIndex = global.params.objfiles.length - 1; + } +} +else +{ + if (firstmodule) + params.objfiles.push(m.objfile.toChars()); +} + } + return false; } /// Returns: a compiled module (semantic3) containing an empty main() function, for the -main flag Module moduleWithEmptyMain() { - auto result = new Module("__main.d", Identifier.idPool("__main"), false, false); + auto result = new Module(Loc.initial, "__main.d", Identifier.idPool("__main"), false, false); // need 2 trailing nulls for sentinel and 2 for lexer auto data = arraydup("version(D_BetterC)extern(C)int main(){return 0;}else int main(){return 0;}\0\0\0\0"); result.src = cast(ubyte[]) data[0 .. $-4]; @@ -2084,3 +2164,37 @@ Module moduleWithEmptyMain() result.semantic3(null); return result; } + +private void readFromStdin(ref OutBuffer sink) nothrow +{ + import core.stdc.stdio; + import dmd.errors; + + enum BufIncrement = 128 * 1024; + + for (size_t j; 1; ++j) + { + char[] buffer = sink.allocate(BufIncrement + 16); + + // Fill up buffer + size_t filled = 0; + do + { + filled += fread(buffer.ptr + filled, 1, buffer.length - filled, stdin); + if (ferror(stdin)) + { + import core.stdc.errno; + error(Loc.initial, "cannot read from stdin, errno = %d", errno); + fatal(); + } + if (feof(stdin)) // successful completion + { + memset(buffer.ptr + filled, '\0', 16); + sink.setsize(j * BufIncrement + filled); + return; + } + } while (filled < BufIncrement); + } + + assert(0); +} diff --git a/dmd/module.h b/dmd/module.h index 00ed6139db..d640c46d3a 100644 --- a/dmd/module.h +++ b/dmd/module.h @@ -1,6 +1,6 @@ /* Compiler implementation of the D programming language - * Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * written by Walter Bright * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. @@ -55,8 +55,6 @@ class Package : public ScopeDsymbol bool equals(const RootObject * const o) const override; - Package *isPackage() override final { return this; } - bool isAncestorPackageOf(const Package * const pkg) const; void accept(Visitor *v) override { v->visit(this); } @@ -117,11 +115,9 @@ class Module final : public Package Modules aimports; // all imported modules - unsigned debuglevel; // debug level Identifiers *debugids; // debug identifiers Identifiers *debugidsNot; // forward referenced debug identifiers - unsigned versionlevel; // version level Identifiers *versionids; // version identifiers Identifiers *versionidsNot; // forward referenced version identifiers @@ -133,10 +129,10 @@ class Module final : public Package static Module* create(const char *arg, Identifier *ident, int doDocComment, int doHdrGen); static const char *find(const char *filename); - static Module *load(const Loc &loc, Identifiers *packages, Identifier *ident); + static Module *load(Loc loc, Identifiers *packages, Identifier *ident); const char *kind() const override; - bool read(const Loc &loc); // read file, returns 'true' if succeed, 'false' otherwise. + bool read(Loc loc); // read file, returns 'true' if succeed, 'false' otherwise. Module *parse(); // syntactic parse int needModuleInfo(); bool isPackageAccessible(Package *p, Visibility visibility, SearchOptFlags flags = (SearchOptFlags)SearchOpt::all) override; @@ -184,7 +180,6 @@ class Module final : public Package void *ctfe_cov; // stores coverage information from ctfe - Module *isModule() override { return this; } void accept(Visitor *v) override { v->visit(this); } }; diff --git a/dmd/mtype.d b/dmd/mtype.d index b7106d6eec..fed8ebdaf3 100644 --- a/dmd/mtype.d +++ b/dmd/mtype.d @@ -1,12 +1,12 @@ /** * Defines a D type. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/mtype.d, _mtype.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/mtype.d, _mtype.d) * Documentation: https://dlang.org/phobos/dmd_mtype.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/mtype.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/mtype.d */ module dmd.mtype; @@ -21,7 +21,6 @@ import dmd.aggregate; import dmd.arraytypes; import dmd.astenums; import dmd.ast_node; -import dmd.dcast : implicitConvTo; import dmd.dclass; import dmd.declaration; import dmd.denum; @@ -31,6 +30,7 @@ import dmd.dtemplate; import dmd.enumsem; import dmd.errors; import dmd.expression; +import dmd.dsymbolsem : determineSize; import dmd.globals; import dmd.hdrgen; import dmd.id; @@ -58,6 +58,11 @@ static if (__VERSION__ < 2095) private alias StringValueType = StringValue!Type; } +private auto X(T, U)(T m, U n) +{ + return (m << 4) | n; +} + /*************************** * Return !=0 if modfrom can be implicitly converted to modto */ @@ -67,10 +72,6 @@ bool MODimplicitConv(MOD modfrom, MOD modto) pure nothrow @nogc @safe return true; //printf("MODimplicitConv(from = %x, to = %x)\n", modfrom, modto); - auto X(T, U)(T m, U n) - { - return ((m << 4) | n); - } switch (X(modfrom & ~MODFlags.shared_, modto & ~MODFlags.shared_)) { @@ -98,11 +99,6 @@ MATCH MODmethodConv(MOD modfrom, MOD modto) pure nothrow @nogc @safe if (MODimplicitConv(modfrom, modto)) return MATCH.constant; - auto X(T, U)(T m, U n) - { - return ((m << 4) | n); - } - switch (X(modfrom, modto)) { case X(0, MODFlags.wild): @@ -236,9 +232,9 @@ unittest /************************************ * Convert MODxxxx to STCxxx */ -StorageClass ModToStc(uint mod) pure nothrow @nogc @safe +STC ModToStc(uint mod) pure nothrow @nogc @safe { - StorageClass stc = 0; + STC stc = STC.none; if (mod & MODFlags.immutable_) stc |= STC.immutable_; if (mod & MODFlags.const_) @@ -303,13 +299,16 @@ extern (C++) abstract class Type : ASTNode Type wcto; // MODFlags.wildconst Type swto; // MODFlags.shared_ | MODFlags.wild Type swcto; // MODFlags.shared_ | MODFlags.wildconst + Type pto; // merged pointer to this type + Type rto; // reference to this type + Type arrayof; // array of this type + + // ImportC: store the name of the typedef resolving to this type + // So `uint8_t x;` will be printed as `uint8_t x;` and not as the resolved `ubyte x;` + Identifier typedefIdent; } Mcache* mcache; - Type pto; // merged pointer to this type - Type rto; // reference to this type - Type arrayof; // array of this type - TypeInfoDeclaration vtinfo; // TypeInfo object for this Type void* ctype; // for back end @@ -524,7 +523,7 @@ version (IN_LLVM) static Type merge(Type t) { - import dmd.basicmangle : tyToDecoBuffer; + import dmd.mangle.basic : tyToDecoBuffer; OutBuffer buf; buf.reserve(3); @@ -537,12 +536,9 @@ version (IN_LLVM) auto sv = t.stringtable.update(buf[]); if (sv.value) return sv.value; - else - { - t.deco = cast(char*)sv.toDchars(); - sv.value = t; - return t; - } + t.deco = cast(char*)sv.toDchars(); + sv.value = t; + return t; } for (size_t i = 0; basetab[i] != Terror; i++) @@ -650,43 +646,43 @@ version (IN_LLVM) return buf.extractChars(); } - bool isintegral() + bool isIntegral() { return false; } // real, imaginary, or complex - bool isfloating() + bool isFloating() { return false; } - bool isreal() + bool isReal() { return false; } - bool isimaginary() + bool isImaginary() { return false; } - bool iscomplex() + bool isComplex() { return false; } - bool isscalar() + bool isScalar() { return false; } - bool isunsigned() + bool isUnsigned() { return false; } - bool isscope() + bool isScopeClass() { return false; } @@ -714,7 +710,7 @@ version (IN_LLVM) */ bool isBoolean() { - return isscalar(); + return isScalar(); } final bool isConst() const nothrow pure @nogc @safe @@ -773,9 +769,6 @@ version (IN_LLVM) memcpy(cast(void*)t, cast(void*)this, sz); // t.mod = NULL; // leave mod unchanged t.deco = null; - t.arrayof = null; - t.pto = null; - t.rto = null; t.vtinfo = null; t.ctype = null; t.mcache = null; @@ -1119,70 +1112,71 @@ version (IN_LLVM) * Apply STCxxxx bits to existing type. * Use *before* semantic analysis is run. */ - extern (D) final Type addSTC(StorageClass stc) + 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; } - else + + if ((stc & STC.shared_) && !t.isShared()) + { + if (t.isWild()) + { + if (t.isConst()) + t = t.makeSharedWildConst(); + else + t = t.makeSharedWild(); + } + else + { + if (t.isConst()) + t = t.makeSharedConst(); + else + t = t.makeShared(); + } + } + if ((stc & STC.const_) && !t.isConst()) { - if ((stc & STC.shared_) && !t.isShared()) + if (t.isShared()) { if (t.isWild()) - { - if (t.isConst()) - t = t.makeSharedWildConst(); - else - t = t.makeSharedWild(); - } + t = t.makeSharedWildConst(); else - { - if (t.isConst()) - t = t.makeSharedConst(); - else - t = t.makeShared(); - } + t = t.makeSharedConst(); } - if ((stc & STC.const_) && !t.isConst()) + else { - if (t.isShared()) - { - if (t.isWild()) - t = t.makeSharedWildConst(); - else - t = t.makeSharedConst(); - } + if (t.isWild()) + t = t.makeWildConst(); else - { - if (t.isWild()) - t = t.makeWildConst(); - else - t = t.makeConst(); - } + t = t.makeConst(); } - if ((stc & STC.wild) && !t.isWild()) + } + if ((stc & STC.wild) && !t.isWild()) + { + if (t.isShared()) { - if (t.isShared()) - { - if (t.isConst()) - t = t.makeSharedWildConst(); - else - t = t.makeSharedWild(); - } + if (t.isConst()) + t = t.makeSharedWildConst(); else - { - if (t.isConst()) - t = t.makeWildConst(); - else - t = t.makeWild(); - } + t = t.makeSharedWild(); + } + else + { + if (t.isConst()) + t = t.makeWildConst(); + else + t = t.makeWild(); } } + return t; } @@ -1286,24 +1280,6 @@ version (IN_LLVM) return ((te = isTypeEnum()) !is null) ? te.toBasetype2() : this; } - /******************************* - * Determine if converting 'this' to 'to' is an identity operation, - * a conversion to const operation, or the types aren't the same. - * Returns: - * MATCH.exact 'this' == 'to' - * MATCH.constant 'to' is const - * MATCH.nomatch conversion to mutable or invariant - */ - MATCH constConv(Type to) - { - //printf("Type::constConv(this = %s, to = %s)\n", toChars(), to.toChars()); - if (equals(to)) - return MATCH.exact; - if (ty == to.ty && MODimplicitConv(mod, to.mod)) - return MATCH.constant; - return MATCH.nomatch; - } - /*************************************** * Compute MOD bits matching `this` argument type to wild parameter type. * Params: @@ -1319,21 +1295,19 @@ version (IN_LLVM) { if (isImmutable()) return MODFlags.immutable_; - else if (isWildConst()) + if (isWildConst()) { if (t.isWildConst()) return MODFlags.wild; - else - return MODFlags.wildconst; + return MODFlags.wildconst; } - else if (isWild()) + if (isWild()) return MODFlags.wild; - else if (isConst()) + if (isConst()) return MODFlags.const_; - else if (isMutable()) + if (isMutable()) return MODFlags.mutable; - else - assert(0); + assert(0); } return 0; } @@ -1357,7 +1331,7 @@ version (IN_LLVM) * Use when we prefer the default initializer to be a literal, * rather than a global immutable variable. */ - Expression defaultInitLiteral(const ref Loc loc) + Expression defaultInitLiteral(Loc loc) { static if (LOGDEFAULTINIT) { @@ -1366,12 +1340,6 @@ version (IN_LLVM) return defaultInit(this, loc); } - // if initializer is 0 - bool isZeroInit(const ref Loc loc) - { - return false; // assume not - } - /*************************************** * Return !=0 if the type or any of its subtypes is wild. */ @@ -1431,33 +1399,6 @@ version (IN_LLVM) return t; } - /******************************************* - * Compute number of elements for a (possibly multidimensional) static array, - * or 1 for other types. - * Params: - * loc = for error message - * Returns: - * number of elements, uint.max on overflow - */ - final uint numberOfElems(const ref Loc loc) - { - //printf("Type::numberOfElems()\n"); - uinteger_t n = 1; - Type tb = this; - while ((tb = tb.toBasetype()).ty == Tsarray) - { - bool overflow = false; - n = mulu(n, (cast(TypeSArray)tb).dim.toUInteger(), overflow); - if (overflow || n >= uint.max) - { - error(loc, "static array `%s` size overflowed to %llu", toChars(), cast(ulong)n); - return uint.max; - } - tb = (cast(TypeSArray)tb).next; - } - return cast(uint)n; - } - /**************************************** * Return the mask that an integral type will * fit into. @@ -1584,6 +1525,8 @@ version (IN_LLVM) 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) @@ -1633,7 +1576,7 @@ extern (C++) final class TypeError : Type return this; } - override Expression defaultInitLiteral(const ref Loc loc) + override Expression defaultInitLiteral(Loc loc) { return ErrorExp.get(); } @@ -1667,7 +1610,7 @@ extern (C++) abstract class TypeNext : Type /******************************* * For TypeFunction, nextOf() can return NULL if the function return - * type is meant to be inferred, and semantic() hasn't yet ben run + * 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 @@ -1870,35 +1813,6 @@ extern (C++) abstract class TypeNext : Type return t; } - override MATCH constConv(Type to) - { - //printf("TypeNext::constConv from = %s, to = %s\n", toChars(), to.toChars()); - if (equals(to)) - return MATCH.exact; - - if (!(ty == to.ty && MODimplicitConv(mod, to.mod))) - return MATCH.nomatch; - - Type tn = to.nextOf(); - if (!(tn && next.ty == tn.ty)) - return MATCH.nomatch; - - MATCH m; - if (to.isConst()) // whole tail const conversion - { - // Recursive shared level check - m = next.constConv(tn); - if (m == MATCH.exact) - m = MATCH.constant; - } - else - { - //printf("\tnext => %s, to.next => %s\n", next.toChars(), tn.toChars()); - m = next.equals(tn) ? MATCH.constant : MATCH.nomatch; - } - return m; - } - override final MOD deduceWild(Type t, bool isRef) { if (ty == Tfunction) @@ -2093,64 +2007,42 @@ extern (C++) final class TypeBasic : Type return target.alignsize(this); } - override bool isintegral() + override bool isIntegral() { - //printf("TypeBasic::isintegral('%s') x%x\n", toChars(), flags); + //printf("TypeBasic::isIntegral('%s') x%x\n", toChars(), flags); return (flags & TFlags.integral) != 0; } - override bool isfloating() + override bool isFloating() { return (flags & TFlags.floating) != 0; } - override bool isreal() + override bool isReal() { return (flags & TFlags.real_) != 0; } - override bool isimaginary() + override bool isImaginary() { return (flags & TFlags.imaginary) != 0; } - override bool iscomplex() + override bool isComplex() { return (flags & TFlags.complex) != 0; } - override bool isscalar() + override bool isScalar() { return (flags & (TFlags.integral | TFlags.floating)) != 0; } - override bool isunsigned() + override bool isUnsigned() { return (flags & TFlags.unsigned) != 0; } - override bool isZeroInit(const ref Loc loc) - { - switch (ty) - { - case Tchar: - case Twchar: - case Tdchar: - case Timaginary32: - case Timaginary64: - case Timaginary80: - case Tfloat32: - case Tfloat64: - case Tfloat80: - case Tcomplex32: - case Tcomplex64: - case Tcomplex80: - return false; // no - default: - return true; // yes - } - } - override bool hasUnsafeBitpatterns() { return ty == Tbool; @@ -2204,25 +2096,25 @@ extern (C++) final class TypeVector : Type return cast(uint)basetype.size(); } - override bool isintegral() + override bool isIntegral() { - //printf("TypeVector::isintegral('%s') x%x\n", toChars(), flags); - return basetype.nextOf().isintegral(); + //printf("TypeVector::isIntegral('%s') x%x\n", toChars(), flags); + return basetype.nextOf().isIntegral(); } - override bool isfloating() + override bool isFloating() { - return basetype.nextOf().isfloating(); + return basetype.nextOf().isFloating(); } - override bool isscalar() + override bool isScalar() { - return basetype.nextOf().isscalar(); + return basetype.nextOf().isScalar(); } - override bool isunsigned() + override bool isUnsigned() { - return basetype.nextOf().isunsigned(); + return basetype.nextOf().isUnsigned(); } override bool isBoolean() @@ -2230,7 +2122,7 @@ extern (C++) final class TypeVector : Type return false; } - override Expression defaultInitLiteral(const ref Loc loc) + override Expression defaultInitLiteral(Loc loc) { //printf("TypeVector::defaultInitLiteral()\n"); assert(basetype.ty == Tsarray); @@ -2250,11 +2142,6 @@ extern (C++) final class TypeVector : Type return tb; } - override bool isZeroInit(const ref Loc loc) - { - return basetype.isZeroInit(loc); - } - override void accept(Visitor v) { v.visit(this); @@ -2331,27 +2218,12 @@ extern (C++) final class TypeSArray : TypeArray return nty.isSomeChar; } - override bool isZeroInit(const ref Loc loc) - { - return next.isZeroInit(loc); - } - override structalign_t alignment() { return next.alignment(); } - override MATCH constConv(Type to) - { - if (auto tsa = to.isTypeSArray()) - { - if (!dim.equals(tsa.dim)) - return MATCH.nomatch; - } - return TypeNext.constConv(to); - } - - override Expression defaultInitLiteral(const ref Loc loc) + override Expression defaultInitLiteral(Loc loc) { static if (LOGDEFAULTINIT) { @@ -2449,11 +2321,6 @@ extern (C++) final class TypeDArray : TypeArray return nty.isSomeChar; } - override bool isZeroInit(const ref Loc loc) - { - return true; - } - override bool isBoolean() { return true; @@ -2500,28 +2367,11 @@ extern (C++) final class TypeAArray : TypeArray return result; } - override bool isZeroInit(const ref Loc loc) - { - return true; - } - override bool isBoolean() { return true; } - override MATCH constConv(Type to) - { - if (auto taa = to.isTypeAArray()) - { - MATCH mindex = index.constConv(taa.index); - MATCH mkey = next.constConv(taa.next); - // Pick the worst match - return mkey < mindex ? mkey : mindex; - } - return Type.constConv(to); - } - override void accept(Visitor v) { v.visit(this); @@ -2558,24 +2408,7 @@ extern (C++) final class TypePointer : TypeNext return result; } - override MATCH constConv(Type to) - { - if (next.ty == Tfunction) - { - if (to.nextOf() && next.equals((cast(TypeNext)to).next)) - return Type.constConv(to); - else - return MATCH.nomatch; - } - return TypeNext.constConv(to); - } - - override bool isscalar() - { - return true; - } - - override bool isZeroInit(const ref Loc loc) + override bool isScalar() { return true; } @@ -2612,11 +2445,6 @@ extern (C++) final class TypeReference : TypeNext return result; } - override bool isZeroInit(const ref Loc loc) - { - return true; - } - override void accept(Visitor v) { v.visit(this); @@ -2650,20 +2478,21 @@ extern (C++) final class TypeFunction : TypeNext // getters and setters are generated for them private extern (D) static struct BitFields { - bool isnothrow; /// nothrow - bool isnogc; /// is @nogc - bool isproperty; /// can be called without parentheses - bool isref; /// returns a reference - bool isreturn; /// 'this' is returned by ref + bool isNothrow; /// nothrow + bool isNogc; /// is @nogc + bool isProperty; /// can be called without parentheses + bool isRef; /// returns a reference + bool isReturn; /// 'this' is returned by ref bool isScopeQual; /// 'this' is scope - bool isreturninferred; /// 'this' is return from inference - bool isscopeinferred; /// 'this' is scope from inference - bool islive; /// is @live + bool isReturnInferred; /// 'this' is return from inference + bool isScopeInferred; /// 'this' is scope from inference + bool isLive; /// is @live bool incomplete; /// return type or default arguments removed bool isInOutParam; /// inout on the parameters bool isInOutQual; /// inout on the qualifier - bool isctor; /// the function is a constructor - bool isreturnscope; /// `this` is returned by value + bool isCtor; /// the function is a constructor + bool isReturnScope; /// `this` is returned by value + bool isRvalue; /// returned reference should be treated as rvalue } import dmd.common.bitfields : generateBitFields; @@ -2675,7 +2504,7 @@ extern (C++) final class TypeFunction : TypeNext byte inuse; ArgumentList inferenceArguments; // function arguments to determine `auto ref` in type semantic - extern (D) this(ParameterList pl, Type treturn, LINK linkage, StorageClass stc = 0) @safe + extern (D) this(ParameterList pl, Type treturn, LINK linkage, STC stc = STC.none) @safe { super(Tfunction, treturn); //if (!treturn) *(char*)0=0; @@ -2687,26 +2516,28 @@ extern (C++) final class TypeFunction : TypeNext if (stc & STC.pure_) this.purity = PURE.fwdref; if (stc & STC.nothrow_) - this.isnothrow = true; + this.isNothrow = true; if (stc & STC.nogc) - this.isnogc = true; + this.isNogc = true; if (stc & STC.property) - this.isproperty = true; + this.isProperty = true; if (stc & STC.live) - this.islive = true; + this.isLive = true; if (stc & STC.ref_) - this.isref = true; + this.isRef = true; if (stc & STC.return_) - this.isreturn = true; + this.isReturn = true; if (stc & STC.returnScope) - this.isreturnscope = true; + this.isReturnScope = true; if (stc & STC.returninferred) - this.isreturninferred = true; + this.isReturnInferred = true; if (stc & STC.scope_) this.isScopeQual = true; if (stc & STC.scopeinferred) - this.isscopeinferred = true; + this.isScopeInferred = true; + if (stc & STC.rvalue) + this.isRvalue = true; this.trust = TRUST.default_; if (stc & STC.safe) @@ -2717,9 +2548,9 @@ extern (C++) final class TypeFunction : TypeNext this.trust = TRUST.trusted; } - static TypeFunction create(Parameters* parameters, Type treturn, ubyte varargs, LINK linkage, StorageClass stc = 0) @safe + static TypeFunction create(Parameters* parameters, Type treturn, ubyte varargs, LINK linkage, StorageClass stc = STC.none) @safe { - return new TypeFunction(ParameterList(parameters, cast(VarArg)varargs), treturn, linkage, stc); + return new TypeFunction(ParameterList(parameters, cast(VarArg)varargs), treturn, linkage, cast(STC) stc); } override const(char)* kind() const @@ -2732,22 +2563,23 @@ extern (C++) final class TypeFunction : TypeNext Type treturn = next ? next.syntaxCopy() : null; auto t = new TypeFunction(parameterList.syntaxCopy(), treturn, linkage); t.mod = mod; - t.isnothrow = isnothrow; - t.isnogc = isnogc; - t.islive = islive; + t.isNothrow = isNothrow; + t.isNogc = isNogc; + t.isLive = isLive; t.purity = purity; - t.isproperty = isproperty; - t.isref = isref; - t.isreturn = isreturn; - t.isreturnscope = isreturnscope; + t.isProperty = isProperty; + t.isRef = isRef; + t.isReturn = isReturn; + t.isReturnScope = isReturnScope; t.isScopeQual = isScopeQual; - t.isreturninferred = isreturninferred; - t.isscopeinferred = isscopeinferred; + t.isReturnInferred = isReturnInferred; + t.isScopeInferred = isScopeInferred; + t.isRvalue = isRvalue; t.isInOutParam = isInOutParam; t.isInOutQual = isInOutQual; t.trust = trust; t.inferenceArguments = inferenceArguments; - t.isctor = isctor; + t.isCtor = isCtor; return t; } @@ -2775,13 +2607,20 @@ extern (C++) final class TypeFunction : TypeNext return linkage == LINK.d && parameterList.varargs == VarArg.variadic; } - extern(D) static const(char)* getMatchError(A...)(const(char)* format, A args) + /********************************* + * Append error message to buf. + * Input: + * buf = message sink + * format = printf format + */ + extern(C) static void getMatchError(ref OutBuffer buf, const(char)* format, ...) { if (global.gag && !global.params.v.showGaggedErrors) - return null; - OutBuffer buf; - buf.printf(format, args); - return buf.extractChars(); + return; + va_list ap; + va_start(ap, format); + buf.vprintf(format, ap); + va_end(ap); } /******************************** @@ -2790,10 +2629,10 @@ extern (C++) final class TypeFunction : TypeNext * * Params: * argumentList = array of function arguments - * pMessage = address to store error message, or `null` + * buf = if not null, append error message to it * Returns: re-ordered argument list, or `null` on error */ - extern(D) Expressions* resolveNamedArgs(ArgumentList argumentList, const(char)** pMessage) + extern(D) Expressions* resolveNamedArgs(ArgumentList argumentList, OutBuffer* buf) { Expression[] args = argumentList.arguments ? (*argumentList.arguments)[] : null; Identifier[] names = argumentList.names ? (*argumentList.names)[] : null; @@ -2817,8 +2656,8 @@ extern (C++) final class TypeFunction : TypeNext const pi = findParameterIndex(name); if (pi == -1) { - if (pMessage) - *pMessage = getMatchError("no parameter named `%s`", name.toChars()); + if (buf) + getMatchError(*buf, "no parameter named `%s`", name.toChars()); return null; } ci = pi; @@ -2828,8 +2667,8 @@ extern (C++) final class TypeFunction : TypeNext if (!isVariadic) { // Without named args, let the caller diagnose argument overflow - if (hasNamedArgs && pMessage) - *pMessage = getMatchError("argument `%s` goes past end of parameter list", arg.toChars()); + if (hasNamedArgs && buf) + getMatchError(*buf, "argument `%s` goes past end of parameter list", arg.toChars()); return null; } while (ci >= newArgs.length) @@ -2838,8 +2677,8 @@ extern (C++) final class TypeFunction : TypeNext if ((*newArgs)[ci]) { - if (pMessage) - *pMessage = getMatchError("parameter `%s` assigned twice", parameterList[ci].toChars()); + if (buf) + getMatchError(*buf, "parameter `%s` assigned twice", parameterList[ci].toChars()); return null; } (*newArgs)[ci++] = arg; @@ -2857,8 +2696,8 @@ extern (C++) final class TypeFunction : TypeNext if (this.incomplete) continue; - if (pMessage) - *pMessage = getMatchError("missing argument for parameter #%d: `%s`", + if (buf) + getMatchError(*buf, "missing argument for parameter #%d: `%s`", i + 1, parameterToChars(parameterList[i], this, false)); return null; } @@ -2872,46 +2711,6 @@ extern (C++) final class TypeFunction : TypeNext return newArgs; } - /** Extends TypeNext.constConv by also checking for matching attributes **/ - override MATCH constConv(Type to) - { - // Attributes need to match exactly, otherwise it's an implicit conversion - if (this.ty != to.ty || !this.attributesEqual(cast(TypeFunction) to)) - return MATCH.nomatch; - - return super.constConv(to); - } - - extern (D) bool checkRetType(const ref Loc loc) - { - Type tb = next.toBasetype(); - if (tb.ty == Tfunction) - { - error(loc, "functions cannot return a function"); - next = Type.terror; - } - if (tb.ty == Ttuple) - { - error(loc, "functions cannot return a sequence (use `std.typecons.Tuple`)"); - next = Type.terror; - } - if (!isref && (tb.ty == Tstruct || tb.ty == Tsarray)) - { - if (auto ts = tb.baseElemOf().isTypeStruct()) - { - if (!ts.sym.members) - { - error(loc, "functions cannot return opaque type `%s` by value", tb.toChars()); - next = Type.terror; - } - } - } - if (tb.ty == Terror) - return true; - return false; - } - - /// Returns: `true` the function is `isInOutQual` or `isInOutParam` ,`false` otherwise. bool iswild() const pure nothrow @safe @nogc { @@ -2932,9 +2731,9 @@ extern (C++) final class TypeFunction : TypeNext return (this.trust == other.trust || (trustSystemEqualsDefault && this.trust <= TRUST.system && other.trust <= TRUST.system)) && this.purity == other.purity && - this.isnothrow == other.isnothrow && - this.isnogc == other.isnogc && - this.islive == other.islive; + this.isNothrow == other.isNothrow && + this.isNogc == other.isNogc && + this.isLive == other.isLive; } override void accept(Visitor v) @@ -2998,11 +2797,6 @@ extern (C++) final class TypeDelegate : TypeNext return target.ptrsize; } - override bool isZeroInit(const ref Loc loc) - { - return true; - } - override bool isBoolean() { return true; @@ -3028,7 +2822,7 @@ extern (C++) final class TypeTraits : Type /// Cached type/symbol after semantic analysis. RootObject obj; - final extern (D) this(const ref Loc loc, TraitsExp exp) @safe + final extern (D) this(Loc loc, TraitsExp exp) @safe { super(Ttraits); this.loc = loc; @@ -3065,7 +2859,7 @@ extern (C++) final class TypeMixin : Type Expressions* exps; RootObject obj; // cached result of semantic analysis. - extern (D) this(const ref Loc loc, Expressions* exps) @safe + extern (D) this(Loc loc, Expressions* exps) @safe { super(Tmixin); this.loc = loc; @@ -3173,16 +2967,13 @@ extern (C++) final class TypeIdentifier : TypeQualified { Identifier ident; - // The symbol representing this identifier, before alias resolution - Dsymbol originalSymbol; - - extern (D) this(const ref Loc loc, Identifier ident) + extern (D) this(Loc loc, Identifier ident) { super(Tident, loc); this.ident = ident; } - static TypeIdentifier create(const ref Loc loc, Identifier ident) + static TypeIdentifier create(Loc loc, Identifier ident) { return new TypeIdentifier(loc, ident); } @@ -3213,7 +3004,7 @@ extern (C++) final class TypeInstance : TypeQualified { TemplateInstance tempinst; - extern (D) this(const ref Loc loc, TemplateInstance tempinst) + extern (D) this(Loc loc, TemplateInstance tempinst) { super(Tinstance, loc); this.tempinst = tempinst; @@ -3246,7 +3037,7 @@ extern (C++) final class TypeTypeof : TypeQualified Expression exp; int inuse; - extern (D) this(const ref Loc loc, Expression exp) + extern (D) this(Loc loc, Expression exp) { super(Ttypeof, loc); this.exp = exp; @@ -3276,7 +3067,7 @@ extern (C++) final class TypeTypeof : TypeQualified */ extern (C++) final class TypeReturn : TypeQualified { - extern (D) this(const ref Loc loc) + extern (D) this(Loc loc) { super(Treturn, loc); } @@ -3346,7 +3137,7 @@ extern (C++) final class TypeStruct : Type * Use when we prefer the default initializer to be a literal, * rather than a global immutable variable. */ - override Expression defaultInitLiteral(const ref Loc loc) + override Expression defaultInitLiteral(Loc loc) { static if (LOGDEFAULTINIT) { @@ -3396,13 +3187,6 @@ extern (C++) final class TypeStruct : Type return structinit; } - override bool isZeroInit(const ref Loc loc) - { - // Determine zeroInit here, as this can be called before semantic2 - sym.determineSize(sym.loc); - return sym.zeroInit; - } - override bool isAssignable() { bool assignable = true; @@ -3497,77 +3281,6 @@ extern (C++) final class TypeStruct : Type return sym.hasInvariant() || sym.hasFieldWithInvariant; } - extern (D) MATCH implicitConvToWithoutAliasThis(Type to) - { - //printf("TypeStruct::implicitConvToWithoutAliasThis(%s => %s)\n", toChars(), to.toChars()); - - auto tos = to.isTypeStruct(); - if (!(tos && sym == tos.sym)) - return MATCH.nomatch; - - if (mod == to.mod) - return MATCH.exact; - - if (MODimplicitConv(mod, to.mod)) - return MATCH.constant; - - /* Check all the fields. If they can all be converted, - * allow the conversion. - */ - MATCH m = MATCH.constant; - uint offset = ~0; // must never match a field offset - foreach (v; sym.fields[]) - { - /* Why are we only looking at the first member of a union? - * The check should check for overlap of v with the previous field, - * not just starting at the same point - */ - if (!global.params.fixImmutableConv && v.offset == offset) // v is at same offset as previous field - continue; // ignore - - Type tvf = v.type.addMod(mod); // from type - Type tvt = v.type.addMod(to.mod); // to type - - // field match - MATCH mf = tvf.implicitConvTo(tvt); - //printf("\t%s => %s, match = %d\n", v.type.toChars(), tvt.toChars(), mf); - - if (mf == MATCH.nomatch) - return MATCH.nomatch; - if (mf < m) // if field match is worse - m = mf; - offset = v.offset; - } - return m; - } - - extern (D) MATCH implicitConvToThroughAliasThis(Type to) - { - auto tos = to.isTypeStruct(); - if (!(tos && sym == tos.sym) && - sym.aliasthis && - !(att & AliasThisRec.tracing)) - { - if (auto ato = aliasthisOf(this)) - { - att = cast(AliasThisRec)(att | AliasThisRec.tracing); - MATCH m = ato.implicitConvTo(to); - att = cast(AliasThisRec)(att & ~AliasThisRec.tracing); - return m; - } - } - return MATCH.nomatch; - } - - override MATCH constConv(Type to) - { - if (equals(to)) - return MATCH.exact; - if (ty == to.ty && sym == (cast(TypeStruct)to).sym && MODimplicitConv(mod, to.mod)) - return MATCH.constant; - return MATCH.nomatch; - } - override MOD deduceWild(Type t, bool isRef) { if (ty == t.ty && sym == (cast(TypeStruct)t).sym) @@ -3629,39 +3342,39 @@ extern (C++) final class TypeEnum : Type return t.alignsize(); } - override bool isintegral() + override bool isIntegral() { - return memType().isintegral(); + return memType().isIntegral(); } - override bool isfloating() + override bool isFloating() { - return memType().isfloating(); + return memType().isFloating(); } - override bool isreal() + override bool isReal() { - return memType().isreal(); + return memType().isReal(); } - override bool isimaginary() + override bool isImaginary() { - return memType().isimaginary(); + return memType().isImaginary(); } - override bool iscomplex() + override bool isComplex() { - return memType().iscomplex(); + return memType().isComplex(); } - override bool isscalar() + override bool isScalar() { - return memType().isscalar(); + return memType().isScalar(); } - override bool isunsigned() + override bool isUnsigned() { - return memType().isunsigned(); + return memType().isUnsigned(); } override bool isBoolean() @@ -3694,15 +3407,6 @@ extern (C++) final class TypeEnum : Type return memType().needsNested(); } - override MATCH constConv(Type to) - { - if (equals(to)) - return MATCH.exact; - if (ty == to.ty && sym == (cast(TypeEnum)to).sym && MODimplicitConv(mod, to.mod)) - return MATCH.constant; - return MATCH.nomatch; - } - extern (D) Type toBasetype2() { if (!sym.members && !sym.memtype) @@ -3711,11 +3415,6 @@ extern (C++) final class TypeEnum : Type return tb.castMod(mod); // retain modifier bits from 'this' } - override bool isZeroInit(const ref Loc loc) - { - return sym.getDefaultValue(loc).toBool().hasValue(false); - } - override bool hasVoidInitPointers() { return memType().hasVoidInitPointers(); @@ -3771,58 +3470,6 @@ extern (C++) final class TypeClass : Type return sym; } - extern (D) MATCH implicitConvToWithoutAliasThis(Type to) - { - ClassDeclaration cdto = to.isClassHandle(); - MATCH m = constConv(to); - if (m > MATCH.nomatch) - return m; - - if (cdto && cdto.isBaseOf(sym, null) && MODimplicitConv(mod, to.mod)) - { - //printf("'to' is base\n"); - return MATCH.convert; - } - return MATCH.nomatch; - } - - extern (D) MATCH implicitConvToThroughAliasThis(Type to) - { - MATCH m; - if (sym.aliasthis && !(att & AliasThisRec.tracing)) - { - if (auto ato = aliasthisOf(this)) - { - att = cast(AliasThisRec)(att | AliasThisRec.tracing); - m = ato.implicitConvTo(to); - att = cast(AliasThisRec)(att & ~AliasThisRec.tracing); - } - } - return m; - } - - override MATCH constConv(Type to) - { - if (equals(to)) - return MATCH.exact; - if (ty == to.ty && sym == (cast(TypeClass)to).sym && MODimplicitConv(mod, to.mod)) - return MATCH.constant; - - /* Conversion derived to const(base) - */ - int offset = 0; - if (to.isBaseOf(this, &offset) && offset == 0 && MODimplicitConv(mod, to.mod)) - { - // Disallow: - // derived to base - // inout(derived) to inout(base) - if (!to.isMutable() && !to.isWild()) - return MATCH.convert; - } - - return MATCH.nomatch; - } - override MOD deduceWild(Type t, bool isRef) { ClassDeclaration cd = t.isClassHandle(); @@ -3844,12 +3491,7 @@ extern (C++) final class TypeClass : Type return wm; } - override bool isZeroInit(const ref Loc loc) - { - return true; - } - - override bool isscope() + override bool isScopeClass() { return sym.stack; } @@ -3903,17 +3545,19 @@ extern (C++) final class TypeTuple : Type extern (D) this(Expressions* exps) { super(Ttuple); - auto arguments = new Parameters(exps ? exps.length : 0); - if (exps) + if (!exps) { - for (size_t i = 0; i < exps.length; i++) - { - Expression e = (*exps)[i]; - if (e.type.ty == Ttuple) - error(e.loc, "cannot form sequence of sequences"); - auto arg = new Parameter(e.loc, STC.undefined_, e.type, null, null, null); - (*arguments)[i] = arg; - } + this.arguments = new Parameters(0); + return; + } + auto arguments = new Parameters(exps.length); + + for (size_t i = 0; i < exps.length; i++) + { + Expression e = (*exps)[i]; + assert(e.type.ty != Ttuple); + auto arg = new Parameter(e.loc, STC.none, e.type, null, null, null); + (*arguments)[i] = arg; } this.arguments = arguments; //printf("TypeTuple() %p, %s\n", this, toChars()); @@ -3937,15 +3581,15 @@ extern (C++) final class TypeTuple : Type { super(Ttuple); arguments = new Parameters(); - arguments.push(new Parameter(Loc.initial, 0, t1, null, null, null)); + arguments.push(new Parameter(Loc.initial, STC.none, t1, null, null, null)); } extern (D) this(Type t1, Type t2) { super(Ttuple); arguments = new Parameters(); - arguments.push(new Parameter(Loc.initial, 0, t1, null, null, null)); - arguments.push(new Parameter(Loc.initial, 0, t2, null, null, null)); + arguments.push(new Parameter(Loc.initial, STC.none, t1, null, null, null)); + arguments.push(new Parameter(Loc.initial, STC.none, t2, null, null, null)); } static TypeTuple create() @safe @@ -4092,12 +3736,6 @@ extern (C++) final class TypeNoreturn : Type return this; } - override MATCH constConv(Type to) - { - // Either another noreturn or conversion to any type - return this.implicitConvTo(to); - } - override bool isBoolean() { return true; // bottom type can be implicitly converted to any other type @@ -4136,7 +3774,7 @@ extern (C++) final class TypeTag : Type /// struct S { int a; } s1, *s2; MOD mod; /// modifiers to apply after type is resolved (only MODFlags.const_ at the moment) - extern (D) this(const ref Loc loc, TOK tok, Identifier id, structalign_t packalign, Type base, Dsymbols* members) @safe + extern (D) this(Loc loc, TOK tok, Identifier id, structalign_t packalign, Type base, Dsymbols* members) @safe { //printf("TypeTag ctor %s %p\n", id ? id.toChars() : "null".ptr, this); super(Ttag); @@ -4176,11 +3814,11 @@ extern (C++) struct ParameterList { /// The raw (unexpanded) formal parameters, possibly containing tuples. Parameters* parameters; - StorageClass stc; // storage class of ... + STC stc; // storage class of ... VarArg varargs = VarArg.none; bool hasIdentifierList; // true if C identifier-list style - this(Parameters* parameters, VarArg varargs = VarArg.none, StorageClass stc = 0) @safe + this(Parameters* parameters, VarArg varargs = VarArg.none, STC stc = STC.none) @safe { this.parameters = parameters; this.varargs = varargs; @@ -4237,7 +3875,8 @@ extern (C++) struct ParameterList foreach (_, p1; cast() this) { auto p2 = other[idx++]; - if (!p2 || p1 != p2) { + if (!p2 || p1 != p2) + { diff = true; break; } @@ -4278,13 +3917,13 @@ extern (C++) final class Parameter : ASTNode import dmd.attrib : UserAttributeDeclaration; Loc loc; - StorageClass storageClass; + STC storageClass; Type type; Identifier ident; Expression defaultArg; UserAttributeDeclaration userAttribDecl; // user defined attributes - extern (D) this(const ref Loc loc, StorageClass storageClass, Type type, Identifier ident, Expression defaultArg, UserAttributeDeclaration userAttribDecl) @safe + extern (D) this(Loc loc, STC storageClass, Type type, Identifier ident, Expression defaultArg, UserAttributeDeclaration userAttribDecl) @safe { this.loc = loc; this.type = type; @@ -4294,9 +3933,9 @@ extern (C++) final class Parameter : ASTNode this.userAttribDecl = userAttribDecl; } - static Parameter create(const ref Loc loc, StorageClass storageClass, Type type, Identifier ident, Expression defaultArg, UserAttributeDeclaration userAttribDecl) @safe + static Parameter create(Loc loc, StorageClass storageClass, Type type, Identifier ident, Expression defaultArg, UserAttributeDeclaration userAttribDecl) @safe { - return new Parameter(loc, storageClass, type, ident, defaultArg, userAttribDecl); + return new Parameter(loc, cast(STC) storageClass, type, ident, defaultArg, userAttribDecl); } Parameter syntaxCopy() @@ -4316,7 +3955,7 @@ extern (C++) final class Parameter : ASTNode Type isLazyArray() { Type tb = type.toBasetype(); - if (tb.ty == Tsarray || tb.ty == Tarray) + if (tb.isStaticOrDynamicArray()) { Type tel = (cast(TypeArray)tb).next.toBasetype(); if (auto td = tel.isTypeDelegate()) @@ -4426,7 +4065,7 @@ extern (C++) final class Parameter : ASTNode /*************************************** * Expands tuples in args in depth first order. Calls - * dg(void *ctx, size_t argidx, Parameter *arg) for each Parameter. + * dg(void* ctx, size_t argidx, Parameter* arg) for each Parameter. * If dg returns !=0, stops and returns that value else returns 0. * Use this function to avoid the O(N + N^2/2) complexity of * calculating dim and calling N times getNth. @@ -4508,21 +4147,21 @@ extern (C++) final class Parameter : ASTNode bool isCovariant(bool returnByRef, const Parameter p) const pure nothrow @nogc @safe { - ulong thisSTC = this.storageClass; - ulong otherSTC = p.storageClass; + STC thisSTC = this.storageClass; + STC otherSTC = p.storageClass; if (thisSTC & STC.constscoperef) thisSTC |= STC.scope_; if (otherSTC & STC.constscoperef) otherSTC |= STC.scope_; - const mask = STC.ref_ | STC.out_ | STC.lazy_ | (((thisSTC | otherSTC) & STC.constscoperef) ? STC.in_ : 0); + const mask = STC.ref_ | STC.out_ | STC.lazy_ | (((thisSTC | otherSTC) & STC.constscoperef) ? STC.in_ : STC.none); if ((thisSTC & mask) != (otherSTC & mask)) return false; return isCovariantScope(returnByRef, thisSTC, otherSTC); } - extern (D) static bool isCovariantScope(bool returnByRef, StorageClass from, StorageClass to) pure nothrow @nogc @safe + extern (D) static bool isCovariantScope(bool returnByRef, STC from, STC to) pure nothrow @nogc @safe { // Workaround for failing covariance when finding a common type of delegates, // some of which have parameters with inferred scope @@ -4651,19 +4290,19 @@ void attributesApply(const TypeFunction tf, void delegate(string) dg, TRUSTforma { if (tf.purity) dg("pure"); - if (tf.isnothrow) + if (tf.isNothrow) dg("nothrow"); - if (tf.isnogc) + if (tf.isNogc) dg("@nogc"); - if (tf.isproperty) + if (tf.isProperty) dg("@property"); - if (tf.isref) + if (tf.isRef) dg("ref"); - if (tf.isreturn && !tf.isreturninferred) + if (tf.isReturn && !tf.isReturnInferred) dg("return"); - if (tf.isScopeQual && !tf.isscopeinferred) + if (tf.isScopeQual && !tf.isScopeInferred) dg("scope"); - if (tf.islive) + if (tf.isLive) dg("@live"); TRUST trustAttrib = tf.trust; @@ -4685,10 +4324,10 @@ void attributesApply(const TypeFunction tf, void delegate(string) dg, TRUSTforma AggregateDeclaration isAggregate(Type t) { t = t.toBasetype(); - if (t.ty == Tclass) - return (cast(TypeClass)t).sym; - if (t.ty == Tstruct) - return (cast(TypeStruct)t).sym; + if (auto tc = t.isTypeClass()) + return tc.sym; + if (auto ts = t.isTypeStruct()) + return ts.sym; return null; } @@ -4703,27 +4342,27 @@ AggregateDeclaration isAggregate(Type t) bool isIndexableNonAggregate(Type t) { t = t.toBasetype(); - return (t.ty == Tpointer || t.ty == Tsarray || t.ty == Tarray || t.ty == Taarray || + 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 StorageClass is so wide + * Shrinking the representation is necessary because STC is so wide * Params: * stc = storage class of parameter * Returns: * value from enum ScopeRef */ -ScopeRef buildScopeRef(StorageClass stc) pure nothrow @nogc @safe +ScopeRef buildScopeRef(STC stc) pure nothrow @nogc @safe { if (stc & STC.out_) stc |= STC.ref_; // treat `out` and `ref` the same ScopeRef result; - final switch (stc & (STC.ref_ | STC.scope_ | STC.return_)) + switch (stc & (STC.ref_ | STC.scope_ | STC.return_)) { - case 0: result = ScopeRef.None; break; + case STC.none: result = ScopeRef.None; break; /* can occur in case test/compilable/testsctreturn.d * related to https://issues.dlang.org/show_bug.cgi?id=20149 @@ -4741,6 +4380,8 @@ ScopeRef buildScopeRef(StorageClass stc) pure nothrow @nogc @safe result = stc & STC.returnScope ? ScopeRef.Ref_ReturnScope : ScopeRef.ReturnRef_Scope; break; + default: + assert(0); } return result; } diff --git a/dmd/mtype.h b/dmd/mtype.h index 01b934ecec..490bcf3001 100644 --- a/dmd/mtype.h +++ b/dmd/mtype.h @@ -1,6 +1,6 @@ /* Compiler implementation of the D programming language - * Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * written by Walter Bright * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. @@ -44,7 +44,7 @@ typedef struct TYPE type; namespace dmd { - Type *typeSemantic(Type *t, const Loc &loc, Scope *sc); + Type *typeSemantic(Type *t, Loc loc, Scope *sc); Type *merge(Type *type); } @@ -148,9 +148,6 @@ class Type : public ASTNode MOD mod; // modifiers MODxxxx char *deco; void* mcache; - Type *pto; // merged pointer to this type - Type *rto; // reference to this type - Type *arrayof; // array of this type TypeInfoDeclaration *vtinfo; // TypeInfo object for this Type type *ctype; // for back end @@ -237,14 +234,14 @@ class Type : public ASTNode 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 isscope(); + 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 isAssignable(); virtual bool isBoolean(); @@ -269,13 +266,11 @@ class Type : public ASTNode virtual Type *makeSharedWildConst(); virtual Type *makeMutable(); Type *toBasetype(); - virtual MATCH constConv(Type *to); virtual unsigned char deduceWild(Type *t, bool isRef); virtual ClassDeclaration *isClassHandle(); virtual structalign_t alignment(); - virtual Expression *defaultInitLiteral(const Loc &loc); - virtual bool isZeroInit(const Loc &loc = Loc()); // if initializer is 0 + virtual Expression *defaultInitLiteral(Loc loc); virtual int hasWild() const; virtual bool hasVoidInitPointers(); virtual bool hasUnsafeBitpatterns(); @@ -325,7 +320,7 @@ class TypeError final : public Type const char *kind() override; TypeError *syntaxCopy() override; - Expression *defaultInitLiteral(const Loc &loc) override; + Expression *defaultInitLiteral(Loc loc) override; void accept(Visitor *v) override { v->visit(this); } }; @@ -345,7 +340,6 @@ class TypeNext : public Type Type *makeSharedWild() override final; Type *makeSharedWildConst() override final; Type *makeMutable() override final; - MATCH constConv(Type *to) override; unsigned char deduceWild(Type *t, bool isRef) override final; void transitive(); void accept(Visitor *v) override { v->visit(this); } @@ -360,14 +354,13 @@ class TypeBasic final : public Type 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; - bool isZeroInit(const Loc &loc) 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; @@ -383,14 +376,13 @@ class TypeVector final : public Type const char *kind() override; TypeVector *syntaxCopy() override; unsigned alignsize() override; - bool isintegral() override; - bool isfloating() override; - bool isscalar() override; - bool isunsigned() override; + bool isIntegral() override; + bool isFloating() override; + bool isScalar() override; + bool isUnsigned() override; bool isBoolean() override; - Expression *defaultInitLiteral(const Loc &loc) override; + Expression *defaultInitLiteral(Loc loc) override; TypeBasic *elementType(); - bool isZeroInit(const Loc &loc) override; void accept(Visitor *v) override { v->visit(this); } }; @@ -412,10 +404,8 @@ class TypeSArray final : public TypeArray bool isIncomplete(); unsigned alignsize() override; bool isString() override; - bool isZeroInit(const Loc &loc) override; structalign_t alignment() override; - MATCH constConv(Type *to) override; - Expression *defaultInitLiteral(const Loc &loc) override; + Expression *defaultInitLiteral(Loc loc) override; bool hasUnsafeBitpatterns() override; bool hasVoidInitPointers() override; bool hasInvariant() override; @@ -434,7 +424,6 @@ class TypeDArray final : public TypeArray TypeDArray *syntaxCopy() override; unsigned alignsize() override; bool isString() override; - bool isZeroInit(const Loc &loc) override; bool isBoolean() override; void accept(Visitor *v) override { v->visit(this); } @@ -449,9 +438,7 @@ class TypeAArray final : public TypeArray static TypeAArray *create(Type *t, Type *index); const char *kind() override; TypeAArray *syntaxCopy() override; - bool isZeroInit(const Loc &loc) override; bool isBoolean() override; - MATCH constConv(Type *to) override; void accept(Visitor *v) override { v->visit(this); } }; @@ -462,9 +449,7 @@ class TypePointer final : public TypeNext static TypePointer *create(Type *t); const char *kind() override; TypePointer *syntaxCopy() override; - MATCH constConv(Type *to) override; - bool isscalar() override; - bool isZeroInit(const Loc &loc) override; + bool isScalar() override; void accept(Visitor *v) override { v->visit(this); } }; @@ -474,7 +459,6 @@ class TypeReference final : public TypeNext public: const char *kind() override; TypeReference *syntaxCopy() override; - bool isZeroInit(const Loc &loc) override; void accept(Visitor *v) override { v->visit(this); } }; @@ -487,7 +471,7 @@ enum RET enum class TRUST : unsigned char { default_ = 0, - system = 1, // @system (same as TRUSTdefault) + system = 1, // @system (same as TRUST.default_ unless feature "safer" is enabled) trusted = 2, // @trusted safe = 3 // @safe }; @@ -516,7 +500,7 @@ class Parameter final : public ASTNode Expression *defaultArg; UserAttributeDeclaration *userAttribDecl; // user defined attributes - static Parameter *create(const Loc &loc, StorageClass storageClass, Type *type, Identifier *ident, + static Parameter *create(Loc loc, StorageClass storageClass, Type *type, Identifier *ident, Expression *defaultArg, UserAttributeDeclaration *userAttribDecl); Parameter *syntaxCopy(); Type *isLazyArray(); @@ -562,28 +546,27 @@ class TypeFunction final : public TypeNext bool hasLazyParameters(); bool isDstyleVariadic() const; - MATCH constConv(Type *to) override; - - bool isnothrow() const; - void isnothrow(bool v); - bool isnogc() const; - void isnogc(bool v); - bool isproperty() const; - void isproperty(bool v); - bool isref() const; - void isref(bool v); - bool isreturn() const; - void isreturn(bool v); - bool isreturnscope() const; - void isreturnscope(bool v); + + bool isNothrow() const; + void isNothrow(bool v); + bool isNogc() const; + void isNogc(bool v); + bool isProperty() const; + void isProperty(bool v); + bool isRef() const; + void isRef(bool v); + bool isReturn() const; + void isReturn(bool v); + bool isReturnScope() const; + void isReturnScope(bool v); bool isScopeQual() const; void isScopeQual(bool v); - bool isreturninferred() const; - void isreturninferred(bool v); - bool isscopeinferred() const; - void isscopeinferred(bool v); - bool islive() const; - void islive(bool v); + bool isReturnInferred() const; + void isReturnInferred(bool v); + bool isScopeInferred() const; + void isScopeInferred(bool v); + bool isLive() const; + void isLive(bool v); bool incomplete() const; void incomplete(bool v); bool isInOutParam() const; @@ -604,7 +587,6 @@ class TypeDelegate final : public TypeNext const char *kind() override; TypeDelegate *syntaxCopy() override; unsigned alignsize() override; - bool isZeroInit(const Loc &loc) override; bool isBoolean() override; void accept(Visitor *v) override { v->visit(this); } @@ -650,9 +632,8 @@ class TypeIdentifier final : public TypeQualified { public: Identifier *ident; - Dsymbol *originalSymbol; // The symbol representing this identifier, before alias resolution - static TypeIdentifier *create(const Loc &loc, Identifier *ident); + static TypeIdentifier *create(Loc loc, Identifier *ident); const char *kind() override; TypeIdentifier *syntaxCopy() override; void accept(Visitor *v) override { v->visit(this); } @@ -713,8 +694,7 @@ class TypeStruct final : public Type unsigned alignsize() override; TypeStruct *syntaxCopy() override; structalign_t alignment() override; - Expression *defaultInitLiteral(const Loc &loc) override; - bool isZeroInit(const Loc &loc) override; + Expression *defaultInitLiteral(Loc loc) override; bool isAssignable() override; bool isBoolean() override; bool needsDestruction() override; @@ -723,7 +703,6 @@ class TypeStruct final : public Type bool hasVoidInitPointers() override; bool hasUnsafeBitpatterns() override; bool hasInvariant() override; - MATCH constConv(Type *to) override; unsigned char deduceWild(Type *t, bool isRef) override; void accept(Visitor *v) override { v->visit(this); } @@ -737,22 +716,20 @@ class TypeEnum final : public Type const char *kind() override; TypeEnum *syntaxCopy() override; unsigned alignsize() override; - Type *memType(const Loc &loc); - bool isintegral() override; - bool isfloating() override; - bool isreal() override; - bool isimaginary() override; - bool iscomplex() override; - bool isscalar() override; - bool isunsigned() 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 isAssignable() override; bool needsDestruction() override; bool needsCopyOrPostblit() override; bool needsNested() override; - MATCH constConv(Type *to) override; - bool isZeroInit(const Loc &loc) override; bool hasVoidInitPointers() override; bool hasUnsafeBitpatterns() override; bool hasInvariant() override; @@ -771,10 +748,8 @@ class TypeClass final : public Type const char *kind() override; TypeClass *syntaxCopy() override; ClassDeclaration *isClassHandle() override; - MATCH constConv(Type *to) override; unsigned char deduceWild(Type *t, bool isRef) override; - bool isZeroInit(const Loc &loc) override; - bool isscope() override; + bool isScopeClass() override; bool isBoolean() override; void accept(Visitor *v) override { v->visit(this); } @@ -825,7 +800,6 @@ class TypeNoreturn final : public Type public: const char *kind() override; TypeNoreturn *syntaxCopy() override; - MATCH constConv(Type* to) override; bool isBoolean() override; unsigned alignsize() override; @@ -852,7 +826,8 @@ namespace dmd bool equivalent(Type *src, Type *t); Covariant covariant(Type *, Type *, StorageClass * = nullptr, bool = false); bool isBaseOf(Type *tthis, Type *t, int *poffset); - Type *trySemantic(Type *type, const Loc &loc, Scope *sc); + bool isZeroInit(Type *t, Loc loc = Loc()); + Type *trySemantic(Type *type, Loc loc, Scope *sc); Type *pointerTo(Type *type); Type *referenceTo(Type *type); Type *merge2(Type *type); @@ -876,6 +851,7 @@ namespace dmd Type *addStorageClass(Type *type, StorageClass stc); Type *substWildTo(Type *type, unsigned mod); uinteger_t size(Type *type); - uinteger_t size(Type *type, const Loc &loc); + uinteger_t size(Type *type, Loc loc); MATCH implicitConvTo(Type* from, Type* to); + MATCH constConv(Type* from, Type* to); } diff --git a/dmd/mustuse.d b/dmd/mustuse.d index fc7618bfc0..fab9723636 100644 --- a/dmd/mustuse.d +++ b/dmd/mustuse.d @@ -1,11 +1,11 @@ /** * Compile-time checks associated with the @mustuse attribute. * - * Copyright: Copyright (C) 2022-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 2022-2025 by The D Language Foundation, All Rights Reserved * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/mustuse.d, _mustuse.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/mustuse.d, _mustuse.d) * Documentation: https://dlang.org/phobos/dmd_mustuse.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/mustuse.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/mustuse.d */ module dmd.mustuse; @@ -111,26 +111,13 @@ private bool isAssignmentOpId(Identifier id) { import dmd.id : Id; - return id == Id.assign - || id == Id.addass - || id == Id.subass - || id == Id.mulass - || id == Id.divass - || id == Id.modass - || id == Id.andass - || id == Id.orass - || id == Id.xorass - || id == Id.shlass - || id == Id.shrass - || id == Id.ushrass - || id == Id.catass - || id == Id.indexass - || id == Id.slice - || id == Id.sliceass + return id == Id.opAssign + || id == Id.opIndexAssign + || id == Id.opSlice + || id == Id.opSliceAssign || id == Id.opOpAssign || id == Id.opIndexOpAssign - || id == Id.opSliceOpAssign - || id == Id.powass; + || id == Id.opSliceOpAssign; } /** diff --git a/dmd/nogc.d b/dmd/nogc.d index 9e45e4549f..f1494a409f 100644 --- a/dmd/nogc.d +++ b/dmd/nogc.d @@ -3,12 +3,12 @@ * * Specification: $(LINK2 https://dlang.org/spec/function.html#nogc-functions, No-GC Functions) * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/nogc.d, _nogc.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/nogc.d, _nogc.d) * Documentation: https://dlang.org/phobos/dmd_nogc.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/nogc.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/nogc.d */ module dmd.nogc; @@ -18,17 +18,24 @@ import core.stdc.stdio; import dmd.aggregate; import dmd.astenums; import dmd.declaration; +import dmd.common.outbuffer; +import dmd.dmodule; import dmd.dscope; import dmd.dtemplate : isDsymbol; +import dmd.dsymbol : PASS; import dmd.errors; import dmd.expression; import dmd.func; import dmd.globals; import dmd.init; +import dmd.location; import dmd.mtype; -import dmd.postordervisitor; +import dmd.rootobject : RootObject, DYNCAST; +import dmd.semantic2; +import dmd.semantic3; import dmd.tokens; import dmd.visitor; +import dmd.visitor.postorder; /************************************** * Look for GC-allocations @@ -40,6 +47,7 @@ public: FuncDeclaration f; bool checkOnly; // don't print errors bool err; + bool nogcExceptions; // -preview=dip1008 enabled extern (D) this(FuncDeclaration f) scope @safe { @@ -73,20 +81,19 @@ public: * Register that expression `e` requires the GC * Params: * e = expression that uses GC - * format = error message when `e` is used in a `@nogc` function. - * Must contain format strings "`@nogc` %s `%s`" referring to the function. + * msg = error message when `e` is used in a `@nogc` function. * Returns: `true` if `err` was set, `false` if it's not in a `@nogc` and not checkonly (-betterC) */ - private bool setGC(Expression e, const(char)* format) + private bool setGC(Expression e, const(char)* msg) { if (checkOnly) { err = true; return true; } - if (f.setGC(e.loc, format)) + if (f.setGC(e.loc, msg)) { - error(e.loc, format, f.kind(), f.toPrettyChars()); + error(e.loc, "%s causes a GC allocation in `@nogc` %s `%s`", msg, f.kind(), f.toChars()); err = true; return true; } @@ -104,7 +111,7 @@ public: auto fd = stripHookTraceImpl(e.f); if (fd.ident == Id._d_arraysetlengthT) { - if (setGC(e, "setting `length` in `@nogc` %s `%s` may cause a GC allocation")) + if (setGC(e, "setting this array's `length`")) return; f.printGCUsage(e.loc, "setting `length` may cause a GC allocation"); } @@ -114,7 +121,7 @@ public: { if (e.type.ty != Tarray || !e.elements || !e.elements.length || e.onstack) return; - if (setGC(e, "array literal in `@nogc` %s `%s` may cause a GC allocation")) + if (setGC(e, "this array literal")) return; f.printGCUsage(e.loc, "array literal may cause a GC allocation"); } @@ -123,13 +130,15 @@ public: { if (!e.keys.length) return; - if (setGC(e, "associative array literal in `@nogc` %s `%s` may cause a GC allocation")) + if (setGC(e, "this associative array literal")) return; f.printGCUsage(e.loc, "associative array literal may cause a GC allocation"); } override void visit(NewExp e) { + if (e.placement) + return; // placement new doesn't use the GC if (e.member && !e.member.isNogc() && f.setGC(e.loc, null)) { // @nogc-ness is already checked in NewExp::semantic @@ -137,10 +146,10 @@ public: } if (e.onstack) return; - if (global.params.ehnogc && e.thrownew) + if (nogcExceptions && e.thrownew) return; // separate allocator is called for this, not the GC - if (setGC(e, "cannot use `new` in `@nogc` %s `%s`")) + if (setGC(e, "allocating with `new`")) return; f.printGCUsage(e.loc, "`new` causes a GC allocation"); } @@ -163,7 +172,7 @@ public: Type t1b = e.e1.type.toBasetype(); if (e.modifiable && t1b.ty == Taarray) { - if (setGC(e, "assigning an associative array element in `@nogc` %s `%s` may cause a GC allocation")) + if (setGC(e, "assigning this associative array element")) return; f.printGCUsage(e.loc, "assigning an associative array element may cause a GC allocation"); } @@ -173,7 +182,7 @@ public: { if (e.e1.op == EXP.arrayLength) { - if (setGC(e, "setting `length` in `@nogc` %s `%s` may cause a GC allocation")) + if (setGC(e, "setting this array's `length`")) return; f.printGCUsage(e.loc, "setting `length` may cause a GC allocation"); } @@ -186,14 +195,14 @@ public: err = true; return; } - if (setGC(e, "cannot use operator `~=` in `@nogc` %s `%s`")) + if (setGC(e, "appending to this array with operator `~=`")) return; f.printGCUsage(e.loc, "operator `~=` may cause a GC allocation"); } override void visit(CatExp e) { - if (setGC(e, "cannot use operator `~` in `@nogc` %s `%s`")) + if (setGC(e, "concatenating with operator `~`")) return; f.printGCUsage(e.loc, "operator `~` may cause a GC allocation"); } @@ -201,7 +210,7 @@ public: Expression checkGC(Scope* sc, Expression e) { - if (sc.flags & SCOPE.ctfeBlock) // ignore GC in ctfe blocks + if (sc.ctfeBlock) // ignore GC in ctfe blocks return e; /* If betterC, allow GC to happen in non-CTFE code. @@ -211,13 +220,14 @@ Expression checkGC(Scope* sc, Expression e) const betterC = !global.params.useGC; FuncDeclaration f = sc.func; if (e && e.op != EXP.error && f && sc.intypeof != 1 && - (!(sc.flags & SCOPE.ctfe) || betterC) && + (!sc.ctfe || betterC) && (f.type.ty == Tfunction && - (cast(TypeFunction)f.type).isnogc || f.nogcInprocess || global.params.v.gc) && - !(sc.flags & SCOPE.debug_)) + (cast(TypeFunction)f.type).isNogc || f.nogcInprocess || global.params.v.gc) && + !sc.debug_) { scope NOGCVisitor gcv = new NOGCVisitor(f); gcv.checkOnly = betterC; + gcv.nogcExceptions = sc.previews.dip1008; walkPostorder(e, gcv); if (gcv.err) { @@ -234,6 +244,18 @@ Expression checkGC(Scope* sc, Expression e) return e; } +extern (D) void printGCUsage(FuncDeclaration fd, Loc loc, const(char)* warn) +{ + if (!global.params.v.gc) + return; + + Module m = fd.getModule(); + if (m && m.isRoot() && !fd.inUnittest()) + { + message(loc, "vgc: %s", warn); + } +} + /** * Removes `_d_HookTraceImpl` if found from `fd`. * This is needed to be able to find hooks that are called though the hook's `*Trace` wrapper. @@ -244,7 +266,6 @@ private FuncDeclaration stripHookTraceImpl(FuncDeclaration fd) { import dmd.id : Id; import dmd.dsymbol : Dsymbol; - import dmd.rootobject : RootObject, DYNCAST; if (fd.ident != Id._d_HookTraceImpl) return fd; @@ -256,3 +277,76 @@ private FuncDeclaration stripHookTraceImpl(FuncDeclaration fd) assert(s, "Expected _d_HookTraceImpl's second template parameter to be an alias to the hook!"); return s.isFuncDeclaration; } + +/************************************** + * The function is doing something that may allocate with the GC, + * so mark it as not nogc (not no-how). + * + * Params: + * fd = function + * loc = location of GC action + * fmt = format string for error message. Must include "%s `%s`" for the function kind and name. + * args = arguments to format string + * + * 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...) +{ + //printf("setGC() %s\n", toChars()); + if (fd.nogcInprocess && fd.semanticRun < PASS.semantic3 && fd._scope) + { + fd.semantic2(fd._scope); + fd.semantic3(fd._scope); + } + + if (fd.nogcInprocess) + { + fd.nogcInprocess = false; + if (fmt) + fd.nogcViolation = new AttributeViolation(loc, fmt, args); // action that requires GC + else if (args.length > 0) + { + if (auto sa = args[0].isDsymbol()) + { + if (FuncDeclaration fd2 = sa.isFuncDeclaration()) + { + fd.nogcViolation = new AttributeViolation(loc, fd2); // call to non-@nogc function + } + } + } + + fd.type.toTypeFunction().isNogc = false; + if (fd.fes) + fd.fes.func.setGC(Loc.init, null, null); + } + else if (fd.isNogc()) + return true; + return false; +} + +/************************************** + * The function calls non-`@nogc` function f, mark it as not nogc. + * Params: + * fd = function doin 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) +{ + return fd.setGC(fd.loc, null, f); +} + + bool isNogc(FuncDeclaration fd) +{ + //printf("isNogc() %s, inprocess: %d\n", toChars(), !!(flags & FUNCFLAG.nogcInprocess)); + if (fd.nogcInprocess) + fd.setGC(fd.loc, null); + return fd.type.toTypeFunction().isNogc; +} + +extern (D) bool isNogcBypassingInference(FuncDeclaration fd) +{ + return !fd.nogcInprocess && fd.isNogc(); +} diff --git a/dmd/nspace.d b/dmd/nspace.d index 52c2b79a4e..0c93f0e799 100644 --- a/dmd/nspace.d +++ b/dmd/nspace.d @@ -36,12 +36,12 @@ * are valid D identifier. * * See_Also: https://github.com/dlang/dmd/pull/10031 - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/nspace.d, _nspace.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/nspace.d, _nspace.d) * Documentation: https://dlang.org/phobos/dmd_nspace.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/nspace.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/nspace.d */ module dmd.nspace; @@ -62,10 +62,11 @@ extern (C++) final class Nspace : ScopeDsymbol */ Expression identExp; - extern (D) this(const ref Loc loc, Identifier ident, Expression identExp, Dsymbols* members) + extern (D) this(Loc loc, Identifier ident, Expression identExp, Dsymbols* members) { super(loc, ident); //printf("Nspace::Nspace(ident = %s)\n", ident.toChars()); + this.dsym = DSYM.nspace; this.members = members; this.identExp = identExp; } @@ -88,11 +89,6 @@ extern (C++) final class Nspace : ScopeDsymbol return "namespace"; } - override inout(Nspace) isNspace() inout - { - return this; - } - override void accept(Visitor v) { v.visit(this); diff --git a/dmd/nspace.h b/dmd/nspace.h index cbee2fb791..7b4c302303 100644 --- a/dmd/nspace.h +++ b/dmd/nspace.h @@ -1,6 +1,6 @@ /* Compiler implementation of the D programming language - * Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * written by Walter Bright * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. @@ -23,6 +23,5 @@ class Nspace final : public ScopeDsymbol Nspace *syntaxCopy(Dsymbol *s) override; bool hasPointers() override; const char *kind() const override; - Nspace *isNspace() override { return this; } void accept(Visitor *v) override { v->visit(this); } }; diff --git a/dmd/ob.d b/dmd/ob.d index 756caf870b..099f811589 100644 --- a/dmd/ob.d +++ b/dmd/ob.d @@ -1,14 +1,12 @@ /** * Flow analysis for Ownership/Borrowing * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/ob.d, _ob.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/ob.d, _ob.d) * Documentation: https://dlang.org/phobos/dmd_escape.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/ob.d - * Bug reports: use 'live' keyword: - * https://issues.dlang.org/buglist.cgi?bug_status=NEW&bug_status=REOPENED&keywords=live + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/ob.d * References: https://github.com/dlang/DIPs/blob/master/DIPs/accepted/DIP1021.md Argument Ownership and Function Calls */ @@ -32,7 +30,7 @@ import dmd.dtemplate; import dmd.errors; import dmd.escape; import dmd.expression; -import dmd.foreachvar; + import dmd.func; import dmd.globals; import dmd.hdrgen; @@ -46,6 +44,7 @@ import dmd.stmtstate; import dmd.tokens; import dmd.typesem; import dmd.visitor; +import dmd.visitor.foreachvar; import dmd.root.bitarray; import dmd.common.outbuffer; @@ -380,7 +379,7 @@ void toObNodes(ref ObNodes obnodes, Statement s) return ob; } - // block_goto(blx, BCgoto, null) + // block_goto(blx, BC.goto_, null) ObNode* gotoNextNode() { return gotoNextNodeIs(newNode()); @@ -1337,7 +1336,7 @@ void genKill(ref ObState obstate, ObNode* ob) } } - void dgReadVar(const ref Loc loc, ObNode* ob, VarDeclaration v, bool mutable) + void dgReadVar(Loc loc, ObNode* ob, VarDeclaration v, bool mutable) { if (log) printf("dgReadVar() %s %d\n", v.toChars(), mutable); @@ -1352,12 +1351,12 @@ void genKill(ref ObState obstate, ObNode* ob) { alias visit = typeof(super).visit; extern (D) void delegate(ObNode*, VarDeclaration, Expression, bool) dgWriteVar; - extern (D) void delegate(const ref Loc loc, ObNode* ob, VarDeclaration v, bool mutable) dgReadVar; + extern (D) void delegate(Loc loc, ObNode* ob, VarDeclaration v, bool mutable) dgReadVar; ObNode* ob; ObState* obstate; extern (D) this(void delegate(ObNode*, VarDeclaration, Expression, bool) dgWriteVar, - void delegate(const ref Loc loc, ObNode* ob, VarDeclaration v, bool mutable) dgReadVar, + void delegate(Loc loc, ObNode* ob, VarDeclaration v, bool mutable) dgReadVar, ObNode* ob, ref ObState obstate) scope { this.dgWriteVar = dgWriteVar; @@ -1684,7 +1683,7 @@ void genKill(ref ObState obstate, ObNode* ob) override void visit(ArrayLiteralExp e) { Type tb = e.type.toBasetype(); - if (tb.ty == Tsarray || tb.ty == Tarray) + if (tb.isStaticOrDynamicArray()) { if (e.basis) e.basis.accept(this); @@ -1724,6 +1723,8 @@ void genKill(ref ObState obstate, ObNode* ob) override void visit(NewExp e) { + if (e.placement) + e.placement.accept(this); if (e.arguments) { foreach (ex; *e.arguments) @@ -2001,7 +2002,7 @@ void escapeLive(Expression e, scope void delegate(VarDeclaration) onVar) true, ); - escapeByValue(e, er, true); + escapeByValue(e, er); } /*************************************** @@ -2086,7 +2087,7 @@ void checkObErrors(ref ObState obstate) } } - void dgReadVar(const ref Loc loc, ObNode* ob, VarDeclaration v, bool mutable, PtrVarState[] gen) + void dgReadVar(Loc loc, ObNode* ob, VarDeclaration v, bool mutable, PtrVarState[] gen) { if (log) printf("dgReadVar() %s\n", v.toChars()); const vi = obstate.vars.find(v); @@ -2104,12 +2105,12 @@ void checkObErrors(ref ObState obstate) { alias visit = typeof(super).visit; extern (D) void delegate(ObNode*, PtrVarState[], VarDeclaration, Expression) dgWriteVar; - extern (D) void delegate(const ref Loc loc, ObNode* ob, VarDeclaration v, bool mutable, PtrVarState[]) dgReadVar; + extern (D) void delegate(Loc loc, ObNode* ob, VarDeclaration v, bool mutable, PtrVarState[]) dgReadVar; PtrVarState[] cpvs; ObNode* ob; ObState* obstate; - extern (D) this(void delegate(const ref Loc loc, ObNode* ob, VarDeclaration v, bool mutable, PtrVarState[]) dgReadVar, + extern (D) this(void delegate(Loc loc, ObNode* ob, VarDeclaration v, bool mutable, PtrVarState[]) dgReadVar, void delegate(ObNode*, PtrVarState[], VarDeclaration, Expression) dgWriteVar, PtrVarState[] cpvs, ObNode* ob, ref ObState obstate) scope { @@ -2425,7 +2426,7 @@ void checkObErrors(ref ObState obstate) override void visit(ArrayLiteralExp e) { Type tb = e.type.toBasetype(); - if (tb.ty == Tsarray || tb.ty == Tarray) + if (tb.isStaticOrDynamicArray()) { if (e.basis) e.basis.accept(this); @@ -2465,6 +2466,9 @@ void checkObErrors(ref ObState obstate) override void visit(NewExp e) { + if (e.placement) + e.placement.accept(this); + if (e.arguments) { foreach (ex; *e.arguments) diff --git a/dmd/objc.d b/dmd/objc.d index b5f7ea3961..1163b7d18b 100644 --- a/dmd/objc.d +++ b/dmd/objc.d @@ -3,12 +3,12 @@ * * Specification: $(LINK2 https://dlang.org/spec/objc_interface.html, Interfacing to Objective-C) * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/objc.d, _objc.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/objc.d, _objc.d) * Documentation: https://dlang.org/phobos/dmd_objc.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/objc.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/objc.d */ module dmd.objc; @@ -22,7 +22,6 @@ import dmd.cond; import dmd.dclass; import dmd.declaration; import dmd.denum; -import dmd.dmangle; import dmd.dmodule; import dmd.dscope; import dmd.dstruct; @@ -38,6 +37,7 @@ import dmd.hdrgen; import dmd.id; import dmd.identifier; import dmd.location; +import dmd.mangle; import dmd.mtype; import dmd.root.array; import dmd.common.outbuffer; @@ -93,39 +93,66 @@ struct ObjcSelector return sel; } + static const(char)[] toPascalCase(const(char)[] id) { + OutBuffer buf; + char firstChar = id[0]; + if (firstChar >= 'a' && firstChar <= 'z') + firstChar = cast(char)(firstChar - 'a' + 'A'); + + buf.writeByte(firstChar); + buf.writestring(id[1..$]); + return cast(const(char)[])buf.extractSlice(false); + } + extern (C++) static ObjcSelector* create(FuncDeclaration fdecl) { OutBuffer buf; auto ftype = cast(TypeFunction)fdecl.type; const id = fdecl.ident.toString(); const nparams = ftype.parameterList.length; + // Special case: property setter - if (ftype.isproperty && nparams == 1) + if (ftype.isProperty && nparams == 1) { - // rewrite "identifier" as "setIdentifier" - char firstChar = id[0]; - if (firstChar >= 'a' && firstChar <= 'z') - firstChar = cast(char)(firstChar - 'a' + 'A'); - buf.writestring("set"); - buf.writeByte(firstChar); - buf.write(id[1 .. id.length - 1]); + + // Special case: "isXYZ:" + if (id.length >= 2 && id[0..2] == "is") + { + buf.writestring("set"); + buf.write(toPascalCase(id[2..$])); + } + else + { + buf.writestring("set"); + buf.write(toPascalCase(id)); + } buf.writeByte(':'); goto Lcomplete; } + // write identifier in selector buf.write(id[]); - // add mangled type and colon for each parameter - if (nparams) + + // To make it easier to match the selectors of objects nicely, + // the implementation has been replaced so that the parameter name followed by a colon + // is used instead. + // eg. void myFunction(int a, int b, int c) would be mangled to a selector as `myFunction:b:c: + if (nparams > 1) { - buf.writeByte('_'); - foreach (i, fparam; ftype.parameterList) + buf.writeByte(':'); + foreach(i; 1..nparams) { - mangleToBuffer(fparam.type, buf); + buf.write(ftype.parameterList[i].ident.toString()); buf.writeByte(':'); } } + else if (nparams == 1) + { + buf.writeByte(':'); + } Lcomplete: buf.writeByte('\0'); + // the slice is not expected to include a terminating 0 return lookup(cast(const(char)*)buf[].ptr, buf.length - 1, nparams); } @@ -591,6 +618,16 @@ version (IN_LLVM) {} else return 0; }); + + // Avoid attempting to generate selectors for template instances. + if (fd.parent && fd.parent.isTemplateInstance()) + return; + + // No selector declared, generate one. + if (fd._linkage == LINK.objc && !fd.objc.selector) + { + fd.objc.selector = ObjcSelector.create(fd); + } } override void validateSelector(FuncDeclaration fd) @@ -703,8 +740,8 @@ version (IN_LLVM) {} else { if (cd.classKind == ClassKind.objc && fd.isStatic && !cd.objc.isMeta) return cd.objc.metaclass; - else - return cd; + + return cd; } override void addToClassMethodList(FuncDeclaration fd, ClassDeclaration cd) const @@ -794,11 +831,10 @@ version (IN_LLVM) {} else { if (classDeclaration.baseClass) return getRuntimeMetaclass(classDeclaration.baseClass); - else - return classDeclaration; + + return classDeclaration; } - else - return classDeclaration.objc.metaclass; + return classDeclaration.objc.metaclass; } override void addSymbols(AttribDeclaration attribDeclaration, diff --git a/dmd/objc.h b/dmd/objc.h index 03b72dec4e..c0bff98e6a 100644 --- a/dmd/objc.h +++ b/dmd/objc.h @@ -1,6 +1,6 @@ /* Compiler implementation of the D programming language - * Copyright (C) 2015-2024 by The D Language Foundation, All Rights Reserved + * Copyright (C) 2015-2025 by The D Language Foundation, All Rights Reserved * written by Michel Fortin * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. diff --git a/dmd/opover.d b/dmd/opover.d index 0d32d7d971..7baaeaa7fc 100644 --- a/dmd/opover.d +++ b/dmd/opover.d @@ -3,12 +3,12 @@ * * Specification: $(LINK2 https://dlang.org/spec/operatoroverloading.html, Operator Overloading) * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/opover.d, _opover.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/opover.d, _opover.d) * Documentation: https://dlang.org/phobos/dmd_opover.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/opover.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/opover.d */ module dmd.opover; @@ -71,81 +71,54 @@ bool isCommutative(EXP op) @safe return false; } -/*********************************** - * Get Identifier for operator overload. - */ -private Identifier opId(Expression e) +/// Returns: whether `op` can be overloaded with `opBinary` +private bool hasOpBinary(EXP op) pure @safe { - switch (e.op) + switch (op) { - case EXP.uadd: return Id.uadd; - case EXP.negate: return Id.neg; - case EXP.tilde: return Id.com; - case EXP.cast_: return Id._cast; - case EXP.in_: return Id.opIn; - case EXP.plusPlus: return Id.postinc; - case EXP.minusMinus: return Id.postdec; - case EXP.add: return Id.add; - case EXP.min: return Id.sub; - case EXP.mul: return Id.mul; - case EXP.div: return Id.div; - case EXP.mod: return Id.mod; - case EXP.pow: return Id.pow; - case EXP.leftShift: return Id.shl; - case EXP.rightShift: return Id.shr; - case EXP.unsignedRightShift: return Id.ushr; - case EXP.and: return Id.iand; - case EXP.or: return Id.ior; - case EXP.xor: return Id.ixor; - case EXP.concatenate: return Id.cat; - case EXP.assign: return Id.assign; - case EXP.addAssign: return Id.addass; - case EXP.minAssign: return Id.subass; - case EXP.mulAssign: return Id.mulass; - case EXP.divAssign: return Id.divass; - case EXP.modAssign: return Id.modass; - case EXP.powAssign: return Id.powass; - case EXP.leftShiftAssign: return Id.shlass; - case EXP.rightShiftAssign: return Id.shrass; - case EXP.unsignedRightShiftAssign: return Id.ushrass; - case EXP.andAssign: return Id.andass; - case EXP.orAssign: return Id.orass; - case EXP.xorAssign: return Id.xorass; - case EXP.concatenateAssign: return Id.catass; - case EXP.equal: return Id.eq; - case EXP.lessThan: - case EXP.lessOrEqual: - case EXP.greaterThan: - case EXP.greaterOrEqual: return Id.cmp; - case EXP.array: return Id.index; - case EXP.star: return Id.opStar; - default: assert(0); + case EXP.add: return true; + case EXP.min: return true; + case EXP.mul: return true; + case EXP.div: return true; + case EXP.mod: return true; + case EXP.and: return true; + case EXP.or: return true; + case EXP.xor: return true; + case EXP.leftShift: return true; + case EXP.rightShift: return true; + case EXP.unsignedRightShift: return true; + case EXP.concatenate: return true; + case EXP.pow: return true; + case EXP.in_: return true; + default: return false; } } -/*********************************** - * Get Identifier for reverse operator overload, - * `null` if not supported for this operator. - */ -private Identifier opId_r(Expression e) +/** + * Remove the = from op=, e.g. += becomes + + * + * Params: + * op = tag for a binary assign operator + * Returns: the corresponding binary operator, or `op` if it wasn't an assign operator +*/ +private EXP stripAssignOp(EXP op) { - switch (e.op) + switch (op) { - case EXP.in_: return Id.opIn_r; - case EXP.add: return Id.add_r; - case EXP.min: return Id.sub_r; - case EXP.mul: return Id.mul_r; - case EXP.div: return Id.div_r; - case EXP.mod: return Id.mod_r; - case EXP.pow: return Id.pow_r; - case EXP.leftShift: return Id.shl_r; - case EXP.rightShift: return Id.shr_r; - case EXP.unsignedRightShift:return Id.ushr_r; - case EXP.and: return Id.iand_r; - case EXP.or: return Id.ior_r; - case EXP.xor: return Id.ixor_r; - case EXP.concatenate: return Id.cat_r; - default: return null; + case EXP.addAssign: return EXP.add; + case EXP.minAssign: return EXP.min; + case EXP.mulAssign: return EXP.mul; + case EXP.divAssign: return EXP.div; + case EXP.modAssign: return EXP.mod; + case EXP.andAssign: return EXP.and; + case EXP.orAssign: return EXP.or; + case EXP.xorAssign: return EXP.xor; + case EXP.leftShiftAssign: return EXP.leftShift; + case EXP.rightShiftAssign: return EXP.rightShift; + case EXP.unsignedRightShiftAssign: return EXP.unsignedRightShift; + case EXP.concatenateAssign: return EXP.concatenate; + case EXP.powAssign: return EXP.pow; + default: return op; } } @@ -154,53 +127,7 @@ private Identifier opId_r(Expression e) */ Objects* opToArg(Scope* sc, EXP op) { - /* Remove the = from op= - */ - switch (op) - { - case EXP.addAssign: - op = EXP.add; - break; - case EXP.minAssign: - op = EXP.min; - break; - case EXP.mulAssign: - op = EXP.mul; - break; - case EXP.divAssign: - op = EXP.div; - break; - case EXP.modAssign: - op = EXP.mod; - break; - case EXP.andAssign: - op = EXP.and; - break; - case EXP.orAssign: - op = EXP.or; - break; - case EXP.xorAssign: - op = EXP.xor; - break; - case EXP.leftShiftAssign: - op = EXP.leftShift; - break; - case EXP.rightShiftAssign: - op = EXP.rightShift; - break; - case EXP.unsignedRightShiftAssign: - op = EXP.unsignedRightShift; - break; - case EXP.concatenateAssign: - op = EXP.concatenate; - break; - case EXP.powAssign: - op = EXP.pow; - break; - default: - break; - } - Expression e = new StringExp(Loc.initial, EXPtoString(op)); + Expression e = new StringExp(Loc.initial, EXPtoString(stripAssignOp(op))); e = e.expressionSemantic(sc); auto tiargs = new Objects(); tiargs.push(e); @@ -208,7 +135,7 @@ Objects* opToArg(Scope* sc, EXP op) } // Try alias this on first operand -private Expression checkAliasThisForLhs(AggregateDeclaration ad, Scope* sc, BinExp e) +Expression checkAliasThisForLhs(AggregateDeclaration ad, Scope* sc, BinExp e, Type[2] aliasThisStop) { if (!ad || !ad.aliasthis) return null; @@ -216,35 +143,29 @@ private Expression checkAliasThisForLhs(AggregateDeclaration ad, Scope* sc, BinE /* Rewrite (e1 op e2) as: * (e1.aliasthis op e2) */ - if (isRecursiveAliasThis(e.att1, e.e1.type)) + if (isRecursiveAliasThis(aliasThisStop[0], e.e1.type)) return null; //printf("att %s e1 = %s\n", Token.toChars(e.op), e.e1.type.toChars()); BinExp be = cast(BinExp)e.copy(); // Resolve 'alias this' but in case of assigment don't resolve properties yet // because 'e1 = e2' could mean 'e1(e2)' or 'e1() = e2' - bool findOnly = (e.op == EXP.assign); + bool findOnly = e.isAssignExp() !is null; be.e1 = resolveAliasThis(sc, e.e1, true, findOnly); if (!be.e1) return null; - Expression result; - if (be.op == EXP.concatenateAssign) - result = be.op_overload(sc); - else - result = be.trySemantic(sc); - - return result; + return be.trySemanticAliasThis(sc, aliasThisStop); } // Try alias this on second operand -private Expression checkAliasThisForRhs(AggregateDeclaration ad, Scope* sc, BinExp e) +Expression checkAliasThisForRhs(AggregateDeclaration ad, Scope* sc, BinExp e, Type[2] aliasThisStop) { if (!ad || !ad.aliasthis) return null; /* Rewrite (e1 op e2) as: * (e1 op e2.aliasthis) */ - if (isRecursiveAliasThis(e.att2, e.e2.type)) + if (isRecursiveAliasThis(aliasThisStop[1], e.e2.type)) return null; //printf("att %s e2 = %s\n", Token.toChars(e.op), e.e2.type.toChars()); BinExp be = cast(BinExp)e.copy(); @@ -252,1168 +173,995 @@ private Expression checkAliasThisForRhs(AggregateDeclaration ad, Scope* sc, BinE if (!be.e2) return null; - Expression result; - if (be.op == EXP.concatenateAssign) - result = be.op_overload(sc); - else - result = be.trySemantic(sc); - - return result; + return be.trySemanticAliasThis(sc, aliasThisStop); } -/************************************ - * Operator overload. - * Check for operator overload, if so, replace - * with function call. - * Params: - * e = expression with operator - * sc = context - * pop = if not null, is set to the operator that was actually overloaded, - * which may not be `e.op`. Happens when operands are reversed to - * match an overload - * Returns: - * `null` if not an operator overload, - * otherwise the lowered expression - */ -Expression op_overload(Expression e, Scope* sc, EXP* pop = null) +Expression opOverloadUnary(UnaExp e, Scope* sc) { - Expression visit(Expression e) + if (auto ae = e.e1.isArrayExp()) + { + ae.e1 = ae.e1.expressionSemantic(sc); + ae.e1 = resolveProperties(sc, ae.e1); + Expression ae1old = ae.e1; + const(bool) maybeSlice = (ae.arguments.length == 0 || ae.arguments.length == 1 && (*ae.arguments)[0].isIntervalExp()); + IntervalExp ie = null; + if (maybeSlice && ae.arguments.length) { - assert(0); + ie = (*ae.arguments)[0].isIntervalExp(); } - - Expression visitUna(UnaExp e) + Type att = null; // first cyclic `alias this` type + while (true) { - //printf("UnaExp::op_overload() (%s)\n", e.toChars()); - Expression result; - if (auto ae = e.e1.isArrayExp()) + if (ae.e1.isErrorExp()) { - ae.e1 = ae.e1.expressionSemantic(sc); - ae.e1 = resolveProperties(sc, ae.e1); - Expression ae1old = ae.e1; - const(bool) maybeSlice = (ae.arguments.length == 0 || ae.arguments.length == 1 && (*ae.arguments)[0].op == EXP.interval); - IntervalExp ie = null; - if (maybeSlice && ae.arguments.length) - { - ie = (*ae.arguments)[0].isIntervalExp(); - } - Type att = null; // first cyclic `alias this` type - while (true) - { - if (ae.e1.op == EXP.error) - { - return ae.e1; - } - Expression e0 = null; - Expression ae1save = ae.e1; - ae.lengthVar = null; - Type t1b = ae.e1.type.toBasetype(); - AggregateDeclaration ad = isAggregate(t1b); - if (!ad) - break; - if (search_function(ad, Id.opIndexUnary)) - { - // Deal with $ - result = resolveOpDollar(sc, ae, &e0); - if (!result) // op(a[i..j]) might be: a.opSliceUnary!(op)(i, j) - goto Lfallback; - if (result.op == EXP.error) - return result; - /* Rewrite op(a[arguments]) as: - * a.opIndexUnary!(op)(arguments) - */ - Expressions* a = ae.arguments.copy(); - Objects* tiargs = opToArg(sc, e.op); - result = new DotTemplateInstanceExp(e.loc, ae.e1, Id.opIndexUnary, tiargs); - result = new CallExp(e.loc, result, a); - if (maybeSlice) // op(a[]) might be: a.opSliceUnary!(op)() - result = result.trySemantic(sc); - else - result = result.expressionSemantic(sc); - if (result) - { - return Expression.combine(e0, result); - } - } - Lfallback: - if (maybeSlice && search_function(ad, Id.opSliceUnary)) - { - // Deal with $ - result = resolveOpDollar(sc, ae, ie, &e0); - if (result.op == EXP.error) - return result; - /* Rewrite op(a[i..j]) as: - * a.opSliceUnary!(op)(i, j) - */ - auto a = new Expressions(); - if (ie) - { - a.push(ie.lwr); - a.push(ie.upr); - } - Objects* tiargs = opToArg(sc, e.op); - result = new DotTemplateInstanceExp(e.loc, ae.e1, Id.opSliceUnary, tiargs); - result = new CallExp(e.loc, result, a); - result = result.expressionSemantic(sc); - result = Expression.combine(e0, result); - return result; - } - // Didn't find it. Forward to aliasthis - if (ad.aliasthis && !isRecursiveAliasThis(att, ae.e1.type)) - { - /* Rewrite op(a[arguments]) as: - * op(a.aliasthis[arguments]) - */ - ae.e1 = resolveAliasThis(sc, ae1save, true); - if (ae.e1) - continue; - } - break; - } - ae.e1 = ae1old; // recovery - ae.lengthVar = null; + return ae.e1; } - e.e1 = e.e1.expressionSemantic(sc); - e.e1 = resolveProperties(sc, e.e1); - Type att = null; // first cyclic `alias this` type - while (1) - { - if (e.e1.op == EXP.error) - { - return e.e1; - } + Expression ae1save = ae.e1; + ae.lengthVar = null; - AggregateDeclaration ad = isAggregate(e.e1.type); - if (!ad) - break; + AggregateDeclaration ad = isAggregate(ae.e1.type); + if (!ad) + break; - Dsymbol fd = null; - /* Rewrite as: - * e1.opUnary!(op)() + if (search_function(ad, Id.opIndexUnary)) + { + Expression e0; + // Deal with $ + Expression ae2 = resolveOpDollar(sc, ae, e0); + if (!ae2) // op(a[i..j]) might be: a.opSliceUnary!(op)(i, j) + goto Lfallback; + if (ae2.isErrorExp()) + return ae2; + /* Rewrite op(a[arguments]) as: + * a.opIndexUnary!(op)(arguments) */ - fd = search_function(ad, Id.opUnary); - if (fd) - { - Objects* tiargs = opToArg(sc, e.op); - result = new DotTemplateInstanceExp(e.loc, e.e1, fd.ident, tiargs); - result = new CallExp(e.loc, result); + Expression result = dotTemplateCall(ae.e1, Id.opIndexUnary, opToArg(sc, e.op), (*ae.arguments)[]); + if (maybeSlice) // op(a[]) might be: a.opSliceUnary!(op)() + result = result.trySemantic(sc); + else result = result.expressionSemantic(sc); - return result; - } - // D1-style operator overloads, deprecated - if (e.op != EXP.prePlusPlus && e.op != EXP.preMinusMinus) - { - auto id = opId(e); - fd = search_function(ad, id); - if (fd) - { - // @@@DEPRECATED_2.110@@@. - // Deprecated in 2.088, made an error in 2.100 - error(e.loc, "`%s` is obsolete. Use `opUnary(string op)() if (op == \"%s\")` instead.", id.toChars(), EXPtoString(e.op).ptr); - return ErrorExp.get(); - } - } - // Didn't find it. Forward to aliasthis - if (ad.aliasthis && !isRecursiveAliasThis(att, e.e1.type)) - { - /* Rewrite op(e1) as: - * op(e1.aliasthis) - */ - //printf("att una %s e1 = %s\n", EXPtoString(op).ptr, this.e1.type.toChars()); - if (auto e1 = resolveAliasThis(sc, e.e1, true)) - { - e.e1 = e1; - continue; - } - break; - } - break; - } - return result; - } - Expression visitArray(ArrayExp ae) - { - //printf("ArrayExp::op_overload() (%s)\n", ae.toChars()); - ae.e1 = ae.e1.expressionSemantic(sc); - ae.e1 = resolveProperties(sc, ae.e1); - Expression ae1old = ae.e1; - const(bool) maybeSlice = (ae.arguments.length == 0 || ae.arguments.length == 1 && (*ae.arguments)[0].op == EXP.interval); - IntervalExp ie = null; - if (maybeSlice && ae.arguments.length) - { - ie = (*ae.arguments)[0].isIntervalExp(); + if (result) + return Expression.combine(e0, result); } - Expression result; - Type att = null; // first cyclic `alias this` type - while (true) + Lfallback: + if (maybeSlice && search_function(ad, Id.opSliceUnary)) { - if (ae.e1.op == EXP.error) - { - return ae.e1; - } - Expression e0 = null; - Expression ae1save = ae.e1; - ae.lengthVar = null; - Type t1b = ae.e1.type.toBasetype(); - AggregateDeclaration ad = isAggregate(t1b); - if (!ad) - { - // If the non-aggregate expression ae.e1 is indexable or sliceable, - // convert it to the corresponding concrete expression. - if (isIndexableNonAggregate(t1b) || ae.e1.op == EXP.type) - { - // Convert to SliceExp - if (maybeSlice) - { - result = new SliceExp(ae.loc, ae.e1, ie); - result = result.expressionSemantic(sc); - return result; - } - // Convert to IndexExp - if (ae.arguments.length == 1) - { - result = new IndexExp(ae.loc, ae.e1, (*ae.arguments)[0]); - result = result.expressionSemantic(sc); - return result; - } - } - break; - } - if (search_function(ad, Id.index)) - { - // Deal with $ - result = resolveOpDollar(sc, ae, &e0); - if (!result) // a[i..j] might be: a.opSlice(i, j) - goto Lfallback; - if (result.op == EXP.error) - return result; - /* Rewrite e1[arguments] as: - * e1.opIndex(arguments) - */ - Expressions* a = ae.arguments.copy(); - result = new DotIdExp(ae.loc, ae.e1, Id.index); - result = new CallExp(ae.loc, result, a); - if (maybeSlice) // a[] might be: a.opSlice() - result = result.trySemantic(sc); - else - result = result.expressionSemantic(sc); - if (result) - { - return Expression.combine(e0, result); - } - } - Lfallback: - if (maybeSlice && ae.e1.op == EXP.type) - { - result = new SliceExp(ae.loc, ae.e1, ie); - result = result.expressionSemantic(sc); - result = Expression.combine(e0, result); - return result; - } - if (maybeSlice && search_function(ad, Id.slice)) - { - // Deal with $ - result = resolveOpDollar(sc, ae, ie, &e0); + // Deal with $ + Expression e0; + auto ae2 = resolveOpDollar(sc, ae, ie, e0); + if (ae2.isErrorExp()) + return ae2; + /* Rewrite op(a[i..j]) as: + * a.opSliceUnary!(op)(i, j) + */ + Expression result = ie ? + dotTemplateCall(ae.e1, Id.opSliceUnary, opToArg(sc, e.op), ie.lwr, ie.upr) : + dotTemplateCall(ae.e1, Id.opSliceUnary, opToArg(sc, e.op)); - if (result.op == EXP.error) - { - if (!e0 && !search_function(ad, Id.dollar)) { - ae.loc.errorSupplemental("Aggregate declaration '%s' does not define 'opDollar'", ae.e1.toChars()); - } - return result; - } - /* Rewrite a[i..j] as: - * a.opSlice(i, j) - */ - auto a = new Expressions(); - if (ie) - { - a.push(ie.lwr); - a.push(ie.upr); - } - result = new DotIdExp(ae.loc, ae.e1, Id.slice); - result = new CallExp(ae.loc, result, a); - result = result.expressionSemantic(sc); - result = Expression.combine(e0, result); - return result; - } - // Didn't find it. Forward to aliasthis - if (ad.aliasthis && !isRecursiveAliasThis(att, ae.e1.type)) - { - //printf("att arr e1 = %s\n", this.e1.type.toChars()); - /* Rewrite op(a[arguments]) as: - * op(a.aliasthis[arguments]) - */ - ae.e1 = resolveAliasThis(sc, ae1save, true); - if (ae.e1) - continue; - } - break; + return Expression.combine(e0, result.expressionSemantic(sc)); } - ae.e1 = ae1old; // recovery - ae.lengthVar = null; - return result; - } - - /*********************************************** - * This is mostly the same as UnaryExp::op_overload(), but has - * a different rewrite. - */ - Expression visitCast(CastExp e, Type att = null) - { - //printf("CastExp::op_overload() (%s)\n", e.toChars()); - Expression result; - AggregateDeclaration ad = isAggregate(e.e1.type); - if (ad) + // Didn't find it. Forward to aliasthis + if (ad.aliasthis && !isRecursiveAliasThis(att, ae.e1.type)) { - Dsymbol fd = null; - /* Rewrite as: - * e1.opCast!(T)() + /* Rewrite op(a[arguments]) as: + * op(a.aliasthis[arguments]) */ - fd = search_function(ad, Id._cast); - if (fd) - { - version (all) - { - // Backwards compatibility with D1 if opCast is a function, not a template - if (fd.isFuncDeclaration()) - { - // Rewrite as: e1.opCast() - return build_overload(e.loc, sc, e.e1, null, fd); - } - } - auto tiargs = new Objects(); - tiargs.push(e.to); - result = new DotTemplateInstanceExp(e.loc, e.e1, fd.ident, tiargs); - result = new CallExp(e.loc, result); - result = result.expressionSemantic(sc); - return result; - } - // Didn't find it. Forward to aliasthis - if (ad.aliasthis && !isRecursiveAliasThis(att, e.e1.type)) - { - /* Rewrite op(e1) as: - * op(e1.aliasthis) - */ - if (auto e1 = resolveAliasThis(sc, e.e1, true)) - { - result = e.copy(); - (cast(UnaExp)result).e1 = e1; - result = visitCast(result.isCastExp(), att); - return result; - } - } + ae.e1 = resolveAliasThis(sc, ae1save, true); + if (ae.e1) + continue; } - return result; + break; + } + ae.e1 = ae1old; // recovery + ae.lengthVar = null; + } + e.e1 = e.e1.expressionSemantic(sc); + e.e1 = resolveProperties(sc, e.e1); + Type att = null; // first cyclic `alias this` type + while (1) + { + if (e.e1.isErrorExp()) + { + return e.e1; } - Expression visitBin(BinExp e) + AggregateDeclaration ad = isAggregate(e.e1.type); + if (!ad) + break; + + /* Rewrite as: + * e1.opUnary!(op)() + */ + if (Dsymbol fd = search_function(ad, Id.opUnary)) + return dotTemplateCall(e.e1, Id.opUnary, opToArg(sc, e.op)).expressionSemantic(sc); + + // Didn't find it. Forward to aliasthis + if (ad.aliasthis && !isRecursiveAliasThis(att, e.e1.type)) { - //printf("BinExp::op_overload() (%s)\n", e.toChars()); - Identifier id = opId(e); - Identifier id_r = opId_r(e); - int argsset = 0; - AggregateDeclaration ad1 = isAggregate(e.e1.type); - AggregateDeclaration ad2 = isAggregate(e.e2.type); - if (e.op == EXP.assign && ad1 == ad2) - { - StructDeclaration sd = ad1.isStructDeclaration(); - if (sd && - (!sd.hasIdentityAssign || - /* Do a blit if we can and the rvalue is something like .init, - * where a postblit is not necessary. - */ - (sd.hasBlitAssign && !e.e2.isLvalue()))) - { - /* This is bitwise struct assignment. */ - return null; - } - } - Dsymbol s = null; - Dsymbol s_r = null; - Objects* tiargs = null; - if (e.op == EXP.plusPlus || e.op == EXP.minusMinus) - { - // Bug4099 fix - if (ad1 && search_function(ad1, Id.opUnary)) - return null; - } - if (e.op != EXP.equal && e.op != EXP.notEqual && e.op != EXP.assign && e.op != EXP.plusPlus && e.op != EXP.minusMinus) - { - /* Try opBinary and opBinaryRight - */ - if (ad1) - { - s = search_function(ad1, Id.opBinary); - if (s && !s.isTemplateDeclaration()) - { - error(e.e1.loc, "`%s.opBinary` isn't a template", e.e1.toChars()); - return ErrorExp.get(); - } - } - if (ad2) - { - s_r = search_function(ad2, Id.opBinaryRight); - if (s_r && !s_r.isTemplateDeclaration()) - { - error(e.e2.loc, "`%s.opBinaryRight` isn't a template", e.e2.toChars()); - return ErrorExp.get(); - } - if (s_r && s_r == s) // https://issues.dlang.org/show_bug.cgi?id=12778 - s_r = null; - } - // Set tiargs, the template argument list, which will be the operator string - if (s || s_r) - { - id = Id.opBinary; - id_r = Id.opBinaryRight; - tiargs = opToArg(sc, e.op); - } - } - if (!s && !s_r) - { - // Try the D1-style operators, deprecated - if (ad1 && id) - { - s = search_function(ad1, id); - if (s && id != Id.assign) - { - // @@@DEPRECATED_2.110@@@. - // Deprecated in 2.088, made an error in 2.100 - if (id == Id.postinc || id == Id.postdec) - error(e.loc, "`%s` is obsolete. Use `opUnary(string op)() if (op == \"%s\")` instead.", id.toChars(), EXPtoString(e.op).ptr); - else - error(e.loc, "`%s` is obsolete. Use `opBinary(string op)(...) if (op == \"%s\")` instead.", id.toChars(), EXPtoString(e.op).ptr); - return ErrorExp.get(); - } - } - if (ad2 && id_r) - { - s_r = search_function(ad2, id_r); - // https://issues.dlang.org/show_bug.cgi?id=12778 - // If both x.opBinary(y) and y.opBinaryRight(x) found, - // and they are exactly same symbol, x.opBinary(y) should be preferred. - if (s_r && s_r == s) - s_r = null; - if (s_r) - { - // @@@DEPRECATED_2.110@@@. - // Deprecated in 2.088, made an error in 2.100 - error(e.loc, "`%s` is obsolete. Use `opBinaryRight(string op)(...) if (op == \"%s\")` instead.", id_r.toChars(), EXPtoString(e.op).ptr); - return ErrorExp.get(); - } - } - } - Expressions* args1 = new Expressions(); - Expressions* args2 = new Expressions(); - if (s || s_r) - { - /* Try: - * a.opfunc(b) - * b.opfunc_r(a) - * and see which is better. - */ - args1.setDim(1); - (*args1)[0] = e.e1; - expandTuples(args1); - args2.setDim(1); - (*args2)[0] = e.e2; - expandTuples(args2); - argsset = 1; - MatchAccumulator m; - if (s) - { - functionResolve(m, s, e.loc, sc, tiargs, e.e1.type, ArgumentList(args2)); - if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors())) - { - return ErrorExp.get(); - } - } - FuncDeclaration lastf = m.lastf; - if (s_r) - { - functionResolve(m, s_r, e.loc, sc, tiargs, e.e2.type, ArgumentList(args1)); - if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors())) - { - return ErrorExp.get(); - } - } - if (m.count > 1) - { - // Error, ambiguous - error(e.loc, "overloads `%s` and `%s` both match argument list for `%s`", m.lastf.type.toChars(), m.nextf.type.toChars(), m.lastf.toChars()); - } - else if (m.last == MATCH.nomatch) - { - if (tiargs) - goto L1; - m.lastf = null; - } - if (e.op == EXP.plusPlus || e.op == EXP.minusMinus) - { - // Kludge because operator overloading regards e++ and e-- - // as unary, but it's implemented as a binary. - // Rewrite (e1 ++ e2) as e1.postinc() - // Rewrite (e1 -- e2) as e1.postdec() - return build_overload(e.loc, sc, e.e1, null, m.lastf ? m.lastf : s); - } - else if (lastf && m.lastf == lastf || !s_r && m.last == MATCH.nomatch) - { - // Rewrite (e1 op e2) as e1.opfunc(e2) - return build_overload(e.loc, sc, e.e1, e.e2, m.lastf ? m.lastf : s); - } - else - { - // Rewrite (e1 op e2) as e2.opfunc_r(e1) - return build_overload(e.loc, sc, e.e2, e.e1, m.lastf ? m.lastf : s_r); - } - } - L1: - version (all) + /* Rewrite op(e1) as: + * op(e1.aliasthis) + */ + //printf("att una %s e1 = %s\n", EXPtoString(op).ptr, this.e1.type.toChars()); + if (auto e1 = resolveAliasThis(sc, e.e1, true)) { - // Retained for D1 compatibility - if (isCommutative(e.op) && !tiargs) - { - s = null; - s_r = null; - if (ad1 && id_r) - { - s_r = search_function(ad1, id_r); - } - if (ad2 && id) - { - s = search_function(ad2, id); - if (s && s == s_r) // https://issues.dlang.org/show_bug.cgi?id=12778 - s = null; - } - if (s || s_r) - { - /* Try: - * a.opfunc_r(b) - * b.opfunc(a) - * and see which is better. - */ - if (!argsset) - { - args1.setDim(1); - (*args1)[0] = e.e1; - expandTuples(args1); - args2.setDim(1); - (*args2)[0] = e.e2; - expandTuples(args2); - } - MatchAccumulator m; - if (s_r) - { - functionResolve(m, s_r, e.loc, sc, tiargs, e.e1.type, ArgumentList(args2)); - if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors())) - { - return ErrorExp.get(); - } - } - FuncDeclaration lastf = m.lastf; - if (s) - { - functionResolve(m, s, e.loc, sc, tiargs, e.e2.type, ArgumentList(args1)); - if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors())) - { - return ErrorExp.get(); - } - } - if (m.count > 1) - { - // Error, ambiguous - error(e.loc, "overloads `%s` and `%s` both match argument list for `%s`", m.lastf.type.toChars(), m.nextf.type.toChars(), m.lastf.toChars()); - } - else if (m.last == MATCH.nomatch) - { - m.lastf = null; - } - - if (lastf && m.lastf == lastf || !s && m.last == MATCH.nomatch) - { - // Rewrite (e1 op e2) as e1.opfunc_r(e2) - return build_overload(e.loc, sc, e.e1, e.e2, m.lastf ? m.lastf : s_r); - } - else - { - // Rewrite (e1 op e2) as e2.opfunc(e1) - Expression result = build_overload(e.loc, sc, e.e2, e.e1, m.lastf ? m.lastf : s); - // When reversing operands of comparison operators, - // need to reverse the sense of the op - if (pop) - *pop = reverseRelation(e.op); - return result; - } - } - } + e.e1 = e1; + continue; } + break; + } - Expression rewrittenLhs; - if (!(e.op == EXP.assign && ad2 && ad1 == ad2)) // https://issues.dlang.org/show_bug.cgi?id=2943 - { - if (Expression result = checkAliasThisForLhs(ad1, sc, e)) - { - /* https://issues.dlang.org/show_bug.cgi?id=19441 - * - * alias this may not be used for partial assignment. - * If a struct has a single member which is aliased this - * directly or aliased to a ref getter function that returns - * the mentioned member, then alias this may be - * used since the object will be fully initialised. - * If the struct is nested, the context pointer is considered - * one of the members, hence the `ad1.fields.length == 2 && ad1.vthis` - * condition. - */ - if (result.op != EXP.assign) - return result; // i.e: Rewrote `e1 = e2` -> `e1(e2)` + // For ++ and --, rewrites to += and -= are also tried, so don't error yet + if (!e.isPreExp()) + { + error(e.loc, "operator `%s` is not defined for `%s`", EXPtoString(e.op).ptr, ad.toChars()); + errorSupplemental(ad.loc, "perhaps overload the operator with `auto opUnary(string op : \"%s\")() {}`", + EXPtoString(e.op).ptr); + return ErrorExp.get(); + } - auto ae = result.isAssignExp(); - if (ae.e1.op != EXP.dotVariable) - return result; // i.e: Rewrote `e1 = e2` -> `e1() = e2` + break; + } + return null; +} - auto dve = ae.e1.isDotVarExp(); - if (auto ad = dve.var.isMember2()) - { - // i.e: Rewrote `e1 = e2` -> `e1.some.var = e2` - // Ensure that `var` is the only field member in `ad` - if (ad.fields.length == 1 || (ad.fields.length == 2 && ad.vthis)) - { - if (dve.var == ad.aliasthis.sym) - return result; - } - } - rewrittenLhs = ae.e1; - } - } - if (!(e.op == EXP.assign && ad1 && ad1 == ad2)) // https://issues.dlang.org/show_bug.cgi?id=2943 - { - if (Expression result = checkAliasThisForRhs(ad2, sc, e)) - return result; - } - if (rewrittenLhs) +Expression opOverloadArray(ArrayExp ae, Scope* sc) +{ + ae.e1 = ae.e1.expressionSemantic(sc); + ae.e1 = resolveProperties(sc, ae.e1); + Expression ae1old = ae.e1; + const(bool) maybeSlice = (ae.arguments.length == 0 || ae.arguments.length == 1 && (*ae.arguments)[0].isIntervalExp()); + IntervalExp ie = null; + if (maybeSlice && ae.arguments.length) + { + ie = (*ae.arguments)[0].isIntervalExp(); + } + Type att = null; // first cyclic `alias this` type + while (true) + { + if (ae.e1.isErrorExp()) + { + return ae.e1; + } + Expression e0 = null; + Expression ae1save = ae.e1; + ae.lengthVar = null; + Type t1b = ae.e1.type.toBasetype(); + AggregateDeclaration ad = isAggregate(t1b); + if (!ad) + { + // If the non-aggregate expression ae.e1 is indexable or sliceable, + // convert it to the corresponding concrete expression. + if (isIndexableNonAggregate(t1b) || ae.e1.isTypeExp()) { - error(e.loc, "cannot use `alias this` to partially initialize variable `%s` of type `%s`. Use `%s`", - e.e1.toChars(), ad1.toChars(), rewrittenLhs.toChars()); - return ErrorExp.get(); + // Convert to SliceExp + if (maybeSlice) + return new SliceExp(ae.loc, ae.e1, ie).expressionSemantic(sc); + + // Convert to IndexExp + if (ae.arguments.length == 1) + return new IndexExp(ae.loc, ae.e1, (*ae.arguments)[0]).expressionSemantic(sc); } - return null; + break; } + if (search_function(ad, Id.opIndex)) + { + // Deal with $ + auto ae2 = resolveOpDollar(sc, ae, e0); + if (!ae2) // a[i..j] might be: a.opSlice(i, j) + goto Lfallback; + if (ae2.isErrorExp()) + return ae2; + /* Rewrite e1[arguments] as: + * e1.opIndex(arguments) + */ + Expressions* a = ae.arguments.copy(); + Expression result = new DotIdExp(ae.loc, ae.e1, Id.opIndex); + result = new CallExp(ae.loc, result, a); + if (maybeSlice) // a[] might be: a.opSlice() + result = result.trySemantic(sc); + else + result = result.expressionSemantic(sc); - Expression visitEqual(EqualExp e) + if (result) + return Expression.combine(e0, result); + } + Lfallback: + if (maybeSlice && ae.e1.isTypeExp()) { - //printf("EqualExp::op_overload() (%s)\n", e.toChars()); - Type t1 = e.e1.type.toBasetype(); - Type t2 = e.e2.type.toBasetype(); + Expression result = new SliceExp(ae.loc, ae.e1, ie); + result = result.expressionSemantic(sc); + return Expression.combine(e0, result); + } + if (maybeSlice && search_function(ad, Id.opSlice)) + { + // Deal with $ + auto ae2 = resolveOpDollar(sc, ae, ie, e0); - /* Array equality is handled by expressionSemantic() potentially - * lowering to object.__equals(), which takes care of overloaded - * operators for the element types. - */ - if ((t1.ty == Tarray || t1.ty == Tsarray) && - (t2.ty == Tarray || t2.ty == Tsarray)) + if (ae2.isErrorExp()) { - return null; - } + if (!e0 && !search_function(ad, Id.dollar)) + ad.loc.errorSupplemental("perhaps define `opDollar` for `%s`", ad.toChars()); - /* Check for class equality with null literal or typeof(null). - */ - if (t1.ty == Tclass && e.e2.op == EXP.null_ || - t2.ty == Tclass && e.e1.op == EXP.null_) - { - error(e.loc, "use `%s` instead of `%s` when comparing with `null`", - EXPtoString(e.op == EXP.equal ? EXP.identity : EXP.notIdentity).ptr, - EXPtoString(e.op).ptr); - return ErrorExp.get(); - } - if (t1.ty == Tclass && t2.ty == Tnull || - t1.ty == Tnull && t2.ty == Tclass) - { - // Comparing a class with typeof(null) should not call opEquals - return null; + return ae2; } - - /* Check for class equality. + /* Rewrite a[i..j] as: + * a.opSlice(i, j) */ - if (t1.ty == Tclass && t2.ty == Tclass) + auto a = new Expressions(); + if (ie) { - ClassDeclaration cd1 = t1.isClassHandle(); - ClassDeclaration cd2 = t2.isClassHandle(); - if (!(cd1.classKind == ClassKind.cpp || cd2.classKind == ClassKind.cpp)) - { - /* Rewrite as: - * .object.opEquals(e1, e2) - */ - if (!ClassDeclaration.object) - { - error(e.loc, "cannot compare classes for equality because `object.Object` was not declared"); - return null; - } - - Expression e1x = e.e1; - Expression e2x = e.e2; - - /* The explicit cast is necessary for interfaces - * https://issues.dlang.org/show_bug.cgi?id=4088 - */ - Type to = ClassDeclaration.object.getType(); - if (cd1.isInterfaceDeclaration()) - e1x = new CastExp(e.loc, e.e1, t1.isMutable() ? to : to.constOf()); - if (cd2.isInterfaceDeclaration()) - e2x = new CastExp(e.loc, e.e2, t2.isMutable() ? to : to.constOf()); - - Expression result = new IdentifierExp(e.loc, Id.empty); - result = new DotIdExp(e.loc, result, Id.object); - result = new DotIdExp(e.loc, result, Id.eq); - result = new CallExp(e.loc, result, e1x, e2x); - if (e.op == EXP.notEqual) - result = new NotExp(e.loc, result); - result = result.expressionSemantic(sc); - return result; - } + a.push(ie.lwr); + a.push(ie.upr); } - - if (Expression result = compare_overload(e, sc, Id.eq, null)) - { - if (lastComma(result).op == EXP.call && e.op == EXP.notEqual) - { - result = new NotExp(result.loc, result); - result = result.expressionSemantic(sc); - } - return result; - } - - /* Check for pointer equality. - */ - if (t1.ty == Tpointer || t2.ty == Tpointer) - { - /* Rewrite: - * ptr1 == ptr2 - * as: - * ptr1 is ptr2 - * - * This is just a rewriting for deterministic AST representation - * as the backend input. - */ - auto op2 = e.op == EXP.equal ? EXP.identity : EXP.notIdentity; - Expression r = new IdentityExp(op2, e.loc, e.e1, e.e2); - return r.expressionSemantic(sc); - } - - /* Check for struct equality without opEquals. + Expression result = new DotIdExp(ae.loc, ae.e1, Id.opSlice); + result = new CallExp(ae.loc, result, a); + result = result.expressionSemantic(sc); + return Expression.combine(e0, result); + } + // Didn't find it. Forward to aliasthis + if (ad.aliasthis && !isRecursiveAliasThis(att, ae.e1.type)) + { + //printf("att arr e1 = %s\n", this.e1.type.toChars()); + /* Rewrite op(a[arguments]) as: + * op(a.aliasthis[arguments]) */ - if (t1.ty == Tstruct && t2.ty == Tstruct) - { - auto sd = t1.isTypeStruct().sym; - if (sd != t2.isTypeStruct().sym) - return null; - - import dmd.clone : needOpEquals; - if (global.params.fieldwise != FeatureState.enabled && !needOpEquals(sd)) - { - // Use bitwise equality. - auto op2 = e.op == EXP.equal ? EXP.identity : EXP.notIdentity; - Expression r = new IdentityExp(op2, e.loc, e.e1, e.e2); - return r.expressionSemantic(sc); - } + ae.e1 = resolveAliasThis(sc, ae1save, true); + if (ae.e1) + continue; + } + break; + } + ae.e1 = ae1old; // recovery + ae.lengthVar = null; + return null; +} - /* Do memberwise equality. - * https://dlang.org/spec/expression.html#equality_expressions - * Rewrite: - * e1 == e2 - * as: - * e1.tupleof == e2.tupleof - * - * If sd is a nested struct, and if it's nested in a class, it will - * also compare the parent class's equality. Otherwise, compares - * the identity of parent context through void*. - */ - e = e.copy().isEqualExp(); - e.e1 = new DotIdExp(e.loc, e.e1, Id._tupleof); - e.e2 = new DotIdExp(e.loc, e.e2, Id._tupleof); - - auto sc2 = sc.push(); - sc2.flags |= SCOPE.noaccesscheck; - Expression r = e.expressionSemantic(sc2); - sc2.pop(); - return r; - } +/*********************************************** + * This is mostly the same as opOverloadUnary but has + * a different rewrite. + */ +Expression opOverloadCast(CastExp e, Scope* sc, Type att = null) +{ + AggregateDeclaration ad = isAggregate(e.e1.type); + if (!ad) + return null; - /* Check for tuple equality. - */ - if (e.e1.op == EXP.tuple && e.e2.op == EXP.tuple) + // Rewrite as: e1.opCast!(T)() + if (Dsymbol fd = search_function(ad, Id.opCast)) + { + version (all) + { + // Backwards compatibility with D1 if opCast is a function, not a template + if (fd.isFuncDeclaration()) { - auto tup1 = e.e1.isTupleExp(); - auto tup2 = e.e2.isTupleExp(); - size_t dim = tup1.exps.length; - if (dim != tup2.exps.length) - { - error(e.loc, "mismatched sequence lengths, `%d` and `%d`", - cast(int)dim, cast(int)tup2.exps.length); - return ErrorExp.get(); - } - - Expression result; - if (dim == 0) - { - // zero-length tuple comparison should always return true or false. - result = IntegerExp.createBool(e.op == EXP.equal); - } - else - { - for (size_t i = 0; i < dim; i++) - { - auto ex1 = (*tup1.exps)[i]; - auto ex2 = (*tup2.exps)[i]; - auto eeq = new EqualExp(e.op, e.loc, ex1, ex2); - - if (!result) - result = eeq; - else if (e.op == EXP.equal) - result = new LogicalExp(e.loc, EXP.andAnd, result, eeq); - else - result = new LogicalExp(e.loc, EXP.orOr, result, eeq); - } - assert(result); - } - result = Expression.combine(tup1.e0, tup2.e0, result); - result = result.expressionSemantic(sc); - - return result; + // Rewrite as: e1.opCast() + return build_overload(e.loc, sc, e.e1, null, fd); } - return null; } - - Expression visitCmp(CmpExp e) + auto tiargs = new Objects(); + tiargs.push(e.to); + return dotTemplateCall(e.e1, Id.opCast, tiargs).expressionSemantic(sc); + } + // Didn't find it. Forward to aliasthis + if (ad.aliasthis && !isRecursiveAliasThis(att, e.e1.type)) + { + // Rewrite `e1.opCast()` as `e1.aliasthis.opCast()` + if (auto e1 = resolveAliasThis(sc, e.e1, true)) { - //printf("CmpExp:: () (%s)\n", e.toChars()); - return compare_overload(e, sc, Id.cmp, pop); + CastExp result = e.copy().isCastExp(); + result.e1 = e1; + return result.opOverloadCast(sc, att); } + } + return null; +} - /********************************* - * Operator overloading for op= - */ - Expression visitBinAssign(BinAssignExp e) +// When no operator overload functions are found for `e`, recursively try with `alias this` +// Returns: `null` when still no overload found, otherwise resolved lowering +Expression binAliasThis(BinExp e, Scope* sc, Type[2] aliasThisStop) +{ + AggregateDeclaration ad1 = isAggregate(e.e1.type); + AggregateDeclaration ad2 = isAggregate(e.e2.type); + Expression rewrittenLhs; + if (!(e.isAssignExp && ad2 && ad1 == ad2)) // https://issues.dlang.org/show_bug.cgi?id=2943 + { + if (Expression result = checkAliasThisForLhs(ad1, sc, e, aliasThisStop)) { - //printf("BinAssignExp::op_overload() (%s)\n", e.toChars()); - if (auto ae = e.e1.isArrayExp()) - { - ae.e1 = ae.e1.expressionSemantic(sc); - ae.e1 = resolveProperties(sc, ae.e1); - Expression ae1old = ae.e1; - const(bool) maybeSlice = (ae.arguments.length == 0 || ae.arguments.length == 1 && (*ae.arguments)[0].op == EXP.interval); - IntervalExp ie = null; - if (maybeSlice && ae.arguments.length) - { - ie = (*ae.arguments)[0].isIntervalExp(); - } - Type att = null; // first cyclic `alias this` type - while (true) - { - if (ae.e1.op == EXP.error) - { - return ae.e1; - } - Expression e0 = null; - Expression ae1save = ae.e1; - ae.lengthVar = null; - Type t1b = ae.e1.type.toBasetype(); - AggregateDeclaration ad = isAggregate(t1b); - if (!ad) - break; - if (search_function(ad, Id.opIndexOpAssign)) - { - // Deal with $ - Expression result = resolveOpDollar(sc, ae, &e0); - if (!result) // (a[i..j] op= e2) might be: a.opSliceOpAssign!(op)(e2, i, j) - goto Lfallback; - if (result.op == EXP.error) - return result; - result = e.e2.expressionSemantic(sc); - if (result.op == EXP.error) - return result; - e.e2 = result; - /* Rewrite a[arguments] op= e2 as: - * a.opIndexOpAssign!(op)(e2, arguments) - */ - Expressions* a = ae.arguments.copy(); - a.insert(0, e.e2); - Objects* tiargs = opToArg(sc, e.op); - result = new DotTemplateInstanceExp(e.loc, ae.e1, Id.opIndexOpAssign, tiargs); - result = new CallExp(e.loc, result, a); - if (maybeSlice) // (a[] op= e2) might be: a.opSliceOpAssign!(op)(e2) - result = result.trySemantic(sc); - else - result = result.expressionSemantic(sc); - if (result) - { - return Expression.combine(e0, result); - } - } - Lfallback: - if (maybeSlice && search_function(ad, Id.opSliceOpAssign)) - { - // Deal with $ - Expression result = resolveOpDollar(sc, ae, ie, &e0); - if (result.op == EXP.error) - return result; - result = e.e2.expressionSemantic(sc); - if (result.op == EXP.error) - return result; - e.e2 = result; - /* Rewrite (a[i..j] op= e2) as: - * a.opSliceOpAssign!(op)(e2, i, j) - */ - auto a = new Expressions(); - a.push(e.e2); - if (ie) - { - a.push(ie.lwr); - a.push(ie.upr); - } - Objects* tiargs = opToArg(sc, e.op); - result = new DotTemplateInstanceExp(e.loc, ae.e1, Id.opSliceOpAssign, tiargs); - result = new CallExp(e.loc, result, a); - result = result.expressionSemantic(sc); - result = Expression.combine(e0, result); - return result; - } - // Didn't find it. Forward to aliasthis - if (ad.aliasthis && !isRecursiveAliasThis(att, ae.e1.type)) - { - /* Rewrite (a[arguments] op= e2) as: - * a.aliasthis[arguments] op= e2 - */ - ae.e1 = resolveAliasThis(sc, ae1save, true); - if (ae.e1) - continue; - } - break; - } - ae.e1 = ae1old; // recovery - ae.lengthVar = null; - } - Expression result = e.binSemanticProp(sc); - if (result) - return result; - // Don't attempt 'alias this' if an error occurred - if (e.e1.type.ty == Terror || e.e2.type.ty == Terror) - { - return ErrorExp.get(); - } - Identifier id = opId(e); - Expressions* args2 = new Expressions(); - AggregateDeclaration ad1 = isAggregate(e.e1.type); - Dsymbol s = null; - Objects* tiargs = null; - /* Try opOpAssign + /* https://issues.dlang.org/show_bug.cgi?id=19441 + * + * alias this may not be used for partial assignment. + * If a struct has a single member which is aliased this + * directly or aliased to a ref getter function that returns + * the mentioned member, then alias this may be + * used since the object will be fully initialised. + * If the struct is nested, the context pointer is considered + * one of the members, hence the `ad1.fields.length == 2 && ad1.vthis` + * condition. */ - if (ad1) - { - s = search_function(ad1, Id.opOpAssign); - if (s && !s.isTemplateDeclaration()) - { - error(e.loc, "`%s.opOpAssign` isn't a template", e.e1.toChars()); - return ErrorExp.get(); - } - } - // Set tiargs, the template argument list, which will be the operator string - if (s) - { - id = Id.opOpAssign; - tiargs = opToArg(sc, e.op); - } + auto ae = result.isAssignExp(); + if (!ae) + return result; // i.e: Rewrote `e1 = e2` -> `e1(e2)` - // Try D1-style operator overload, deprecated - if (!s && ad1 && id) - { - s = search_function(ad1, id); - if (s) - { - // @@@DEPRECATED_2.110@@@. - // Deprecated in 2.088, made an error in 2.100 - scope char[] op = EXPtoString(e.op).dup; - op[$-1] = '\0'; // remove trailing `=` - error(e.loc, "`%s` is obsolete. Use `opOpAssign(string op)(...) if (op == \"%s\")` instead.", id.toChars(), op.ptr); - return ErrorExp.get(); - } - } + auto dve = ae.e1.isDotVarExp(); + if (!dve) + return result; // i.e: Rewrote `e1 = e2` -> `e1() = e2` - if (s) + if (auto ad = dve.var.isMember2()) { - /* Try: - * a.opOpAssign(b) - */ - args2.setDim(1); - (*args2)[0] = e.e2; - expandTuples(args2); - MatchAccumulator m; - functionResolve(m, s, e.loc, sc, tiargs, e.e1.type, ArgumentList(args2)); - if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors())) + // i.e: Rewrote `e1 = e2` -> `e1.some.var = e2` + // Ensure that `var` is the only field member in `ad` + if (ad.fields.length == 1 || (ad.fields.length == 2 && ad.vthis)) { - return ErrorExp.get(); - } - if (m.count > 1) - { - // Error, ambiguous - error(e.loc, "overloads `%s` and `%s` both match argument list for `%s`", m.lastf.type.toChars(), m.nextf.type.toChars(), m.lastf.toChars()); - } - else if (m.last == MATCH.nomatch) - { - if (tiargs) - goto L1; - m.lastf = null; + if (dve.var == ad.aliasthis.sym) + return result; } - // Rewrite (e1 op e2) as e1.opOpAssign(e2) - return build_overload(e.loc, sc, e.e1, e.e2, m.lastf ? m.lastf : s); } - L1: - result = checkAliasThisForLhs(ad1, sc, e); - if (result || !s) // no point in trying Rhs alias-this if there's no overload of any kind in lhs - return result; + rewrittenLhs = ae.e1; + } + } + if (!(e.isAssignExp && ad1 && ad1 == ad2)) // https://issues.dlang.org/show_bug.cgi?id=2943 + { + if (Expression result = checkAliasThisForRhs(ad2, sc, e, aliasThisStop)) + return result; + } + if (rewrittenLhs) + { + error(e.loc, "cannot use `alias this` to partially initialize variable `%s` of type `%s`. Use `%s`", + e.e1.toChars(), ad1.toChars(), rewrittenLhs.toChars()); + return ErrorExp.get(); + } + return null; +} - return checkAliasThisForRhs(isAggregate(e.e2.type), sc, e); +Expression opOverloadAssign(AssignExp e, Scope* sc, Type[2] aliasThisStop) +{ + AggregateDeclaration ad1 = isAggregate(e.e1.type); + AggregateDeclaration ad2 = isAggregate(e.e2.type); + if (ad1 == ad2) + { + StructDeclaration sd = ad1.isStructDeclaration(); + if (sd && + (!sd.hasIdentityAssign || + /* Do a blit if we can and the rvalue is something like .init, + * where a postblit is not necessary. + */ + (sd.hasBlitAssign && !e.e2.isLvalue()))) + { + /* This is bitwise struct assignment. */ + return null; } + } + Dsymbol s = search_function(ad1, Id.opAssign); - if (pop) - *pop = e.op; + bool choseReverse; + if (auto result = pickBestBinaryOverload(sc, null, s, null, e, choseReverse)) + return result; - switch (e.op) - { - case EXP.cast_ : return visitCast(e.isCastExp()); - case EXP.array : return visitArray(e.isArrayExp()); + return binAliasThis(e, sc, aliasThisStop); +} + +Expression opOverloadBinary(BinExp e, Scope* sc, Type[2] aliasThisStop) +{ + if (Expression err = binSemanticProp(e, sc)) + return err; - case EXP.notEqual : - case EXP.equal : return visitEqual(e.isEqualExp()); + AggregateDeclaration ad1 = isAggregate(e.e1.type); + AggregateDeclaration ad2 = isAggregate(e.e2.type); - case EXP.lessOrEqual : - case EXP.greaterThan : - case EXP.greaterOrEqual: - case EXP.lessThan : return visitCmp(cast(CmpExp)e); + // Try opBinary and opBinaryRight + Dsymbol s = search_function(ad1, Id.opBinary); + if (s && !s.isTemplateDeclaration()) + { + error(e.e1.loc, "`%s.opBinary` isn't a template", e.e1.toChars()); + return ErrorExp.get(); + } - default: - if (auto ex = e.isBinAssignExp()) return visitBinAssign(ex); - if (auto ex = e.isBinExp()) return visitBin(ex); - if (auto ex = e.isUnaExp()) return visitUna(ex); - return visit(e); + Dsymbol s_r = search_function(ad2, Id.opBinaryRight); + if (s_r && !s_r.isTemplateDeclaration()) + { + error(e.e2.loc, "`%s.opBinaryRight` isn't a template", e.e2.toChars()); + return ErrorExp.get(); } + if (s_r && s_r == s) // https://issues.dlang.org/show_bug.cgi?id=12778 + s_r = null; + + bool choseReverse; + if (auto result = pickBestBinaryOverload(sc, opToArg(sc, e.op), s, s_r, e, choseReverse)) + return result; + + return binAliasThis(e, sc, aliasThisStop); } -/****************************************** - * Common code for overloading of EqualExp and CmpExp +/** + * If applicable, print an error relating to implementing / fixing `opBinary` functions. + * Params: + * e = binary operation + * sc = scope to try `opBinary!""` semantic in for error messages + * Returns: `true` when an error related to `opBinary` was printed */ -private Expression compare_overload(BinExp e, Scope* sc, Identifier id, EXP* pop) +bool suggestBinaryOverloads(BinExp e, Scope* sc) { - //printf("BinExp::compare_overload(id = %s) %s\n", id.toChars(), e.toChars()); + if (!e.op.hasOpBinary) + return false; + AggregateDeclaration ad1 = isAggregate(e.e1.type); AggregateDeclaration ad2 = isAggregate(e.e2.type); - Dsymbol s = null; - Dsymbol s_r = null; + if (ad1) { - s = search_function(ad1, id); + if (Dsymbol s = search_function(ad1, Id.opBinary)) + { + // This expressionSemantic will fail, otherwise operator overloading would have succeeded before + dotTemplateCall(e.e1, Id.opBinary, opToArg(sc, e.op), e.e2).expressionSemantic(sc); + errorSupplemental(s.loc, "`opBinary` defined here"); + return true; + } + error(e.loc, "operator `%s` is not defined for type `%s`", EXPtoString(e.op).ptr, e.e1.type.toChars); + errorSupplemental(ad1.loc, "perhaps overload the operator with `auto opBinary(string op : \"%s\")(%s rhs) {}`", EXPtoString(e.op).ptr, e.e2.type.toChars); + return true; } - if (ad2) + else if (ad2) { - s_r = search_function(ad2, id); - if (s == s_r) - s_r = null; + if (Dsymbol s_r = search_function(ad1, Id.opBinaryRight)) + { + dotTemplateCall(e.e2, Id.opBinaryRight, opToArg(sc, e.op), e.e1).expressionSemantic(sc); + errorSupplemental(s_r.loc, "`opBinaryRight` defined here"); + return true; + } + error(e.loc, "operator `%s` is not defined for type `%s`", EXPtoString(e.op).ptr, e.e2.type.toChars); + errorSupplemental(ad2.loc, "perhaps overload the operator with `auto opBinaryRight(string op : \"%s\")(%s rhs) {}`", EXPtoString(e.op).ptr, e.e1.type.toChars); + return true; } - Objects* tiargs = null; - if (s || s_r) + return false; +} + +/** + * If applicable, print an error relating to implementing / fixing `opOpAssign` or `opUnary` functions. + * Params: + * exp = binary operation + * sc = scope to try `opOpAssign!""` semantic in for error messages + * parent = if `exp` was lowered from this `PreExp` or `PostExp`, mention `opUnary` as well + * Returns: `true` when an error related to `opOpAssign` was printed + */ +bool suggestOpOpAssign(BinAssignExp exp, Scope* sc, Expression parent) +{ + auto ad = isAggregate(exp.e1.type); + if (!ad) + return false; + + if (parent && (parent.isPreExp() || parent.isPostExp())) + { + error(exp.loc, "operator `%s` not supported for `%s` of type `%s`", EXPtoString(parent.op).ptr, exp.e1.toChars(), ad.toChars()); + errorSupplemental(ad.loc, + "perhaps implement `auto opUnary(string op : \"%s\")() {}`"~ + " or `auto opOpAssign(string op : \"%s\")(int) {}`", + EXPtoString(stripAssignOp(parent.op)).ptr, + EXPtoString(stripAssignOp(exp.op)).ptr + ); + return true; + } + + if (const s = search_function(ad, Id.opOpAssign)) + { + // This expressionSemantic will fail, otherwise operator overloading would have succeeded before + dotTemplateCall(exp.e1, Id.opOpAssign, opToArg(sc, exp.op), exp.e2).expressionSemantic(sc); + } + else + { + error(exp.loc, "operator `%s` not supported for `%s` of type `%s`", EXPtoString(exp.op).ptr, exp.e1.toChars(), ad.toChars()); + errorSupplemental(ad.loc, "perhaps implement `auto opOpAssign(string op : \"%s\")(%s) {}`", + EXPtoString(stripAssignOp(exp.op)).ptr, exp.e2.type.toChars()); + } + return true; +} + +// Helper to construct e.id!tiargs(args), e.g. `lhs.opBinary!"+"(rhs)` +private Expression dotTemplateCall(Expression e, Identifier id, Objects* tiargs, Expression[] args...) +{ + auto ti = new DotTemplateInstanceExp(e.loc, e, id, tiargs); + auto expressions = new Expressions(); + expressions.pushSlice(args); + return new CallExp(e.loc, ti, expressions); +} + +Expression opOverloadEqual(EqualExp e, Scope* sc, Type[2] aliasThisStop) +{ + Type t1 = e.e1.type.toBasetype(); + Type t2 = e.e2.type.toBasetype(); + + /* Array equality is handled by expressionSemantic() potentially + * lowering to object.__equals(), which takes care of overloaded + * operators for the element types. + */ + if (t1.isStaticOrDynamicArray() && t2.isStaticOrDynamicArray()) { - /* Try: - * a.opEquals(b) - * b.opEquals(a) - * and see which is better. + return null; + } + + /* Check for class equality with null literal or typeof(null). + */ + if (t1.isTypeClass() && e.e2.isNullExp() || + t2.isTypeClass() && e.e1.isNullExp()) + { + error(e.loc, "use `%s` instead of `%s` when comparing with `null`", + EXPtoString(e.op == EXP.equal ? EXP.identity : EXP.notIdentity).ptr, + EXPtoString(e.op).ptr); + return ErrorExp.get(); + } + if (t1.isTypeClass() && t2.isTypeNull() || + t1.isTypeNull() && t2.isTypeClass()) + { + // Comparing a class with typeof(null) should not call opEquals + return null; + } + + /* Check for class equality. + */ + if (t1.isTypeClass() && t2.isTypeClass()) + { + ClassDeclaration cd1 = t1.isClassHandle(); + ClassDeclaration cd2 = t2.isClassHandle(); + if (!(cd1.classKind == ClassKind.cpp || cd2.classKind == ClassKind.cpp)) + { + /* Rewrite as: + * .object.opEquals(e1, e2) + */ + if (!ClassDeclaration.object) + { + error(e.loc, "cannot compare classes for equality because `object.Object` was not declared"); + return null; + } + + Expression e1x = e.e1; + Expression e2x = e.e2; + + /* The explicit cast is necessary for interfaces + * https://issues.dlang.org/show_bug.cgi?id=4088 + */ + Type to = ClassDeclaration.object.getType(); + if (cd1.isInterfaceDeclaration()) + e1x = new CastExp(e.loc, e.e1, t1.isMutable() ? to : to.constOf()); + if (cd2.isInterfaceDeclaration()) + e2x = new CastExp(e.loc, e.e2, t2.isMutable() ? to : to.constOf()); + + Expression result = new IdentifierExp(e.loc, Id.empty); + result = new DotIdExp(e.loc, result, Id.object); + result = new DotIdExp(e.loc, result, Id.opEquals); + result = new CallExp(e.loc, result, e1x, e2x); + if (e.op == EXP.notEqual) + result = new NotExp(e.loc, result); + result = result.expressionSemantic(sc); + return result; + } + } + + EXP cmpOp; + if (Expression result = compare_overload(e, sc, Id.opEquals, cmpOp, aliasThisStop)) + { + if (lastComma(result).isCallExp() && e.op == EXP.notEqual) + { + result = new NotExp(result.loc, result); + result = result.expressionSemantic(sc); + } + return result; + } + + /* Check for pointer equality. + */ + if (t1.isTypePointer() || t2.isTypePointer()) + { + /* Rewrite: + * ptr1 == ptr2 + * as: + * ptr1 is ptr2 + * + * This is just a rewriting for deterministic AST representation + * as the backend input. */ - Expressions* args1 = new Expressions(1); - (*args1)[0] = e.e1; - expandTuples(args1); - Expressions* args2 = new Expressions(1); - (*args2)[0] = e.e2; - expandTuples(args2); - MatchAccumulator m; - if (0 && s && s_r) + auto op2 = e.op == EXP.equal ? EXP.identity : EXP.notIdentity; + Expression r = new IdentityExp(op2, e.loc, e.e1, e.e2); + return r.expressionSemantic(sc); + } + + /* Check for struct equality without opEquals. + */ + if (t1.isTypeStruct() && t2.isTypeStruct()) + { + auto sd = t1.isTypeStruct().sym; + if (sd != t2.isTypeStruct().sym) + return null; + + import dmd.clone : needOpEquals; + if (!sc.previews.fieldwise && !needOpEquals(sd)) { - printf("s : %s\n", s.toPrettyChars()); - printf("s_r: %s\n", s_r.toPrettyChars()); + // Use bitwise equality. + auto op2 = e.op == EXP.equal ? EXP.identity : EXP.notIdentity; + Expression r = new IdentityExp(op2, e.loc, e.e1, e.e2); + return r.expressionSemantic(sc); } - if (s) + + /* Do memberwise equality. + * https://dlang.org/spec/expression.html#equality_expressions + * Rewrite: + * e1 == e2 + * as: + * e1.tupleof == e2.tupleof + * + * If sd is a nested struct, and if it's nested in a class, it will + * also compare the parent class's equality. Otherwise, compares + * the identity of parent context through void*. + */ + e = e.copy().isEqualExp(); + e.e1 = new DotIdExp(e.loc, e.e1, Id._tupleof); + e.e2 = new DotIdExp(e.loc, e.e2, Id._tupleof); + + auto sc2 = sc.push(); + sc2.noAccessCheck = true; + Expression r = e.expressionSemantic(sc2); + sc2.pop(); + return r; + } + + /* Check for tuple equality. + */ + auto tup1 = e.e1.isTupleExp(); + auto tup2 = e.e2.isTupleExp(); + if (tup1 && tup2) + { + size_t dim = tup1.exps.length; + if (dim != tup2.exps.length) { - functionResolve(m, s, e.loc, sc, tiargs, e.e1.type, ArgumentList(args2)); - if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors())) - return ErrorExp.get(); + error(e.loc, "mismatched sequence lengths, `%d` and `%d`", + cast(int)dim, cast(int)tup2.exps.length); + return ErrorExp.get(); } - FuncDeclaration lastf = m.lastf; - int count = m.count; - if (s_r) + + Expression result; + if (dim == 0) { - functionResolve(m, s_r, e.loc, sc, tiargs, e.e2.type, ArgumentList(args1)); - if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors())) - return ErrorExp.get(); + // zero-length tuple comparison should always return true or false. + result = IntegerExp.createBool(e.op == EXP.equal); } - if (m.count > 1) + else { - /* The following if says "not ambiguous" if there's one match - * from s and one from s_r, in which case we pick s. - * This doesn't follow the spec, but is a workaround for the case - * where opEquals was generated from templates and we cannot figure - * out if both s and s_r came from the same declaration or not. - * The test case is: - * import std.typecons; - * void main() { - * assert(tuple("has a", 2u) == tuple("has a", 1)); - * } - */ - if (!(m.lastf == lastf && m.count == 2 && count == 1)) + for (size_t i = 0; i < dim; i++) { - // Error, ambiguous - error(e.loc, "overloads `%s` and `%s` both match argument list for `%s`", m.lastf.type.toChars(), m.nextf.type.toChars(), m.lastf.toChars()); + auto ex1 = (*tup1.exps)[i]; + auto ex2 = (*tup2.exps)[i]; + auto eeq = new EqualExp(e.op, e.loc, ex1, ex2); + + if (!result) + result = eeq; + else if (e.op == EXP.equal) + result = new LogicalExp(e.loc, EXP.andAnd, result, eeq); + else + result = new LogicalExp(e.loc, EXP.orOr, result, eeq); } + assert(result); } - else if (m.last == MATCH.nomatch) + result = Expression.combine(tup1.e0, tup2.e0, result); + result = result.expressionSemantic(sc); + + return result; + } + return null; +} + +Expression opOverloadCmp(CmpExp exp, Scope* sc, Type[2] aliasThisStop) +{ + //printf("CmpExp:: () (%s)\n", e.toChars()); + EXP cmpOp = exp.op; + auto e = compare_overload(exp, sc, Id.opCmp, cmpOp, aliasThisStop); + if (!e) + return null; + + if (!e.type.isScalar() && e.type.equals(exp.e1.type)) + { + error(e.loc, "recursive `opCmp` expansion"); + return ErrorExp.get(); + } + if (!e.isCallExp()) + return e; + + Type t1 = exp.e1.type.toBasetype(); + Type t2 = exp.e2.type.toBasetype(); + if (!t1.isTypeClass() || !t2.isTypeClass()) + { + return new CmpExp(cmpOp, exp.loc, e, IntegerExp.literal!0).expressionSemantic(sc); + } + + // Lower to object.__cmp(e1, e2) + Expression cl = new IdentifierExp(exp.loc, Id.empty); + cl = new DotIdExp(exp.loc, cl, Id.object); + cl = new DotIdExp(exp.loc, cl, Id.__cmp); + cl = cl.expressionSemantic(sc); + + auto arguments = new Expressions(); + // Check if op_overload found a better match by calling e2.opCmp(e1) + // If the operands were swapped, then the result must be reversed + // e1.opCmp(e2) == -e2.opCmp(e1) + // cmpop takes care of this + if (exp.op == cmpOp) + { + arguments.push(exp.e1); + arguments.push(exp.e2); + } + else + { + // Use better match found by op_overload + arguments.push(exp.e2); + arguments.push(exp.e1); + } + + cl = new CallExp(e.loc, cl, arguments); + cl = new CmpExp(cmpOp, exp.loc, cl, new IntegerExp(0)); + return cl.expressionSemantic(sc); +} + +/********************************* + * Operator overloading for op= + */ +Expression opOverloadBinaryAssign(BinAssignExp e, Scope* sc, Type[2] aliasThisStop) +{ + if (auto ae = e.e1.isArrayExp()) + { + ae.e1 = ae.e1.expressionSemantic(sc); + ae.e1 = resolveProperties(sc, ae.e1); + Expression ae1old = ae.e1; + const(bool) maybeSlice = (ae.arguments.length == 0 || ae.arguments.length == 1 && (*ae.arguments)[0].isIntervalExp()); + IntervalExp ie = null; + if (maybeSlice && ae.arguments.length) { - m.lastf = null; + ie = (*ae.arguments)[0].isIntervalExp(); } - Expression result; - if (lastf && m.lastf == lastf || !s_r && m.last == MATCH.nomatch) + Type att = null; // first cyclic `alias this` type + while (true) { - // Rewrite (e1 op e2) as e1.opfunc(e2) - result = build_overload(e.loc, sc, e.e1, e.e2, m.lastf ? m.lastf : s); + if (ae.e1.isErrorExp()) + return ae.e1; + + Expression e0 = null; + Expression ae1save = ae.e1; + ae.lengthVar = null; + AggregateDeclaration ad = isAggregate(ae.e1.type); + if (!ad) + break; + if (search_function(ad, Id.opIndexOpAssign)) + { + // Deal with $ + Expression ae2 = resolveOpDollar(sc, ae, e0); + if (!ae2) // (a[i..j] op= e2) might be: a.opSliceOpAssign!(op)(e2, i, j) + goto Lfallback; + if (ae2.isErrorExp()) + return ae2; + e.e2 = e.e2.expressionSemantic(sc); + if (e.e2.isErrorExp()) + return e.e2; + + /* Rewrite a[arguments] op= e2 as: + * a.opIndexOpAssign!(op)(e2, arguments) + */ + Expressions* a = ae.arguments.copy(); + a.insert(0, e.e2); + Expression result = dotTemplateCall(ae.e1, Id.opIndexOpAssign, opToArg(sc, e.op), (*a)[]); + if (maybeSlice) // (a[] op= e2) might be: a.opSliceOpAssign!(op)(e2) + result = result.trySemantic(sc); + else + result = result.expressionSemantic(sc); + + if (result) + return Expression.combine(e0, result); + } + Lfallback: + if (maybeSlice && search_function(ad, Id.opSliceOpAssign)) + { + // Deal with $ + Expression ae2 = resolveOpDollar(sc, ae, ie, e0); + if (ae2.isErrorExp()) + return ae2; + + e.e2 = e.e2.expressionSemantic(sc); + if (e.e2.isErrorExp()) + return e.e2; + + /* Rewrite (a[i..j] op= e2) as: + * a.opSliceOpAssign!(op)(e2, i, j) + */ + auto result = ie ? + dotTemplateCall(ae.e1, Id.opSliceOpAssign, opToArg(sc, e.op), e.e2, ie.lwr, ie.upr) : + dotTemplateCall(ae.e1, Id.opSliceOpAssign, opToArg(sc, e.op), e.e2); + + return Expression.combine(e0, result.expressionSemantic(sc)); + } + // Didn't find it. Forward to aliasthis + if (ad.aliasthis && !isRecursiveAliasThis(att, ae.e1.type)) + { + /* Rewrite (a[arguments] op= e2) as: + * a.aliasthis[arguments] op= e2 + */ + ae.e1 = resolveAliasThis(sc, ae1save, true); + if (ae.e1) + continue; + } + break; } - else + ae.e1 = ae1old; // recovery + ae.lengthVar = null; + } + + if (Expression result = e.binSemanticProp(sc)) + return result; + + // Don't attempt 'alias this' if an error occurred + if (e.e1.type.isTypeError() || e.e2.type.isTypeError()) + return ErrorExp.get(); + + AggregateDeclaration ad1 = isAggregate(e.e1.type); + Dsymbol s = search_function(ad1, Id.opOpAssign); + if (s && !s.isTemplateDeclaration()) + { + error(e.loc, "`%s.opOpAssign` isn't a template", e.e1.toChars()); + return ErrorExp.get(); + } + + bool choseReverse; + if (auto res = pickBestBinaryOverload(sc, opToArg(sc, e.op), s, null, e, choseReverse)) + return res; + + Expression result = checkAliasThisForLhs(ad1, sc, e, aliasThisStop); + if (result || !s) // no point in trying Rhs alias-this if there's no overload of any kind in lhs + return result; + + return checkAliasThisForRhs(isAggregate(e.e2.type), sc, e, aliasThisStop); +} + +/** +Given symbols `s` and `s_r`, try to instantiate `e.e1.s!tiargs(e.e2)` and `e.e2.s_r!tiargs(e.e1)`, +and return the one with the best match level. + +Params: + sc = scope + tiargs = (optional) template arguments to instantiate symbols with + s = (optional) symbol of straightforward template (e.g. opBinary) + s_r = (optional) symbol of reversed template (e.g. opBinaryRight) + e = binary expression being overloaded, supplying arguments to the function calls + choseReverse = set to true when `s_r` was chosen instead of `s` +Returns: + Resulting operator overload function call, or `null` if neither symbol worked +*/ +private Expression pickBestBinaryOverload(Scope* sc, Objects* tiargs, Dsymbol s, Dsymbol s_r, BinExp e, out bool choseReverse) +{ + if (!s && !s_r) + return null; + + Expressions* args1 = new Expressions(1); + (*args1)[0] = e.e1; + expandTuples(args1); + Expressions* args2 = new Expressions(1); + (*args2)[0] = e.e2; + expandTuples(args2); + MatchAccumulator m; + + if (s) + { + functionResolve(m, s, e.loc, sc, tiargs, e.e1.type, ArgumentList(args2), null); + if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors())) + return ErrorExp.get(); + } + FuncDeclaration lastf = m.lastf; + int count = m.count; + if (s_r) + { + functionResolve(m, s_r, e.loc, sc, tiargs, e.e2.type, ArgumentList(args1), null); + if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors())) + return ErrorExp.get(); + } + if (m.count > 1) + { + /* The following if says "not ambiguous" if there's one match + * from s and one from s_r, in which case we pick s. + * This doesn't follow the spec, but is a workaround for the case + * where opEquals was generated from templates and we cannot figure + * out if both s and s_r came from the same declaration or not. + * The test case is: + * import std.typecons; + * void main() { + * assert(tuple("has a", 2u) == tuple("has a", 1)); + * } + */ + if (!(m.lastf == lastf && m.count == 2 && count == 1)) { - // Rewrite (e1 op e2) as e2.opfunc_r(e1) - result = build_overload(e.loc, sc, e.e2, e.e1, m.lastf ? m.lastf : s_r); - // When reversing operands of comparison operators, - // need to reverse the sense of the op - if (pop) - *pop = reverseRelation(e.op); + // Error, ambiguous + error(e.loc, "overloads `%s` and `%s` both match argument list for `%s`", m.lastf.type.toChars(), m.nextf.type.toChars(), m.lastf.toChars()); } - return result; } + else if (m.last == MATCH.nomatch) + { + if (tiargs) + return null; + m.lastf = null; + } + + if (lastf && m.lastf == lastf || !s_r && m.last == MATCH.nomatch) + { + choseReverse = false; + // Rewrite (e1 op e2) as e1.opfunc(e2) + return build_overload(e.loc, sc, e.e1, e.e2, m.lastf ? m.lastf : s); + } + else + { + choseReverse = true; + // Rewrite (e1 op e2) as e2.opfunc_r(e1) + return build_overload(e.loc, sc, e.e2, e.e1, m.lastf ? m.lastf : s_r); + } +} + +/****************************************** + * Common code for overloading of EqualExp and CmpExp + */ +private Expression compare_overload(BinExp e, Scope* sc, Identifier id, ref EXP cmpOp, Type[2] aliasThisStop) +{ + //printf("BinExp::compare_overload(id = %s) %s\n", id.toChars(), e.toChars()); + AggregateDeclaration ad1 = isAggregate(e.e1.type); + AggregateDeclaration ad2 = isAggregate(e.e2.type); + Dsymbol s = search_function(ad1, id); + Dsymbol s_r = search_function(ad2, id); + + if (s == s_r) + s_r = null; + + bool choseReverse; + if (auto res = pickBestBinaryOverload(sc, null, s, s_r, e, choseReverse)) + { + if (choseReverse) + cmpOp = reverseRelation(e.op); + return res; + } + /* * https://issues.dlang.org/show_bug.cgi?id=16657 * at this point, no matching opEquals was found for structs, * so we should not follow the alias this comparison code. */ - if ((e.op == EXP.equal || e.op == EXP.notEqual) && ad1 == ad2) + if (e.isEqualExp() && ad1 == ad2) + return null; + Expression result = checkAliasThisForLhs(ad1, sc, e, aliasThisStop); + if (result) + return result; + + result = checkAliasThisForRhs(isAggregate(e.e2.type), sc, e, aliasThisStop); + if (result) + return result; + + if (s || s_r) return null; - Expression result = checkAliasThisForLhs(ad1, sc, e); - return result ? result : checkAliasThisForRhs(isAggregate(e.e2.type), sc, e); + + Expression suggestOverloading(Expression other, AggregateDeclaration ad) + { + error(e.loc, "no operator `%s` for type `%s`", EXPtoString(e.op).ptr, ad.toChars); + string op = e.isEqualExp() ? "bool" : "int"; + errorSupplemental(ad.loc, "perhaps overload it with `%.*s %s(%s other) const {}`", op.fTuple.expand, id.toChars, other.type.toChars); + return ErrorExp.get(); + } + + // Classes have opCmp and opEquals defined in `Object` to fall back on already + if (ad1 && ad1.isStructDeclaration) + return suggestOverloading(e.e2, ad1); + if (ad2 && ad2.isStructDeclaration) + return suggestOverloading(e.e1, ad2); + + return null; } /*********************************** * Utility to build a function call out of this reference and argument. */ -Expression build_overload(const ref Loc loc, Scope* sc, Expression ethis, Expression earg, Dsymbol d) +Expression build_overload(Loc loc, Scope* sc, Expression ethis, Expression earg, Dsymbol d) { assert(d); Expression e; - Declaration decl = d.isDeclaration(); - if (decl) + if (Declaration decl = d.isDeclaration()) e = new DotVarExp(loc, ethis, decl, false); else e = new DotIdExp(loc, ethis, d.ident); @@ -1427,17 +1175,17 @@ Expression build_overload(const ref Loc loc, Scope* sc, Expression ethis, Expres */ Dsymbol search_function(ScopeDsymbol ad, Identifier funcid) { - Dsymbol s = ad.search(Loc.initial, funcid); - if (s) + if (!ad) + return null; + if (Dsymbol s = ad.search(Loc.initial, funcid)) { //printf("search_function: s = '%s'\n", s.kind()); Dsymbol s2 = s.toAlias(); //printf("search_function: s2 = '%s'\n", s2.kind()); FuncDeclaration fd = s2.isFuncDeclaration(); - if (fd && fd.type.ty == Tfunction) + if (fd && fd.type.isTypeFunction()) return fd; - TemplateDeclaration td = s2.isTemplateDeclaration(); - if (td) + if (TemplateDeclaration td = s2.isTemplateDeclaration()) return td; } return null; @@ -1466,7 +1214,7 @@ bool inferForeachAggregate(Scope* sc, bool isForeach, ref Expression feaggr, out aggr = aggr.expressionSemantic(sc); aggr = resolveProperties(sc, aggr); aggr = aggr.optimize(WANTvalue); - if (!aggr.type || aggr.op == EXP.error) + if (!aggr.type || aggr.isErrorExp()) return false; Type tab = aggr.type.toBasetype(); switch (tab.ty) @@ -1480,8 +1228,7 @@ bool inferForeachAggregate(Scope* sc, bool isForeach, ref Expression feaggr, out case Tclass: case Tstruct: { - AggregateDeclaration ad = (tab.ty == Tclass) ? tab.isTypeClass().sym - : tab.isTypeStruct().sym; + AggregateDeclaration ad = isAggregate(tab); if (!sliced) { sapply = search_function(ad, isForeach ? Id.apply : Id.applyReverse); @@ -1571,11 +1318,11 @@ bool inferApplyArgTypes(ForeachStatement fes, Scope* sc, ref Dsymbol sapply) // Determine ethis for sapply Expression ethis; Type tab = fes.aggr.type.toBasetype(); - if (tab.ty == Tclass || tab.ty == Tstruct) + if (tab.isTypeClass() || tab.isTypeStruct()) ethis = fes.aggr; else { - assert(tab.ty == Tdelegate && fes.aggr.op == EXP.delegate_); + assert(tab.isTypeDelegate() && fes.aggr.isDelegateExp()); ethis = fes.aggr.isDelegateExp().e1; } @@ -1615,7 +1362,7 @@ bool inferApplyArgTypes(ForeachStatement fes, Scope* sc, ref Dsymbol sapply) } p = (*fes.parameters)[1]; } - if (!p.type && tab.ty != Ttuple) + if (!p.type && !tab.isTypeTuple()) { p.type = tab.nextOf(); // value type p.type = p.type.addStorageClass(p.storageClass); @@ -1647,8 +1394,7 @@ bool inferApplyArgTypes(ForeachStatement fes, Scope* sc, ref Dsymbol sapply) case Tclass: case Tstruct: { - AggregateDeclaration ad = (tab.ty == Tclass) ? tab.isTypeClass().sym - : tab.isTypeStruct().sym; + AggregateDeclaration ad = isAggregate(tab); if (fes.parameters.length == 1) { if (!p.type) diff --git a/dmd/optimize.d b/dmd/optimize.d index ab1b67c53b..66b8c6a495 100644 --- a/dmd/optimize.d +++ b/dmd/optimize.d @@ -1,12 +1,12 @@ /** * Perform constant folding. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/optimize.d, _optimize.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/optimize.d, _optimize.d) * Documentation: https://dlang.org/phobos/dmd_optimize.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/optimize.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/optimize.d */ module dmd.optimize; @@ -84,7 +84,7 @@ Expression expandVar(int result, VarDeclaration v) { Type tb = v.type.toBasetype(); if (v.storage_class & STC.manifest || - tb.isscalar() || + tb.isScalar() || ((result & WANTexpand) && (tb.ty != Tsarray && tb.ty != Tstruct))) { if (v._init) @@ -183,31 +183,29 @@ private Expression fromConstInitializer(int result, Expression e1) { //printf("fromConstInitializer(result = %x, %s)\n", result, e1.toChars()); //static int xx; if (xx++ == 10) assert(0); + auto ve = e1.isVarExp(); + if (!ve) + return e1; + Expression e = e1; - if (auto ve = e1.isVarExp()) + VarDeclaration v = ve.var.isVarDeclaration(); + e = expandVar(result, v); + if (!e) + return e1; + + // If it is a comma expression involving a declaration, we mustn't + // perform a copy -- we'd get two declarations of the same variable. + // See https://issues.dlang.org/show_bug.cgi?id=4465. + if (e.op == EXP.comma && e.isCommaExp().e1.isDeclarationExp()) + e = e1; + else if (e.type != e1.type && e1.type && e1.type.ty != Tident) { - VarDeclaration v = ve.var.isVarDeclaration(); - e = expandVar(result, v); - if (e) - { - // If it is a comma expression involving a declaration, we mustn't - // perform a copy -- we'd get two declarations of the same variable. - // See https://issues.dlang.org/show_bug.cgi?id=4465. - if (e.op == EXP.comma && e.isCommaExp().e1.isDeclarationExp()) - e = e1; - else if (e.type != e1.type && e1.type && e1.type.ty != Tident) - { - // Type 'paint' operation - e = e.copy(); - e.type = e1.type; - } - e.loc = e1.loc; - } - else - { - e = e1; - } + // Type 'paint' operation + e = e.copy(); + e.type = e1.type; } + e.loc = e1.loc; + return e; } @@ -372,10 +370,10 @@ Expression optimize(Expression e, int result, bool keepLvalue = false) void visitStructLiteral(StructLiteralExp e) { - if (e.stageflags & stageOptimize) + if (e.stageflags & StructLiteralExp.StageFlags.optimize) return; const old = e.stageflags; - e.stageflags |= stageOptimize; + e.stageflags |= StructLiteralExp.StageFlags.optimize; if (e.elements) { foreach (ref ex; (*e.elements)[]) @@ -753,6 +751,7 @@ Expression optimize(Expression e, int result, bool keepLvalue = false) void visitNew(NewExp e) { + expOptimize(e.placement, WANTvalue); expOptimize(e.thisexp, WANTvalue); // Optimize parameters if (e.arguments) @@ -1004,7 +1003,7 @@ Expression optimize(Expression e, int result, bool keepLvalue = false) } } - extern (D) void shift_optimize(BinExp e, UnionExp function(const ref Loc, Type, Expression, Expression) shift) + extern (D) void shift_optimize(BinExp e, UnionExp function(Loc, Type, Expression, Expression) shift) { if (binOptimize(e, result)) return; @@ -1071,7 +1070,7 @@ Expression optimize(Expression e, int result, bool keepLvalue = false) if (binOptimize(e, result)) return; // All negative integral powers are illegal. - if (e.e1.type.isintegral() && (e.e2.op == EXP.int64) && cast(sinteger_t)e.e2.toInteger() < 0) + if (e.e1.type.isIntegral() && (e.e2.op == EXP.int64) && cast(sinteger_t)e.e2.toInteger() < 0) { error(e.loc, "cannot raise `%s` to a negative integer power. Did you mean `(cast(real)%s)^^%s` ?", e.e1.type.toBasetype().toChars(), e.e1.toChars(), e.e2.toChars()); return errorReturn(); @@ -1080,7 +1079,7 @@ Expression optimize(Expression e, int result, bool keepLvalue = false) if (e.e2.op == EXP.float64 && e.e2.toReal() == real_t(cast(sinteger_t)e.e2.toReal())) { // This only applies to floating point, or positive integral powers. - if (e.e1.type.isfloating() || cast(sinteger_t)e.e2.toInteger() >= 0) + if (e.e1.type.isFloating() || cast(sinteger_t)e.e2.toInteger() >= 0) e.e2 = new IntegerExp(e.loc, e.e2.toInteger(), Type.tint64); } if (e.e1.isConst() == 1 && e.e2.isConst() == 1) diff --git a/dmd/parse.d b/dmd/parse.d index 757aefb26b..e68017c87e 100644 --- a/dmd/parse.d +++ b/dmd/parse.d @@ -3,12 +3,12 @@ * * Specification: $(LINK2 https://dlang.org/spec/grammar.html, D Grammar) * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/parse.d, _parse.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/parse.d, _parse.d) * Documentation: https://dlang.org/phobos/dmd_parse.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/parse.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/parse.d */ module dmd.parse; @@ -49,14 +49,12 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer bool doUnittests; // parse unittest blocks } - bool transitionIn = false; /// `-transition=in` is active, `in` parameters are listed - /********************* * Use this constructor for string mixins. * Input: * loc = location in source file of mixin */ - extern (D) this(const ref Loc loc, AST.Module _module, const(char)[] input, bool doDocComment, + extern (D) this(Loc loc, AST.Module _module, const(char)[] input, bool doDocComment, ErrorSink errorSink, const CompileEnv* compileEnv, const bool doUnittests) scope { //printf("Parser::Parser()1 %d\n", doUnittests); @@ -110,43 +108,44 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer parseModuleAttributes(msg, isdeprecated); // ModuleDeclaration leads off - if (token.value == TOK.module_) + if (token.value != TOK.module_) + return true; + + const loc = token.loc; + nextToken(); + + /* parse ModuleFullyQualifiedName + * https://dlang.org/spec/module.html#ModuleFullyQualifiedName + */ + + if (token.value != TOK.identifier) { - const loc = token.loc; - nextToken(); + error("identifier expected following `module`"); + return false; + } - /* parse ModuleFullyQualifiedName - * https://dlang.org/spec/module.html#ModuleFullyQualifiedName - */ + Identifier[] a; + Identifier id = token.ident; + while (nextToken() == TOK.dot) + { + a ~= id; + nextToken(); if (token.value != TOK.identifier) { - error("identifier expected following `module`"); + error("identifier expected following `package`"); return false; } + id = token.ident; + } - Identifier[] a; - Identifier id = token.ident; - - while (nextToken() == TOK.dot) - { - a ~= id; - nextToken(); - if (token.value != TOK.identifier) - { - error("identifier expected following `package`"); - return false; - } - id = token.ident; - } + md = new AST.ModuleDeclaration(loc, a, id, msg, isdeprecated); - md = new AST.ModuleDeclaration(loc, a, id, msg, isdeprecated); + if (token.value != TOK.semicolon) + error("`;` expected following module declaration instead of `%s`", token.toChars()); + nextToken(); + addComment(mod, comment); - if (token.value != TOK.semicolon) - error("`;` expected following module declaration instead of `%s`", token.toChars()); - nextToken(); - addComment(mod, comment); - } return true; } @@ -328,7 +327,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer pAttrs.comment = token.blockComment.ptr; } AST.Visibility.Kind prot; - StorageClass stc; + STC stc; AST.Condition condition; linkage = linksave; @@ -537,12 +536,12 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer } // Workaround 14894. Add an empty unittest declaration to keep // the number of symbols in this scope independent of -unittest. - s = new AST.UnitTestDeclaration(loc, token.loc, STC.undefined_, null); + s = new AST.UnitTestDeclaration(loc, token.loc, STC.none, null); } break; case TOK.new_: - s = parseNew(pAttrs); + s = parseNewDeclaration(pAttrs); break; case TOK.colon: @@ -651,6 +650,8 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer case TOK.auto_: stc = STC.auto_; + if (peekNext() == TOK.ref_) + stc |= STC.autoref; goto Lstc; case TOK.scope_: @@ -743,7 +744,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer a = parseBlock(pLastDecl, pAttrs); auto stc2 = getStorageClass!AST(pAttrs); - if (stc2 != STC.undefined_) + if (stc2 != STC.none) { s = new AST.StorageClassDeclaration(scdLoc, stc2, a); } @@ -931,22 +932,14 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer { const attrLoc = token.loc; - nextToken(); - - AST.Expression e = null; // default - if (token.value == TOK.leftParenthesis) - { - nextToken(); - e = parseAssignExp(); - check(TOK.rightParenthesis); - } + AST.Expression e = parseAlign(); if (pAttrs.setAlignment) { if (e) error("redundant alignment attribute `align(%s)`", e.toChars()); else - error("redundant alignment attribute `align`"); + error("redundant alignment attribute `align(default)`"); } pAttrs.setAlignment = true; @@ -1048,6 +1041,30 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer nextToken(); continue; + // The following are all errors, the cases are just for better error messages than the default case + case TOK.return_: + case TOK.goto_: + case TOK.break_: + case TOK.continue_: + error("`%s` statement must be inside function scope", token.toChars()); + goto Lerror; + case TOK.asm_: + case TOK.do_: + case TOK.for_: + case TOK.foreach_: + case TOK.foreach_reverse_: + case TOK.if_: + case TOK.switch_: + case TOK.try_: + case TOK.while_: + error("`%s` statement must be inside function scope", token.toChars()); + if (peekNext() == TOK.leftParenthesis || peekNext() == TOK.leftCurly) + { + parseStatement(0); + s = null; + continue; + } + goto Lerror; default: error("declaration expected, not `%s`", token.toChars()); Lerror: @@ -1084,7 +1101,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer * Starts with token on the first ident. * Ends with scanner past closing ';' */ - private AST.Dsymbols* parseAutoDeclarations(StorageClass storageClass, const(char)* comment) + private AST.Dsymbols* parseAutoDeclarations(STC storageClass, const(char)* comment) { //printf("parseAutoDeclarations\n"); auto a = new AST.Dsymbols(); @@ -1199,9 +1216,9 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer * Returns: * The combination of both storage classes (`orig | added`). */ - private StorageClass appendStorageClass(StorageClass orig, StorageClass added) + private STC appendStorageClass(STC orig, STC added) { - void checkConflictSTCGroup(bool at = false)(StorageClass group) + void checkConflictSTCGroup(bool at = false)(STC group) { if (added & group && orig & group & ((orig & group) - 1)) error( @@ -1291,13 +1308,13 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer * If the attribute is builtin, the return value will be non-zero. * Otherwise, 0 is returned, and `pudas` will be appended to. */ - private StorageClass parseAttribute(ref AST.Expressions* udas) + private STC parseAttribute(ref AST.Expressions* udas) { nextToken(); if (token.value == TOK.identifier) { // If we find a builtin attribute, we're done, return immediately. - if (StorageClass stc = isBuiltinAtAttribute(token.ident)) + if (STC stc = isBuiltinAtAttribute(token.ident)) return stc; // Allow identifier, template instantiation, or function call @@ -1315,10 +1332,10 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer if (udas is null) udas = new AST.Expressions(); udas.push(exp); - return 0; + return STC.none; } - AST.Expression templateArgToExp(RootObject o, const ref Loc loc) + AST.Expression templateArgToExp(RootObject o, Loc loc) { switch (o.dyncast) { @@ -1342,7 +1359,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer auto args = parseTemplateArgumentList(); foreach (arg; *args) udas.push(templateArgToExp(arg, token.loc)); - return 0; + return STC.none; } if (auto o = parseTemplateSingleArgument()) @@ -1350,7 +1367,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer if (udas is null) udas = new AST.Expressions(); udas.push(templateArgToExp(o, token.loc)); - return 0; + return STC.none; } if (token.isKeyword()) @@ -1358,17 +1375,17 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer else error("`@identifier` or `@(ArgumentList)` expected, not `@%s`", token.toChars()); - return 0; + return STC.none; } /*********************************************** * Parse const/immutable/shared/inout/nothrow/pure postfix */ - private StorageClass parsePostfix(StorageClass storageClass, AST.Expressions** pudas) + private STC parsePostfix(STC storageClass, AST.Expressions** pudas) { while (1) { - StorageClass stc; + STC stc; switch (token.value) { case TOK.const_: @@ -1405,6 +1422,10 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer stc = STC.scope_; break; + case TOK.rvalue: + stc = STC.rvalue; + break; + case TOK.at: { AST.Expressions* udas = null; @@ -1441,16 +1462,16 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer } } - private StorageClass parseTypeCtor() + private STC parseTypeCtor() { - StorageClass storageClass = STC.undefined_; + STC storageClass = STC.none; while (1) { if (peekNext() == TOK.leftParenthesis) return storageClass; - StorageClass stc; + STC stc; switch (token.value) { case TOK.const_: @@ -1511,28 +1532,26 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer if (token.value != TOK.identifier) { error("identifier expected following `template`"); - goto Lerr; + return null; } id = token.ident; nextToken(); tpl = parseTemplateParameterList(); if (!tpl) - goto Lerr; + return null; constraint = parseConstraint(); if (token.value != TOK.leftCurly) { error("`{` expected after template parameter list, not `%s`", token.toChars()); /* } */ - goto Lerr; + nextToken(); + return null; } decldefs = parseBlock(null); tempdecl = new AST.TemplateDeclaration(loc, id, tpl, constraint, decldefs, ismixin); return tempdecl; - - Lerr: - return null; } /****************************************** @@ -1705,19 +1724,28 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer * mixin Foo; * mixin Foo!(args); * mixin a.b.c!(args).Foo!(args); - * mixin Foo!(args) identifier; * mixin typeof(expr).identifier!(args); + * mixin Foo!(args) identifier; + * mixin identifier = Foo!(args); */ private AST.Dsymbol parseMixin() { AST.TemplateMixin tm; - Identifier id; + Identifier id, name; AST.Objects* tiargs; //printf("parseMixin()\n"); const locMixin = token.loc; nextToken(); // skip 'mixin' + // mixin Identifier = MixinTemplateName TemplateArguments; + if (token.value == TOK.identifier && peekNext() == TOK.assign) + { + name = token.ident; + nextToken(); + nextToken(); + } + auto loc = token.loc; AST.TypeQualified tqual = null; if (token.value == TOK.dot) @@ -1780,14 +1808,14 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer nextToken(); } - id = null; - if (token.value == TOK.identifier) + // mixin MixinTemplateName TemplateArguments Identifier; + if (!name && token.value == TOK.identifier) { - id = token.ident; + name = token.ident; nextToken(); } - tm = new AST.TemplateMixin(locMixin, id, tqual, tiargs); + tm = new AST.TemplateMixin(locMixin, name, tqual, tiargs); if (token.value != TOK.semicolon) error("`;` expected after `mixin`"); nextToken(); @@ -2144,11 +2172,11 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer nextToken(); if (id == Id.Windows) return returnLinkage(LINK.windows); - else if (id == Id.D) + if (id == Id.D) return returnLinkage(LINK.d); - else if (id == Id.System) + if (id == Id.System) return returnLinkage(LINK.system); - else if (id == Id.Objective) // Looking for tokens "Objective-C" + if (id == Id.Objective) // Looking for tokens "Objective-C" { if (token.value != TOK.min) return invalidLinkage(); @@ -2255,17 +2283,9 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer nextToken(); if (token.value == TOK.identifier) s = new AST.DebugSymbol(token.loc, token.ident); - else if (token.value == TOK.int32Literal || token.value == TOK.int64Literal) - { - // @@@DEPRECATED_2.111@@@ - // Deprecated in 2.101, remove in 2.111 - deprecation("`debug = ` is deprecated, use debug identifiers instead"); - - s = new AST.DebugSymbol(token.loc, cast(uint)token.unsvalue); - } else { - error("identifier or integer expected, not `%s`", token.toChars()); + error("identifier expected, not `%s`", token.toChars()); s = null; } nextToken(); @@ -2280,7 +2300,6 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer */ private AST.Condition parseDebugCondition() { - uint level = 1; Identifier id = null; Loc loc = token.loc; @@ -2290,21 +2309,13 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer if (token.value == TOK.identifier) id = token.ident; - else if (token.value == TOK.int32Literal || token.value == TOK.int64Literal) - { - // @@@DEPRECATED_2.111@@@ - // Deprecated in 2.101, remove in 2.111 - deprecation("`debug( )` is deprecated, use debug identifiers instead"); - - level = cast(uint)token.unsvalue; - } else - error("identifier or integer expected inside `debug(...)`, not `%s`", token.toChars()); + error("identifier expected inside `debug(...)`, not `%s`", token.toChars()); loc = token.loc; nextToken(); check(TOK.rightParenthesis); } - return new AST.DebugCondition(loc, mod, level, id); + return new AST.DebugCondition(loc, mod, id); } /************************************** @@ -2316,16 +2327,9 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer nextToken(); if (token.value == TOK.identifier) s = new AST.VersionSymbol(token.loc, token.ident); - else if (token.value == TOK.int32Literal || token.value == TOK.int64Literal) - { - // @@@DEPRECATED_2.111@@@ - // Deprecated in 2.101, remove in 2.111 - deprecation("`version = ` is deprecated, use version identifiers instead"); - s = new AST.VersionSymbol(token.loc, cast(uint)token.unsvalue); - } else { - error("identifier or integer expected, not `%s`", token.toChars()); + error("identifier expected, not `%s`", token.toChars()); s = null; } nextToken(); @@ -2340,7 +2344,6 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer */ private AST.Condition parseVersionCondition() { - uint level = 1; Identifier id = null; Loc loc; @@ -2355,26 +2358,18 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer loc = token.loc; if (token.value == TOK.identifier) id = token.ident; - else if (token.value == TOK.int32Literal || token.value == TOK.int64Literal) - { - // @@@DEPRECATED_2.111@@@ - // Deprecated in 2.101, remove in 2.111 - deprecation("`version( )` is deprecated, use version identifiers instead"); - - level = cast(uint)token.unsvalue; - } else if (token.value == TOK.unittest_) id = Identifier.idPool(Token.toString(TOK.unittest_)); else if (token.value == TOK.assert_) id = Identifier.idPool(Token.toString(TOK.assert_)); else - error("identifier or integer expected inside `version(...)`, not `%s`", token.toChars()); + error("identifier expected inside `version(...)`, not `%s`", token.toChars()); nextToken(); check(TOK.rightParenthesis); } else error("(condition) expected following `version`"); - return new AST.VersionCondition(loc, mod, level, id); + return new AST.VersionCondition(loc, mod, id); } /*********************************************** @@ -2420,7 +2415,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer { AST.Expressions* udas = null; const loc = token.loc; - StorageClass stc = getStorageClass!AST(pAttrs); + STC stc = getStorageClass!AST(pAttrs); nextToken(); if (token.value == TOK.leftParenthesis && peekNext() == TOK.this_ && peekNext2() == TOK.rightParenthesis) @@ -2471,7 +2466,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer if (stc & STC.static_) error(loc, "constructor cannot be static"); } - else if (StorageClass ss = stc & (STC.shared_ | STC.static_)) // this() + else if (STC ss = stc & (STC.shared_ | STC.static_)) // this() { if (ss == STC.static_) error(loc, "use `static this()` to declare a static constructor"); @@ -2481,7 +2476,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); // RetrunType -> auto + AST.Type tf = new AST.TypeFunction(parameterList, null, linkage, stc); // ReturnType -> auto tf = tf.addSTC(stc); auto f = new AST.CtorDeclaration(loc, Loc.initial, stc, tf); @@ -2513,7 +2508,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer { AST.Expressions* udas = null; const loc = token.loc; - StorageClass stc = getStorageClass!AST(pAttrs); + STC stc = getStorageClass!AST(pAttrs); nextToken(); check(TOK.this_); @@ -2521,7 +2516,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer check(TOK.rightParenthesis); stc = parsePostfix(stc, &udas); - if (StorageClass ss = stc & (STC.shared_ | STC.static_)) + if (STC ss = stc & (STC.shared_ | STC.static_)) { if (ss == STC.static_) error(loc, "use `static ~this()` to declare a static destructor"); @@ -2549,7 +2544,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer { //Expressions *udas = NULL; const loc = token.loc; - StorageClass stc = getStorageClass!AST(pAttrs); + STC stc = getStorageClass!AST(pAttrs); nextToken(); nextToken(); @@ -2561,7 +2556,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer error(loc, "use `shared static this()` to declare a shared static constructor"); else if (stc & STC.static_) appendStorageClass(stc, STC.static_); // complaint for the redundancy - else if (StorageClass modStc = stc & STC.TYPECTOR) + else if (STC modStc = stc & STC.TYPECTOR) { OutBuffer buf; AST.stcToBuffer(buf, modStc); @@ -2583,7 +2578,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer { AST.Expressions* udas = null; const loc = token.loc; - StorageClass stc = getStorageClass!AST(pAttrs); + STC stc = getStorageClass!AST(pAttrs); nextToken(); nextToken(); @@ -2596,7 +2591,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer error(loc, "use `shared static ~this()` to declare a shared static destructor"); else if (stc & STC.static_) appendStorageClass(stc, STC.static_); // complaint for the redundancy - else if (StorageClass modStc = stc & STC.TYPECTOR) + else if (STC modStc = stc & STC.TYPECTOR) { OutBuffer buf; AST.stcToBuffer(buf, modStc); @@ -2624,7 +2619,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer { //Expressions *udas = NULL; const loc = token.loc; - StorageClass stc = getStorageClass!AST(pAttrs); + STC stc = getStorageClass!AST(pAttrs); nextToken(); nextToken(); @@ -2633,9 +2628,9 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer check(TOK.rightParenthesis); stc = parsePostfix(stc & ~STC.TYPECTOR, null) | stc; - if (StorageClass ss = stc & (STC.shared_ | STC.static_)) + if (STC ss = stc & (STC.shared_ | STC.static_)) appendStorageClass(stc, ss); // complaint for the redundancy - else if (StorageClass modStc = stc & STC.TYPECTOR) + else if (STC modStc = stc & STC.TYPECTOR) { OutBuffer buf; AST.stcToBuffer(buf, modStc); @@ -2657,7 +2652,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer { AST.Expressions* udas = null; const loc = token.loc; - StorageClass stc = getStorageClass!AST(pAttrs); + STC stc = getStorageClass!AST(pAttrs); nextToken(); nextToken(); @@ -2667,9 +2662,9 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer check(TOK.rightParenthesis); stc = parsePostfix(stc & ~STC.TYPECTOR, &udas) | stc; - if (StorageClass ss = stc & (STC.shared_ | STC.static_)) + if (STC ss = stc & (STC.shared_ | STC.static_)) appendStorageClass(stc, ss); // complaint for the redundancy - else if (StorageClass modStc = stc & STC.TYPECTOR) + else if (STC modStc = stc & STC.TYPECTOR) { OutBuffer buf; AST.stcToBuffer(buf, modStc); @@ -2698,7 +2693,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer private AST.Dsymbol parseInvariant(PrefixAttributes!AST* pAttrs) { const loc = token.loc; - StorageClass stc = getStorageClass!AST(pAttrs); + STC stc = getStorageClass!AST(pAttrs); nextToken(); if (token.value == TOK.leftParenthesis) // optional () or invariant (expression); @@ -2740,7 +2735,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer private AST.Dsymbol parseUnitTest(PrefixAttributes!AST* pAttrs) { const loc = token.loc; - StorageClass stc = getStorageClass!AST(pAttrs); + STC stc = getStorageClass!AST(pAttrs); nextToken(); @@ -2779,44 +2774,19 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer * @disable new(); * Current token is 'new'. */ - private AST.Dsymbol parseNew(PrefixAttributes!AST* pAttrs) + private AST.Dsymbol parseNewDeclaration(PrefixAttributes!AST* pAttrs) { const loc = token.loc; - StorageClass stc = getStorageClass!AST(pAttrs); + STC stc = getStorageClass!AST(pAttrs); if (!(stc & STC.disable)) { error("`new` allocator must be annotated with `@disabled`"); } nextToken(); - - /* @@@DEPRECATED_2.108@@@ - * After deprecation period (2.108), remove all code in the version(all) block. - */ - version (all) - { - auto parameterList = parseParameterList(null); // parameterList ignored - if (parameterList.parameters.length > 0 || parameterList.varargs != VarArg.none) - deprecation("`new` allocator with non-empty parameter list is deprecated"); - auto f = new AST.NewDeclaration(loc, stc); - if (token.value != TOK.semicolon) - { - deprecation("`new` allocator with function definition is deprecated"); - parseContracts(f); // body ignored - f.fbody = null; - f.fensures = null; - f.frequires = null; - } - else - nextToken(); - return f; - } - else - { - check(TOK.leftParenthesis); - check(TOK.rightParenthesis); - check(TOK.semicolon); - return new AST.NewDeclaration(loc, stc); - } + check(TOK.leftParenthesis); + check(TOK.rightParenthesis); + check(TOK.semicolon); + return new AST.NewDeclaration(loc, stc); } /********************************************** @@ -2826,7 +2796,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer { auto parameters = new AST.Parameters(); VarArg varargs = VarArg.none; - StorageClass varargsStc; + STC varargsStc; // Attributes allowed for ... enum VarArgsStc = STC.const_ | STC.immutable_ | STC.shared_ | STC.scope_ | STC.return_ | STC.returnScope; @@ -2836,8 +2806,8 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer { Identifier ai = null; AST.Type at; - StorageClass storageClass = 0; - StorageClass stc; + STC storageClass = STC.none; + STC stc; AST.Expression ae; AST.Expressions* udas = null; for (; 1; nextToken()) @@ -2889,7 +2859,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer case TOK.at: { AST.Expressions* exps = null; - StorageClass stc2 = parseAttribute(exps); + STC stc2 = parseAttribute(exps); if (stc2 & atAttrGroup) { error("`@%s` attribute for function parameter is not supported", token.toChars()); @@ -2906,7 +2876,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer // Don't call nextToken again. } case TOK.in_: - if (transitionIn) + if (compileEnv.transitionIn) eSink.message(scanloc, "Usage of 'in' on parameter"); stc = STC.in_; if (compileEnv.previewIn) @@ -2935,6 +2905,8 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer case TOK.auto_: stc = STC.auto_; + if (peekNext() == TOK.ref_) + stc |= STC.autoref; goto L2; case TOK.return_: @@ -2952,8 +2924,10 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer // if stcx is not a power of 2 if (stcx & (stcx - 1) && !(stcx == (STC.in_ | STC.ref_))) error("incompatible parameter storage classes"); - //if ((storageClass & STC.scope_) && (storageClass & (STC.ref_ | STC.out_))) - //error("scope cannot be ref or out"); + + // Deprecated in 2.111 + if ((storageClass & STC.auto_) && (storageClass & STC.ref_) && !(storageClass & STC.autoref)) + deprecation("`auto` and `ref` storage classes should be adjacent"); const tv = peekNext(); Loc loc; @@ -2991,7 +2965,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer if (token.value == TOK.at) { AST.Expressions* exps = null; - StorageClass stc2 = parseAttribute(exps); + STC stc2 = parseAttribute(exps); if (stc2 & atAttrGroup) { error("`@%s` attribute for function parameter is not supported", token.toChars()); @@ -3089,7 +3063,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer Identifier ident = null; AST.Expressions* udas; - StorageClass stc; + STC stc; AST.Expression deprecationMessage; enum attributeErrorMessage = "`%s` is not a valid attribute for enum members"; Lattrs: @@ -3098,7 +3072,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer switch (token.value) { case TOK.at: - if (StorageClass _stc = parseAttribute(udas)) + if (STC _stc = parseAttribute(udas)) { if (_stc == STC.disable) stc |= _stc; @@ -3485,7 +3459,12 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer name = _alias; _alias = null; } - s.addAlias(name, _alias); + if (s.isstatic) + error(loc, "static import `%s` cannot have an import bind list", s.toPrettyChars()); + if (!s.aliasId) + s.ident = null; // make it an anonymous import + s.names.push(name); + s.aliases.push(_alias); } while (token.value == TOK.comma); break; // no comma-separated imports of this form @@ -3524,7 +3503,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer * shared inout type * shared inout const type */ - StorageClass stc = 0; + STC stc = STC.none; while (1) { switch (token.value) @@ -4008,7 +3987,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer auto parameterList = parseParameterList(null); - StorageClass stc = parsePostfix(STC.undefined_, null); + STC stc = parsePostfix(STC.none, null); auto tf = new AST.TypeFunction(parameterList, t, linkage, stc); if (stc & (STC.const_ | STC.immutable_ | STC.shared_ | STC.wild | STC.return_)) { @@ -4044,7 +4023,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer * Reference: https://dlang.org/spec/declaration.html#Declarator */ private AST.Type parseDeclarator(AST.Type t, ref int palt, Identifier* pident, - AST.TemplateParameters** tpl = null, StorageClass storageClass = 0, + AST.TemplateParameters** tpl = null, STC storageClass = STC.none, bool* pdisable = null, AST.Expressions** pudas = null) { //printf("parseDeclarator(tpl = %p)\n", tpl); @@ -4185,7 +4164,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer /* Parse const/immutable/shared/inout/nothrow/pure/return postfix */ // merge prefix storage classes - StorageClass stc = parsePostfix(storageClass, pudas); + STC stc = parsePostfix(storageClass, pudas); AST.Type tf = new AST.TypeFunction(parameterList, t, linkage, stc); tf = tf.addSTC(stc); @@ -4212,11 +4191,11 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer return ts; } - private void parseStorageClasses(ref StorageClass storage_class, ref LINK link, + private void parseStorageClasses(ref STC storage_class, ref LINK link, ref bool setAlignment, ref AST.Expression ealign, ref AST.Expressions* udas, out Loc linkloc) { - StorageClass stc; + STC stc; bool sawLinkage = false; // seen a linkage declaration linkloc = Loc.initial; @@ -4259,6 +4238,8 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer case TOK.auto_: stc = STC.auto_; + if (peekNext() == TOK.ref_) + stc |= STC.autoref; goto L1; case TOK.scope_: @@ -4350,14 +4331,8 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer } case TOK.align_: { - nextToken(); setAlignment = true; - if (token.value == TOK.leftParenthesis) - { - nextToken(); - ealign = parseExpression(); - check(TOK.rightParenthesis); - } + ealign = parseAlign(); continue; } default: @@ -4367,6 +4342,27 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer } } + /** + * Parse `align` or `align(n)` + * Returns: + * expression `n` if it is present, or `null` otherwise. + */ + private AST.Expression parseAlign() + { + assert(token.value == TOK.align_); + AST.Expression e = null; + nextToken(); + if (token.value == TOK.leftParenthesis) + { + nextToken(); + if (token.value == TOK.default_) + nextToken(); + else + e = parseAssignExp(); + check(TOK.rightParenthesis); + } + return e; + } /********************************** * Parse Declarations. * These can be: @@ -4377,7 +4373,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer */ private AST.Dsymbols* parseDeclarations(bool autodecl, PrefixAttributes!AST* pAttrs, const(char)* comment) { - StorageClass storage_class = STC.undefined_; + STC storage_class = STC.none; LINK link = linkage; Loc linkloc = this.linkLoc; bool setAlignment = false; @@ -4508,7 +4504,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer if (pAttrs) { storage_class |= pAttrs.storageClass; - //pAttrs.storageClass = STC.undefined_; + //pAttrs.storageClass = STC.none; } AST.Type tfirst = null; @@ -4539,7 +4535,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer if (ident) checkCstyleTypeSyntax(loc, t, alt, ident); else if (!isThis && (t != AST.Type.terror)) - noIdentifierForDeclarator(t); + noIdentifierForDeclarator(t, token); if (isAliasDeclaration) { @@ -4601,6 +4597,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer default: error("semicolon expected to close `alias` declaration, not `%s`", token.toChars()); + nextToken(); break; } } @@ -4613,9 +4610,9 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer AST.Expression constraint = null; //printf("%s funcdecl t = %s, storage_class = x%lx\n", loc.toChars(), t.toChars(), storage_class); - auto f = new AST.FuncDeclaration(loc, Loc.initial, ident, storage_class | (disable ? STC.disable : 0), t); + auto f = new AST.FuncDeclaration(loc, Loc.initial, ident, storage_class | (disable ? STC.disable : STC.none), t); if (pAttrs) - pAttrs.storageClass = STC.undefined_; + pAttrs.storageClass = STC.none; if (tpl) constraint = parseConstraint(); AST.Dsymbol s = parseContracts(f, !!tpl); @@ -4651,7 +4648,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer auto tempdecl = new AST.TemplateDeclaration(loc, tplIdent, tpl, constraint, decldefs); s = tempdecl; - StorageClass stc2 = STC.undefined_; + STC stc2 = STC.none; if (storage_class & STC.static_) { assert(f.storage_class & STC.static_); @@ -4664,7 +4661,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer f.storage_class &= ~STC.deprecated_; stc2 |= STC.deprecated_; } - if (stc2 != STC.undefined_) + if (stc2 != STC.none) { auto ax = new AST.Dsymbols(); ax.push(s); @@ -4704,7 +4701,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer auto v = new AST.VarDeclaration(loc, t, ident, _init); v.storage_class = storage_class; if (pAttrs) - pAttrs.storageClass = STC.undefined_; + pAttrs.storageClass = STC.none; s = v; } @@ -4764,11 +4761,12 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer return a; } - /// Report an error that a declaration of type `t` is missing an identifier + /// Report an error that a declaration of type `t` is missing an identifier and got `tok` instead /// The parser is expected to sit on the next token after the type. - private void noIdentifierForDeclarator(AST.Type t) + private void noIdentifierForDeclarator(AST.Type t, Token tok) { - error("no identifier for declarator `%s`", t.toChars()); + error("variable name expected after type `%s`, not `%s`", t.toChars(), tok.toChars); + // A common mistake is to use a reserved keyword as an identifier, e.g. `in` or `out` if (token.isKeyword) { @@ -4818,7 +4816,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer Loc linkloc = this.linkLoc; AST.Expressions* udas; LINK link = linkage; - StorageClass storage_class = STC.undefined_; + STC storage_class = STC.none; AST.Expression ealign; bool setAlignment = false; @@ -4876,7 +4874,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer return; hasParsedAttributes = true; udas = null; - storage_class = STC.undefined_; + storage_class = STC.none; link = linkage; linkloc = this.linkLoc; setAlignment = false; @@ -4891,7 +4889,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer AST.Dsymbol s; bool attributesAppended; - const StorageClass funcStc = parseTypeCtor(); + const STC funcStc = parseTypeCtor(); Token* tk; // function literal? if (token.value == TOK.function_ || @@ -5046,6 +5044,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer default: error("semicolon expected to close `alias` declaration, not `%s`", token.toChars()); + nextToken(); break; } break; @@ -5063,7 +5062,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer AST.TemplateParameters* tpl = null; AST.ParameterList parameterList; AST.Type tret = null; - StorageClass stc = 0; + STC stc = STC.none; TOK save = TOK.reserved; switch (token.value) @@ -5079,7 +5078,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer { // function auto ref (parameters) { statements... } // delegate auto ref (parameters) { statements... } - stc = STC.auto_ | STC.ref_; + stc = STC.auto_ | STC.ref_ | STC.autoref; nextToken(); } else @@ -5121,7 +5120,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer { // auto ref (parameters) => expression // auto ref (parameters) { statements... } - stc = STC.auto_ | STC.ref_; + stc = STC.auto_ | STC.ref_ | STC.autoref; nextToken(); } else @@ -5142,7 +5141,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer // (parameters) { statements... } parameterList = parseParameterList(&tpl); stc = parsePostfix(stc, null); - if (StorageClass modStc = stc & STC.TYPECTOR) + if (STC modStc = stc & STC.TYPECTOR) { if (save == TOK.function_) { @@ -5231,7 +5230,10 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer error("missing `do { ... }` after `in` or `out`"); const returnloc = token.loc; nextToken(); - f.fbody = new AST.ReturnStatement(returnloc, parseExpression()); + if (f.isCtorDeclaration) + f.fbody = new AST.ExpStatement(returnloc, parseExpression()); + else + f.fbody = new AST.ReturnStatement(returnloc, parseExpression()); f.endloc = token.loc; check(TOK.semicolon); break; @@ -5383,9 +5385,14 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer error("template constraint must follow parameter lists and attributes"); else error("cannot use function constraints for non-template functions. Use `static if` instead"); + + parseConstraint(); } else + { error("semicolon expected following function declaration, not `%s`", token.toChars()); + nextToken(); + } } break; } @@ -5404,7 +5411,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer */ private void checkDanglingElse(Loc elseloc) { - if (token.value != TOK.else_ && token.value != TOK.catch_ && token.value != TOK.finally_ && lookingForElse.linnum != 0) + 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()); } @@ -5482,8 +5489,8 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer AST.Type at; Loc aloc; - StorageClass storageClass = 0; - StorageClass stc = 0; + STC storageClass = STC.none; + STC stc = STC.none; Lagain: if (stc) { @@ -5569,7 +5576,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer } at = parseType(&ai); if (!ai) - noIdentifierForDeclarator(at); + noIdentifierForDeclarator(at, token); Larg: auto p = new AST.Parameter(aloc, storageClass, at, ai, null, null); parameters.push(p); @@ -5652,7 +5659,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer } /*** - * Parse an assignment condition for if or while statements. + * Parse an assignment condition for `if`, `switch` or `while` statements. * * Returns: * The variable that is declared inside the condition @@ -5660,8 +5667,8 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer AST.Parameter parseAssignCondition() { AST.Parameter param = null; - StorageClass storageClass = 0; - StorageClass stc = 0; + STC storageClass = STC.none; + STC stc = STC.none; Lwhile: while (1) { @@ -5678,6 +5685,8 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer case TOK.auto_: stc = STC.auto_; + if (peekNext() == TOK.ref_) + stc |= STC.autoref; break; case TOK.const_: @@ -5842,7 +5851,6 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer case TOK.plusPlus: case TOK.minusMinus: case TOK.new_: - case TOK.delete_: case TOK.delegate_: case TOK.function_: case TOK.typeid_: @@ -5854,6 +5862,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer case TOK.moduleString: case TOK.functionString: case TOK.prettyFunction: + case TOK.rvalue: Lexp: { AST.Expression exp = parseExpression(); @@ -6278,12 +6287,8 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer if (token.value == TOK.assign) { if (auto ds = parseDebugSpecification()) - { - if (ds.ident) - eSink.error(ds.loc, "%s `%s` declaration must be at module level", ds.kind, ds.toPrettyChars); - else - eSink.error(ds.loc, "%s `%s` level declaration must be at module level", ds.kind, ds.toPrettyChars); - } + eSink.error(ds.loc, "%s `%s` declaration must be at module level", ds.kind, ds.toPrettyChars); + break; } cond = parseDebugCondition(); @@ -6294,12 +6299,8 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer if (token.value == TOK.assign) { if (auto vs = parseVersionSpecification()) - { - if (vs.ident) - eSink.error(vs.loc, "%s `%s` declaration must be at module level", vs.kind, vs.toPrettyChars); - else - eSink.error(vs.loc, "%s `%s` level declaration must be at module level", vs.kind, vs.toPrettyChars); - } + eSink.error(vs.loc, "%s `%s` declaration must be at module level", vs.kind, vs.toPrettyChars); + break; } cond = parseVersionCondition(); @@ -6789,7 +6790,6 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer { case TOK.identifier: { - if (commaExpected) error("comma expected separating field initializers"); const t = peek(&token); @@ -6823,6 +6823,20 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer default: if (commaExpected) error("comma expected separating field initializers"); + const t = peek(&token); + if (t.value == TOK.colon) + { + error("incorrect syntax for associative array, expected `[]`, found `{}`"); + while (token.value != TOK.rightCurly && token.value != TOK.endOfFile) + { + nextToken(); + } + if (token.value == TOK.rightCurly) + { + nextToken(); + } + break; + } auto value = parseInitializer(); _is.addInit(null, value); commaExpected = true; @@ -6995,7 +7009,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer Loc labelloc; nextToken(); - StorageClass stc = parsePostfix(STC.undefined_, null); // optional FunctionAttributes + STC stc = parsePostfix(STC.none, null); // optional FunctionAttributes if (stc & (STC.const_ | STC.immutable_ | STC.shared_ | STC.wild)) error("`const`/`immutable`/`shared`/`inout` attributes are not allowed on `asm` blocks"); @@ -8387,6 +8401,15 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer e = new AST.TypeidExp(loc, o); break; } + case TOK.rvalue: + { + nextToken(); + check(TOK.leftParenthesis, "`__rvalue`"); + e = parseAssignExp(); + e.rvalue = true; + check(TOK.rightParenthesis); + break; + } case TOK.traits: { /* __traits(identifier, args...) @@ -8624,7 +8647,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 = new AST.IntegerExp(loc, 0, AST.Type.tint32); + e = AST.ErrorExp.get(); nextToken(); break; } @@ -8688,15 +8711,6 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer e = new AST.ComExp(loc, e); break; - case TOK.delete_: - // @@@DEPRECATED_2.109@@@ - // Use of `delete` keyword has been an error since 2.099. - // Remove from the parser after 2.109. - nextToken(); - e = parseUnaryExp(); - e = new AST.DeleteExp(loc, e, false); - break; - case TOK.cast_: // cast(type) expression { nextToken(); @@ -8763,7 +8777,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer case TOK.const_: case TOK.immutable_: // immutable(type)(arguments) / immutable(type).init { - StorageClass stc = parseTypeCtor(); + STC stc = parseTypeCtor(); AST.Type t = parseBasicType(); t = t.addSTC(stc); @@ -8814,7 +8828,6 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer case TOK.dot: case TOK.plusPlus: case TOK.minusMinus: - case TOK.delete_: case TOK.new_: case TOK.leftParenthesis: case TOK.identifier: @@ -9479,12 +9492,31 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer } /******************************************* + * Params: + * thisexp = If not null, it is the `this` reference for the creation + * of an inner class. + * https://dlang.org/spec/class.html#nested-explicit + * https://dlang.org/spec/expression.html#postfix_expressions + * If null, then it is a NewExpression. + * https://dlang.org/spec/expression.html#NewExpression + * Returns: + * NewExpression */ private AST.Expression parseNewExp(AST.Expression thisexp) { const loc = token.loc; - nextToken(); + nextToken(); // skip past `new` + + // parse PlacementExpression if any + AST.Expression placement; + if (token.value == TOK.leftParenthesis) + { + nextToken(); + placement = parseAssignExp(); + check(TOK.rightParenthesis); + } + AST.Expressions* arguments = null; AST.Identifiers* names = null; @@ -9520,7 +9552,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer } auto cd = new AST.ClassDeclaration(loc, id, baseclasses, members, false); - auto e = new AST.NewAnonClassExp(loc, thisexp, cd, arguments); + auto e = new AST.NewAnonClassExp(loc, placement, thisexp, cd, arguments); return e; } @@ -9544,7 +9576,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer parseNamedArguments(arguments, names); } - auto e = new AST.NewExp(loc, thisexp, t, arguments, names); + auto e = new AST.NewExp(loc, placement, thisexp, t, arguments, names); return e; } @@ -9572,7 +9604,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer * Returns: * storage class for attribute, 0 if not */ - static StorageClass isBuiltinAtAttribute(Identifier ident) + static STC isBuiltinAtAttribute(Identifier ident) { return (ident == Id.property) ? STC.property : (ident == Id.nogc) ? STC.nogc : @@ -9582,10 +9614,10 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer (ident == Id.live) ? STC.live : (ident == Id.future) ? STC.future : (ident == Id.disable) ? STC.disable : - 0; + STC.none; } - enum StorageClass atAttrGroup = + enum STC atAttrGroup = STC.property | STC.nogc | STC.safe | @@ -9782,7 +9814,7 @@ enum ParseStatementFlags : int struct PrefixAttributes(AST) { - StorageClass storageClass; + STC storageClass; AST.Expression depmsg; LINK link; AST.Visibility visibility; @@ -9834,13 +9866,13 @@ private enum CARRAYDECL = 1; /***************************** * Destructively extract storage class from pAttrs. */ -private StorageClass getStorageClass(AST)(PrefixAttributes!(AST)* pAttrs) +private STC getStorageClass(AST)(PrefixAttributes!(AST)* pAttrs) { - StorageClass stc = STC.undefined_; + STC stc = STC.none; if (pAttrs) { stc = pAttrs.storageClass; - pAttrs.storageClass = STC.undefined_; + pAttrs.storageClass = STC.none; } return stc; } diff --git a/dmd/postordervisitor.d b/dmd/postordervisitor.d deleted file mode 100644 index fe189d47e9..0000000000 --- a/dmd/postordervisitor.d +++ /dev/null @@ -1,153 +0,0 @@ -/** - * A depth-first visitor for expressions. - * - * Copyright: Copyright (C) 1999-2024 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/src/dmd/apply.d, _apply.d) - * Documentation: https://dlang.org/phobos/dmd_apply.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/apply.d - */ - -module dmd.postordervisitor; - -import dmd.arraytypes; -import dmd.dtemplate; -import dmd.expression; -import dmd.root.array; -import dmd.visitor; - -bool walkPostorder(Expression e, StoppableVisitor v) -{ - scope PostorderExpressionVisitor pv = new PostorderExpressionVisitor(v); - e.accept(pv); - return v.stop; -} - -/************************************** - * An Expression tree walker that will visit each Expression e in the tree, - * in depth-first evaluation order, and call fp(e,param) on it. - * fp() signals whether the walking continues with its return value: - * Returns: - * 0 continue - * 1 done - * It's a bit slower than using virtual functions, but more encapsulated and less brittle. - * Creating an iterator for this would be much more complex. - */ -private extern (C++) final class PostorderExpressionVisitor : StoppableVisitor -{ - alias visit = typeof(super).visit; -public: - StoppableVisitor v; - - extern (D) this(StoppableVisitor v) scope @safe - { - this.v = v; - } - - bool doCond(Expression e) - { - if (!stop && e) - e.accept(this); - return stop; - } - - extern(D) bool doCond(Expression[] e) - { - for (size_t i = 0; i < e.length && !stop; i++) - doCond(e[i]); - return stop; - } - - bool applyTo(Expression e) - { - e.accept(v); - stop = v.stop; - return true; - } - - override void visit(Expression e) - { - applyTo(e); - } - - override void visit(NewExp e) - { - //printf("NewExp::apply(): %s\n", toChars()); - doCond(e.thisexp) || doCond(e.arguments.peekSlice()) || applyTo(e); - } - - override void visit(NewAnonClassExp e) - { - //printf("NewAnonClassExp::apply(): %s\n", toChars()); - doCond(e.thisexp) || doCond(e.arguments.peekSlice()) || applyTo(e); - } - - override void visit(TypeidExp e) - { - doCond(isExpression(e.obj)) || applyTo(e); - } - - override void visit(UnaExp e) - { - doCond(e.e1) || applyTo(e); - } - - override void visit(BinExp e) - { - doCond(e.e1) || doCond(e.e2) || applyTo(e); - } - - override void visit(AssertExp e) - { - //printf("CallExp::apply(apply_fp_t fp, void *param): %s\n", toChars()); - doCond(e.e1) || doCond(e.msg) || applyTo(e); - } - - override void visit(CallExp e) - { - //printf("CallExp::apply(apply_fp_t fp, void *param): %s\n", toChars()); - doCond(e.e1) || doCond(e.arguments.peekSlice()) || applyTo(e); - } - - override void visit(ArrayExp e) - { - //printf("ArrayExp::apply(apply_fp_t fp, void *param): %s\n", toChars()); - doCond(e.e1) || doCond(e.arguments.peekSlice()) || applyTo(e); - } - - override void visit(SliceExp e) - { - doCond(e.e1) || doCond(e.lwr) || doCond(e.upr) || applyTo(e); - } - - override void visit(ArrayLiteralExp e) - { - doCond(e.basis) || doCond(e.elements.peekSlice()) || applyTo(e); - } - - override void visit(AssocArrayLiteralExp e) - { - doCond(e.keys.peekSlice()) || doCond(e.values.peekSlice()) || applyTo(e); - } - - override void visit(StructLiteralExp e) - { - if (e.stageflags & stageApply) - return; - const old = e.stageflags; - e.stageflags |= stageApply; - doCond(e.elements.peekSlice()) || applyTo(e); - e.stageflags = old; - } - - override void visit(TupleExp e) - { - doCond(e.e0) || doCond(e.exps.peekSlice()) || applyTo(e); - } - - override void visit(CondExp e) - { - doCond(e.econd) || doCond(e.e1) || doCond(e.e2) || applyTo(e); - } -} diff --git a/dmd/pragmasem.d b/dmd/pragmasem.d index 2b70422c8c..d028da1dfe 100644 --- a/dmd/pragmasem.d +++ b/dmd/pragmasem.d @@ -6,9 +6,9 @@ * Copyright: Copyright (C) 1999-2023 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/src/dmd/pragmasem.d, _pragmasem.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/pragmasem.d, _pragmasem.d) * Documentation: https://dlang.org/phobos/dmd_pragmasem.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/pragmasem.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/pragmasem.d */ module dmd.pragmasem; @@ -21,6 +21,7 @@ import dmd.attrib; import dmd.dinterpret; import dmd.dscope; import dmd.dsymbol; +import dmd.dsymbolsem : include; import dmd.errors; import dmd.expression; import dmd.expressionsem; @@ -40,10 +41,10 @@ void pragmaDeclSemantic(PragmaDeclaration pd, Scope* sc) { import dmd.aggregate; import dmd.common.outbuffer; - import dmd.dmangle; import dmd.dmodule; import dmd.dsymbolsem; import dmd.identifier; + import dmd.mangle : isValidMangling; import dmd.root.rmem; import dmd.root.utf; import dmd.target; @@ -581,10 +582,9 @@ package PINLINE evalPragmaInline(Loc loc, Scope* sc, Expressions* args) const opt = e.toBool(); if (opt.isEmpty()) return PINLINE.default_; - else if (opt.get()) + if (opt.get()) return PINLINE.always; - else - return PINLINE.never; + return PINLINE.never; } /** @@ -636,11 +636,9 @@ private bool pragmaMsgSemantic(Loc loc, Scope* sc, Expressions* args) OutBuffer buf; if (expressionsToString(buf, sc, args, loc, "while evaluating `pragma(msg, %s)`", false)) return false; - else - { - buf.writestring("\n"); - fprintf(stderr, "%s", buf.extractChars); - } + + buf.writestring("\n"); + fprintf(stderr, "%s", buf.extractChars); return true; } diff --git a/dmd/printast.d b/dmd/printast.d index 02dc65390b..5e4c9f787f 100644 --- a/dmd/printast.d +++ b/dmd/printast.d @@ -1,12 +1,12 @@ /** * Provides an AST printer for debugging. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/printast.d, _printast.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/printast.d, _printast.d) * Documentation: https://dlang.org/phobos/dmd_printast.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/printast.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/printast.d */ module dmd.printast; @@ -51,6 +51,12 @@ extern (C++) final class PrintASTVisitor : Visitor printf("%.*s %s\n", cast(int)s.length, s.ptr, e.type ? e.type.toChars() : ""); } + override void visit(IdentifierExp e) + { + printIndent(indent); + printf("Identifier `%s` %s\n", e.ident.toChars(), e.type ? e.type.toChars() : ""); + } + override void visit(IntegerExp e) { printIndent(indent); diff --git a/dmd/root/aav.d b/dmd/root/aav.d index 8929679e37..014d4a579c 100644 --- a/dmd/root/aav.d +++ b/dmd/root/aav.d @@ -1,12 +1,12 @@ /** * Associative array implementation. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: Walter Bright, https://www.digitalmars.com * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/root/aav.d, root/_aav.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/root/aav.d, root/_aav.d) * Documentation: https://dlang.org/phobos/dmd_root_aav.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/root/aav.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/root/aav.d */ module dmd.root.aav; diff --git a/dmd/root/array.d b/dmd/root/array.d index 81355774de..a80fc804cb 100644 --- a/dmd/root/array.d +++ b/dmd/root/array.d @@ -2,12 +2,12 @@ /** * Dynamic array implementation. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/root/array.d, root/_array.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/root/array.d, root/_array.d) * Documentation: https://dlang.org/phobos/dmd_root_array.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/root/array.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/root/array.d */ module dmd.root.array; @@ -52,7 +52,7 @@ public: ~this() pure nothrow { debug (stomp) memset(data.ptr, 0xFF, data.length); - if (data.ptr != &smallarray[0]) + if (data.ptr && data.ptr != &smallarray[0]) mem.xfree(data.ptr); } ///returns elements comma separated in [] diff --git a/dmd/root/array.h b/dmd/root/array.h index 3b290cb78c..e7c6838969 100644 --- a/dmd/root/array.h +++ b/dmd/root/array.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2011-2024 by The D Language Foundation, All Rights Reserved +/* Copyright (C) 2011-2025 by The D Language Foundation, All Rights Reserved * written by Walter Bright * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. diff --git a/dmd/root/bitarray.d b/dmd/root/bitarray.d index c32d59eec3..b5adaa8903 100644 --- a/dmd/root/bitarray.d +++ b/dmd/root/bitarray.d @@ -1,12 +1,12 @@ /** * Implementation of a bit array. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/root/bitarray.d, root/_bitarray.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/root/bitarray.d, root/_bitarray.d) * Documentation: https://dlang.org/phobos/dmd_root_array.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/root/bitarray.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/root/bitarray.d */ module dmd.root.bitarray; diff --git a/dmd/root/bitarray.h b/dmd/root/bitarray.h index 0bea2d55a0..c50247f35f 100644 --- a/dmd/root/bitarray.h +++ b/dmd/root/bitarray.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2011-2024 by The D Language Foundation, All Rights Reserved +/* Copyright (C) 2011-2025 by The D Language Foundation, All Rights Reserved * written by Walter Bright * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. diff --git a/dmd/root/complex.d b/dmd/root/complex.d index de4c8d3467..777c103ae9 100644 --- a/dmd/root/complex.d +++ b/dmd/root/complex.d @@ -1,12 +1,12 @@ /** * Implements a complex number type. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/root/complex.d, _complex.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/root/complex.d, _complex.d) * Documentation: https://dlang.org/phobos/dmd_root_complex.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/root/complex.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/root/complex.d */ module dmd.root.complex; diff --git a/dmd/root/complex_t.h b/dmd/root/complex_t.h index 8134f9e259..58a0705398 100644 --- a/dmd/root/complex_t.h +++ b/dmd/root/complex_t.h @@ -1,6 +1,6 @@ /* Compiler implementation of the D programming language - * Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * written by Walter Bright * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. diff --git a/dmd/root/ctfloat.d b/dmd/root/ctfloat.d index 48847a79fd..2996d7cd60 100644 --- a/dmd/root/ctfloat.d +++ b/dmd/root/ctfloat.d @@ -1,12 +1,12 @@ /** * Collects functions for compile-time floating-point calculations. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/root/ctfloat.d, root/_ctfloat.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/root/ctfloat.d, root/_ctfloat.d) * Documentation: https://dlang.org/phobos/dmd_root_ctfloat.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/root/ctfloat.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/root/ctfloat.d */ module dmd.root.ctfloat; diff --git a/dmd/root/ctfloat.h b/dmd/root/ctfloat.h index e672ac78b0..fc4271c8fc 100644 --- a/dmd/root/ctfloat.h +++ b/dmd/root/ctfloat.h @@ -1,5 +1,5 @@ -/* Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved +/* Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * written by Walter Bright * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. diff --git a/dmd/root/dcompat.h b/dmd/root/dcompat.h index af1ce8e0f0..306333e41f 100644 --- a/dmd/root/dcompat.h +++ b/dmd/root/dcompat.h @@ -1,5 +1,5 @@ -/* Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved +/* Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * written by Walter Bright * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. diff --git a/dmd/root/dsystem.h b/dmd/root/dsystem.h index 76cb6ea875..aee288a419 100644 --- a/dmd/root/dsystem.h +++ b/dmd/root/dsystem.h @@ -1,6 +1,6 @@ /* Compiler implementation of the D programming language - * Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * written by Walter Bright * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. diff --git a/dmd/root/file.d b/dmd/root/file.d index 971abe102d..caa67cab12 100644 --- a/dmd/root/file.d +++ b/dmd/root/file.d @@ -1,12 +1,12 @@ /** * Read a file from disk and store it in memory. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: Walter Bright, https://www.digitalmars.com * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/root/file.d, root/_file.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/root/file.d, root/_file.d) * Documentation: https://dlang.org/phobos/dmd_root_file.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/root/file.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/root/file.d */ module dmd.root.file; diff --git a/dmd/root/filename.d b/dmd/root/filename.d index 40c259b87a..692cbf92dd 100644 --- a/dmd/root/filename.d +++ b/dmd/root/filename.d @@ -1,12 +1,12 @@ /** * Encapsulate path and file names. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: Walter Bright, https://www.digitalmars.com * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/root/filename.d, root/_filename.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/root/filename.d, root/_filename.d) * Documentation: https://dlang.org/phobos/dmd_root_filename.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/root/filename.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/root/filename.d */ module dmd.root.filename; @@ -42,7 +42,8 @@ version (Windows) extern (Windows) DWORD GetFullPathNameW(LPCWSTR, DWORD, LPWSTR, LPWSTR*) nothrow @nogc; extern (Windows) void SetLastError(DWORD) nothrow @nogc; - extern (C) char* getcwd(char* buffer, size_t maxlen) nothrow; + extern (C) char* _getcwd(char* buffer, size_t maxlen) nothrow; + alias getcwd = _getcwd; } version (CRuntime_Glibc) @@ -277,7 +278,7 @@ nothrow: * Returns: * the slice */ - extern (D) static const(char)[] sansExt(const char[] filename) + extern (D) static const(char)[] sansExt(const char[] filename) @safe { auto e = ext(filename); size_t length = e.length; @@ -591,13 +592,13 @@ nothrow: /*************************** * Free returned value with FileName::free() */ - extern (C++) static const(char)* defaultExt(const(char)* name, const(char)* ext) + extern (C++) static const(char)* defaultExt(const(char)* name, const(char)* ext) pure { return defaultExt(name.toDString, ext.toDString).ptr; } /// Ditto - extern (D) static const(char)[] defaultExt(const char[] name, const char[] ext) + extern (D) static const(char)[] defaultExt(const char[] name, const char[] ext) pure { auto e = FileName.ext(name); if (e.length) // it already has an extension @@ -615,13 +616,13 @@ nothrow: /*************************** * Free returned value with FileName::free() */ - extern (C++) static const(char)* forceExt(const(char)* name, const(char)* ext) + extern (C++) static const(char)* forceExt(const(char)* name, const(char)* ext) pure { return forceExt(name.toDString, ext.toDString).ptr; } /// Ditto - extern (D) static const(char)[] forceExt(const char[] name, const char[] ext) + extern (D) static const(char)[] forceExt(const char[] name, const char[] ext) pure { if (auto e = FileName.ext(name)) return addExt(name[0 .. $ - e.length - 1], ext); @@ -876,10 +877,9 @@ nothrow: const dw = GetFileAttributesW(&wname[0]); if (dw == -1) return 0; - else if (dw & FILE_ATTRIBUTE_DIRECTORY) + if (dw & FILE_ATTRIBUTE_DIRECTORY) return 2; - else - return 1; + return 1; }); } else @@ -1101,7 +1101,7 @@ nothrow: return str.ptr; } - const(char)[] toString() const pure nothrow @nogc @trusted + const(char)[] toString() const pure nothrow @nogc @safe { return str; } diff --git a/dmd/root/filename.h b/dmd/root/filename.h index 5f13887086..c9894fac7f 100644 --- a/dmd/root/filename.h +++ b/dmd/root/filename.h @@ -1,5 +1,5 @@ -/* Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved +/* Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * written by Walter Bright * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. diff --git a/dmd/root/hash.d b/dmd/root/hash.d index 441620e60b..d327f4b258 100644 --- a/dmd/root/hash.d +++ b/dmd/root/hash.d @@ -1,12 +1,12 @@ /** * Hash functions for arbitrary binary data. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: Martin Nowak, Walter Bright, https://www.digitalmars.com * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/root/hash.d, root/_hash.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/root/hash.d, root/_hash.d) * Documentation: https://dlang.org/phobos/dmd_root_hash.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/root/hash.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/root/hash.d */ module dmd.root.hash; diff --git a/dmd/root/longdouble.d b/dmd/root/longdouble.d index c4451c3fd8..0bbd27a39e 100644 --- a/dmd/root/longdouble.d +++ b/dmd/root/longdouble.d @@ -1,7 +1,7 @@ /** * 80-bit floating point value implementation if the C/D compiler does not support them natively. * - * Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * All Rights Reserved, written by Rainer Schuetze * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. @@ -170,25 +170,23 @@ nothrow @nogc pure: T opCast(T)() const @trusted { static if (is(T == bool)) return mantissa != 0 || (exp_sign & 0x7fff) != 0; - else static if (is(T == byte)) return cast(T)ld_read(&this); - else static if (is(T == ubyte)) return cast(T)ld_read(&this); - else static if (is(T == short)) return cast(T)ld_read(&this); - else static if (is(T == ushort)) return cast(T)ld_read(&this); - else static if (is(T == int)) return cast(T)ld_read(&this); - else static if (is(T == uint)) return cast(T)ld_read(&this); - else static if (is(T == float)) return cast(T)ld_read(&this); - else static if (is(T == double)) return cast(T)ld_read(&this); + else static if (is(T == byte) || is(T == ubyte) || + is(T == short) || is(T == ushort) || + is(T == int) || is(T == uint)) + return cast(T)ld_read(&this); + else static if (is(T == float) || is(T == double)) + return cast(T)ld_read(&this); else static if (is(T == long)) return ld_readll(&this); else static if (is(T == ulong)) return ld_readull(&this); else static if (is(T == real)) { // convert to front end real if built with dmd - if (real.sizeof > 8) + static if (real.sizeof > 8) return *cast(real*)&this; else return ld_read(&this); } - else static assert(false, "usupported type"); + else static assert(false, "unsupported type"); } } @@ -217,24 +215,18 @@ version(LDC) extern(D): private: - string fld_arg (string arg)() { return `__asm("fldt $0", "*m,~{st}", &` ~ arg ~ `);`; } - string fstp_arg (string arg)() { return `__asm("fstpt $0", "=*m,~{st}", &` ~ arg ~ `);`; } - string fld_parg (string arg)() { return `__asm("fldt $0", "*m,~{st}", ` ~ arg ~ `);`; } - string fstp_parg(string arg)() { return `__asm("fstpt $0", "=*m,~{st}", ` ~ arg ~ `);`; } + enum fld_arg(string arg) = `__asm("fldt $0", "*m,~{st}", &` ~ arg ~ `);`; + enum fstp_arg(string arg) = `__asm("fstpt $0", "=*m,~{st}", &` ~ arg ~ `);`; + enum fld_parg(string arg) = `__asm("fldt $0", "*m,~{st}", ` ~ arg ~ `);`; + enum fstp_parg(string arg) = `__asm("fstpt $0", "=*m,~{st}", ` ~ arg ~ `);`; } else version(D_InlineAsm_X86_64) { // longdouble_soft passed by reference extern(D): private: - string fld_arg(string arg)() - { - return "asm nothrow @nogc pure @trusted { mov RAX, " ~ arg ~ "; fld real ptr [RAX]; }"; - } - string fstp_arg(string arg)() - { - return "asm nothrow @nogc pure @trusted { mov RAX, " ~ arg ~ "; fstp real ptr [RAX]; }"; - } + enum fld_arg(string arg) = "asm nothrow @nogc pure @trusted { mov RAX, " ~ arg ~ "; fld real ptr [RAX]; }"; + enum fstp_arg(string arg) = "asm nothrow @nogc pure @trusted { mov RAX, " ~ arg ~ "; fstp real ptr [RAX]; }"; alias fld_parg = fld_arg; alias fstp_parg = fstp_arg; } @@ -243,22 +235,10 @@ else version(D_InlineAsm_X86) // longdouble_soft passed by value extern(D): private: - string fld_arg(string arg)() - { - return "asm nothrow @nogc pure @trusted { lea EAX, " ~ arg ~ "; fld real ptr [EAX]; }"; - } - string fstp_arg(string arg)() - { - return "asm nothrow @nogc pure @trusted { lea EAX, " ~ arg ~ "; fstp real ptr [EAX]; }"; - } - string fld_parg(string arg)() - { - return "asm nothrow @nogc pure @trusted { mov EAX, " ~ arg ~ "; fld real ptr [EAX]; }"; - } - string fstp_parg(string arg)() - { - return "asm nothrow @nogc pure @trusted { mov EAX, " ~ arg ~ "; fstp real ptr [EAX]; }"; - } + enum fld_arg(string arg) = "asm nothrow @nogc pure @trusted { lea EAX, " ~ arg ~ "; fld real ptr [EAX]; }"; + enum fstp_arg(string arg) = "asm nothrow @nogc pure @trusted { lea EAX, " ~ arg ~ "; fstp real ptr [EAX]; }"; + enum fld_parg(string arg) = "asm nothrow @nogc pure @trusted { mov EAX, " ~ arg ~ "; fld real ptr [EAX]; }"; + enum fstp_parg(string arg) = "asm nothrow @nogc pure @trusted { mov EAX, " ~ arg ~ "; fstp real ptr [EAX]; }"; } double ld_read(const longdouble_soft* pthis) @@ -266,7 +246,7 @@ double ld_read(const longdouble_soft* pthis) double res; version(AsmX86) { - mixin(fld_parg!("pthis")); + mixin(fld_parg!"pthis"); asm nothrow @nogc pure @trusted { fstp res; @@ -287,13 +267,11 @@ ulong ld_readull(const longdouble_soft* pthis) // so we roll our own conversion (it also allows the usual C wrap-around // instead of the "invalid value" created by the FPU) int expo = pthis.exponent - 0x3fff; - ulong u; if(expo < 0 || expo > 127) return 0; - if(expo < 64) - u = pthis.mantissa >> (63 - expo); - else - u = pthis.mantissa << (expo - 63); + ulong u = expo < 64 ? + pthis.mantissa >> (63 - expo) : + pthis.mantissa << (expo - 63); if(pthis.sign) u = ~u + 1; return u; @@ -320,7 +298,7 @@ void ld_set(longdouble_soft* pthis, double d) { fld d; } - mixin(fstp_parg!("pthis")); + mixin(fstp_parg!"pthis"); } } @@ -332,7 +310,7 @@ void ld_setll(longdouble_soft* pthis, long d) { fild qword ptr d; } - mixin(fstp_parg!("pthis")); + mixin(fstp_parg!"pthis"); } } @@ -353,13 +331,13 @@ void ld_setull(longdouble_soft* pthis, ulong d) } d ^= (1L << 63); auto pTwoPow63 = &twoPow63; - mixin(fld_parg!("pTwoPow63")); + mixin(fld_parg!"pTwoPow63"); asm nothrow @nogc pure @trusted { fild qword ptr d; faddp; } - mixin(fstp_parg!("pthis")); + mixin(fstp_parg!"pthis"); } } @@ -372,13 +350,13 @@ longdouble_soft ldexpl(longdouble_soft ld, int exp) { fild dword ptr exp; } - mixin(fld_arg!("ld")); + mixin(fld_arg!"ld"); asm nothrow @nogc pure @trusted { fscale; // ST(0) = ST(0) * (2**ST(1)) fstp ST(1); } - mixin(fstp_arg!("ld")); + mixin(fstp_arg!"ld"); } return ld; } @@ -388,13 +366,13 @@ longdouble_soft ld_add(longdouble_soft ld1, longdouble_soft ld2) { version(AsmX86) { - mixin(fld_arg!("ld1")); - mixin(fld_arg!("ld2")); + mixin(fld_arg!"ld1"); + mixin(fld_arg!"ld2"); asm nothrow @nogc pure @trusted { fadd; } - mixin(fstp_arg!("ld1")); + mixin(fstp_arg!"ld1"); } return ld1; } @@ -403,13 +381,13 @@ longdouble_soft ld_sub(longdouble_soft ld1, longdouble_soft ld2) { version(AsmX86) { - mixin(fld_arg!("ld1")); - mixin(fld_arg!("ld2")); + mixin(fld_arg!"ld1"); + mixin(fld_arg!"ld2"); asm nothrow @nogc pure @trusted { fsub; } - mixin(fstp_arg!("ld1")); + mixin(fstp_arg!"ld1"); } return ld1; } @@ -418,13 +396,13 @@ longdouble_soft ld_mul(longdouble_soft ld1, longdouble_soft ld2) { version(AsmX86) { - mixin(fld_arg!("ld1")); - mixin(fld_arg!("ld2")); + mixin(fld_arg!"ld1"); + mixin(fld_arg!"ld2"); asm nothrow @nogc pure @trusted { fmul; } - mixin(fstp_arg!("ld1")); + mixin(fstp_arg!"ld1"); } return ld1; } @@ -433,13 +411,13 @@ longdouble_soft ld_div(longdouble_soft ld1, longdouble_soft ld2) { version(AsmX86) { - mixin(fld_arg!("ld1")); - mixin(fld_arg!("ld2")); + mixin(fld_arg!"ld1"); + mixin(fld_arg!"ld2"); asm nothrow @nogc pure @trusted { fdiv; } - mixin(fstp_arg!("ld1")); + mixin(fstp_arg!"ld1"); } return ld1; } @@ -450,8 +428,8 @@ bool ld_cmpb(longdouble_soft x, longdouble_soft y) bool res; version(AsmX86) { - mixin(fld_arg!("y")); - mixin(fld_arg!("x")); + mixin(fld_arg!"y"); + mixin(fld_arg!"x"); asm nothrow @nogc pure @trusted { fucomip ST(1); @@ -471,8 +449,8 @@ bool ld_cmpbe(longdouble_soft x, longdouble_soft y) bool res; version(AsmX86) { - mixin(fld_arg!("y")); - mixin(fld_arg!("x")); + mixin(fld_arg!"y"); + mixin(fld_arg!"x"); asm nothrow @nogc pure @trusted { fucomip ST(1); @@ -492,8 +470,8 @@ bool ld_cmpa(longdouble_soft x, longdouble_soft y) bool res; version(AsmX86) { - mixin(fld_arg!("y")); - mixin(fld_arg!("x")); + mixin(fld_arg!"y"); + mixin(fld_arg!"x"); asm nothrow @nogc pure @trusted { fucomip ST(1); @@ -513,8 +491,8 @@ bool ld_cmpae(longdouble_soft x, longdouble_soft y) bool res; version(AsmX86) { - mixin(fld_arg!("y")); - mixin(fld_arg!("x")); + mixin(fld_arg!"y"); + mixin(fld_arg!"x"); asm nothrow @nogc pure @trusted { fucomip ST(1); @@ -534,8 +512,8 @@ bool ld_cmpe(longdouble_soft x, longdouble_soft y) bool res; version(AsmX86) { - mixin(fld_arg!("y")); - mixin(fld_arg!("x")); + mixin(fld_arg!"y"); + mixin(fld_arg!"x"); asm nothrow @nogc pure @trusted { fucomip ST(1); @@ -555,8 +533,8 @@ bool ld_cmpne(longdouble_soft x, longdouble_soft y) bool res; version(AsmX86) { - mixin(fld_arg!("y")); - mixin(fld_arg!("x")); + mixin(fld_arg!"y"); + mixin(fld_arg!"x"); asm nothrow @nogc pure @trusted { fucomip ST(1); @@ -577,8 +555,8 @@ int ld_cmp(longdouble_soft x, longdouble_soft y) int res; version(AsmX86) { - mixin(fld_arg!("y")); - mixin(fld_arg!("x")); + mixin(fld_arg!"y"); + mixin(fld_arg!"x"); asm nothrow @nogc pure @trusted { fucomip ST(1); @@ -611,12 +589,12 @@ longdouble_soft sqrtl(longdouble_soft ld) { version(AsmX86) { - mixin(fld_arg!("ld")); + mixin(fld_arg!"ld"); asm nothrow @nogc pure @trusted { fsqrt; } - mixin(fstp_arg!("ld")); + mixin(fstp_arg!"ld"); } return ld; } @@ -627,12 +605,12 @@ longdouble_soft sinl (longdouble_soft ld) { version(AsmX86) { - mixin(fld_arg!("ld")); + mixin(fld_arg!"ld"); asm nothrow @nogc pure @trusted { fsin; // exact for |x|<=PI/4 } - mixin(fstp_arg!("ld")); + mixin(fstp_arg!"ld"); } return ld; } @@ -640,12 +618,12 @@ longdouble_soft cosl (longdouble_soft ld) { version(AsmX86) { - mixin(fld_arg!("ld")); + mixin(fld_arg!"ld"); asm nothrow @nogc pure @trusted { fcos; // exact for |x|<=PI/4 } - mixin(fstp_arg!("ld")); + mixin(fstp_arg!"ld"); } return ld; } @@ -653,13 +631,13 @@ longdouble_soft tanl (longdouble_soft ld) { version(AsmX86) { - mixin(fld_arg!("ld")); + mixin(fld_arg!"ld"); asm nothrow @nogc pure @trusted { fptan; fstp ST(0); // always 1 } - mixin(fstp_arg!("ld")); + mixin(fstp_arg!"ld"); } return ld; } @@ -674,8 +652,8 @@ longdouble_soft ld_mod(longdouble_soft x, longdouble_soft y) short sw; version(AsmX86) { - mixin(fld_arg!("y")); - mixin(fld_arg!("x")); + mixin(fld_arg!"y"); + mixin(fld_arg!"x"); asm nothrow @nogc pure @trusted { FM1: // We don't use fprem1 because for some inexplicable @@ -688,7 +666,7 @@ longdouble_soft ld_mod(longdouble_soft x, longdouble_soft y) jp FM1; // continue till ST < ST1 fstp ST(1); // leave remainder on stack } - mixin(fstp_arg!("x")); + mixin(fstp_arg!"x"); } return x; } diff --git a/dmd/root/longdouble.h b/dmd/root/longdouble.h index 8671678d65..150b9f55e1 100644 --- a/dmd/root/longdouble.h +++ b/dmd/root/longdouble.h @@ -1,5 +1,5 @@ -/* Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved +/* Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * written by Rainer Schuetze * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. diff --git a/dmd/root/man.d b/dmd/root/man.d index 8a84edf8f4..2ec7de7615 100644 --- a/dmd/root/man.d +++ b/dmd/root/man.d @@ -1,12 +1,12 @@ /** * Open an online manual page. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: Walter Bright, https://www.digitalmars.com * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/root/man.d, root/_man.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/root/man.d, root/_man.d) * Documentation: https://dlang.org/phobos/dmd_root_man.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/root/man.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/root/man.d */ module dmd.root.man; diff --git a/dmd/root/optional.d b/dmd/root/optional.d index e7d0e1ef78..2b518eb8b1 100644 --- a/dmd/root/optional.d +++ b/dmd/root/optional.d @@ -1,12 +1,12 @@ /** * Implementation of an 'Optional' type * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/root/optional.d, root/_optional.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/root/optional.d, root/_optional.d) * Documentation: https://dlang.org/phobos/dmd_root_optional.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/root/optional.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/root/optional.d */ module dmd.root.optional; diff --git a/dmd/root/optional.h b/dmd/root/optional.h index a92deddc63..12891f879b 100644 --- a/dmd/root/optional.h +++ b/dmd/root/optional.h @@ -3,12 +3,12 @@ /** * Optional implementation. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/root/optional.h, root/_optional.h) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/root/optional.h, root/_optional.h) * Documentation: https://dlang.org/phobos/dmd_root_optional.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/root/optional.h + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/root/optional.h */ #include "dcompat.h" // for d_bool diff --git a/dmd/root/port.d b/dmd/root/port.d index 7a3e4920b6..b94476e5c0 100644 --- a/dmd/root/port.d +++ b/dmd/root/port.d @@ -1,12 +1,12 @@ /** * Portable routines for functions that have different implementations on different platforms. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: Walter Bright, https://www.digitalmars.com * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/root/port.d, root/_port.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/root/port.d, root/_port.d) * Documentation: https://dlang.org/phobos/dmd_root_port.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/root/port.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/root/port.d */ module dmd.root.port; diff --git a/dmd/root/port.h b/dmd/root/port.h index 6c7dddd43a..dfb56b0fef 100644 --- a/dmd/root/port.h +++ b/dmd/root/port.h @@ -1,5 +1,5 @@ -/* Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved +/* Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * written by Walter Bright * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. diff --git a/dmd/root/region.d b/dmd/root/region.d index a9fab16253..a8efbca1e6 100644 --- a/dmd/root/region.d +++ b/dmd/root/region.d @@ -1,12 +1,12 @@ /** * Region storage allocator implementation. * - * Copyright: Copyright (C) 2019-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 2019-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/src/dmd/root/region.d, root/_region.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/root/region.d, root/_region.d) * Documentation: https://dlang.org/phobos/dmd_root_region.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/root/region.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/root/region.d */ module dmd.root.region; diff --git a/dmd/root/rmem.d b/dmd/root/rmem.d index 04bdc22ad1..5e218ba9c6 100644 --- a/dmd/root/rmem.d +++ b/dmd/root/rmem.d @@ -1,12 +1,12 @@ /** * Allocate memory using `malloc` or the GC depending on the configuration. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: Walter Bright, https://www.digitalmars.com * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/root/rmem.d, root/_rmem.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/root/rmem.d, root/_rmem.d) * Documentation: https://dlang.org/phobos/dmd_root_rmem.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/root/rmem.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/root/rmem.d */ module dmd.root.rmem; @@ -157,7 +157,7 @@ enum CHUNK_SIZE = (256 * 4096 - 64); __gshared size_t heapleft = 0; __gshared void* heapp; -version (IN_LLVM) __gshared size_t heaptotal = 0; // Total amount of memory allocated using malloc +__gshared size_t heapTotal = 0; // Total amount of memory allocated using malloc extern (D) void* allocmemoryNoFree(size_t m_size) nothrow @nogc { @@ -176,13 +176,13 @@ extern (D) void* allocmemoryNoFree(size_t m_size) nothrow @nogc if (m_size > CHUNK_SIZE) { - version (IN_LLVM) heaptotal += m_size; + heapTotal += m_size; return Mem.check(malloc(m_size)); } heapleft = CHUNK_SIZE; heapp = Mem.check(malloc(CHUNK_SIZE)); - version (IN_LLVM) heaptotal += CHUNK_SIZE; + heapTotal += CHUNK_SIZE; goto L1; } diff --git a/dmd/root/rmem.h b/dmd/root/rmem.h index 09c0fc07cc..b6645ec129 100644 --- a/dmd/root/rmem.h +++ b/dmd/root/rmem.h @@ -1,5 +1,5 @@ -/* Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved +/* Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * written by Walter Bright * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. diff --git a/dmd/root/speller.d b/dmd/root/speller.d index ae09cba37f..a2151106d0 100644 --- a/dmd/root/speller.d +++ b/dmd/root/speller.d @@ -3,12 +3,12 @@ * * Does not have any dependencies on the rest of DMD. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: Walter Bright, https://www.digitalmars.com * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/root/speller.d, root/_speller.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/root/speller.d, root/_speller.d) * Documentation: https://dlang.org/phobos/dmd_root_speller.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/root/speller.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/root/speller.d */ module dmd.root.speller; diff --git a/dmd/root/string.d b/dmd/root/string.d index 4b4c3e1f64..369a79be66 100644 --- a/dmd/root/string.d +++ b/dmd/root/string.d @@ -1,12 +1,12 @@ /** * Contains various string related functions. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: Walter Bright, https://www.digitalmars.com * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/root/string.d, root/_string.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/root/string.d, root/_string.d) * Documentation: https://dlang.org/phobos/dmd_root_string.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/root/string.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/root/string.d */ module dmd.root.string; @@ -20,6 +20,27 @@ inout(char)[] toDString (inout(char)* s) pure nothrow @nogc return s ? s[0 .. strlen(s)] : null; } +private struct FTuple(T...) +{ + T expand; +} + +/// Returns: a (length, ptr) tuple for passing a D string to `printf`-style functions with the format string `%.*s` +auto fTuple(const(char)[] str) +{ + return FTuple!(int, const(char)*)(cast(int) str.length, str.ptr); +} + +/// +unittest +{ + import core.stdc.stdio: snprintf; + char[6] buf = '.'; + const(char)[] str = "cutoff"[0..4]; + snprintf(buf.ptr, buf.length, "%.*s", str.fTuple.expand); + assert(buf[] == "cuto\0."); +} + /** Compare two slices for equality, in a case-insensitive way @@ -344,13 +365,23 @@ auto splitLines(const char[] text) public this(const char[] text) { this.text = text; + this.index = 0; + this.eolIndex = 0; + this.nextIndex = 0; } - public bool empty() { return index == text.length; } + public bool empty() { advance(); return index >= text.length; } public void popFront() { advance(); index = nextIndex; } - public const(char)[] front() { advance(); return text[index .. eolIndex]; } + public const(char)[] front() + { + advance(); + if (index > eolIndex || index >= text.length) + return ""; + + return text[index .. eolIndex]; + } private void advance() { @@ -397,7 +428,7 @@ auto splitLines(const char[] text) if (i + 2 < text.length && text[i + 1] == 0x80 && (text[i + 2] == 0xA8 || text[i + 2] == 0xA9) - ) + ) { eolIndex = i; nextIndex = i + 3; @@ -409,6 +440,10 @@ auto splitLines(const char[] text) break; } } + + // No newline found; set indices to the end of the text + eolIndex = text.length; + nextIndex = text.length; } } @@ -433,7 +468,7 @@ Returns: a `FindSplit` object that casts to `true` iff `needle` was found inside `str`. In that case, `split[1]` is the needle, and `split[0]`/`split[2]` are before/after the needle. */ -FindSplit findSplit(return scope const(char)[] str, scope const(char)[] needle) +FindSplit findSplit(return scope const(char)[] str, scope const(char)[] needle) @safe { if (needle.length > str.length) return FindSplit([str, null, null]); @@ -469,7 +504,7 @@ Params: Returns: substring of `str` inbetween `l` and `r` */ -const(char)[] findBetween(const(char)[] str, const(char)[] l, const(char)[] r) +const(char)[] findBetween(const(char)[] str, const(char)[] l, const(char)[] r) @safe { if (auto s0 = str.findSplit(l)) if (auto s1 = s0[2].findSplit(r)) diff --git a/dmd/root/stringtable.d b/dmd/root/stringtable.d index 1fba919dec..c7a2c8c56d 100644 --- a/dmd/root/stringtable.d +++ b/dmd/root/stringtable.d @@ -1,12 +1,12 @@ /** * A specialized associative array with string keys stored in a variable length structure. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: Walter Bright, https://www.digitalmars.com * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/root/stringtable.d, root/_stringtable.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/root/stringtable.d, root/_stringtable.d) * Documentation: https://dlang.org/phobos/dmd_root_stringtable.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/root/stringtable.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/root/stringtable.d */ module dmd.root.stringtable; diff --git a/dmd/root/utf.d b/dmd/root/utf.d index 36f0b985c3..5b2c42f4ee 100644 --- a/dmd/root/utf.d +++ b/dmd/root/utf.d @@ -1,12 +1,12 @@ /** * Functions related to UTF encoding. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/root/utf.d, _utf.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/root/utf.d, _utf.d) * Documentation: https://dlang.org/phobos/dmd_root_utf.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/root/utf.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/root/utf.d */ module dmd.root.utf; diff --git a/dmd/rootobject.d b/dmd/rootobject.d index 7c926fea05..71b36a4375 100644 --- a/dmd/rootobject.d +++ b/dmd/rootobject.d @@ -1,12 +1,12 @@ /** * Provide the root object that AST classes in dmd inherit from. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: Walter Bright, https://www.digitalmars.com * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/rootobject.d, _rootobject.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/rootobject.d, _rootobject.d) * Documentation: https://dlang.org/phobos/dmd_rootobject.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/rootobject.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/rootobject.d */ module dmd.rootobject; diff --git a/dmd/rootobject.h b/dmd/rootobject.h index 718a54f027..330d2c99b2 100644 --- a/dmd/rootobject.h +++ b/dmd/rootobject.h @@ -1,5 +1,5 @@ -/* Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved +/* Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * written by Walter Bright * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. diff --git a/dmd/safe.d b/dmd/safe.d index f1bd6c98c8..3be9efecc2 100644 --- a/dmd/safe.d +++ b/dmd/safe.d @@ -3,12 +3,12 @@ * * Specification: $(LINK2 https://dlang.org/spec/function.html#function-safety, Function Safety) * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/safe.d, _safe.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/safe.d, _safe.d) * Documentation: https://dlang.org/phobos/dmd_safe.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/safe.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/safe.d */ module dmd.safe; @@ -17,18 +17,27 @@ import core.stdc.stdio; import dmd.aggregate; import dmd.astenums; +import dmd.common.outbuffer; import dmd.dcast : implicitConvTo; import dmd.dclass; import dmd.declaration; import dmd.dscope; +import dmd.dsymbol; +import dmd.dsymbolsem : determineSize; +import dmd.errors; import dmd.expression; +import dmd.func; +import dmd.funcsem : isRootTraitsCompilesScope; +import dmd.globals : FeatureState, global; import dmd.id; import dmd.identifier; +import dmd.location; import dmd.mtype; +import dmd.rootobject; +import dmd.root.string : fTuple; import dmd.target; import dmd.tokens; import dmd.typesem : hasPointers, arrayOf, size; -import dmd.funcsem : setUnsafe, setUnsafePreview; /************************************************************* * Check for unsafe access in @safe code: @@ -51,102 +60,102 @@ bool checkUnsafeAccess(Scope* sc, Expression e, bool readonly, bool printmsg) if (e.op != EXP.dotVariable) return false; auto dve = cast(DotVarExp)e; - if (VarDeclaration v = dve.var.isVarDeclaration()) - { - if (!sc.func) - return false; - auto ad = v.isMember2(); - if (!ad) - return false; + VarDeclaration v = dve.var.isVarDeclaration(); + if (!v) + return false; + if (!sc.func) + return false; + auto ad = v.isMember2(); + if (!ad) + return false; - import dmd.globals : global; - if (v.isSystem()) - { - if (sc.setUnsafePreview(global.params.systemVariables, !printmsg, e.loc, - "cannot access `@system` field `%s.%s` in `@safe` code", ad, v)) - return true; - } + if (v.isSystem()) + { + if (sc.setUnsafePreview(sc.previews.systemVariables, !printmsg, e.loc, + "accessing `@system` field `%s.%s`", ad, v)) + return true; + } - // This branch shouldn't be here, but unfortunately calling `ad.determineSize` - // breaks code with circular reference errors. Specifically, test23589.d fails - if (ad.sizeok != Sizeok.done && !sc.func.isSafeBypassingInference()) - return false; + // This branch shouldn't be here, but unfortunately calling `ad.determineSize` + // breaks code with circular reference errors. Specifically, test23589.d fails + if (ad.sizeok != Sizeok.done && !sc.func.isSafeBypassingInference()) + return false; - // needed to set v.overlapped and v.overlapUnsafe - if (ad.sizeok != Sizeok.done) - ad.determineSize(ad.loc); + // needed to set v.overlapped and v.overlapUnsafe + if (ad.sizeok != Sizeok.done) + ad.determineSize(ad.loc); - import dmd.globals : FeatureState; - const hasPointers = v.type.hasPointers(); - if (hasPointers) + import dmd.globals : FeatureState; + const hasPointers = v.type.hasPointers(); + if (hasPointers) + { + if (v.overlapped) { - if (v.overlapped) + if (sc.func.isSafeBypassingInference() && sc.setUnsafe(!printmsg, e.loc, + "accessing overlapped field `%s.%s` with pointers", ad, v)) { - if (sc.func.isSafeBypassingInference() && sc.setUnsafe(!printmsg, e.loc, - "field `%s.%s` cannot access pointers in `@safe` code that overlap other fields", ad, v)) - { - return true; - } - else - { - // @@@DEPRECATED_2.116@@@ - // https://issues.dlang.org/show_bug.cgi?id=20655 - // Inferring `@system` because of union access breaks code, - // so make it a deprecation safety violation as of 2.106 - // To turn into an error, remove `isSafeBypassingInference` check in the - // above if statement and remove the else branch - sc.setUnsafePreview(FeatureState.default_, !printmsg, e.loc, - "field `%s.%s` cannot access pointers in `@safe` code that overlap other fields", ad, v); - } + return true; } - } - - if (v.type.hasInvariant()) - { - if (v.overlapped) + else { - if (sc.setUnsafe(!printmsg, e.loc, - "field `%s.%s` cannot access structs with invariants in `@safe` code that overlap other fields", - ad, v)) - return true; + // @@@DEPRECATED_2.116@@@ + // https://issues.dlang.org/show_bug.cgi?id=20655 + // Inferring `@system` because of union access breaks code, + // so make it a deprecation safety violation as of 2.106 + // To turn into an error, remove `isSafeBypassingInference` check in the + // above if statement and remove the else branch + sc.setUnsafePreview(FeatureState.default_, !printmsg, e.loc, + "accessing overlapped field `%s.%s` with pointers", ad, v); } } + } - // @@@DEPRECATED_2.119@@@ - // https://issues.dlang.org/show_bug.cgi?id=24477 - // Should probably be turned into an error in a new edition - if (v.type.hasUnsafeBitpatterns() && v.overlapped && sc.setUnsafePreview( - FeatureState.default_, !printmsg, e.loc, - "cannot access overlapped field `%s.%s` with unsafe bit patterns in `@safe` code", ad, v) - ) + if (v.type.hasInvariant()) + { + if (v.overlapped) { - return true; + if (sc.setUnsafe(!printmsg, e.loc, + "accessing overlapped field `%s.%s` with a structs invariant", + ad, v)) + return true; } + } - if (readonly || !e.type.isMutable()) - return false; + // @@@DEPRECATED_2.119@@@ + // https://issues.dlang.org/show_bug.cgi?id=24477 + // Should probably be turned into an error in a new edition + if (v.type.hasUnsafeBitpatterns() && v.overlapped && sc.setUnsafePreview( + FeatureState.default_, !printmsg, e.loc, + "accessing overlapped field `%s.%s` with unsafe bit patterns", ad, v) + ) + { + return true; + } - if (hasPointers && v.type.toBasetype().ty != Tstruct) - { - if ((!ad.type.alignment.isDefault() && ad.type.alignment.get() < target.ptrsize || - (v.offset & (target.ptrsize - 1)))) - { - if (sc.setUnsafe(!printmsg, e.loc, - "field `%s.%s` cannot modify misaligned pointers in `@safe` code", ad, v)) - return true; - } - } + if (readonly || !e.type.isMutable()) + return false; - if (v.overlapUnsafe) + if (hasPointers && v.type.toBasetype().ty != Tstruct) + { + if ((!ad.type.alignment.isDefault() && ad.type.alignment.get() < target.ptrsize) || + (v.offset & (target.ptrsize - 1))) { if (sc.setUnsafe(!printmsg, e.loc, - "field `%s.%s` cannot modify fields in `@safe` code that overlap fields with other storage classes", - ad, v)) - { + "modifying misaligned pointers through field `%s.%s`", ad, v)) return true; - } } } + + if (v.overlapUnsafe) + { + if (sc.setUnsafe(!printmsg, e.loc, + "modifying field `%s.%s` which overlaps with fields with other storage classes", + ad, v)) + { + return true; + } + } + return false; } @@ -173,6 +182,10 @@ bool isSafeCast(Expression e, Type tfrom, Type tto, ref string msg) auto tfromb = tfrom.toBasetype(); auto ttob = tto.toBasetype(); + // Casting to void* is always safe, https://github.com/dlang/dmd/issues/20514 + if (ttob.isTypePointer() && ttob.nextOf().toBasetype().ty == Tvoid) + return true; + if (ttob.ty == Tclass && tfromb.ty == Tclass) { ClassDeclaration cdfrom = tfromb.isClassHandle(); @@ -302,9 +315,245 @@ bool checkUnsafeDotExp(Scope* sc, Expression e, Identifier id, int flag) if (!(flag & DotExpFlag.noDeref)) // this use is attempting a dereference { if (id == Id.ptr) - return sc.setUnsafe(false, e.loc, "`%s.ptr` cannot be used in `@safe` code, use `&%s[0]` instead", e, e); - else - return sc.setUnsafe(false, e.loc, "`%s.%s` cannot be used in `@safe` code", e, id); + return sc.setUnsafe(false, e.loc, "using `%s.ptr` (instead of `&%s[0])`", e, e); + return sc.setUnsafe(false, e.loc, "using `%s.%s`", e, id); + } + return false; +} + +/************************************** + * Safer D adds safety checks to functions with the default + * trust setting. + */ +bool isSaferD(FuncDeclaration fd) +{ + return fd.type.toTypeFunction().trust == TRUST.default_ && fd.saferD; +} + +bool isSafe(FuncDeclaration fd) +{ + if (fd.safetyInprocess) + setFunctionToUnsafe(fd); + return fd.type.toTypeFunction().trust == TRUST.safe; +} + +extern (D) bool isSafeBypassingInference(FuncDeclaration fd) +{ + return !(fd.safetyInprocess) && fd.isSafe(); +} + +bool isTrusted(FuncDeclaration fd) +{ + if (fd.safetyInprocess) + setFunctionToUnsafe(fd); + return fd.type.toTypeFunction().trust == TRUST.trusted; +} + +/***************************************************** + * Report safety violation for function `fd`, or squirrel away + * error message in fd.safetyViolation if needed later. + * 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 + * gag = suppress error message (used in escape.d) + * loc = location of error + * format = printf-style format string + * args = arguments for %s format specifier + */ +extern (D) void reportSafeError(FuncDeclaration fd, bool gag, Loc loc, + 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); + } + else if (args.length > 0) + { + if (FuncDeclaration fd2 = (cast(Dsymbol) args[0]).isFuncDeclaration()) + { + fd.safetyViolation = new AttributeViolation(loc, fd2); // call to non-@nogc function + } + } + } + else if (fd.isSafe() || fd.isSaferD()) + { + if (!gag && format) + { + OutBuffer buf; + buf.writestring(AttributeViolation(loc, format, args).action); + if (fd.isSafe()) + 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`"); + } + .error(loc, "%s", buf.extractChars()); + } } +} + + +/********************************************** + * Function is doing something unsafe. If inference + * is in process, commit the function to be @system. + * Params: + * fd = the naughty function + * Returns: + * true if this is a safe function and so an error OR is inferred to be @system, + * false otherwise. + */ +extern (D) bool setFunctionToUnsafe(FuncDeclaration fd) +{ + if (fd.safetyInprocess) + { + fd.safetyInprocess = false; + fd.type.toTypeFunction().trust = TRUST.system; + + if (fd.fes) + setFunctionToUnsafe(fd.fes.func); + return true; + } + else if (fd.isSafe() || fd.isSaferD()) + return true; return false; } + + +/************************************** + * The function is calling `@system` function `f`, so mark it as unsafe. + * + * Params: + * fd = caller + * f = function being called (needed for diagnostic of inferred functions) + * Returns: whether there's a safe error + */ +extern (D) bool setUnsafeCall(FuncDeclaration fd, FuncDeclaration f) +{ + if (setFunctionToUnsafe(fd)) + { + reportSafeError(fd, false, f.loc, null, f, null); + return fd.isSafe(); + } + return false; +} + +/************************************** + * A statement / expression in this scope is not `@safe`, + * so mark the enclosing function as `@system` + * + * Params: + * sc = scope that the unsafe statement / expression is in + * gag = surpress error message (used in escape.d) + * loc = location of error + * format = printf-style format string + * args = arguments for format string + * Returns: whether there is a safe error + */ +bool setUnsafe(Scope* sc, bool gag, Loc loc, const(char)* format, RootObject[] args...) +{ + if (sc.intypeof) + return false; // typeof(cast(int*)0) is safe + + if (sc.debug_) // debug {} scopes are permissive + return false; + + if (!sc.func) + { + if (sc.varDecl) + { + if (sc.varDecl.storage_class & STC.safe) + { + string action = AttributeViolation(loc, format, args).action; + .error(loc, "%.*s can't initialize `@safe` variable `%s`", action.fTuple.expand, sc.varDecl.toChars()); + return true; + } + else if (!(sc.varDecl.storage_class & STC.trusted)) + { + sc.varDecl.storage_class |= STC.system; + sc.varDecl.systemInferred = true; + } + } + return false; + } + + + if (isRootTraitsCompilesScope(sc)) // __traits(compiles, x) + { + if (sc.func.isSafeBypassingInference()) + { + // Message wil be gagged, but still call error() to update global.errors and for + // -verrors=spec + string action = AttributeViolation(loc, format, args).action; + .error(loc, "%.*s is not allowed in a `@safe` function", action.fTuple.expand); + return true; + } + return false; + } + + if (setFunctionToUnsafe(sc.func)) + { + if (format || args.length > 0) + { + reportSafeError(sc.func, gag, loc, format, args); + } + return sc.func.isSafe(); // it is only an error if in an @safe function + } + return false; +} + +/*************************************** + * Like `setUnsafe`, but for safety errors still behind preview switches + * + * Given a `FeatureState fs`, for example dip1000 / dip25 / systemVariables, + * the behavior changes based on the setting: + * + * - In case of `-revert=fs`, it does nothing. + * - In case of `-preview=fs`, it's the same as `setUnsafe` + * - By default, print a deprecation in `@safe` functions, or store an attribute violation in inferred functions. + * + * Params: + * sc = used to find affected function/variable, and for checking whether we are in a deprecated / speculative scope + * fs = feature state from the preview flag + * gag = surpress error message + * loc = location of error + * format = printf-style format string + * args = arguments for format string + * Returns: whether an actual safe error (not deprecation) occured + */ +bool setUnsafePreview(Scope* sc, FeatureState fs, bool gag, Loc loc, const(char)* format, RootObject[] args...) +{ + //printf("setUnsafePreview() fs:%d %s\n", fs, fmt); + assert(format); + with (FeatureState) final switch (fs) + { + case disabled: + return false; + + case enabled: + return sc.setUnsafe(gag, loc, format, args); + + case default_: + if (!sc.func) + return false; + if (sc.func.isSafeBypassingInference()) + { + if (!gag && !sc.isDeprecated()) + { + string action = AttributeViolation(loc, format, args).action; + deprecation(loc, "%.*s will become `@system` in a future release", action.fTuple.expand); + } + } + else if (!sc.func.safetyViolation) + { + import dmd.func : AttributeViolation; + sc.func.safetyViolation = new AttributeViolation(loc, format, args); + } + return false; + } +} diff --git a/dmd/sapply.d b/dmd/sapply.d deleted file mode 100644 index 340fbad78d..0000000000 --- a/dmd/sapply.d +++ /dev/null @@ -1,179 +0,0 @@ -/** - * Provides a depth-first statement visitor. - * - * Copyright: Copyright (C) 1999-2024 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/src/dmd/sparse.d, _sparse.d) - * Documentation: https://dlang.org/phobos/dmd_sapply.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/sapply.d - */ - -module dmd.sapply; - -import dmd.statement; -import dmd.visitor; - -bool walkPostorder(Statement s, StoppableVisitor v) -{ - scope PostorderStatementVisitor pv = new PostorderStatementVisitor(v); - s.accept(pv); - return v.stop; -} - -/************************************** - * A Statement tree walker that will visit each Statement s in the tree, - * in depth-first evaluation order, and call fp(s,param) on it. - * fp() signals whether the walking continues with its return value: - * Returns: - * 0 continue - * 1 done - * It's a bit slower than using virtual functions, but more encapsulated and less brittle. - * Creating an iterator for this would be much more complex. - */ -private extern (C++) final class PostorderStatementVisitor : StoppableVisitor -{ - alias visit = typeof(super).visit; -public: - StoppableVisitor v; - - extern (D) this(StoppableVisitor v) scope @safe - { - this.v = v; - } - - bool doCond(Statement s) - { - if (!stop && s) - s.accept(this); - return stop; - } - - bool applyTo(Statement s) - { - s.accept(v); - stop = v.stop; - return true; - } - - override void visit(Statement s) - { - applyTo(s); - } - - override void visit(PeelStatement s) - { - doCond(s.s) || applyTo(s); - } - - override void visit(CompoundStatement s) - { - for (size_t i = 0; i < s.statements.length; i++) - if (doCond((*s.statements)[i])) - return; - applyTo(s); - } - - override void visit(UnrolledLoopStatement s) - { - for (size_t i = 0; i < s.statements.length; i++) - if (doCond((*s.statements)[i])) - return; - applyTo(s); - } - - override void visit(ScopeStatement s) - { - doCond(s.statement) || applyTo(s); - } - - override void visit(WhileStatement s) - { - doCond(s._body) || applyTo(s); - } - - override void visit(DoStatement s) - { - doCond(s._body) || applyTo(s); - } - - override void visit(ForStatement s) - { - doCond(s._init) || doCond(s._body) || applyTo(s); - } - - override void visit(ForeachStatement s) - { - doCond(s._body) || applyTo(s); - } - - override void visit(ForeachRangeStatement s) - { - doCond(s._body) || applyTo(s); - } - - override void visit(IfStatement s) - { - doCond(s.ifbody) || doCond(s.elsebody) || applyTo(s); - } - - override void visit(PragmaStatement s) - { - doCond(s._body) || applyTo(s); - } - - override void visit(SwitchStatement s) - { - doCond(s._body) || applyTo(s); - } - - override void visit(CaseStatement s) - { - doCond(s.statement) || applyTo(s); - } - - override void visit(DefaultStatement s) - { - doCond(s.statement) || applyTo(s); - } - - override void visit(SynchronizedStatement s) - { - doCond(s._body) || applyTo(s); - } - - override void visit(WithStatement s) - { - doCond(s._body) || applyTo(s); - } - - override void visit(TryCatchStatement s) - { - if (doCond(s._body)) - return; - for (size_t i = 0; i < s.catches.length; i++) - if (doCond((*s.catches)[i].handler)) - return; - applyTo(s); - } - - override void visit(TryFinallyStatement s) - { - doCond(s._body) || doCond(s.finalbody) || applyTo(s); - } - - override void visit(ScopeGuardStatement s) - { - doCond(s.statement) || applyTo(s); - } - - override void visit(DebugStatement s) - { - doCond(s.statement) || applyTo(s); - } - - override void visit(LabelStatement s) - { - doCond(s.statement) || applyTo(s); - } -} diff --git a/dmd/sarif.d b/dmd/sarif.d new file mode 100644 index 0000000000..ba2867874b --- /dev/null +++ b/dmd/sarif.d @@ -0,0 +1,364 @@ +/** + * Provides SARIF (Static Analysis Results Interchange Format) reporting functionality. + * + * 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/sarif.d, sarif.d) + * Coverage: $(LINK2 https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/sarif.d, Code Coverage) + * + * Description: + * - This module generates SARIF reports for DMD errors, warnings, and messages. + * - It supports JSON serialization of SARIF tools, results, and invocations. + * - The generated reports are compatible with SARIF 2.1.0 schema. + */ + +module dmd.sarif; + +import core.stdc.stdarg; +import core.stdc.stdio; +import core.stdc.string; +import dmd.errorsink; +import dmd.globals; +import dmd.location; +import dmd.common.outbuffer; +import dmd.root.rmem; +import dmd.console; +import dmd.errors; + +/// Contains information about the tool used for analysis in SARIF reports. +struct ToolInformation { + string name; /// Name of the tool. + string toolVersion; /// Version of the tool. + + /// Converts the tool information to a JSON string. + /// + /// Returns: + /// - A JSON representation of the tool's name and version. + string toJson() nothrow { + OutBuffer buffer; + buffer.writestring(`{"name": "`); + buffer.writestring(name); + buffer.writestring(`", "version": "`); + buffer.writestring(toolVersion); + buffer.writestring(`"}`); + return cast(string) buffer.extractChars()[0 .. buffer.length()].dup; + } +} + +/** +Converts an integer to a string. + +Params: + value = The integer value to convert. + +Returns: + A string representation of the integer. +*/ +string intToString(int value) nothrow { + char[32] buffer; + import core.stdc.stdio : sprintf; + sprintf(buffer.ptr, "%d", value); + return buffer[0 .. buffer.length].dup; +} + +/// Represents a SARIF result containing a rule ID, message, and location. +struct SarifResult +{ + string ruleId; /// Rule identifier. + string message; /// Error or warning message. + string uri; /// URI of the affected file. + int startLine; /// Line number where the issue occurs. + int startColumn; /// Column number where the issue occurs. + + /// Converts the SARIF result to a JSON string. + /// + /// Returns: + /// - A JSON string representing the SARIF result, including the rule ID, message, and location. + string toJson() nothrow + { + OutBuffer buffer; + buffer.writestring(`{"ruleId": "`); + buffer.writestring(ruleId); + buffer.writestring(`", "message": "`); + buffer.writestring(message); + buffer.writestring(`", "location": {"artifactLocation": {"uri": "`); + buffer.writestring(uri); + buffer.writestring(`"}, "region": {"startLine": `); + buffer.writestring(intToString(startLine)); + buffer.writestring(`, "startColumn": `); + buffer.writestring(intToString(startColumn)); + buffer.writestring(`}}}`); + return cast(string) buffer.extractChars()[0 .. buffer.length()].dup; + } +} + +/** +Adds a SARIF diagnostic entry to the diagnostics list. + +Formats a diagnostic message and appends it to the global diagnostics array, allowing errors, warnings, or other diagnostics to be captured in SARIF format. + +Params: + loc = The location in the source code where the diagnostic was generated (includes file, line, and column). + format = The printf-style format string for the diagnostic message. + ap = The variadic argument list containing values to format into the diagnostic message. + kind = The type of diagnostic, indicating whether it is an error, warning, deprecation, etc. +*/ +void addSarifDiagnostic(const SourceLoc loc, const(char)* format, va_list ap, ErrorKind kind) nothrow +{ + char[1024] buffer; + int written = vsnprintf(buffer.ptr, buffer.length, format, ap); + + // Handle any truncation + string formattedMessage = cast(string) buffer[0 .. (written < 0 || written > buffer.length ? buffer.length : written)].dup; + + // Add the Diagnostic to the global diagnostics array + diagnostics ~= Diagnostic(loc, formattedMessage, kind); +} + +/// Represents a SARIF report containing tool information, invocation, and results. +struct SarifReport +{ + ToolInformation tool; /// Information about the analysis tool. + Invocation invocation; /// Execution information. + SarifResult[] results; /// List of SARIF results (errors, warnings, etc.). + + /// Converts the SARIF report to a JSON string. + /// + /// Returns: + /// - A JSON string representing the SARIF report, including the tool information, invocation, and results. + string toJson() nothrow + { + OutBuffer buffer; + buffer.writestring(`{"tool": `); + buffer.writestring(tool.toJson()); + buffer.writestring(`, "invocation": `); + buffer.writestring(invocation.toJson()); + buffer.writestring(`, "results": [`); + if (results.length > 0) + { + buffer.writestring(results[0].toJson()); + foreach (result; results[1 .. $]) + { + buffer.writestring(`, `); + buffer.writestring(result.toJson()); + } + } + buffer.writestring(`]}`); + return cast(string) buffer.extractChars()[0 .. buffer.length()].dup; + } +} + +/// Represents invocation information for the analysis process. +struct Invocation +{ + bool executionSuccessful; /// Whether the execution was successful. + + /// Converts the invocation information to a JSON string. + /// + /// Returns: + /// - A JSON representation of the invocation status. + string toJson() nothrow + { + OutBuffer buffer; + buffer.writestring(`{"executionSuccessful": `); + buffer.writestring(executionSuccessful ? "true" : "false"); + buffer.writestring(`}`); + return cast(string) buffer.extractChars()[0 .. buffer.length()].dup; + } +} + +/** +Formats an error message using a format string and a variable argument list. + +Params: + format = The format string to use. + ap = A variable argument list for the format string. + +Returns: + A formatted error message string. +*/ +string formatErrorMessage(const(char)* format, va_list ap) nothrow +{ + char[2048] buffer; + import core.stdc.stdio : vsnprintf; + vsnprintf(buffer.ptr, buffer.length, format, ap); + return buffer[0 .. buffer.length].dup; +} + +/** +Converts an `ErrorKind` value to a SARIF-compatible string representation for the severity level. + +Params: + kind = The `ErrorKind` value to convert (e.g., error, warning, deprecation). + +Returns: + A SARIF-compatible string representing the `ErrorKind` level, such as "error" or "warning". +*/ +string errorKindToString(ErrorKind kind) nothrow +{ + final switch (kind) + { + case ErrorKind.error: return "error"; // Serious problem + case ErrorKind.warning: return "warning"; // Problem found + case ErrorKind.deprecation: return "note"; // Minor problem, opportunity for improvement + case ErrorKind.tip: return "note"; // Minor improvement suggestion + case ErrorKind.message: return "none"; // Not applicable for "fail" kind, so use "none" + } +} + +/** +Generates a SARIF (Static Analysis Results Interchange Format) report and prints it to `stdout`. + +This function constructs a JSON-formatted SARIF report that includes information about the tool used (such as compiler version and URI), the invocation status (indicating whether the execution was successful), and a detailed array of diagnostics (results) when `executionSuccessful` is set to `false`. Each diagnostic entry in the results array contains the rule identifier (`ruleId`), a text message describing the issue (`message`), the severity level (`level`), and the location of the issue in the source code, including the file path, line number, and column number. The SARIF report adheres to the SARIF 2.1.0 standard. + +Params: + executionSuccessful = `true` for an empty `results` array; `false` for detailed errors. + +Throws: + This function is marked as `nothrow` and does not throw exceptions. + +See_Also: + $(LINK2 https://schemastore.azurewebsites.net/schemas/json/sarif-2.1.0.json, SARIF 2.1.0 schema) +*/ +void generateSarifReport(bool executionSuccessful) nothrow +{ + // Create an OutBuffer to store the SARIF report + OutBuffer ob; + ob.doindent = true; + + // Extract and clean the version string + string toolVersion = global.versionString(); + // Remove 'v' prefix if it exists + if (toolVersion.length > 0 && toolVersion[0] == 'v') + toolVersion = toolVersion[1 .. $]; + + // Find the first non-numeric character after the version number + size_t length = toolVersion.length; + const(char)* nonNumeric = strchr(toolVersion.ptr, '-'); + if (nonNumeric) + length = cast(size_t)(nonNumeric - toolVersion.ptr); + + string cleanedVersion = toolVersion[0 .. length]; + + // Build SARIF report + ob.writestringln("{"); + ob.level = 1; + + ob.writestringln(`"version": "2.1.0",`); + ob.writestringln(`"$schema": "https://schemastore.azurewebsites.net/schemas/json/sarif-2.1.0.json",`); + ob.writestringln(`"runs": [{`); + + // Tool Information + ob.level += 1; + ob.writestringln(`"tool": {`); + ob.level += 1; + ob.writestringln(`"driver": {`); + ob.level += 1; + + // Write "name" field + ob.writestring(`"name": "`); + ob.writestring(global.compileEnv.vendor.ptr); + ob.writestringln(`",`); + + // Write "version" field + ob.writestring(`"version": "`); + ob.writestring(cleanedVersion); + ob.writestringln(`",`); + + // Write "informationUri" field + ob.writestringln(`"informationUri": "https://dlang.org/dmd.html"`); + ob.level -= 1; + ob.writestringln("}"); + ob.level -= 1; + ob.writestringln("},"); + + // Invocation Information + ob.writestringln(`"invocations": [{`); + ob.level += 1; + ob.writestring(`"executionSuccessful": `); + ob.writestring(executionSuccessful ? "true" : "false"); + ob.writestringln(""); + ob.level -= 1; + ob.writestringln("}],"); + + // Results Array + ob.writestringln(`"results": [`); + ob.level += 1; + + foreach (idx, diag; diagnostics) + { + ob.writestringln("{"); + ob.level += 1; + + // Rule ID + ob.writestring(`"ruleId": "DMD-` ~ errorKindToString(diag.kind) ~ `",`); + ob.writestringln(""); + + // Message Information + ob.writestringln(`"message": {`); + ob.level += 1; + ob.writestring(`"text": "`); + ob.writestring(diag.message); + ob.writestringln(`"`); + ob.level -= 1; + ob.writestringln("},"); + + // Error Severity Level + ob.writestring(`"level": "` ~ errorKindToString(diag.kind) ~ `",`); + ob.writestringln(""); + + // Location Information + ob.writestringln(`"locations": [{`); + ob.level += 1; + ob.writestringln(`"physicalLocation": {`); + ob.level += 1; + + // Artifact Location + ob.writestringln(`"artifactLocation": {`); + ob.level += 1; + ob.writestring(`"uri": "`); + ob.writestring(diag.loc.filename); + ob.writestringln(`"`); + ob.level -= 1; + ob.writestringln("},"); + + // Region Information + ob.writestringln(`"region": {`); + ob.level += 1; + ob.writestring(`"startLine": `); + ob.printf(`%d,`, diag.loc.linnum); + ob.writestringln(""); + ob.writestring(`"startColumn": `); + ob.printf(`%d`, diag.loc.charnum); + ob.writestringln(""); + ob.level -= 1; + ob.writestringln("}"); + + // Close physicalLocation and locations + ob.level -= 1; + ob.writestringln("}"); + ob.level -= 1; + ob.writestringln("}]"); + + // Closing brace for each diagnostic item + ob.level -= 1; + if (idx < diagnostics.length - 1) + ob.writestringln("},"); + else + ob.writestringln("}"); + } + + // Close the run and SARIF JSON + ob.level -= 1; + ob.writestringln("]"); + ob.level -= 1; + ob.writestringln("}]"); + ob.level -= 1; + ob.writestringln("}"); + + // Extract the final null-terminated string and print it to stdout + const(char)* sarifOutput = ob.extractChars(); + fputs(sarifOutput, stdout); + fflush(stdout); +} diff --git a/dmd/scope.h b/dmd/scope.h index 652c169193..58ccb46101 100644 --- a/dmd/scope.h +++ b/dmd/scope.h @@ -1,6 +1,6 @@ /* Compiler implementation of the D programming language - * Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * written by Walter Bright * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. @@ -40,33 +40,15 @@ enum class CSX : uint16_t halt = 0x20, // assert(0) }; -enum class SCOPE +enum class Contract : uint8_t { - // Flags that would not be inherited beyond scope nesting - ctor = 0x0001, // constructor type - noaccesscheck = 0x0002, // don't do access checks - condition = 0x0004, // inside static if/assert condition - debug_ = 0x0008, // inside debug conditional - - // Flags that would be inherited beyond scope nesting - constraint = 0x0010, // inside template constraint - invariant_ = 0x0020, // inside invariant code - require = 0x0040, // inside in contract code - ensure = 0x0060, // inside out contract code - contract = 0x0060, // [mask] we're inside contract code - ctfe = 0x0080, // inside a ctfe-only expression - compile = 0x0100, // inside __traits(compile) - ignoresymbolvisibility = 0x0200, // ignore symbol visibility (Bugzilla 15907) - - Cfile = 0x0800, // C semantics apply - free = 0x8000, // is on free list - fullinst = 0x10000, // fully instantiate templates - ctfeBlock = 0x20000, // inside a `if (__ctfe)` block - dip1000 = 0x40000, // dip1000 errors enabled for this scope - dip25 = 0x80000, // dip25 errors enabled for this scope + none = 0u, + invariant_ = 1u, + require = 2u, + ensure = 3u, }; -struct Scope +struct Scope final { Scope *enclosing; // enclosing Scope @@ -76,10 +58,10 @@ struct Scope VarDeclaration *varDecl; // variable we are in during semantic2 Dsymbol *parent; // parent to use LabelStatement *slabel; // enclosing labelled statement - SwitchStatement *sw; // enclosing switch statement + SwitchStatement *switchStatement; // enclosing switch statement Statement *tryBody; // enclosing _body of TryCatchStatement or TryFinallyStatement - TryFinallyStatement *tf; // enclosing try finally statement - ScopeGuardStatement *os; // enclosing scope(xxx) statement + TryFinallyStatement *tryFinally; // enclosing try finally statement + ScopeGuardStatement *scopeGuard; // enclosing scope(xxx) statement Statement *sbreak; // enclosing statement that supports "break" Statement *scontinue; // enclosing statement that supports "continue" ForeachStatement *fes; // if nested function for ForeachStatement, this is it @@ -123,7 +105,35 @@ struct Scope DeprecatedDeclaration *depdecl; // customized deprecation message - unsigned flags; + uint16_t flags; + uint16_t previews; // state of preview switches + + bool ctor() const; + bool ctor(bool v); + bool noAccessCheck() const; + bool noAccessCheck(bool v); + bool condition() const; + bool condition(bool v); + bool debug_() const; + bool debug_(bool v); + bool inTemplateConstraint() const; + bool inTemplateConstraint(bool v); + Contract contract() const; + Contract contract(Contract v); + bool ctfe() const; + bool ctfe(bool v); + bool traitsCompiles() const; + bool traitsCompiles(bool v); + bool ignoresymbolvisibility() const; + bool ignoresymbolvisibility(bool v); + bool inCfile() const; + bool inCfile(bool v); + bool canFree() const; + bool canFree(bool v); + bool fullinst() const; + bool fullinst(bool v); + bool ctfeBlock() const; + bool ctfeBlock(bool v); UserAttributeDeclaration *userAttribDecl; // user defined attributes @@ -133,6 +143,7 @@ struct Scope AliasDeclaration *aliasAsg; // if set, then aliasAsg is being assigned a new value, // do not set wasRead for it + StructDeclaration *argStruct; // elimiate recursion when looking for rvalue construction - Dsymbol *search(const Loc &loc, Identifier *ident, Dsymbol *&pscopesym, SearchOptFlags flags = (SearchOptFlags)SearchOpt::all); + Dsymbol *search(Loc loc, Identifier *ident, Dsymbol *&pscopesym, SearchOptFlags flags = (SearchOptFlags)SearchOpt::all); }; diff --git a/dmd/semantic2.d b/dmd/semantic2.d index 97f939282b..1c58e63936 100644 --- a/dmd/semantic2.d +++ b/dmd/semantic2.d @@ -1,12 +1,12 @@ /** * Performs the semantic2 stage, which deals with initializer expressions. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/semantic2.d, _semantic2.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/semantic2.d, _semantic2.d) * Documentation: https://dlang.org/phobos/dmd_semantic2.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/semantic2.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/semantic2.d */ module dmd.semantic2; @@ -21,6 +21,7 @@ import dmd.astcodegen; import dmd.astenums; import dmd.attrib; import dmd.blockexit; +import dmd.timetrace; import dmd.clone; import dmd.dcast; import dmd.dclass; @@ -56,6 +57,7 @@ import dmd.parse; import dmd.root.filename; import dmd.common.outbuffer; import dmd.root.rmem; +import dmd.root.string : toDString; import dmd.rootobject; import dmd.root.utf; import dmd.sideeffect; @@ -75,20 +77,10 @@ enum LOG = false; * Does semantic analysis on initializers and members of aggregates. */ void semantic2(Dsymbol dsym, Scope* sc) -{ -version (IN_LLVM) -{ - import driver.timetrace_sema; - scope v = new Semantic2Visitor(sc); - scope vtimetrace = new SemanticTimeTraceVisitor!Semantic2Visitor(v); - dsym.accept(vtimetrace); -} -else { scope v = new Semantic2Visitor(sc); dsym.accept(v); } -} private extern(C++) final class Semantic2Visitor : Visitor { @@ -128,43 +120,7 @@ private extern(C++) final class Semantic2Visitor : Visitor else if (result) return; - if (sa.msgs) - { - OutBuffer msgbuf; - for (size_t i = 0; i < sa.msgs.length; i++) - { - Expression e = (*sa.msgs)[i]; - sc = sc.startCTFE(); - e = e.expressionSemantic(sc); - e = resolveProperties(sc, e); - sc = sc.endCTFE(); - e = ctfeInterpretForPragmaMsg(e); - if (e.op == EXP.error) - { - errorSupplemental(sa.loc, "while evaluating `static assert` argument `%s`", (*sa.msgs)[i].toChars()); - return; - } - StringExp se = e.toStringExp(); - if (se) - { - const slice = se.toUTF8(sc).peekString(); - // Hack to keep old formatting to avoid changing error messages everywhere - if (sa.msgs.length == 1) - msgbuf.printf("\"%.*s\"", cast(int)slice.length, slice.ptr); - else - msgbuf.printf("%.*s", cast(int)slice.length, slice.ptr); - } - else - msgbuf.printf("%s", e.toChars()); - } - error(sa.loc, "static assert: %s", msgbuf.extractChars()); - } - else - error(sa.loc, "static assert: `%s` is false", sa.exp.toChars()); - if (sc.tinst) - sc.tinst.printInstantiationTrace(); - if (!global.gag) - fatal(); + staticAssertFail(sa, sc); } override void visit(TemplateInstance tempinst) @@ -190,11 +146,9 @@ private extern(C++) final class Semantic2Visitor : Visitor sc.tinst = tempinst; sc.minst = tempinst.minst; - int needGagging = (tempinst.gagged && !global.gag); - uint olderrors = global.errors; - int oldGaggedErrors = -1; // dead-store to prevent spurious warning - if (needGagging) - oldGaggedErrors = global.startGagging(); + const needGagging = (tempinst.gagged && !global.gag); + const olderrors = global.errors; + const oldGaggedErrors = needGagging ? global.startGagging() : -1; for (size_t i = 0; i < tempinst.members.length; i++) { @@ -274,7 +228,7 @@ private extern(C++) final class Semantic2Visitor : Visitor return; } - UserAttributeDeclaration.checkGNUABITag(vd, vd._linkage); + checkGNUABITag(vd, vd._linkage); if (vd._init && !vd.toParent().isFuncDeclaration()) { @@ -292,7 +246,7 @@ private extern(C++) final class Semantic2Visitor : Visitor // https://issues.dlang.org/show_bug.cgi?id=14166 // https://issues.dlang.org/show_bug.cgi?id=20417 // Don't run CTFE for the temporary variables inside typeof or __traits(compiles) - vd._init = vd._init.initializerSemantic(sc, vd.type, sc.intypeof == 1 || sc.flags & SCOPE.compile ? INITnointerpret : INITinterpret); + vd._init = vd._init.initializerSemantic(sc, vd.type, sc.intypeof == 1 || sc.traitsCompiles ? INITnointerpret : INITinterpret); lowerStaticAAs(vd, sc); vd.inuse--; } @@ -340,7 +294,7 @@ private extern(C++) final class Semantic2Visitor : Visitor { // Cannot initialize a thread-local class or pointer to struct variable with a literal // that itself is a thread-local reference and would need dynamic initialization also. - if ((vd.type.ty == Tclass) && vd.type.isMutable() && !vd.type.isShared()) + if (vd.type.ty == Tclass && vd.type.isMutable() && !vd.type.isShared()) { ExpInitializer ei = vd._init.isExpInitializer(); if (ei && ei.exp.op == EXP.classReference) @@ -403,6 +357,9 @@ private extern(C++) final class Semantic2Visitor : Visitor assert(fd.semanticRun <= PASS.semantic2); fd.semanticRun = PASS.semantic2; + timeTraceBeginEvent(TimeTraceEventType.sema2); + scope(exit) timeTraceEndEvent(TimeTraceEventType.sema2, fd); + //printf("FuncDeclaration::semantic2 [%s] fd: %s type: %s\n", fd.loc.toChars(), fd.toChars(), fd.type ? fd.type.toChars() : "".ptr); // Only check valid functions which have a body to avoid errors @@ -512,7 +469,7 @@ private extern(C++) final class Semantic2Visitor : Visitor return; TypeFunction f = cast(TypeFunction) fd.type; - UserAttributeDeclaration.checkGNUABITag(fd, fd._linkage); + checkGNUABITag(fd, fd._linkage); //semantic for parameters' UDAs foreach (i, param; f.parameterList) { @@ -546,7 +503,7 @@ private extern(C++) final class Semantic2Visitor : Visitor printf("+Nspace::semantic2('%s')\n", ns.toChars()); scope(exit) printf("-Nspace::semantic2('%s')\n", ns.toChars()); } - UserAttributeDeclaration.checkGNUABITag(ns, LINK.cpp); + checkGNUABITag(ns, LINK.cpp); if (!ns.members) return; @@ -604,7 +561,7 @@ private extern(C++) final class Semantic2Visitor : Visitor override void visit(CPPNamespaceDeclaration decl) { - UserAttributeDeclaration.checkGNUABITag(decl, LINK.cpp); + checkGNUABITag(decl, LINK.cpp); visit(cast(AttribDeclaration)decl); } @@ -631,7 +588,7 @@ private extern(C++) final class Semantic2Visitor : Visitor } // Handles compiler-recognized `core.attribute.gnuAbiTag` - if (UserAttributeDeclaration.isGNUABITag(e)) + if (isGNUABITag(e)) doGNUABITagSemantic(e, lastTag); } } @@ -653,8 +610,7 @@ private extern(C++) final class Semantic2Visitor : Visitor return; } - UserAttributeDeclaration.checkGNUABITag( - ad, ad.classKind == ClassKind.cpp ? LINK.cpp : LINK.d); + checkGNUABITag(ad, ad.classKind == ClassKind.cpp ? LINK.cpp : LINK.d); auto sc2 = ad.newScope(sc); @@ -761,8 +717,7 @@ private extern(C++) final class Semantic2Visitor : Visitor */ private void doGNUABITagSemantic(ref Expression e, ref Expression* lastTag) { - import dmd.dmangle; - + import dmd.mangle : isValidMangling; // When `@gnuAbiTag` is used, the type will be the UDA, not the struct literal if (e.op == EXP.type) { @@ -897,6 +852,8 @@ private extern(C++) final class StaticAAVisitor : SemanticTimeTransitiveVisitor loweredExp = loweredExp.ctfeInterpret(); aaExp.lowering = loweredExp; + + semanticTypeInfo(sc, loweredExp.type); } // https://issues.dlang.org/show_bug.cgi?id=24602 @@ -906,3 +863,51 @@ private extern(C++) final class StaticAAVisitor : SemanticTimeTransitiveVisitor this.visit(crExp.value); } } + +/** + * Given a static assert with a failing condition, print an error + * Params: + * sa = Static assert with failing condition + * sc = scope for evaluating assert message and printing context + */ +void staticAssertFail(StaticAssert sa, Scope* sc) +{ + if (sa.msgs) + { + OutBuffer msgbuf; + for (size_t i = 0; i < sa.msgs.length; i++) + { + Expression e = (*sa.msgs)[i]; + sc = sc.startCTFE(); + e = e.expressionSemantic(sc); + e = resolveProperties(sc, e); + sc = sc.endCTFE(); + e = ctfeInterpretForPragmaMsg(e); + if (e.op == EXP.error) + { + errorSupplemental(sa.loc, "while evaluating `static assert` argument `%s`", (*sa.msgs)[i].toChars()); + if (!global.gag) + fatal(); + return; + } + if (StringExp se = e.toStringExp()) + { + const slice = se.toUTF8(sc).peekString(); + // Hack to keep old formatting to avoid changing error messages everywhere + if (sa.msgs.length == 1) + msgbuf.printf("\"%.*s\"", cast(int)slice.length, slice.ptr); + else + msgbuf.printf("%.*s", cast(int)slice.length, slice.ptr); + } + else + msgbuf.printf("%s", e.toChars()); + } + error(sa.loc, "static assert: %s", msgbuf.extractChars()); + } + else + error(sa.loc, "static assert: `%s` is false", sa.exp.toChars()); + if (sc.tinst) + sc.tinst.printInstantiationTrace(); + if (!global.gag) + fatal(); +} diff --git a/dmd/semantic3.d b/dmd/semantic3.d index 024040f829..3d37bf209e 100644 --- a/dmd/semantic3.d +++ b/dmd/semantic3.d @@ -1,12 +1,12 @@ /** * Performs the semantic3 stage, which deals with function bodies. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/semantic3.d, _semantic3.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/semantic3.d, _semantic3.d) * Documentation: https://dlang.org/phobos/dmd_semantic3.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/semantic3.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/semantic3.d */ module dmd.semantic3; @@ -80,20 +80,10 @@ enum LOG = false; * Does semantic analysis on function bodies. */ void semantic3(Dsymbol dsym, Scope* sc) -{ -version (IN_LLVM) -{ - import driver.timetrace_sema; - scope v = new Semantic3Visitor(sc); - scope vtimetrace = new SemanticTimeTraceVisitor!Semantic3Visitor(v); - dsym.accept(vtimetrace); -} -else { scope v = new Semantic3Visitor(sc); dsym.accept(v); } -} private extern(C++) final class Semantic3Visitor : Visitor { @@ -129,16 +119,14 @@ private extern(C++) final class Semantic3Visitor : Visitor sc.tinst = tempinst; sc.minst = tempinst.minst; - int needGagging = (tempinst.gagged && !global.gag); - uint olderrors = global.errors; - int oldGaggedErrors = -1; // dead-store to prevent spurious warning + bool needGagging = tempinst.gagged && !global.gag; + const olderrors = global.errors; + const oldGaggedErrors = needGagging ? global.startGagging() : -1; /* If this is a gagged instantiation, gag errors. * Future optimisation: If the results are actually needed, errors * would already be gagged, so we don't really need to run semantic * on the members. */ - if (needGagging) - oldGaggedErrors = global.startGagging(); for (size_t i = 0; i < tempinst.members.length; i++) { @@ -181,7 +169,7 @@ private extern(C++) final class Semantic3Visitor : Visitor sc = sc.push(tmix.argsym); sc = sc.push(tmix); - uint olderrors = global.errors; + const olderrors = global.errors; for (size_t i = 0; i < tmix.members.length; i++) { @@ -231,6 +219,11 @@ private extern(C++) final class Semantic3Visitor : Visitor override void visit(FuncDeclaration funcdecl) { //printf("FuncDeclaration::semantic3(%s '%s', sc = %p)\n", funcdecl.kind(), funcdecl.toChars(), sc); + import dmd.timetrace; + import dmd.root.string : toDString; + timeTraceBeginEvent(TimeTraceEventType.sema3); + scope (exit) timeTraceEndEvent(TimeTraceEventType.sema3, funcdecl); + /* Determine if function should add `return 0;` */ bool addReturn0() @@ -239,7 +232,7 @@ private extern(C++) final class Semantic3Visitor : Visitor auto f = funcdecl.type.isTypeFunction(); // C11 5.1.2.2.3 - if (sc.flags & SCOPE.Cfile && funcdecl.isCMain() && f.next.ty == Tint32) + if (sc.inCfile && funcdecl.isCMain() && f.next.ty == Tint32) return true; return f.next.ty == Tvoid && (funcdecl.isMain() || funcdecl.isCMain()); @@ -277,7 +270,7 @@ private extern(C++) final class Semantic3Visitor : Visitor //{ static int x; if (++x == 2) *(char*)0=0; } //printf("\tlinkage = %d\n", sc.linkage); - if (funcdecl.ident == Id.assign && !funcdecl.inuse) + if (funcdecl.ident == Id.opAssign && !funcdecl.inuse) { if (funcdecl.storage_class & STC.inference) { @@ -285,7 +278,7 @@ private extern(C++) final class Semantic3Visitor : Visitor * For generated opAssign function, any errors * from its body need to be gagged. */ - uint oldErrors = global.startGagging(); + const oldErrors = global.startGagging(); ++funcdecl.inuse; funcdecl.semantic3(sc); --funcdecl.inuse; @@ -300,11 +293,12 @@ private extern(C++) final class Semantic3Visitor : Visitor } } - //printf(" sc.incontract = %d\n", (sc.flags & SCOPE.contract)); + //printf(" sc.incontract = %d\n", sc.contract); if (funcdecl.semanticRun >= PASS.semantic3) return; funcdecl.semanticRun = PASS.semantic3; funcdecl.hasSemantic3Errors = false; + funcdecl.saferD = sc.previews.safer; if (!funcdecl.type || funcdecl.type.ty != Tfunction) return; @@ -318,7 +312,7 @@ private extern(C++) final class Semantic3Visitor : Visitor return; } - uint oldErrors = global.errors; + const oldErrors = global.errors; auto fds = FuncDeclSem3(funcdecl,sc); fds.checkInContractOverrides(); @@ -344,7 +338,7 @@ private extern(C++) final class Semantic3Visitor : Visitor sc2.ctorflow.callSuper = CSX.none; sc2.sbreak = null; sc2.scontinue = null; - sc2.sw = null; + sc2.switchStatement = null; sc2.fes = funcdecl.fes; sc2.linkage = funcdecl.isCsymbol() ? LINK.c : LINK.d; sc2.stc &= STC.flowThruFunction; @@ -352,9 +346,12 @@ private extern(C++) final class Semantic3Visitor : Visitor sc2.explicitVisibility = 0; sc2.aligndecl = null; if (funcdecl.ident != Id.require && funcdecl.ident != Id.ensure) - sc2.flags = sc.flags & ~SCOPE.contract; - sc2.tf = null; - sc2.os = null; + { + sc2.copyFlagsFrom(sc); + sc2.contract = Contract.none; + } + sc2.tryFinally = null; + sc2.scopeGuard = null; sc2.inLoop = false; sc2.inDefaultArg = false; sc2.userAttribDecl = null; @@ -383,7 +380,7 @@ private extern(C++) final class Semantic3Visitor : Visitor if (!sc.intypeof) { if (fld.tok == TOK.delegate_) - .error(funcdecl.loc, "%s `%s` cannot be %s members", funcdecl.kind, funcdecl.toPrettyChars, ad.kind()); + .error(funcdecl.loc, "%s `%s` cannot be %s members", funcdecl.kind, funcdecl.toErrMsg, ad.kind()); else fld.tok = TOK.function_; } @@ -481,7 +478,7 @@ private extern(C++) final class Semantic3Visitor : Visitor foreach (i, fparam; f.parameterList) { Identifier id = fparam.ident; - StorageClass stc = 0; + STC stc = STC.none; if (!id) { /* Generate identifier for un-named parameter, @@ -553,8 +550,7 @@ private extern(C++) final class Semantic3Visitor : Visitor Statement fpreinv = null; if (funcdecl.addPreInvariant()) { - Expression e = addInvariant(funcdecl.isThis(), funcdecl.vthis); - if (e) + if (Expression e = addInvariant(funcdecl.isThis(), funcdecl.vthis)) fpreinv = new ExpStatement(Loc.initial, e); } @@ -562,8 +558,7 @@ private extern(C++) final class Semantic3Visitor : Visitor Statement fpostinv = null; if (funcdecl.addPostInvariant()) { - Expression e = addInvariant(funcdecl.isThis(), funcdecl.vthis); - if (e) + if (Expression e = addInvariant(funcdecl.isThis(), funcdecl.vthis)) fpostinv = new ExpStatement(Loc.initial, e); } @@ -614,7 +609,7 @@ private extern(C++) final class Semantic3Visitor : Visitor } } - bool inferRef = (f.isref && (funcdecl.storage_class & STC.auto_)); + bool inferRef = (f.isRef && (funcdecl.storage_class & STC.auto_)); funcdecl.fbody = funcdecl.fbody.statementSemantic(sc2); if (!funcdecl.fbody) @@ -658,12 +653,12 @@ private extern(C++) final class Semantic3Visitor : Visitor funcdecl.returns.remove(i); continue; } - if (inferRef && f.isref && !exp.type.constConv(f.next)) // https://issues.dlang.org/show_bug.cgi?id=13336 - f.isref = false; + if (inferRef && f.isRef && !exp.type.constConv(f.next)) // https://issues.dlang.org/show_bug.cgi?id=13336 + f.isRef = false; i++; } } - if (f.isref) // Function returns a reference + if (f.isRef) // Function returns a reference { if (funcdecl.storage_class & STC.auto_) funcdecl.storage_class &= ~STC.auto_; @@ -770,8 +765,8 @@ private extern(C++) final class Semantic3Visitor : Visitor funcdecl.buildEnsureRequire(); // Check for errors related to 'nothrow'. - const blockexit = funcdecl.fbody.blockExit(funcdecl, f.isnothrow ? global.errorSink : null); - if (f.isnothrow && blockexit & BE.throw_) + const blockexit = funcdecl.fbody.blockExit(funcdecl, f.isNothrow ? global.errorSink : null); + if (f.isNothrow && blockexit & BE.throw_) error(funcdecl.loc, "%s `%s` may throw but is marked as `nothrow`", funcdecl.kind(), funcdecl.toPrettyChars()); if (!(blockexit & (BE.throw_ | BE.halt) || funcdecl.hasCatches)) @@ -786,7 +781,7 @@ private extern(C++) final class Semantic3Visitor : Visitor { if (funcdecl.type == f) f = cast(TypeFunction)f.copy(); - f.isnothrow = !(blockexit & BE.throw_); + f.isNothrow = !(blockexit & BE.throw_); } if (funcdecl.fbody.isErrorStatement()) @@ -803,7 +798,8 @@ private extern(C++) final class Semantic3Visitor : Visitor Statement s = new ReturnStatement(funcdecl.loc, null); s = s.statementSemantic(sc2); funcdecl.fbody = new CompoundStatement(funcdecl.loc, funcdecl.fbody, s); - funcdecl.hasReturnExp |= (funcdecl.hasReturnExp & 1 ? 16 : 1); + funcdecl.hasMultipleReturnExp = funcdecl.hasReturnExp; + funcdecl.hasReturnExp = true; } } else if (funcdecl.fes) @@ -814,7 +810,8 @@ private extern(C++) final class Semantic3Visitor : Visitor Expression e = IntegerExp.literal!0; Statement s = new ReturnStatement(Loc.initial, e); funcdecl.fbody = new CompoundStatement(Loc.initial, funcdecl.fbody, s); - funcdecl.hasReturnExp |= (funcdecl.hasReturnExp & 1 ? 16 : 1); + funcdecl.hasMultipleReturnExp = funcdecl.hasReturnExp; + funcdecl.hasReturnExp = true; } assert(!funcdecl.returnLabel); } @@ -829,8 +826,7 @@ private extern(C++) final class Semantic3Visitor : Visitor } else { - const(bool) inlineAsm = (funcdecl.hasReturnExp & 8) != 0; - if ((blockexit & BE.fallthru) && f.next.ty != Tvoid && !inlineAsm && !(sc.flags & SCOPE.Cfile)) + if ((blockexit & BE.fallthru) && f.next.ty != Tvoid && !funcdecl.hasInlineAsm && !sc.inCfile) { if (!funcdecl.hasReturnExp) .error(funcdecl.loc, "%s `%s` has no `return` statement, but is expected to return a value of type `%s`", funcdecl.kind, funcdecl.toPrettyChars, f.next.toChars()); @@ -920,7 +916,7 @@ private extern(C++) final class Semantic3Visitor : Visitor } // Function returns a reference - if (f.isref) + if (f.isRef) { if (!MODimplicitConv(exp.type.mod, tret.mod) && !tret.isTypeSArray()) error(exp.loc, "expression `%s` of type `%s` is not implicitly convertible to return type `ref %s`", @@ -937,7 +933,37 @@ private extern(C++) final class Semantic3Visitor : Visitor // if a copy constructor is present, the return type conversion will be handled by it const hasCopyCtor = exp.type.ty == Tstruct && (cast(TypeStruct)exp.type).sym.hasCopyCtor; if (!hasCopyCtor || !exp.isLvalue()) - exp = exp.implicitCastTo(sc2, tret); + { + const errors = global.startGagging(); + auto implicitlyCastedExp = exp.implicitCastTo(sc2, tret); + global.endGagging(errors); + + // + if (implicitlyCastedExp.isErrorExp()) + { + auto types = toAutoQualChars(exp.type, tret); + error( + exp.loc, + "return value `%s` of type `%s` does not match return type `%s`" + ~ ", and cannot be implicitly converted", + exp.toErrMsg(), + types[0], + types[1], + ); + + if (const func = exp.type.isFunction_Delegate_PtrToFunction()) + if (func.next.equals(tret)) + errorSupplemental( + exp.loc, + "Did you intend to call the %s?", + (exp.type.isPtrToFunction()) + ? "function pointer" + : exp.type.kind + ); + } + + exp = implicitlyCastedExp; + } exp = exp.optimize(WANTvalue); @@ -945,7 +971,7 @@ private extern(C++) final class Semantic3Visitor : Visitor * If NRVO is not possible, all returned lvalues should call their postblits. */ if (!funcdecl.isNRVO()) - exp = doCopyOrMove(sc2, exp, f.next); + exp = doCopyOrMove(sc2, exp, f.next, true, true); if (tret.hasPointers()) checkReturnEscape(*sc2, exp, false); @@ -980,7 +1006,7 @@ private extern(C++) final class Semantic3Visitor : Visitor sc2 = sc2.pop(); } - if (global.params.inclusiveInContracts) + if (sc.previews.inclusiveInContracts) { funcdecl.frequire = funcdecl.mergeFrequireInclusivePreview( funcdecl.frequire, funcdecl.fdrequireParams); @@ -997,7 +1023,7 @@ private extern(C++) final class Semantic3Visitor : Visitor /* Do the semantic analysis on the [in] preconditions and * [out] postconditions. */ - immutable bool isnothrow = f.isnothrow && !funcdecl.nothrowInprocess; + immutable bool isNothrow = f.isNothrow && !funcdecl.nothrowInprocess; if (freq) { /* frequire is composed of the [in] contracts @@ -1006,24 +1032,21 @@ private extern(C++) final class Semantic3Visitor : Visitor sym.parent = sc2.scopesym; sym.endlinnum = funcdecl.endloc.linnum; sc2 = sc2.push(sym); - sc2.flags = (sc2.flags & ~SCOPE.contract) | SCOPE.require; + sc2.contract = Contract.require; // BUG: need to error if accessing out parameters // BUG: need to disallow returns // BUG: verify that all in and ref parameters are read freq = freq.statementSemantic(sc2); - // @@@DEPRECATED_2.111@@@ - pass `isnothrow` instead of `false` to print a more detailed error msg` const blockExit = freq.blockExit(funcdecl, null); if (blockExit & BE.throw_) { - if (isnothrow) - // @@@DEPRECATED_2.111@@@ - // Deprecated in 2.101, can be made an error in 2.111 - deprecation(funcdecl.loc, "`%s`: `in` contract may throw but function is marked as `nothrow`", + if (isNothrow) + error(funcdecl.loc, "`%s`: `in` contract may throw but function is marked as `nothrow`", funcdecl.toPrettyChars()); else if (funcdecl.nothrowInprocess) - f.isnothrow = false; + f.isNothrow = false; } funcdecl.hasNoEH = false; @@ -1051,7 +1074,7 @@ private extern(C++) final class Semantic3Visitor : Visitor } sc2 = scout; //push - sc2.flags = (sc2.flags & ~SCOPE.contract) | SCOPE.ensure; + sc2.contract = Contract.ensure; // BUG: need to disallow returns and throws @@ -1060,17 +1083,14 @@ private extern(C++) final class Semantic3Visitor : Visitor fens = fens.statementSemantic(sc2); - // @@@DEPRECATED_2.111@@@ - pass `isnothrow` instead of `false` to print a more detailed error msg` const blockExit = fens.blockExit(funcdecl, null); if (blockExit & BE.throw_) { - if (isnothrow) - // @@@DEPRECATED_2.111@@@ - // Deprecated in 2.101, can be made an error in 2.111 - deprecation(funcdecl.loc, "`%s`: `out` contract may throw but function is marked as `nothrow`", + if (isNothrow) + error(funcdecl.loc, "`%s`: `out` contract may throw but function is marked as `nothrow`", funcdecl.toPrettyChars()); else if (funcdecl.nothrowInprocess) - f.isnothrow = false; + f.isNothrow = false; } funcdecl.hasNoEH = false; @@ -1209,32 +1229,31 @@ else { if (v.isReference() || (v.storage_class & STC.lazy_)) continue; - if (v.needsScopeDtor()) - { - v.storage_class |= STC.nodtor; - if (!paramsNeedDtor) - continue; - - // same with ExpStatement.scopeCode() - Statement s = new DtorExpStatement(Loc.initial, v.edtor, v); + if (!v.needsScopeDtor()) + continue; + v.storage_class |= STC.nodtor; + if (!paramsNeedDtor) + continue; - s = s.statementSemantic(sc2); + // same with ExpStatement.scopeCode() + Statement s = new DtorExpStatement(Loc.initial, v.edtor, v); - const blockexit = s.blockExit(funcdecl, isnothrow ? global.errorSink : null); - if (blockexit & BE.throw_) - { - funcdecl.hasNoEH = false; - if (isnothrow) - error(funcdecl.loc, "%s `%s` may throw but is marked as `nothrow`", funcdecl.kind(), funcdecl.toPrettyChars()); - else if (funcdecl.nothrowInprocess) - f.isnothrow = false; - } + s = s.statementSemantic(sc2); - if (sbody.blockExit(funcdecl, f.isnothrow ? global.errorSink : null) == BE.fallthru) - sbody = new CompoundStatement(Loc.initial, sbody, s); - else - sbody = new TryFinallyStatement(Loc.initial, sbody, s); + const blockexit = s.blockExit(funcdecl, isNothrow ? global.errorSink : null); + if (blockexit & BE.throw_) + { + funcdecl.hasNoEH = false; + if (isNothrow) + error(funcdecl.loc, "%s `%s` may throw but is marked as `nothrow`", funcdecl.kind(), funcdecl.toPrettyChars()); + else if (funcdecl.nothrowInprocess) + f.isNothrow = false; } + + if (sbody.blockExit(funcdecl, f.isNothrow ? global.errorSink : null) == BE.fallthru) + sbody = new CompoundStatement(Loc.initial, sbody, s); + else + sbody = new TryFinallyStatement(Loc.initial, sbody, s); } } // from this point on all possible 'throwers' are checked @@ -1244,8 +1263,7 @@ else { /* Wrap the entire function body in a synchronized statement */ - ClassDeclaration cd = funcdecl.toParentDecl().isClassDeclaration(); - if (cd) + if (ClassDeclaration cd = funcdecl.toParentDecl().isClassDeclaration()) { if (target.libraryObjectMonitors(funcdecl, sbody)) { @@ -1338,7 +1356,7 @@ else funcdecl.nogcInprocess = false; if (funcdecl.type == f) f = cast(TypeFunction)f.copy(); - f.isnogc = true; + f.isNogc = true; } finishScopeParamInference(funcdecl, f); @@ -1352,8 +1370,8 @@ else { sc = sc.push(); if (funcdecl.isCtorDeclaration()) // https://issues.dlang.org/show_bug.cgi?id=#15665 - f.isctor = true; - sc.stc = 0; + f.isCtor = true; + sc.stc = STC.none; sc.linkage = funcdecl._linkage; // https://issues.dlang.org/show_bug.cgi?id=8496 funcdecl.type = f.typeSemantic(funcdecl.loc, sc); sc = sc.pop(); @@ -1367,7 +1385,7 @@ else // Don't allow D `immutable` and `shared` types to be interfaced with C++ if (type.isImmutable() || type.isShared()) return true; - else if (Type cpptype = target.cpp.parameterType(type)) + if (Type cpptype = target.cpp.parameterType(type)) type = cpptype; if (origType is null) @@ -1409,7 +1427,7 @@ else } if (isCppNonMappableType(f.next.toBasetype())) { - .error(funcdecl.loc, "%s `%s` cannot return type `%s` because its linkage is `extern(C++)`", funcdecl.kind, funcdecl.toPrettyChars, f.next.toChars()); + .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()) errorSupplemental(funcdecl.loc, "slices are specific to D and do not have a counterpart representation in C++", f.next.toChars()); funcdecl.errors = true; @@ -1418,7 +1436,7 @@ else { if (isCppNonMappableType(param.type.toBasetype(), param)) { - .error(funcdecl.loc, "%s `%s` cannot have parameter of type `%s` because its linkage is `extern(C++)`", funcdecl.kind, funcdecl.toPrettyChars, param.type.toChars()); + .error(funcdecl.loc, "%s `%s` cannot have parameter of type `%s` because its linkage is `extern(C++)`", funcdecl.kind, funcdecl.toErrMsg(), param.type.toChars()); if (param.type.toBasetype().isTypeSArray()) errorSupplemental(funcdecl.loc, "perhaps use a `%s*` type instead", param.type.nextOf().mutableOf().unSharedOf().toChars()); @@ -1428,8 +1446,8 @@ else } // Do live analysis - if (global.params.useDIP1021 && funcdecl.fbody && funcdecl.type.ty != Terror && - funcdecl.type.isTypeFunction().islive) + if (sc.previews.dip1021 && funcdecl.fbody && funcdecl.type.ty != Terror && + funcdecl.type.isTypeFunction().isLive) { oblive(funcdecl); } @@ -1466,7 +1484,7 @@ else */ AggregateDeclaration ad = ctor.isMemberDecl(); if (!ctor.fbody || !ad || !ad.fieldDtor || - global.params.dtorFields == FeatureState.disabled || !global.params.useExceptions || ctor.type.toTypeFunction.isnothrow) + global.params.dtorFields == FeatureState.disabled || !global.params.useExceptions || ctor.type.toTypeFunction.isNothrow) return visit(cast(FuncDeclaration)ctor); /* Generate: @@ -1479,16 +1497,12 @@ else auto sexp = new ExpStatement(ctor.loc, ce); auto ss = new ScopeStatement(ctor.loc, sexp, ctor.loc); - // @@@DEPRECATED_2.106@@@ - // Allow negligible attribute violations to allow for a smooth - // transition. Remove this after the usual deprecation period - // after 2.106. if (global.params.dtorFields == FeatureState.default_) { auto ctf = cast(TypeFunction) ctor.type; auto dtf = cast(TypeFunction) ad.fieldDtor.type; - const ngErr = ctf.isnogc && !dtf.isnogc; + const ngErr = ctf.isNogc && !dtf.isNogc; const puErr = ctf.purity && !dtf.purity; const saErr = ctf.trust == TRUST.safe && dtf.trust <= TRUST.system; @@ -1497,13 +1511,13 @@ else // storage_class is apparently not set for dtor & ctor OutBuffer ob; stcToBuffer(ob, - (ngErr ? STC.nogc : 0) | - (puErr ? STC.pure_ : 0) | - (saErr ? STC.system : 0) + (ngErr ? STC.nogc : STC.none) | + (puErr ? STC.pure_ : STC.none) | + (saErr ? STC.system : STC.none) ); - ctor.loc.deprecation("`%s` has stricter attributes than its destructor (`%s`)", ctor.toPrettyChars(), ob.peekChars()); - ctor.loc.deprecationSupplemental("The destructor will be called if an exception is thrown"); - ctor.loc.deprecationSupplemental("Either make the constructor `nothrow` or adjust the field destructors"); + ctor.loc.error("`%s` has stricter attributes than its destructor (`%s`)", ctor.toPrettyChars(), ob.peekChars()); + ctor.loc.errorSupplemental("The destructor will be called if an exception is thrown"); + ctor.loc.errorSupplemental("Either make the constructor `nothrow` or adjust the field destructors"); ce.ignoreAttributes = true; } @@ -1677,7 +1691,7 @@ void semanticTypeInfoMembers(StructDeclaration sd) sd.xeq._scope && sd.xeq.semanticRun < PASS.semantic3done) { - uint errors = global.startGagging(); + const errors = global.startGagging(); sd.xeq.semantic3(sd.xeq._scope); if (global.endGagging(errors)) sd.xeq = sd.xerreq; @@ -1687,7 +1701,7 @@ void semanticTypeInfoMembers(StructDeclaration sd) sd.xcmp._scope && sd.xcmp.semanticRun < PASS.semantic3done) { - uint errors = global.startGagging(); + const errors = global.startGagging(); sd.xcmp.semantic3(sd.xcmp._scope); if (global.endGagging(errors)) sd.xcmp = sd.xerrcmp; @@ -1722,3 +1736,72 @@ void semanticTypeInfoMembers(StructDeclaration sd) sd.dtor.semantic3(sd.dtor._scope); } } + +/*********************************************** + * Check that the function contains any closure. + * If it's @nogc, report suitable errors. + * This is mostly consistent with FuncDeclaration::needsClosure(). + * + * Returns: + * true if any errors occur. + */ +extern (D) bool checkClosure(FuncDeclaration fd) +{ + //printf("checkClosure() %s\n", toPrettyChars()); + if (!fd.needsClosure()) + return false; + + if (fd.setGC(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 + return true; + } + else if (!global.params.useGC) + { + .error(fd.loc, "%s `%s` is `-betterC` yet allocates closure for `%s()` with the GC", fd.kind, fd.toPrettyChars(), fd.toChars()); + if (global.gag) // need not report supplemental errors + return true; + } + else + { + fd.printGCUsage(fd.loc, "using closure causes GC allocation"); + return false; + } + + FuncDeclarations a; + foreach (v; fd.closureVars) + { + foreach (f; v.nestedrefs) + { + assert(f !is fd); + + LcheckAncestorsOfANestedRef: + for (Dsymbol s = f; s && s !is fd; s = s.toParentP(fd)) + { + auto fx = s.isFuncDeclaration(); + if (!fx) + continue; + if (fx.isThis() || + fx.tookAddressOf || + checkEscapingSiblings(fx, fd)) + { + foreach (f2; a) + { + 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()); + + break LcheckAncestorsOfANestedRef; + } + } + } + } + + return true; +} diff --git a/dmd/sideeffect.d b/dmd/sideeffect.d index 57a6639575..5984466d3b 100644 --- a/dmd/sideeffect.d +++ b/dmd/sideeffect.d @@ -1,12 +1,12 @@ /** * Find side-effects of expressions. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/sideeffect.d, _sideeffect.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/sideeffect.d, _sideeffect.d) * Documentation: https://dlang.org/phobos/dmd_sideeffect.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/sideeffect.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/sideeffect.d */ module dmd.sideeffect; @@ -20,14 +20,15 @@ import dmd.expressionsem; import dmd.func; import dmd.funcsem; import dmd.globals; +import dmd.hdrgen; import dmd.id; import dmd.identifier; import dmd.init; import dmd.mtype; -import dmd.postordervisitor; import dmd.tokens; import dmd.typesem; import dmd.visitor; +import dmd.visitor.postorder; /************************************************** * Front-end expression rewriting should create temporary variables for @@ -113,7 +114,7 @@ int callSideEffectLevel(FuncDeclaration f) return 0; assert(f.type.ty == Tfunction); TypeFunction tf = cast(TypeFunction)f.type; - if (!tf.isnothrow) + if (!tf.isNothrow) return 0; final switch (f.isPure()) { @@ -123,7 +124,7 @@ int callSideEffectLevel(FuncDeclaration f) return 0; case PURE.const_: - return mutabilityOfType(tf.isref, tf.next) == 2 ? 2 : 1; + return mutabilityOfType(tf.isRef, tf.next) == 2 ? 2 : 1; } } @@ -138,7 +139,7 @@ int callSideEffectLevel(Type t) assert(t.ty == Tfunction); tf = cast(TypeFunction)t; } - if (!tf.isnothrow) // function can throw + if (!tf.isNothrow) // function can throw return 0; tf.purityLevel(); @@ -152,7 +153,7 @@ int callSideEffectLevel(Type t) } if (purity == PURE.const_) - return mutabilityOfType(tf.isref, tf.next) == 2 ? 2 : 1; + return mutabilityOfType(tf.isRef, tf.next) == 2 ? 2 : 1; return 0; } @@ -346,12 +347,13 @@ bool discardValue(Expression e) BinExp tmp = e.isBinExp(); assert(tmp); - error(e.loc, "the result of the equality expression `%s` is discarded", e.toChars()); + error(e.loc, "the result of the equality expression `%s` is discarded", e.toErrMsg()); bool seenSideEffect = false; foreach(expr; [tmp.e1, tmp.e2]) { - if (hasSideEffect(expr)) { - errorSupplemental(expr.loc, "note that `%s` may have a side effect", expr.toChars()); + if (hasSideEffect(expr)) + { + errorSupplemental(expr.loc, "note that `%s` may have a side effect", expr.toErrMsg()); seenSideEffect |= true; } } @@ -359,7 +361,7 @@ bool discardValue(Expression e) default: break; } - error(e.loc, "`%s` has no effect", e.toChars()); + error(e.loc, "`%s` has no effect", e.toErrMsg()); return true; } @@ -372,7 +374,7 @@ bool discardValue(Expression e) * Returns: * Newly created temporary variable. */ -VarDeclaration copyToTemp(StorageClass stc, const char[] name, Expression e) +VarDeclaration copyToTemp(STC stc, const char[] name, Expression e) { assert(name[0] == '_' && name[1] == '_'); auto vd = new VarDeclaration(e.loc, e.type, @@ -408,10 +410,10 @@ Expression extractSideEffect(Scope* sc, const char[] name, * https://issues.dlang.org/show_bug.cgi?id=17145 */ if (!alwaysCopy && - ((sc.flags & SCOPE.ctfe) ? !hasSideEffect(e) : isTrivialExp(e))) + (sc.ctfe ? !hasSideEffect(e) : isTrivialExp(e))) return e; - auto vd = copyToTemp(0, name, e); + auto vd = copyToTemp(STC.none, name, e); vd.storage_class |= e.isLvalue() ? STC.ref_ : STC.rvalue; e0 = Expression.combine(e0, new DeclarationExp(vd.loc, vd) diff --git a/dmd/statement.d b/dmd/statement.d index 4a89cbefa5..4ac84c840c 100644 --- a/dmd/statement.d +++ b/dmd/statement.d @@ -3,12 +3,12 @@ * * Specification: $(LINK2 https://dlang.org/spec/statement.html, Statements) * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/statement.d, _statement.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/statement.d, _statement.d) * Documentation: https://dlang.org/phobos/dmd_statement.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/statement.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/statement.d */ module dmd.statement; @@ -19,7 +19,6 @@ import core.stdc.stdio; import dmd.arraytypes; import dmd.astenums; import dmd.ast_node; -import dmd.errors; import dmd.cond; import dmd.declaration; import dmd.dsymbol; @@ -30,10 +29,10 @@ import dmd.identifier; import dmd.location; import dmd.mtype; import dmd.rootobject; -import dmd.sapply; import dmd.staticassert; import dmd.tokens; import dmd.visitor; +import dmd.visitor.postorder; /*********************************************************** * Specification: https://dlang.org/spec/statement.html @@ -48,7 +47,7 @@ extern (C++) abstract class Statement : ASTNode return DYNCAST.statement; } - final extern (D) this(const ref Loc loc, STMT stmt) @safe + final extern (D) this(Loc loc, STMT stmt) @safe { this.loc = loc; this.stmt = stmt; @@ -378,25 +377,25 @@ extern (C++) class ExpStatement : Statement { Expression exp; - final extern (D) this(const ref Loc loc, Expression exp) @safe + final extern (D) this(Loc loc, Expression exp) @safe { super(loc, STMT.Exp); this.exp = exp; } - final extern (D) this(const ref Loc loc, Expression exp, STMT stmt) @safe + final extern (D) this(Loc loc, Expression exp, STMT stmt) @safe { super(loc, stmt); this.exp = exp; } - final extern (D) this(const ref Loc loc, Dsymbol declaration) @safe + final extern (D) this(Loc loc, Dsymbol declaration) @safe { super(loc, STMT.Exp); this.exp = new DeclarationExp(loc, declaration); } - static ExpStatement create(const ref Loc loc, Expression exp) @safe + static ExpStatement create(Loc loc, Expression exp) @safe { return new ExpStatement(loc, exp); } @@ -419,7 +418,7 @@ extern (C++) final class DtorExpStatement : ExpStatement // Wraps an expression that is the destruction of 'var' VarDeclaration var; - extern (D) this(const ref Loc loc, Expression exp, VarDeclaration var) @safe + extern (D) this(Loc loc, Expression exp, VarDeclaration var) @safe { super(loc, exp, STMT.DtorExp); this.var = var; @@ -444,14 +443,14 @@ extern (C++) final class MixinStatement : Statement { Expressions* exps; - extern (D) this(const ref Loc loc, Expression exp) + extern (D) this(Loc loc, Expression exp) { Expressions* exps = new Expressions(); exps.push(exp); this(loc, exps); } - extern (D) this(const ref Loc loc, Expressions* exps) @safe + extern (D) this(Loc loc, Expressions* exps) @safe { super(loc, STMT.Mixin); this.exps = exps; @@ -482,13 +481,13 @@ extern (C++) class CompoundStatement : Statement * loc = Instantiation information * statements = An array of `Statement`s, that will referenced by this class */ - final extern (D) this(const ref Loc loc, Statements* statements) @safe + final extern (D) this(Loc loc, Statements* statements) @safe { super(loc, STMT.Compound); this.statements = statements; } - final extern (D) this(const ref Loc loc, Statements* statements, STMT stmt) @safe + final extern (D) this(Loc loc, Statements* statements, STMT stmt) @safe { super(loc, stmt); this.statements = statements; @@ -502,7 +501,7 @@ extern (C++) class CompoundStatement : Statement * sts = A variadic array of `Statement`s, that will copied in this class * The entries themselves will not be copied. */ - final extern (D) this(const ref Loc loc, Statement[] sts...) + final extern (D) this(Loc loc, Statement[] sts...) { super(loc, STMT.Compound); statements = new Statements(); @@ -511,7 +510,7 @@ extern (C++) class CompoundStatement : Statement statements.push(s); } - static CompoundStatement create(const ref Loc loc, Statement s1, Statement s2) + static CompoundStatement create(Loc loc, Statement s1, Statement s2) { return new CompoundStatement(loc, s1, s2); } @@ -575,7 +574,7 @@ version (IN_LLVM) */ extern (C++) final class CompoundDeclarationStatement : CompoundStatement { - extern (D) this(const ref Loc loc, Statements* statements) @safe + extern (D) this(Loc loc, Statements* statements) @safe { super(loc, statements, STMT.CompoundDeclaration); } @@ -599,7 +598,7 @@ extern (C++) final class UnrolledLoopStatement : Statement { Statements* statements; - extern (D) this(const ref Loc loc, Statements* statements) @safe + extern (D) this(Loc loc, Statements* statements) @safe { super(loc, STMT.UnrolledLoop); this.statements = statements; @@ -633,7 +632,7 @@ extern (C++) final class ScopeStatement : Statement Statement statement; Loc endloc; // location of closing curly bracket - extern (D) this(const ref Loc loc, Statement statement, Loc endloc) @safe + extern (D) this(Loc loc, Statement statement, Loc endloc) @safe { super(loc, STMT.Scope); this.statement = statement; @@ -683,7 +682,7 @@ extern (C++) final class ForwardingStatement : Statement /// The wrapped statement. Statement statement; - extern (D) this(const ref Loc loc, ForwardingScopeDsymbol sym, Statement statement) @safe + extern (D) this(Loc loc, ForwardingScopeDsymbol sym, Statement statement) @safe { super(loc, STMT.Forwarding); this.sym = sym; @@ -691,7 +690,7 @@ extern (C++) final class ForwardingStatement : Statement this.statement = statement; } - extern (D) this(const ref Loc loc, Statement statement) @safe + extern (D) this(Loc loc, Statement statement) @safe { auto sym = new ForwardingScopeDsymbol(); sym.symtab = new DsymbolTable(); @@ -720,7 +719,7 @@ extern (C++) final class WhileStatement : Statement Statement _body; Loc endloc; // location of closing curly bracket - extern (D) this(const ref Loc loc, Expression condition, Statement _body, Loc endloc, Parameter param = null) @safe + extern (D) this(Loc loc, Expression condition, Statement _body, Loc endloc, Parameter param = null) @safe { super(loc, STMT.While); this.condition = condition; @@ -762,7 +761,7 @@ extern (C++) final class DoStatement : Statement Expression condition; Loc endloc; // location of ';' after while - extern (D) this(const ref Loc loc, Statement _body, Expression condition, Loc endloc) @safe + extern (D) this(Loc loc, Statement _body, Expression condition, Loc endloc) @safe { super(loc, STMT.Do); this._body = _body; @@ -810,7 +809,7 @@ extern (C++) final class ForStatement : Statement // treat that label as referring to this loop. Statement relatedLabeled; - extern (D) this(const ref Loc loc, Statement _init, Expression condition, Expression increment, Statement _body, Loc endloc) @safe + extern (D) this(Loc loc, Statement _init, Expression condition, Expression increment, Statement _body, Loc endloc) @safe { super(loc, STMT.For); this._init = _init; @@ -871,7 +870,7 @@ extern (C++) final class ForeachStatement : Statement Statements* cases; // put breaks, continues, gotos and returns here ScopeStatements* gotos; // forward referenced goto's go here - extern (D) this(const ref Loc loc, TOK op, Parameters* parameters, Expression aggr, Statement _body, Loc endloc) @safe + extern (D) this(Loc loc, TOK op, Parameters* parameters, Expression aggr, Statement _body, Loc endloc) @safe { super(loc, STMT.Foreach); this.op = op; @@ -912,7 +911,7 @@ extern (C++) final class ForeachStatement : Statement extern (C++) final class ForeachRangeStatement : Statement { TOK op; // TOK.foreach_ or TOK.foreach_reverse_ - Parameter prm; // loop index variable + Parameter param; // loop index variable Expression lwr; Expression upr; Statement _body; @@ -920,11 +919,11 @@ extern (C++) final class ForeachRangeStatement : Statement VarDeclaration key; - extern (D) this(const ref Loc loc, TOK op, Parameter prm, Expression lwr, Expression upr, Statement _body, Loc endloc) @safe + extern (D) this(Loc loc, TOK op, Parameter param, Expression lwr, Expression upr, Statement _body, Loc endloc) @safe { super(loc, STMT.ForeachRange); this.op = op; - this.prm = prm; + this.param = param; this.lwr = lwr; this.upr = upr; this._body = _body; @@ -933,7 +932,7 @@ extern (C++) final class ForeachRangeStatement : Statement override ForeachRangeStatement syntaxCopy() { - return new ForeachRangeStatement(loc, op, prm.syntaxCopy(), lwr.syntaxCopy(), upr.syntaxCopy(), _body ? _body.syntaxCopy() : null, endloc); + return new ForeachRangeStatement(loc, op, param.syntaxCopy(), lwr.syntaxCopy(), upr.syntaxCopy(), _body ? _body.syntaxCopy() : null, endloc); } override bool hasBreak() const pure nothrow @@ -957,17 +956,17 @@ extern (C++) final class ForeachRangeStatement : Statement */ extern (C++) final class IfStatement : Statement { - Parameter prm; + Parameter param; Expression condition; Statement ifbody; Statement elsebody; VarDeclaration match; // for MatchExpression results Loc endloc; // location of closing curly bracket - extern (D) this(const ref Loc loc, Parameter prm, Expression condition, Statement ifbody, Statement elsebody, Loc endloc) @safe + extern (D) this(Loc loc, Parameter param, Expression condition, Statement ifbody, Statement elsebody, Loc endloc) @safe { super(loc, STMT.If); - this.prm = prm; + this.param = param; this.condition = condition; this.ifbody = ifbody; this.elsebody = elsebody; @@ -977,7 +976,7 @@ extern (C++) final class IfStatement : Statement override IfStatement syntaxCopy() { return new IfStatement(loc, - prm ? prm.syntaxCopy() : null, + param ? param.syntaxCopy() : null, condition.syntaxCopy(), ifbody ? ifbody.syntaxCopy() : null, elsebody ? elsebody.syntaxCopy() : null, @@ -1009,7 +1008,7 @@ extern (C++) final class ConditionalStatement : Statement Statement ifbody; Statement elsebody; - extern (D) this(const ref Loc loc, Condition condition, Statement ifbody, Statement elsebody) @safe + extern (D) this(Loc loc, Condition condition, Statement ifbody, Statement elsebody) @safe { super(loc, STMT.Conditional); this.condition = condition; @@ -1044,7 +1043,7 @@ extern (C++) final class StaticForeachStatement : Statement { StaticForeach sfe; - extern (D) this(const ref Loc loc, StaticForeach sfe) @safe + extern (D) this(Loc loc, StaticForeach sfe) @safe { super(loc, STMT.StaticForeach); this.sfe = sfe; @@ -1070,7 +1069,7 @@ extern (C++) final class PragmaStatement : Statement Expressions* args; // array of Expression's Statement _body; - extern (D) this(const ref Loc loc, const Identifier ident, Expressions* args, Statement _body) @safe + extern (D) this(Loc loc, const Identifier ident, Expressions* args, Statement _body) @safe { super(loc, STMT.Pragma); this.ident = ident; @@ -1128,7 +1127,7 @@ extern (C++) final class SwitchStatement : Statement bool hasVars; /// true if has variable case values DefaultStatement sdefault; /// default: Statement tryBody; /// set to TryCatchStatement or TryFinallyStatement if in _body portion - TryFinallyStatement tf; /// set if in the 'finally' block of a TryFinallyStatement + TryFinallyStatement tryFinally; /// set if in the 'finally' block of a TryFinallyStatement GotoCaseStatements gotoCases; /// array of unresolved GotoCaseStatement's CaseStatements* cases; /// array of CaseStatement's VarDeclaration lastVar; /// last observed variable declaration in this statement @@ -1137,7 +1136,7 @@ version (IN_LLVM) bool hasGotoDefault; // true iff there is a `goto default` statement for this switch } - extern (D) this(const ref Loc loc, Parameter param, Expression condition, Statement _body, bool isFinal, Loc endloc) + extern (D) this(Loc loc, Parameter param, Expression condition, Statement _body, bool isFinal, Loc endloc) { super(loc, STMT.Switch); this.param = param; @@ -1185,7 +1184,7 @@ version (IN_LLVM) bool gototarget; // true iff this is the target of a 'goto case' } - extern (D) this(const ref Loc loc, Expression exp, Statement statement) @safe + extern (D) this(Loc loc, Expression exp, Statement statement) @safe { super(loc, STMT.Case); this.exp = exp; @@ -1212,7 +1211,7 @@ extern (C++) final class CaseRangeStatement : Statement Expression last; Statement statement; - extern (D) this(const ref Loc loc, Expression first, Expression last, Statement statement) @safe + extern (D) this(Loc loc, Expression first, Expression last, Statement statement) @safe { super(loc, STMT.CaseRange); this.first = first; @@ -1245,7 +1244,7 @@ version (IN_LLVM) bool gototarget; // true iff this is the target of a 'goto default' } - extern (D) this(const ref Loc loc, Statement statement) @safe + extern (D) this(Loc loc, Statement statement) @safe { super(loc, STMT.Default); this.statement = statement; @@ -1269,7 +1268,7 @@ extern (C++) final class GotoDefaultStatement : Statement { SwitchStatement sw; - extern (D) this(const ref Loc loc) @safe + extern (D) this(Loc loc) @safe { super(loc, STMT.GotoDefault); } @@ -1299,7 +1298,7 @@ version (IN_LLVM) SwitchStatement sw; } - extern (D) this(const ref Loc loc, Expression exp) @safe + extern (D) this(Loc loc, Expression exp) @safe { super(loc, STMT.GotoCase); this.exp = exp; @@ -1322,12 +1321,12 @@ extern (C++) final class SwitchErrorStatement : Statement { Expression exp; - extern (D) this(const ref Loc loc) @safe + extern (D) this(Loc loc) @safe { super(loc, STMT.SwitchError); } - final extern (D) this(const ref Loc loc, Expression exp) @safe + final extern (D) this(Loc loc, Expression exp) @safe { super(loc, STMT.SwitchError); this.exp = exp; @@ -1347,7 +1346,7 @@ extern (C++) final class ReturnStatement : Statement Expression exp; size_t caseDim; - extern (D) this(const ref Loc loc, Expression exp) @safe + extern (D) this(Loc loc, Expression exp) @safe { super(loc, STMT.Return); this.exp = exp; @@ -1382,7 +1381,7 @@ version (IN_LLVM) LabelStatement target; } - extern (D) this(const ref Loc loc, Identifier ident) @safe + extern (D) this(Loc loc, Identifier ident) @safe { super(loc, STMT.Break); this.ident = ident; @@ -1412,7 +1411,7 @@ version (IN_LLVM) LabelStatement target; } - extern (D) this(const ref Loc loc, Identifier ident) @safe + extern (D) this(Loc loc, Identifier ident) @safe { super(loc, STMT.Continue); this.ident = ident; @@ -1437,7 +1436,7 @@ extern (C++) final class SynchronizedStatement : Statement Expression exp; Statement _body; - extern (D) this(const ref Loc loc, Expression exp, Statement _body) @safe + extern (D) this(Loc loc, Expression exp, Statement _body) @safe { super(loc, STMT.Synchronized); this.exp = exp; @@ -1475,7 +1474,7 @@ extern (C++) final class WithStatement : Statement VarDeclaration wthis; Loc endloc; - extern (D) this(const ref Loc loc, Expression exp, Statement _body, Loc endloc) @safe + extern (D) this(Loc loc, Expression exp, Statement _body, Loc endloc) @safe { super(loc, STMT.With); this.exp = exp; @@ -1504,7 +1503,7 @@ extern (C++) final class TryCatchStatement : Statement Statement tryBody; /// set to enclosing TryCatchStatement or TryFinallyStatement if in _body portion - extern (D) this(const ref Loc loc, Statement _body, Catches* catches) @safe + extern (D) this(Loc loc, Statement _body, Catches* catches) @safe { super(loc, STMT.TryCatch); this._body = _body; @@ -1548,7 +1547,7 @@ extern (C++) final class Catch : RootObject // was generated by the compiler, wasn't present in source code bool internalCatch; - extern (D) this(const ref Loc loc, Type type, Identifier ident, Statement handler) @safe + extern (D) this(Loc loc, Type type, Identifier ident, Statement handler) @safe { //printf("Catch(%s, loc = %s)\n", id.toChars(), loc.toChars()); this.loc = loc; @@ -1576,7 +1575,7 @@ extern (C++) final class TryFinallyStatement : Statement Statement tryBody; /// set to enclosing TryCatchStatement or TryFinallyStatement if in _body portion bool bodyFallsThru; /// true if _body falls through to finally - extern (D) this(const ref Loc loc, Statement _body, Statement finalbody) @safe + extern (D) this(Loc loc, Statement _body, Statement finalbody) @safe { super(loc, STMT.TryFinally); this._body = _body; @@ -1584,7 +1583,7 @@ extern (C++) final class TryFinallyStatement : Statement this.bodyFallsThru = true; // assume true until statementSemantic() } - static TryFinallyStatement create(const ref Loc loc, Statement _body, Statement finalbody) @safe + static TryFinallyStatement create(Loc loc, Statement _body, Statement finalbody) @safe { return new TryFinallyStatement(loc, _body, finalbody); } @@ -1618,7 +1617,7 @@ extern (C++) final class ScopeGuardStatement : Statement TOK tok; Statement statement; - extern (D) this(const ref Loc loc, TOK tok, Statement statement) @safe + extern (D) this(Loc loc, TOK tok, Statement statement) @safe { super(loc, STMT.ScopeGuard); this.tok = tok; @@ -1646,7 +1645,7 @@ extern (C++) final class ThrowStatement : Statement // was generated by the compiler, wasn't present in source code bool internalThrow; - extern (D) this(const ref Loc loc, Expression exp) @safe + extern (D) this(Loc loc, Expression exp) @safe { super(loc, STMT.Throw); this.exp = exp; @@ -1671,7 +1670,7 @@ extern (C++) final class DebugStatement : Statement { Statement statement; - extern (D) this(const ref Loc loc, Statement statement) @safe + extern (D) this(Loc loc, Statement statement) @safe { super(loc, STMT.Debug); this.statement = statement; @@ -1701,7 +1700,7 @@ extern (C++) final class GotoStatement : Statement VarDeclaration lastVar; bool inCtfeBlock; /// set if goto is inside an `if (__ctfe)` block - extern (D) this(const ref Loc loc, Identifier ident) @safe + extern (D) this(Loc loc, Identifier ident) @safe { super(loc, STMT.Goto); this.ident = ident; @@ -1735,7 +1734,7 @@ extern (C++) final class LabelStatement : Statement bool breaks; // someone did a 'break ident' bool inCtfeBlock; // inside a block dominated by `if (__ctfe)` - extern (D) this(const ref Loc loc, Identifier ident, Statement statement) @safe + extern (D) this(Loc loc, Identifier ident, Statement statement) @safe { super(loc, STMT.Label); this.ident = ident; @@ -1766,9 +1765,9 @@ extern (C++) final class LabelDsymbol : Dsymbol // can be removed if generic error message deduplication is implemented bool duplicated; - extern (D) this(Identifier ident, const ref Loc loc = Loc.initial) @safe + extern (D) this(Identifier ident, Loc loc = Loc.initial) @safe { - super(loc, ident); + super(DSYM.labelDsymbol, loc, ident); } static LabelDsymbol create(Identifier ident) @safe @@ -1796,13 +1795,13 @@ extern (C++) class AsmStatement : Statement Token* tokens; bool caseSensitive; // for register names - extern (D) this(const ref Loc loc, Token* tokens) @safe + extern (D) this(Loc loc, Token* tokens) @safe { super(loc, STMT.Asm); this.tokens = tokens; } - extern (D) this(const ref Loc loc, Token* tokens, STMT stmt) @safe + extern (D) this(Loc loc, Token* tokens, STMT stmt) @safe { super(loc, stmt); this.tokens = tokens; @@ -1836,7 +1835,7 @@ version (IN_LLVM) LabelDsymbol isBranchToLabel; } - extern (D) this(const ref Loc loc, Token* tokens) @safe + extern (D) this(Loc loc, Token* tokens) @safe { super(loc, tokens, STMT.InlineAsm); } @@ -1868,7 +1867,7 @@ else */ extern (C++) final class GccAsmStatement : AsmStatement { - StorageClass stc; // attributes of the asm {} block + STC stc; // attributes of the asm {} block Expression insn; // string expression that is the template for assembler code Expressions* args; // input and output operands of the statement uint outputargs; // of the operands in 'args', the number of output operands @@ -1878,7 +1877,7 @@ extern (C++) final class GccAsmStatement : AsmStatement Identifiers* labels; // list of goto labels GotoStatements* gotos; // of the goto labels, the equivalent statements they represent - extern (D) this(const ref Loc loc, Token* tokens) @safe + extern (D) this(Loc loc, Token* tokens) @safe { super(loc, tokens, STMT.GccAsm); } @@ -1899,14 +1898,14 @@ extern (C++) final class GccAsmStatement : AsmStatement */ extern (C++) final class CompoundAsmStatement : CompoundStatement { - StorageClass stc; // postfix attributes like nothrow/pure/@trusted + STC stc; // postfix attributes like nothrow/pure/@trusted version (IN_LLVM) { void* abiret; // llvm::Value* } - extern (D) this(const ref Loc loc, Statements* statements, StorageClass stc) @safe + extern (D) this(Loc loc, Statements* statements, STC stc) @safe { super(loc, statements, STMT.CompoundAsm); this.stc = stc; @@ -1939,7 +1938,7 @@ extern (C++) final class ImportStatement : Statement { Dsymbols* imports; // Array of Import's - extern (D) this(const ref Loc loc, Dsymbols* imports) @safe + extern (D) this(Loc loc, Dsymbols* imports) @safe { super(loc, STMT.Import); this.imports = imports; diff --git a/dmd/statement.h b/dmd/statement.h index 1618123e3b..f979112404 100644 --- a/dmd/statement.h +++ b/dmd/statement.h @@ -1,6 +1,6 @@ /* Compiler implementation of the D programming language - * Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * written by Walter Bright * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. @@ -194,7 +194,7 @@ class ExpStatement : public Statement public: Expression *exp; - static ExpStatement *create(const Loc &loc, Expression *exp); + static ExpStatement *create(Loc loc, Expression *exp); ExpStatement *syntaxCopy() override; void accept(Visitor *v) override { v->visit(this); } @@ -226,7 +226,7 @@ class CompoundStatement : public Statement public: Statements *statements; - static CompoundStatement *create(const Loc &loc, Statement *s1, Statement *s2); + static CompoundStatement *create(Loc loc, Statement *s1, Statement *s2); CompoundStatement *syntaxCopy() override; ReturnStatement *endsWithReturnStatement() override final; Statement *last() override final; @@ -363,7 +363,7 @@ class ForeachRangeStatement final : public Statement { public: TOK op; // TOKforeach or TOKforeach_reverse - Parameter *prm; // loop index variable + Parameter *param; // loop index variable Expression *lwr; Expression *upr; Statement *_body; @@ -381,7 +381,7 @@ class ForeachRangeStatement final : public Statement class IfStatement final : public Statement { public: - Parameter *prm; + Parameter *param; Expression *condition; Statement *ifbody; Statement *elsebody; @@ -656,7 +656,7 @@ class TryFinallyStatement final : public Statement Statement *tryBody; // set to enclosing TryCatchStatement or TryFinallyStatement if in _body portion d_bool bodyFallsThru; // true if _body falls through to finally - static TryFinallyStatement *create(const Loc &loc, Statement *body, Statement *finalbody); + static TryFinallyStatement *create(Loc loc, Statement *body, Statement *finalbody); TryFinallyStatement *syntaxCopy() override; bool hasBreak() const override; bool hasContinue() const override; diff --git a/dmd/statementsem.d b/dmd/statementsem.d index 112a029952..c4ab94df7c 100644 --- a/dmd/statementsem.d +++ b/dmd/statementsem.d @@ -3,12 +3,12 @@ * * Specification: $(LINK2 https://dlang.org/spec/statement.html, Statements) * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/statementsem.d, _statementsem.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/statementsem.d, _statementsem.d) * Documentation: https://dlang.org/phobos/dmd_statementsem.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/statementsem.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/statementsem.d */ module dmd.statementsem; @@ -57,6 +57,7 @@ import dmd.opover; import dmd.parse; import dmd.common.outbuffer; import dmd.root.string; +import dmd.safe : isSafe, isSaferD, setUnsafe; import dmd.semantic2; import dmd.sideeffect; import dmd.statement; @@ -82,13 +83,13 @@ version (DMDLIB) */ private Identifier fixupLabelName(Scope* sc, Identifier ident) { - uint flags = (sc.flags & SCOPE.contract); + Contract c = sc.contract; const id = ident.toString(); - if (flags && flags != SCOPE.invariant_ && + if (c != Contract.none && c != Contract.invariant_ && !(id.length >= 2 && id[0] == '_' && id[1] == '_')) // does not start with "__" { OutBuffer buf; - buf.writestring(flags == SCOPE.require ? "__in_" : "__out_"); + buf.writestring(c == Contract.require ? "__in_" : "__out_"); buf.writestring(ident.toString()); ident = Identifier.idPool(buf[]); @@ -123,7 +124,7 @@ private LabelStatement checkLabeledLoop(Scope* sc, Statement statement) @safe */ private Expression checkAssignmentAsCondition(Expression e, Scope* sc) { - if (sc.flags & SCOPE.Cfile) + if (sc.inCfile) return e; auto ec = lastComma(e); if (ec.op == EXP.assign) @@ -214,7 +215,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc) } if (checkMustUse(s.exp, sc)) s.exp = ErrorExp.get(); - if (!(sc.flags & SCOPE.Cfile) && discardValue(s.exp)) + if (!sc.inCfile && discardValue(s.exp)) s.exp = ErrorExp.get(); s.exp = s.exp.optimize(WANTvalue); @@ -672,7 +673,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc) const olderrors = global.startGagging(); discardValue(fs.increment); if (global.endGagging(olderrors)) - deprecation(fs.increment.loc, "`%s` has no effect", fs.increment.toChars()); + deprecation(fs.increment.loc, "`%s` has no effect", fs.increment.toErrMsg()); if (checkNonAssignmentArrayOp(fs.increment)) fs.increment = ErrorExp.get(); fs.increment = fs.increment.optimize(WANTvalue); @@ -787,14 +788,9 @@ Statement statementSemanticVisit(Statement s, Scope* sc) Dsymbol sapplyOld = sapply; // 'sapply' will be NULL if and after 'inferApplyArgTypes' errors - /* Check for inference errors - */ + /* Check for inference errors and apply mutability checks inline */ if (!inferApplyArgTypes(fs, sc, sapply)) { - /** - Try and extract the parameter count of the opApply callback function, e.g.: - int opApply(int delegate(int, float)) => 2 args - */ bool foundMismatch = false; size_t foreachParamCount = 0; if (sapplyOld) @@ -814,6 +810,19 @@ Statement statementSemanticVisit(Statement s, Scope* sc) auto tf = fparam.type.nextOf().isTypeFunction(); foreachParamCount = tf.parameterList.length; foundMismatch = true; + + // Mutability check + 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(); + } } } } @@ -832,6 +841,24 @@ 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 @@ -985,7 +1012,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc) if (dim == 2) { Type tindex = (*fs.parameters)[0].type; - if (!tindex.isintegral()) + if (!tindex.isIntegral()) { error(fs.loc, "foreach: key cannot be of non-integral type `%s`", tindex.toChars()); return retError(); @@ -1274,7 +1301,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc) } else { - r = copyToTemp(0, "__r", fs.aggr); + r = copyToTemp(STC.none, "__r", fs.aggr); r.dsymbolSemantic(sc); _init = new ExpStatement(loc, r); if (vinit) @@ -1319,7 +1346,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc) if (auto ftt = tfront.toBasetype().isTypeFunction()) { tfront = tfront.toBasetype().nextOf(); - if (!ftt.isref) + if (!ftt.isRef) { // .front() does not return a ref. We ignore ref on foreach arg. // see https://issues.dlang.org/show_bug.cgi?id=11934 @@ -1454,25 +1481,25 @@ Statement statementSemanticVisit(Statement s, Scope* sc) return setError(); } - if (fs.prm.type) + if (fs.param.type) { - fs.prm.type = fs.prm.type.typeSemantic(loc, sc); - fs.prm.type = fs.prm.type.addStorageClass(fs.prm.storageClass); - fs.lwr = fs.lwr.implicitCastTo(sc, fs.prm.type); + fs.param.type = fs.param.type.typeSemantic(loc, sc); + fs.param.type = fs.param.type.addStorageClass(fs.param.storageClass); + fs.lwr = fs.lwr.implicitCastTo(sc, fs.param.type); - if (fs.upr.implicitConvTo(fs.prm.type) || (fs.prm.storageClass & STC.ref_)) + if (fs.upr.implicitConvTo(fs.param.type) || (fs.param.storageClass & STC.ref_)) { - fs.upr = fs.upr.implicitCastTo(sc, fs.prm.type); + fs.upr = fs.upr.implicitCastTo(sc, fs.param.type); } else { - // See if upr-1 fits in prm.type + // See if upr-1 fits in param.type Expression limit = new MinExp(loc, fs.upr, IntegerExp.literal!1); limit = limit.expressionSemantic(sc); limit = limit.optimize(WANTvalue); - if (!limit.implicitConvTo(fs.prm.type)) + if (!limit.implicitConvTo(fs.param.type)) { - fs.upr = fs.upr.implicitCastTo(sc, fs.prm.type); + fs.upr = fs.upr.implicitCastTo(sc, fs.param.type); } } } @@ -1485,26 +1512,26 @@ Statement statementSemanticVisit(Statement s, Scope* sc) { /* Just picking the first really isn't good enough. */ - fs.prm.type = fs.lwr.type; + fs.param.type = fs.lwr.type; } else if (fs.lwr.type == fs.upr.type) { /* Same logic as CondExp ?lwr:upr */ - fs.prm.type = fs.lwr.type; + fs.param.type = fs.lwr.type; } else { scope AddExp ea = new AddExp(loc, fs.lwr, fs.upr); if (typeCombine(ea, sc)) return setError(); - fs.prm.type = ea.type; + fs.param.type = ea.type; fs.lwr = ea.e1; fs.upr = ea.e2; } - fs.prm.type = fs.prm.type.addStorageClass(fs.prm.storageClass); + fs.param.type = fs.param.type.addStorageClass(fs.param.storageClass); } - if (fs.prm.type.ty == Terror || fs.lwr.op == EXP.error || fs.upr.op == EXP.error) + if (fs.param.type.ty == Terror || fs.lwr.op == EXP.error || fs.upr.op == EXP.error) { return setError(); } @@ -1549,7 +1576,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc) if (fs.op == TOK.foreach_reverse_) { cond = new PostExp(EXP.minusMinus, loc, new VarExp(loc, fs.key)); - if (fs.prm.type.isscalar()) + if (fs.param.type.isScalar()) { // key-- > tmp cond = new CmpExp(EXP.greaterThan, loc, cond, new VarExp(loc, tmp)); @@ -1562,7 +1589,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc) } else { - if (fs.prm.type.isscalar()) + if (fs.param.type.isScalar()) { // key < tmp cond = new CmpExp(EXP.lessThan, loc, new VarExp(loc, fs.key), new VarExp(loc, tmp)); @@ -1581,30 +1608,30 @@ Statement statementSemanticVisit(Statement s, Scope* sc) //increment = new AddAssignExp(loc, new VarExp(loc, fs.key), IntegerExp.literal!1); increment = new PreExp(EXP.prePlusPlus, loc, new VarExp(loc, fs.key)); } - if ((fs.prm.storageClass & STC.ref_) && fs.prm.type.equals(fs.key.type)) + if ((fs.param.storageClass & STC.ref_) && fs.param.type.equals(fs.key.type)) { fs.key.range = null; - auto v = new AliasDeclaration(loc, fs.prm.ident, fs.key); + auto v = new AliasDeclaration(loc, fs.param.ident, fs.key); fs._body = new CompoundStatement(loc, new ExpStatement(loc, v), fs._body); } else { - ie = new ExpInitializer(loc, new CastExp(loc, new VarExp(loc, fs.key), fs.prm.type)); - auto v = new VarDeclaration(loc, fs.prm.type, fs.prm.ident, ie); - v.storage_class |= STC.temp | STC.foreach_ | (fs.prm.storageClass & STC.ref_); + ie = new ExpInitializer(loc, new CastExp(loc, new VarExp(loc, fs.key), fs.param.type)); + auto v = new VarDeclaration(loc, fs.param.type, fs.param.ident, ie); + v.storage_class |= STC.temp | STC.foreach_ | (fs.param.storageClass & STC.ref_); fs._body = new CompoundStatement(loc, new ExpStatement(loc, v), fs._body); - if (fs.key.range && !fs.prm.type.isMutable()) + if (fs.key.range && !fs.param.type.isMutable()) { /* Limit the range of the key to the specified range */ v.range = new IntRange(fs.key.range.imin, fs.key.range.imax - SignExtendedNumber(1)); } } - if (fs.prm.storageClass & STC.ref_) + if (fs.param.storageClass & STC.ref_) { - if (fs.key.type.constConv(fs.prm.type) == MATCH.nomatch) + if (fs.key.type.constConv(fs.param.type) == MATCH.nomatch) { - error(fs.loc, "argument type mismatch, `%s` to `ref %s`", fs.key.type.toChars(), fs.prm.type.toChars()); + error(fs.loc, "argument type mismatch, `%s` to `ref %s`", fs.key.type.toChars(), fs.param.type.toChars()); return setError(); } } @@ -1627,15 +1654,15 @@ Statement statementSemanticVisit(Statement s, Scope* sc) sym.parent = sc.scopesym; sym.endlinnum = ifs.endloc.linnum; Scope* scd = sc.push(sym); - if (ifs.prm) + if (ifs.param) { - /* Declare prm, which we will set to be the + /* Declare param, which we will set to be the * result of condition. */ auto ei = new ExpInitializer(ifs.loc, ifs.condition); - ifs.match = new VarDeclaration(ifs.loc, ifs.prm.type, ifs.prm.ident, ei); + ifs.match = new VarDeclaration(ifs.loc, ifs.param.type, ifs.param.ident, ei); ifs.match.parent = scd.func; - ifs.match.storage_class |= ifs.prm.storageClass; + ifs.match.storage_class |= ifs.param.storageClass; ifs.match.dsymbolSemantic(scd); auto de = new DeclarationExp(ifs.loc, ifs.match); @@ -1671,8 +1698,8 @@ Statement statementSemanticVisit(Statement s, Scope* sc) if (checkNonAssignmentArrayOp(ifs.condition)) ifs.condition = ErrorExp.get(); - // Convert to boolean after declaring prm so this works: - // if (S prm = S()) {} + // Convert to boolean after declaring param so this works: + // if (S param = S()) {} // where S is a struct that defines opCast!bool. ifs.condition = ifs.condition.toBoolean(scd); @@ -1707,7 +1734,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc) if (ifs.isIfCtfeBlock()) { Scope* scd2 = scd.push(); - scd2.flags |= SCOPE.ctfeBlock; + scd2.ctfeBlock = true; ifs.ifbody = ifs.ifbody.semanticNoScope(scd2); scd2.pop(); } @@ -1743,11 +1770,10 @@ Statement statementSemanticVisit(Statement s, Scope* sc) // This feature allows a limited form of conditional compilation. if (cs.condition.include(sc)) { - DebugCondition dc = cs.condition.isDebugCondition(); - if (dc) + if (DebugCondition dc = cs.condition.isDebugCondition()) { sc = sc.push(); - sc.flags |= SCOPE.debug_; + sc.debug_ = true; cs.ifbody = cs.ifbody.statementSemantic(sc); sc.pop(); } @@ -1788,7 +1814,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc) //printf("SwitchStatement::semantic(%p)\n", ss); ss.tryBody = sc.tryBody; - ss.tf = sc.tf; + ss.tryFinally = sc.tryFinally; if (ss.cases) { result = ss; // already run @@ -1848,7 +1874,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc) break; } ss.condition = integralPromotions(ss.condition, sc); - if (!ss.condition.isErrorExp() && ss.condition.type.isintegral()) + if (!ss.condition.isErrorExp() && ss.condition.type.isIntegral()) break; auto ad = isAggregate(ss.condition.type); @@ -1882,7 +1908,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc) sc = sc.push(); sc.sbreak = ss; - sc.sw = ss; + sc.switchStatement = ss; ss.cases = new CaseStatements(); const inLoopSave = sc.inLoop; @@ -1909,9 +1935,9 @@ Statement statementSemanticVisit(Statement s, Scope* sc) for (Scope* scx = sc; scx; scx = scx.enclosing) { - if (!scx.sw) + if (!scx.switchStatement) continue; - foreach (cs; *scx.sw.cases) + foreach (cs; *scx.switchStatement.cases) { if (cs.exp.equals(gcs.exp)) { @@ -1974,11 +2000,11 @@ version (IN_LLVM) needswitcherror = true; } - ss.hasDefault = sc.sw.sdefault || - !(!ss.isFinal || needswitcherror || global.params.useAssert == CHECKENABLE.on || sc.func.isSafe); + ss.hasDefault = sc.switchStatement.sdefault || + !(!ss.isFinal || needswitcherror || global.params.useAssert == CHECKENABLE.on || sc.func.isSafe || sc.func.isSaferD); if (!ss.hasDefault) { - if (!ss.isFinal && (!ss._body || !ss._body.isErrorStatement()) && !(sc.flags & SCOPE.Cfile)) + if (!ss.isFinal && (!ss._body || !ss._body.isErrorStatement()) && !sc.inCfile) error(ss.loc, "`switch` statement without a `default`; use `final switch` or add `default: assert(0);` or add `default: break;`"); // Generate runtime error if the default is hit @@ -1986,7 +2012,7 @@ version (IN_LLVM) CompoundStatement cs; Statement s; - if (sc.flags & SCOPE.Cfile) + if (sc.inCfile) { s = new BreakStatement(ss.loc, null); // default for C is `default: break;` } @@ -2028,16 +2054,16 @@ version (IN_LLVM) s = new ExpStatement(ss.loc, new HaltExp(ss.loc)); a.reserve(2); - sc.sw.sdefault = new DefaultStatement(ss.loc, s); + sc.switchStatement.sdefault = new DefaultStatement(ss.loc, s); a.push(ss._body); if (ss._body.blockExit(sc.func, null) & BE.fallthru) a.push(new BreakStatement(Loc.initial, null)); - a.push(sc.sw.sdefault); + a.push(sc.switchStatement.sdefault); cs = new CompoundStatement(ss.loc, a); ss._body = cs; } - if (!(sc.flags & SCOPE.Cfile) && ss.checkLabel()) + if (!sc.inCfile && ss.checkLabel()) { sc.pop(); return setError(); @@ -2089,7 +2115,7 @@ version (IN_LLVM) // Make a copy of all the cases so that qsort doesn't scramble the actual // data we pass to codegen (the order of the cases in the switch). - CaseStatements *csCopy = (*ss.cases).copy(); + CaseStatements* csCopy = (*ss.cases).copy(); if (numcases) { @@ -2141,7 +2167,7 @@ version (IN_LLVM) void visitCase(CaseStatement cs) { - SwitchStatement sw = sc.sw; + SwitchStatement sw = sc.switchStatement; bool errors = false; //printf("CaseStatement::semantic() %s\n", toChars()); @@ -2172,7 +2198,7 @@ version (IN_LLVM) { VarDeclaration v = ve.var.isVarDeclaration(); Type t = cs.exp.type.toBasetype(); - if (v && (t.isintegral() || t.ty == Tclass)) + if (v && (t.isIntegral() || t.ty == Tclass)) { /* Flag that we need to do special code generation * for this, i.e. generate a sequence of if-then-else @@ -2197,9 +2223,9 @@ version (IN_LLVM) */ for (Scope* scx = sc; scx; scx = scx.enclosing) { - if (scx.enclosing && scx.enclosing.sw == sw) + if (scx.enclosing && scx.enclosing.switchStatement == sw) continue; - assert(scx.sw == sw); + assert(scx.switchStatement == sw); Dsymbol pscopesym; if (!scx.search(cs.exp.loc, v.ident, pscopesym)) @@ -2254,12 +2280,12 @@ version (IN_LLVM) i++; } - if (sc.sw.tf != sc.tf) + if (sc.switchStatement.tryFinally != sc.tryFinally) { error(cs.loc, "`switch` and `case` are in different `finally` blocks"); errors = true; } - if (sc.sw.tryBody != sc.tryBody) + if (sc.switchStatement.tryBody != sc.tryBody) { error(cs.loc, "case cannot be in different `try` block level from `switch`"); errors = true; @@ -2287,7 +2313,7 @@ version (IN_LLVM) void visitCaseRange(CaseRangeStatement crs) { - SwitchStatement sw = sc.sw; + SwitchStatement sw = sc.switchStatement; if (sw is null) { error(crs.loc, "case range not in `switch` statement"); @@ -2325,7 +2351,7 @@ version (IN_LLVM) uinteger_t fval = crs.first.toInteger(); uinteger_t lval = crs.last.toInteger(); - if ((crs.first.type.isunsigned() && fval > lval) || (!crs.first.type.isunsigned() && cast(sinteger_t)fval > cast(sinteger_t)lval)) + if ((crs.first.type.isUnsigned() && fval > lval) || (!crs.first.type.isUnsigned() && cast(sinteger_t)fval > cast(sinteger_t)lval)) { error(crs.loc, "first `case %s` is greater than last `case %s`", crs.first.toChars(), crs.last.toChars()); errors = true; @@ -2372,26 +2398,26 @@ version (IN_LLVM) { //printf("DefaultStatement::semantic()\n"); bool errors = false; - if (sc.sw) + if (sc.switchStatement) { - if (sc.sw.sdefault) + if (sc.switchStatement.sdefault) { error(ds.loc, "`switch` statement already has a default"); errors = true; } - sc.sw.sdefault = ds; + sc.switchStatement.sdefault = ds; - if (sc.sw.tf != sc.tf) + if (sc.switchStatement.tryFinally != sc.tryFinally) { error(ds.loc, "`switch` and `default` are in different `finally` blocks"); errors = true; } - if (sc.sw.tryBody != sc.tryBody) + if (sc.switchStatement.tryBody != sc.tryBody) { error(ds.loc, "default cannot be in different `try` block level from `switch`"); errors = true; } - if (sc.sw.isFinal) + if (sc.switchStatement.isFinal) { error(ds.loc, "`default` statement not allowed in `final switch` statement"); errors = true; @@ -2417,7 +2443,7 @@ version (IN_LLVM) /* https://dlang.org/spec/statement.html#goto-statement */ - gds.sw = sc.sw; + gds.sw = sc.switchStatement; if (!gds.sw) { error(gds.loc, "`goto default` not in `switch` statement"); @@ -2442,7 +2468,7 @@ version (IN_LLVM) /* https://dlang.org/spec/statement.html#goto-statement */ - if (!sc.sw) + if (!sc.switchStatement) { error(gcs.loc, "`goto case` not in `switch` statement"); return setError(); @@ -2450,19 +2476,19 @@ version (IN_LLVM) version (IN_LLVM) { - gcs.sw = sc.sw; + gcs.sw = sc.switchStatement; } if (gcs.exp) { gcs.exp = gcs.exp.expressionSemantic(sc); - gcs.exp = gcs.exp.implicitCastTo(sc, sc.sw.condition.type); + gcs.exp = gcs.exp.implicitCastTo(sc, sc.switchStatement.condition.type); gcs.exp = gcs.exp.optimize(WANTvalue); if (gcs.exp.op == EXP.error) return setError(); } - sc.sw.gotoCases.push(gcs); + sc.switchStatement.gotoCases.push(gcs); result = gcs; } @@ -2471,7 +2497,7 @@ version (IN_LLVM) /* https://dlang.org/spec/statement.html#return-statement */ - //printf("ReturnStatement.dsymbolSemantic() %p, %s\n", rs, rs.toChars()); + //printf("ReturnStatement.dsymbolSemantic() %s\n", toChars(rs)); FuncDeclaration fd = sc.parent.isFuncDeclaration(); if (fd.fes) @@ -2507,31 +2533,31 @@ version (IN_LLVM) Type tret = tf.next; Type tbret = tret ? tret.toBasetype() : null; - bool inferRef = (tf.isref && (fd.storage_class & STC.auto_)); + bool inferRef = (tf.isRef && (fd.storage_class & STC.auto_)); Expression e0 = null; bool errors = false; - if (sc.flags & SCOPE.contract) + if (sc.contract) { error(rs.loc, "`return` statements cannot be in contracts"); errors = true; } - if (sc.os) + if (sc.scopeGuard) { // @@@DEPRECATED_2.112@@@ // Deprecated in 2.100, transform into an error in 2.112 - if (sc.os.tok == TOK.onScopeFailure) + if (sc.scopeGuard.tok == TOK.onScopeFailure) { deprecation(rs.loc, "`return` statements cannot be in `scope(failure)` bodies."); deprecationSupplemental(rs.loc, "Use try-catch blocks for this purpose"); } else { - error(rs.loc, "`return` statements cannot be in `%s` bodies", Token.toChars(sc.os.tok)); + error(rs.loc, "`return` statements cannot be in `%s` bodies", Token.toChars(sc.scopeGuard.tok)); errors = true; } } - if (sc.tf) + if (sc.tryFinally) { error(rs.loc, "`return` statements cannot be in `finally` bodies"); errors = true; @@ -2552,7 +2578,8 @@ version (IN_LLVM) } else if (rs.exp) { - fd.hasReturnExp |= (fd.hasReturnExp & 1 ? 16 : 1); + fd.hasMultipleReturnExp = fd.hasReturnExp; + fd.hasReturnExp = true; FuncLiteralDeclaration fld = fd.isFuncLiteralDeclaration(); if (tret) @@ -2563,7 +2590,7 @@ version (IN_LLVM) rs.exp = rs.exp.expressionSemantic(sc); rs.exp = rs.exp.arrayFuncConv(sc); // If we're returning by ref, allow the expression to be `shared` - const returnSharedRef = (tf.isref && (fd.inferRetType || tret.isShared())); + const returnSharedRef = (tf.isRef && (fd.inferRetType || tret.isShared())); rs.exp.checkSharedAccess(sc, returnSharedRef); // for static alias this: https://issues.dlang.org/show_bug.cgi?id=17684 @@ -2614,7 +2641,7 @@ version (IN_LLVM) //errors = true; } if (global.endGagging(olderrors)) - deprecation(rs.exp.loc, "`%s` has no effect", rs.exp.toChars()); + deprecation(rs.exp.loc, "`%s` has no effect", rs.exp.toErrMsg()); /* Replace: * return exp; @@ -2681,13 +2708,13 @@ version (IN_LLVM) void turnOffRef(scope void delegate() supplemental) { - tf.isref = false; // return by value - tf.isreturn = false; // ignore 'return' attribute, whether explicit or inferred + tf.isRef = false; // return by value + tf.isReturn = false; // ignore 'return' attribute, whether explicit or inferred fd.storage_class &= ~STC.return_; // If we previously assumed the function could be ref when // checking for `shared`, make sure we were right - if (global.params.noSharedAccess == FeatureState.enabled && rs.exp.type.isShared()) + if (sc.previews.noSharedAccess && rs.exp.type.isShared()) { .error(fd.loc, "%s `%s` function returns `shared` but cannot be inferred `ref`", fd.kind, fd.toPrettyChars); supplemental(); @@ -2754,7 +2781,7 @@ version (IN_LLVM) // https://issues.dlang.org/show_bug.cgi?id=23914 if (inferRef && !resType.isTypeNoreturn()) // deduce 'auto ref' - tf.isref = false; + tf.isRef = false; if (tbret.ty != Tvoid && !resType.isTypeNoreturn()) // if non-void return { @@ -2911,7 +2938,7 @@ version (IN_LLVM) Statement s = ls.statement; if (!s || !s.hasBreak()) error(bs.loc, "label `%s` has no `break`", bs.ident.toChars()); - else if (ls.tf != sc.tf) + else if (ls.tf != sc.tryFinally) error(bs.loc, "cannot break out of `finally` block"); else { @@ -2931,9 +2958,9 @@ version (IN_LLVM) } else if (!sc.sbreak) { - if (sc.os && sc.os.tok != TOK.onScopeFailure) + if (sc.scopeGuard && sc.scopeGuard.tok != TOK.onScopeFailure) { - error(bs.loc, "`break` is not allowed inside `%s` bodies", Token.toChars(sc.os.tok)); + error(bs.loc, "`break` is not allowed inside `%s` bodies", Token.toChars(sc.scopeGuard.tok)); } else if (sc.fes) { @@ -3003,7 +3030,7 @@ version (IN_LLVM) Statement s = ls.statement; if (!s || !s.hasContinue()) error(cs.loc, "label `%s` has no `continue`", cs.ident.toChars()); - else if (ls.tf != sc.tf) + else if (ls.tf != sc.tryFinally) error(cs.loc, "cannot continue out of `finally` block"); else { @@ -3022,9 +3049,9 @@ version (IN_LLVM) } else if (!sc.scontinue) { - if (sc.os && sc.os.tok != TOK.onScopeFailure) + if (sc.scopeGuard && sc.scopeGuard.tok != TOK.onScopeFailure) { - error(cs.loc, "`continue` is not allowed inside `%s` bodies", Token.toChars(sc.os.tok)); + error(cs.loc, "`continue` is not allowed inside `%s` bodies", Token.toChars(sc.scopeGuard.tok)); } else if (sc.fes) { @@ -3092,14 +3119,14 @@ version (IN_LLVM) * _d_monitorenter(tmp); * try { body } finally { _d_monitorexit(tmp); } */ - auto tmp = copyToTemp(0, "__sync", ss.exp); + auto tmp = copyToTemp(STC.none, "__sync", ss.exp); tmp.dsymbolSemantic(sc); auto cs = new Statements(); cs.push(new ExpStatement(ss.loc, tmp)); auto args = new Parameters(); - args.push(new Parameter(Loc.initial, 0, ClassDeclaration.object.type, null, null, null)); + args.push(new Parameter(Loc.initial, STC.none, ClassDeclaration.object.type, null, null, null)); FuncDeclaration fdenter = FuncDeclaration.genCfunc(args, Type.tvoid, Id.monitorenter); Expression e = new CallExp(ss.loc, fdenter, new VarExp(ss.loc, tmp)); @@ -3141,7 +3168,7 @@ version (IN_LLVM) cs.push(new ExpStatement(ss.loc, v)); auto enterArgs = new Parameters(); - enterArgs.push(new Parameter(Loc.initial, 0, t.pointerTo(), null, null, null)); + enterArgs.push(new Parameter(Loc.initial, STC.none, t.pointerTo(), null, null, null)); FuncDeclaration fdenter = FuncDeclaration.genCfunc(enterArgs, Type.tvoid, Id.criticalenter, STC.nothrow_); Expression e = new AddrExp(ss.loc, tmpExp); @@ -3151,7 +3178,7 @@ version (IN_LLVM) cs.push(new ExpStatement(ss.loc, e)); auto exitArgs = new Parameters(); - exitArgs.push(new Parameter(Loc.initial, 0, t, null, null, null)); + exitArgs.push(new Parameter(Loc.initial, STC.none, t, null, null, null)); FuncDeclaration fdexit = FuncDeclaration.genCfunc(exitArgs, Type.tvoid, Id.criticalexit, STC.nothrow_); e = new CallExp(ss.loc, fdexit, tmpExp); @@ -3238,7 +3265,7 @@ version (IN_LLVM) * } * } */ - auto tmp = copyToTemp(0, "__withtmp", ws.exp); + auto tmp = copyToTemp(STC.none, "__withtmp", ws.exp); tmp.dsymbolSemantic(sc); auto es = new ExpStatement(ws.loc, tmp); ws.exp = new VarExp(ws.loc, tmp); @@ -3376,7 +3403,7 @@ version (IN_LLVM) /* If catch exception type is derived from Exception */ if (c.type.toBasetype().implicitConvTo(ClassDeclaration.exception.type) && - (!c.handler || !c.handler.comeFrom()) && !(sc.flags & SCOPE.debug_)) + (!c.handler || !c.handler.comeFrom()) && !sc.debug_) { // Remove c from the array of catches tcs.catches.remove(i); @@ -3400,7 +3427,7 @@ version (IN_LLVM) tfs._body = tfs._body.semanticScope(sc, null, null, tfs); sc = sc.push(); - sc.tf = tfs; + sc.tryFinally = tfs; sc.sbreak = null; sc.scontinue = null; // no break or continue out of finally block tfs.finalbody = tfs.finalbody.semanticNoScope(sc); @@ -3453,13 +3480,13 @@ version (IN_LLVM) // scope(success) and scope(failure) are rewritten to try-catch(-finally) statement, // so the generated catch block cannot be placed in finally block. // See also Catch::semantic. - if (sc.os && sc.os.tok != TOK.onScopeFailure) + if (sc.scopeGuard && sc.scopeGuard.tok != TOK.onScopeFailure) { // If enclosing is scope(success) or scope(exit), this will be placed in finally block. - error(oss.loc, "cannot put `%s` statement inside `%s`", Token.toChars(oss.tok), Token.toChars(sc.os.tok)); + error(oss.loc, "cannot put `%s` statement inside `%s`", Token.toChars(oss.tok), Token.toChars(sc.scopeGuard.tok)); return setError(); } - if (sc.tf) + if (sc.tryFinally) { error(oss.loc, "cannot put `%s` statement inside `finally` block", Token.toChars(oss.tok)); return setError(); @@ -3467,8 +3494,8 @@ version (IN_LLVM) } sc = sc.push(); - sc.tf = null; - sc.os = oss; + sc.tryFinally = null; + sc.scopeGuard = oss; if (oss.tok != TOK.onScopeFailure) { // Jump out from scope(failure) block is allowed. @@ -3504,7 +3531,7 @@ version (IN_LLVM) if (ds.statement) { sc = sc.push(); - sc.flags |= SCOPE.debug_; + sc.debug_ = true; ds.statement = ds.statement.statementSemantic(sc); debugThrowWalker(ds.statement); sc.pop(); @@ -3523,10 +3550,10 @@ version (IN_LLVM) gs.ident = fixupLabelName(sc, gs.ident); gs.label = fd.searchLabel(gs.ident, gs.loc); gs.tryBody = sc.tryBody; - gs.tf = sc.tf; - gs.os = sc.os; + gs.tf = sc.tryFinally; + gs.os = sc.scopeGuard; gs.lastVar = sc.lastVar; - gs.inCtfeBlock = (sc.flags & SCOPE.ctfeBlock) != 0; + gs.inCtfeBlock = sc.ctfeBlock; if (!gs.label.statement && sc.fes) { @@ -3550,7 +3577,7 @@ version (IN_LLVM) fd.gotos = new GotoStatements(); fd.gotos.push(gs); } - else if (!(sc.flags & SCOPE.Cfile) && gs.checkLabel()) + else if (!sc.inCfile && gs.checkLabel()) return setError(); result = gs; @@ -3563,10 +3590,10 @@ version (IN_LLVM) ls.ident = fixupLabelName(sc, ls.ident); ls.tryBody = sc.tryBody; - ls.tf = sc.tf; - ls.os = sc.os; + ls.tf = sc.tryFinally; + ls.os = sc.scopeGuard; ls.lastVar = sc.lastVar; - ls.inCtfeBlock = (sc.flags & SCOPE.ctfeBlock) != 0; + ls.inCtfeBlock = sc.ctfeBlock; LabelDsymbol ls2 = fd.searchLabel(ls.ident, ls.loc); if (ls2.statement && !ls2.duplicated) @@ -3644,9 +3671,9 @@ version (IN_LLVM) } assert(sc.func); - if (!(cas.stc & STC.pure_) && sc.func.setImpure(cas.loc, "`asm` statement is assumed to be impure - mark it with `pure` if it is not")) + 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, "`asm` statement in %s `%s` is assumed to use the GC - mark it with `@nogc` if it does not")) + if (!(cas.stc & STC.nogc) && sc.func.setGC(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` @@ -3655,7 +3682,7 @@ version (IN_LLVM) deprecation(cas.loc, "`asm` statement cannot be marked `@safe`, use `@system` or `@trusted` instead"); if (!(cas.stc & (STC.trusted | STC.safe))) { - sc.setUnsafe(false, cas.loc, "`asm` statement is assumed to be `@system` - mark it with `@trusted` if it is not"); + sc.setUnsafe(false, cas.loc, "executing an `asm` statement without `@trusted` annotation"); } sc.pop(); @@ -3717,7 +3744,7 @@ version (IN_LLVM) * * Returns: true if the `throw` is valid, or false if an error was found */ -public bool throwSemantic(const ref Loc loc, ref Expression exp, Scope* sc) +public bool throwSemantic(Loc loc, ref Expression exp, Scope* sc) { if (!global.params.useExceptions) { @@ -3734,9 +3761,6 @@ public bool throwSemantic(const ref Loc loc, ref Expression exp, Scope* sc) return false; } - if (FuncDeclaration fd = sc.parent.isFuncDeclaration()) - fd.hasReturnExp |= 2; - if (auto ne = exp.isNewExp()) { ne.thrownew = true; @@ -3871,11 +3895,11 @@ private extern(D) Expression applyArray(ForeachStatement fs, Expression flde, auto params = new Parameters(); params.push(new Parameter(Loc.initial, STC.in_, tn.arrayOf(), null, null, null)); auto dgparams = new Parameters(); - dgparams.push(new Parameter(Loc.initial, 0, Type.tvoidptr, null, null, null)); + dgparams.push(new Parameter(Loc.initial, STC.none, Type.tvoidptr, null, null, null)); if (dim == 2) - dgparams.push(new Parameter(Loc.initial, 0, Type.tvoidptr, null, null, null)); + 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, 0, dgty, null, null, null)); + params.push(new Parameter(Loc.initial, STC.none, dgty, null, null, null)); fdapply = FuncDeclaration.genCfunc(params, Type.tint32, fdname.ptr); if (tab.isTypeSArray()) @@ -3936,14 +3960,14 @@ private extern(D) Expression applyAssocArray(ForeachStatement fs, Expression fld if (!fdapply[i]) { auto params = new Parameters(); - params.push(new Parameter(Loc.initial, 0, Type.tvoid.pointerTo(), null, null, null)); + params.push(new Parameter(Loc.initial, STC.none, Type.tvoid.pointerTo(), null, null, null)); params.push(new Parameter(Loc.initial, STC.const_, Type.tsize_t, null, null, null)); auto dgparams = new Parameters(); - dgparams.push(new Parameter(Loc.initial, 0, Type.tvoidptr, null, null, null)); + dgparams.push(new Parameter(Loc.initial, STC.none, Type.tvoidptr, null, null, null)); if (dim == 2) - dgparams.push(new Parameter(Loc.initial, 0, Type.tvoidptr, null, null, null)); + dgparams.push(new Parameter(Loc.initial, STC.none, Type.tvoidptr, null, null, null)); fldeTy[i] = new TypeDelegate(new TypeFunction(ParameterList(dgparams), Type.tint32, LINK.d)); - params.push(new Parameter(Loc.initial, 0, fldeTy[i], null, null, null)); + params.push(new Parameter(Loc.initial, STC.none, fldeTy[i], null, null, null)); fdapply[i] = FuncDeclaration.genCfunc(params, Type.tint32, i ? Id._aaApply2 : Id._aaApply); } @@ -3969,7 +3993,7 @@ private extern(D) Expression applyAssocArray(ForeachStatement fs, Expression fld return ec; } -private extern(D) Statement loopReturn(Expression e, Statements* cases, const ref Loc loc) +private extern(D) Statement loopReturn(Expression e, Statements* cases, Loc loc) { if (!cases.length) { @@ -4015,7 +4039,7 @@ private FuncExp foreachBodyToFunction(Scope* sc, ForeachStatement fs, TypeFuncti auto params = new Parameters(); foreach (i, p; *fs.parameters) { - StorageClass stc = STC.ref_ | (p.storageClass & STC.scope_); + STC stc = STC.ref_ | (p.storageClass & STC.scope_); Identifier id; p.type = p.type.typeSemantic(fs.loc, sc); @@ -4027,12 +4051,12 @@ version (IN_LLVM) } if (tfld) { - Parameter prm = tfld.parameterList[i]; - //printf("\tprm = %s%s\n", (prm.storageClass&STC.ref_?"ref ":"").ptr, prm.ident.toChars()); - stc = (prm.storageClass & STC.ref_) | (p.storageClass & STC.scope_); - if ((p.storageClass & STC.ref_) != (prm.storageClass & STC.ref_)) + Parameter param = tfld.parameterList[i]; + //printf("\tparam = %s%s\n", (param.storageClass&STC.ref_?"ref ":"").ptr, param.ident.toChars()); + stc = (param.storageClass & STC.ref_) | (p.storageClass & STC.scope_); + if ((p.storageClass & STC.ref_) != (param.storageClass & STC.ref_)) { - if (!(prm.storageClass & STC.ref_)) + if (!(param.storageClass & STC.ref_)) { error(fs.loc, "`foreach`: cannot make `%s` `ref`", p.ident.toChars()); return null; @@ -4088,7 +4112,7 @@ else } // https://issues.dlang.org/show_bug.cgi?id=13840 // Throwable nested function inside nothrow function is acceptable. - StorageClass stc = mergeFuncAttrs(STC.safe | STC.pure_ | STC.nogc, fs.func); + STC stc = mergeFuncAttrs(STC.safe | STC.pure_ | STC.nogc, fs.func); auto tf = new TypeFunction(ParameterList(params), Type.tint32, LINK.d, stc); fs.cases = new Statements(); fs.gotos = new ScopeStatements(); @@ -4107,13 +4131,13 @@ void catchSemantic(Catch c, Scope* sc) { //printf("Catch::semantic(%s)\n", ident.toChars()); - if (sc.os && sc.os.tok != TOK.onScopeFailure) + if (sc.scopeGuard && sc.scopeGuard.tok != TOK.onScopeFailure) { // If enclosing is scope(success) or scope(exit), this will be placed in finally block. - error(c.loc, "cannot put `catch` statement inside `%s`", Token.toChars(sc.os.tok)); + error(c.loc, "cannot put `catch` statement inside `%s`", Token.toChars(sc.scopeGuard.tok)); c.errors = true; } - if (sc.tf) + if (sc.tryFinally) { /* This is because the _d_local_unwind() gets the stack munged * up on this. The workaround is to place any try-catches into @@ -4153,7 +4177,7 @@ void catchSemantic(Catch c, Scope* sc) return; } - StorageClass stc; + STC stc; auto cd = c.type.toBasetype().isClassHandle(); if (!cd) { @@ -4169,7 +4193,7 @@ void catchSemantic(Catch c, Scope* sc) } if (!c.internalCatch) { - if (sc.setUnsafe(false, c.loc, "cannot catch C++ class objects in `@safe` code")) + if (sc.setUnsafe(false, c.loc, "catching C++ class objects")) c.errors = true; } } @@ -4181,18 +4205,18 @@ void catchSemantic(Catch c, Scope* sc) else if (!c.internalCatch && ClassDeclaration.exception && cd != ClassDeclaration.exception && !ClassDeclaration.exception.isBaseOf(cd, null) && sc.setUnsafe(false, c.loc, - "can only catch class objects derived from `Exception` in `@safe` code, not `%s`", c.type)) + "catching class objects derived from `%s` instead of `Exception`", c.type)) { c.errors = true; } - else if (global.params.ehnogc) + else if (sc.previews.dip1008) { stc |= STC.scope_; } // DIP1008 requires destruction of the Throwable, even if the user didn't specify an identifier auto ident = c.ident; - if (!ident && global.params.ehnogc) + if (!ident && sc.previews.dip1008) ident = Identifier.generateAnonymousId("var"); if (ident) @@ -4202,7 +4226,7 @@ void catchSemantic(Catch c, Scope* sc) c.var.dsymbolSemantic(sc); sc.insert(c.var); - if (global.params.ehnogc && stc & STC.scope_) + if (sc.previews.dip1008 && stc & STC.scope_) { /* Add a destructor for c.var * try { handler } finally { if (!__ctfe) _d_delThrowable(var); } @@ -4310,7 +4334,7 @@ Statement scopeCode(Statement statement, Scope* sc, out Statement sentry, out St * sexception: x = true; * sfinally: if (!x) statement; */ - auto v = copyToTemp(0, "__os", IntegerExp.createBool(false)); + auto v = copyToTemp(STC.none, "__os", IntegerExp.createBool(false)); v.dsymbolSemantic(sc); sentry = new ExpStatement(statement.loc, v); @@ -4461,7 +4485,7 @@ public auto makeTupleForeach(Scope* sc, bool isStatic, bool isDecl, ForeachState } p.type = p.type.typeSemantic(loc, sc); - if (!p.type.isintegral()) + if (!p.type.isIntegral()) { error(fs.loc, "foreach: key cannot be of non-integral type `%s`", p.type.toChars()); @@ -4503,7 +4527,7 @@ public auto makeTupleForeach(Scope* sc, bool isStatic, bool isDecl, ForeachState * Returns: * `true` iff the declaration was successful. */ - bool declareVariable(StorageClass storageClass, Type type, Identifier ident, Expression e, Type t) + bool declareVariable(STC storageClass, Type type, Identifier ident, Expression e, Type t) { if (storageClass & (STC.out_ | STC.lazy_) || storageClass & STC.ref_ && !te) @@ -4644,7 +4668,7 @@ public auto makeTupleForeach(Scope* sc, bool isStatic, bool isDecl, ForeachState { // expand tuples into multiple `static foreach` variables. assert(e && !t); auto ident = Identifier.generateId("__value"); - declareVariable(0, e.type, ident, e, null); + declareVariable(STC.none, e.type, ident, e, null); import dmd.cond: StaticForeach; auto field = Identifier.idPool(StaticForeach.tupleFieldName.ptr,StaticForeach.tupleFieldName.length); Expression access = new DotIdExp(loc, e, field); @@ -4888,9 +4912,9 @@ private Statements* flatten(Statement statement, Scope* sc) buf.writeByte(0); const str = buf.extractSlice()[0 .. len]; const bool doUnittests = global.params.parsingUnittestsRequired(); - auto loc = adjustLocForMixin(str, cs.loc, global.params.mixinOut); - scope p = new Parser!ASTCodegen(loc, sc._module, str, false, global.errorSink, &global.compileEnv, doUnittests); - p.transitionIn = global.params.v.vin; + scope p = new Parser!ASTCodegen(sc._module, str, false, global.errorSink, &global.compileEnv, doUnittests); + adjustLocForMixin(str, cs.loc, *p.baseLoc, global.params.mixinOut); + p.linnum = p.baseLoc.startLine; p.nextToken(); auto a = new Statements(); diff --git a/dmd/staticassert.d b/dmd/staticassert.d index 08780ca078..52ded559e5 100644 --- a/dmd/staticassert.d +++ b/dmd/staticassert.d @@ -3,12 +3,12 @@ * * Specification: $(LINK2 https://dlang.org/spec/version.html#static-assert, Static Assert) * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/staticassert.d, _staticassert.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/staticassert.d, _staticassert.d) * Documentation: https://dlang.org/phobos/dmd_staticassert.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/staticassert.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/staticassert.d */ module dmd.staticassert; @@ -28,17 +28,17 @@ extern (C++) final class StaticAssert : Dsymbol Expression exp; Expressions* msgs; - extern (D) this(const ref Loc loc, Expression exp, Expression msg) + extern (D) this(Loc loc, Expression exp, Expression msg) { - super(loc, Id.empty); + super(DSYM.staticAssert, loc, Id.empty); this.exp = exp; this.msgs = new Expressions(1); (*this.msgs)[0] = msg; } - extern (D) this(const ref Loc loc, Expression exp, Expressions* msgs) + extern (D) this(Loc loc, Expression exp, Expressions* msgs) { - super(loc, Id.empty); + super(DSYM.staticAssert, loc, Id.empty); this.exp = exp; this.msgs = msgs; } @@ -49,23 +49,11 @@ extern (C++) final class StaticAssert : Dsymbol return new StaticAssert(loc, exp.syntaxCopy(), msgs ? Expression.arraySyntaxCopy(msgs) : null); } - override bool oneMember(out Dsymbol ps, Identifier ident) - { - //printf("StaticAssert::oneMember())\n"); - ps = null; - return true; - } - override const(char)* kind() const { return "static assert"; } - override inout(StaticAssert) isStaticAssert() inout - { - return this; - } - override void accept(Visitor v) { v.visit(this); diff --git a/dmd/staticassert.h b/dmd/staticassert.h index ed76de0701..1ef72850a7 100644 --- a/dmd/staticassert.h +++ b/dmd/staticassert.h @@ -1,6 +1,6 @@ /* Compiler implementation of the D programming language - * Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * written by Walter Bright * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. @@ -21,8 +21,6 @@ class StaticAssert : public Dsymbol Expressions *msg; StaticAssert *syntaxCopy(Dsymbol *s) override; - bool oneMember(Dsymbol *&ps, Identifier *ident) override; const char *kind() const override; - StaticAssert *isStaticAssert() override { return this; } void accept(Visitor *v) override { v->visit(this); } }; diff --git a/dmd/staticcond.d b/dmd/staticcond.d index 3ab6885586..c2e87c7bf3 100644 --- a/dmd/staticcond.d +++ b/dmd/staticcond.d @@ -1,12 +1,12 @@ /** * Lazily evaluate static conditions for `static if`, `static assert` and template constraints. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/staticcond.d, _staticcond.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/staticcond.d, _staticcond.d) * Documentation: https://dlang.org/phobos/dmd_staticcond.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/staticcond.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/staticcond.d */ module dmd.staticcond; diff --git a/dmd/stmtstate.d b/dmd/stmtstate.d index e1ed16594a..1c1fd080ee 100644 --- a/dmd/stmtstate.d +++ b/dmd/stmtstate.d @@ -1,12 +1,12 @@ /** * Used to help transform statement AST into flow graph. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/stmtstate.d, _stmtstate.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/stmtstate.d, _stmtstate.d) * Documentation: https://dlang.org/phobos/dmd_stmtstate.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/stmtstate.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/stmtstate.d */ module dmd.stmtstate; diff --git a/dmd/target.d b/dmd/target.d index f25eecd38f..462f8cbaf3 100644 --- a/dmd/target.d +++ b/dmd/target.d @@ -15,12 +15,12 @@ * - $(LINK2 https://github.com/ldc-developers/ldc, LDC repository) * - $(LINK2 https://github.com/D-Programming-GDC/gcc, GDC repository) * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/target.d, _target.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/target.d, _target.d) * Documentation: https://dlang.org/phobos/dmd_target.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/target.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/target.d */ module dmd.target; @@ -231,7 +231,11 @@ void addPredefinedGlobalIdentifiers(const ref Target tgt) addCRuntimePredefinedGlobalIdent(tgt.c); addCppRuntimePredefinedGlobalIdent(tgt.cpp); - if (tgt.isX86_64) + if (tgt.isAArch64) + { + VersionCondition.addPredefinedGlobalIdent("AArch64"); + } + else if (tgt.isX86_64) { VersionCondition.addPredefinedGlobalIdent("D_InlineAsm_X86_64"); VersionCondition.addPredefinedGlobalIdent("X86_64"); @@ -242,6 +246,7 @@ void addPredefinedGlobalIdentifiers(const ref Target tgt) VersionCondition.addPredefinedGlobalIdent("D_InlineAsm_X86"); VersionCondition.addPredefinedGlobalIdent("X86"); } + if (tgt.isLP64) VersionCondition.addPredefinedGlobalIdent("D_LP64"); else if (tgt.isX86_64) @@ -377,6 +382,7 @@ version (IN_LLVM) version (IN_LLVM) {} else { CPU cpu; // CPU instruction set to target + bool isAArch64; // generate 64 bit Arm code bool isX86_64; // generate 64 bit code for x86_64; true by default for 64 bit dmd bool isX86; // generate 32 bit Intel x86 code } @@ -446,7 +452,7 @@ version (IN_LLVM) uint alignsize(Type type); uint fieldalign(Type type); - Type va_listType(const ref Loc loc, Scope* sc); + Type va_listType(Loc loc, Scope* sc); } else // !IN_LLVM { @@ -456,7 +462,9 @@ else // !IN_LLVM extern (C++) void _init(ref const Param params) { // isX86_64 and cpu are initialized in parseCommandLine - isX86 = !isX86_64; + //printf("isX86_64 %d isAArch64 %d\n", isX86_64, isAArch64); + isX86 = !isX86_64 && !isAArch64; + assert(isX86 + isX86_64 + isAArch64 == 1); // there can be only one this.params = ¶ms; @@ -479,11 +487,12 @@ else // !IN_LLVM */ maxStaticDataSize = int.max; - if (isLP64) + if (isLP64 || isAArch64) { ptrsize = 8; classinfosize = 0x98+16; // 168 } + if (os & (Target.OS.linux | Target.OS.FreeBSD | Target.OS.OpenBSD | Target.OS.DragonFlyBSD | Target.OS.Solaris)) { realsize = 12; @@ -504,7 +513,8 @@ else // !IN_LLVM } else assert(0); - if (isX86_64) + + if (isX86_64 || isAArch64) { if (os & (Target.OS.linux | Target.OS.FreeBSD | Target.OS.OpenBSD | Target.OS.DragonFlyBSD | Target.OS.Solaris)) { @@ -520,8 +530,12 @@ else // !IN_LLVM if (isX86_64) architectureName = "X86_64"; - else + else if (isX86) architectureName = "X86"; + else if (isAArch64) + architectureName = "AArch64"; + else + assert(0); if (os == Target.OS.Windows) { @@ -551,12 +565,11 @@ else // !IN_LLVM { if (os == Target.OS.OSX) return Target.ObjectFormat.macho; - else if (os & Target.OS.Posix) + if (os & Target.OS.Posix) return Target.ObjectFormat.elf; - else if (os == Target.OS.Windows) + if (os == Target.OS.Windows) return Target.ObjectFormat.coff; - else - assert(0, "unkown object format"); + assert(0, "unkown object format"); } /** @@ -644,7 +657,7 @@ else // !IN_LLVM { const size = type.alignsize(); - if ((isX86_64 || os == Target.OS.OSX) && (size == 16 || size == 32)) + if ((isX86_64 || isAArch64 || os == Target.OS.OSX) && (size == 16 || size == 32)) return size; return (8 < size) ? 8 : size; @@ -658,7 +671,7 @@ else // !IN_LLVM * Returns: * `Type` that represents `va_list`. */ - extern (C++) Type va_listType(const ref Loc loc, Scope* sc) + extern (C++) Type va_listType(Loc loc, Scope* sc) { if (tvalist) return tvalist; @@ -669,7 +682,7 @@ else // !IN_LLVM } else if (os & Target.OS.Posix) { - if (isX86_64) + if (isX86_64 || isAArch64) { import dmd.identifier : Identifier; import dmd.mtype : TypeIdentifier; @@ -794,18 +807,16 @@ static if (!IN_LLVM) if (!IN_LLVM && vecsize != 16 && vecsize != 32) return false; - bool supported = false; switch (op) { case EXP.uadd: // Expression is a no-op, supported everywhere. - supported = tvec.isscalar(); - break; + return tvec.isScalar(); case EXP.negate: version (IN_LLVM) { - supported = tvec.isscalar(); + return tvec.isScalar(); } else { @@ -813,36 +824,35 @@ else { // float[4] negate needs SSE support ({V}SUBPS) if (elemty == TY.Tfloat32 && cpu >= CPU.sse) - supported = true; + return true; // double[2] negate needs SSE2 support ({V}SUBPD) - else if (elemty == TY.Tfloat64 && cpu >= CPU.sse2) - supported = true; + if (elemty == TY.Tfloat64 && cpu >= CPU.sse2) + return true; // (u)byte[16]/short[8]/int[4]/long[2] negate needs SSE2 support ({V}PSUB[BWDQ]) - else if (tvec.isintegral() && cpu >= CPU.sse2) - supported = true; + if (tvec.isIntegral() && cpu >= CPU.sse2) + return true; } else if (vecsize == 32) { // float[8]/double[4] negate needs AVX support (VSUBP[SD]) - if (tvec.isfloating() && cpu >= CPU.avx) - supported = true; + if (tvec.isFloating() && cpu >= CPU.avx) + return true; // (u)byte[32]/short[16]/int[8]/long[4] negate needs AVX2 support (VPSUB[BWDQ]) - else if (tvec.isintegral() && cpu >= CPU.avx2) - supported = true; + if (tvec.isIntegral() && cpu >= CPU.avx2) + return true; } -} // !IN_LLVM break; +} // !IN_LLVM case EXP.identity, EXP.notIdentity: - supported = IN_LLVM; - break; + return IN_LLVM; case EXP.lessThan, EXP.greaterThan, EXP.lessOrEqual, EXP.greaterOrEqual: case EXP.equal: case EXP.notEqual: version (IN_LLVM) { - supported = tvec.isscalar(); + return tvec.isScalar(); } else { @@ -850,46 +860,45 @@ else { // float[4] comparison needs SSE support (CMP{EQ,NEQ,LT,LE}PS) if (elemty == TY.Tfloat32 && cpu >= CPU.sse) - supported = true; + return true; // double[2] comparison needs SSE2 support (CMP{EQ,NEQ,LT,LE}PD) - else if (elemty == TY.Tfloat64 && cpu >= CPU.sse2) - supported = true; - else if (tvec.isintegral()) + if (elemty == TY.Tfloat64 && cpu >= CPU.sse2) + return true; + if (tvec.isIntegral()) { if (elemty == TY.Tint64 || elemty == TY.Tuns64) { // (u)long[2] equality needs SSE4.1 support (PCMPEQQ) if ((op == EXP.equal || op == EXP.notEqual) && cpu >= CPU.sse4_1) - supported = true; + return true; // (u)long[2] comparison needs SSE4.2 support (PCMPGTQ) - else if (cpu >= CPU.sse4_2) - supported = true; + if (cpu >= CPU.sse4_2) + return true; } // (u)byte[16]/short[8]/int[4] comparison needs SSE2 support (PCMP{EQ,GT}[BWD]) - else if (cpu >= CPU.sse2) - supported = true; + if (cpu >= CPU.sse2) + return true; } } else if (vecsize == 32) { // float[8]/double[4] comparison needs AVX support (VCMP{EQ,NEQ,LT,LE}P[SD]) - if (tvec.isfloating() && cpu >= CPU.avx) - supported = true; + if (tvec.isFloating() && cpu >= CPU.avx) + return true; // (u)byte[32]/short[16]/int[8]/long[4] comparison needs AVX2 support (VPCMP{EQ,GT}[BWDQ]) - else if (tvec.isintegral() && cpu >= CPU.avx2) - supported = true; + if (tvec.isIntegral() && cpu >= CPU.avx2) + return true; } -} // !IN_LLVM break; +} // !IN_LLVM case EXP.leftShift, EXP.leftShiftAssign, EXP.rightShift, EXP.rightShiftAssign, EXP.unsignedRightShift, EXP.unsignedRightShiftAssign: - supported = IN_LLVM && tvec.isintegral(); - break; + return IN_LLVM && tvec.isIntegral(); case EXP.add, EXP.addAssign, EXP.min, EXP.minAssign: version (IN_LLVM) { - supported = tvec.isscalar(); + return tvec.isScalar(); } else { @@ -897,30 +906,30 @@ else { // float[4] add/sub needs SSE support ({V}ADDPS, {V}SUBPS) if (elemty == TY.Tfloat32 && cpu >= CPU.sse) - supported = true; + return true; // double[2] add/sub needs SSE2 support ({V}ADDPD, {V}SUBPD) - else if (elemty == TY.Tfloat64 && cpu >= CPU.sse2) - supported = true; + if (elemty == TY.Tfloat64 && cpu >= CPU.sse2) + return true; // (u)byte[16]/short[8]/int[4]/long[2] add/sub needs SSE2 support ({V}PADD[BWDQ], {V}PSUB[BWDQ]) - else if (tvec.isintegral() && cpu >= CPU.sse2) - supported = true; + if (tvec.isIntegral() && cpu >= CPU.sse2) + return true; } else if (vecsize == 32) { // float[8]/double[4] add/sub needs AVX support (VADDP[SD], VSUBP[SD]) - if (tvec.isfloating() && cpu >= CPU.avx) - supported = true; + if (tvec.isFloating() && cpu >= CPU.avx) + return true; // (u)byte[32]/short[16]/int[8]/long[4] add/sub needs AVX2 support (VPADD[BWDQ], VPSUB[BWDQ]) - else if (tvec.isintegral() && cpu >= CPU.avx2) - supported = true; + if (tvec.isIntegral() && cpu >= CPU.avx2) + return true; } -} // !IN_LLVM break; +} // !IN_LLVM case EXP.mul, EXP.mulAssign: version (IN_LLVM) { - supported = tvec.isscalar(); + return tvec.isScalar(); } else { @@ -928,36 +937,36 @@ else { // float[4] multiply needs SSE support ({V}MULPS) if (elemty == TY.Tfloat32 && cpu >= CPU.sse) - supported = true; + return true; // double[2] multiply needs SSE2 support ({V}MULPD) - else if (elemty == TY.Tfloat64 && cpu >= CPU.sse2) - supported = true; + if (elemty == TY.Tfloat64 && cpu >= CPU.sse2) + return true; // (u)short[8] multiply needs SSE2 support ({V}PMULLW) - else if ((elemty == TY.Tint16 || elemty == TY.Tuns16) && cpu >= CPU.sse2) - supported = true; + if ((elemty == TY.Tint16 || elemty == TY.Tuns16) && cpu >= CPU.sse2) + return true; // (u)int[4] multiply needs SSE4.1 support ({V}PMULLD) - else if ((elemty == TY.Tint32 || elemty == TY.Tuns32) && cpu >= CPU.sse4_1) - supported = true; + if ((elemty == TY.Tint32 || elemty == TY.Tuns32) && cpu >= CPU.sse4_1) + return true; } else if (vecsize == 32) { // float[8]/double[4] multiply needs AVX support (VMULP[SD]) - if (tvec.isfloating() && cpu >= CPU.avx) - supported = true; + if (tvec.isFloating() && cpu >= CPU.avx) + return true; // (u)short[16] multiply needs AVX2 support (VPMULLW) - else if ((elemty == TY.Tint16 || elemty == TY.Tuns16) && cpu >= CPU.avx2) - supported = true; + if ((elemty == TY.Tint16 || elemty == TY.Tuns16) && cpu >= CPU.avx2) + return true; // (u)int[8] multiply needs AVX2 support (VPMULLD) - else if ((elemty == TY.Tint32 || elemty == TY.Tuns32) && cpu >= CPU.avx2) - supported = true; + if ((elemty == TY.Tint32 || elemty == TY.Tuns32) && cpu >= CPU.avx2) + return true; } -} // !IN_LLVM break; +} // !IN_LLVM case EXP.div, EXP.divAssign: version (IN_LLVM) { - supported = tvec.isscalar(); + return tvec.isScalar(); } else { @@ -965,70 +974,73 @@ else { // float[4] divide needs SSE support ({V}DIVPS) if (elemty == TY.Tfloat32 && cpu >= CPU.sse) - supported = true; + return true; // double[2] divide needs SSE2 support ({V}DIVPD) - else if (elemty == TY.Tfloat64 && cpu >= CPU.sse2) - supported = true; + if (elemty == TY.Tfloat64 && cpu >= CPU.sse2) + return true; } else if (vecsize == 32) { // float[8]/double[4] multiply needs AVX support (VDIVP[SD]) - if (tvec.isfloating() && cpu >= CPU.avx) - supported = true; + if (tvec.isFloating() && cpu >= CPU.avx) + return true; } -} // !IN_LLVM break; +} // !IN_LLVM case EXP.mod, EXP.modAssign: - supported = IN_LLVM && tvec.isscalar(); - break; + return IN_LLVM && tvec.isScalar(); case EXP.and, EXP.andAssign, EXP.or, EXP.orAssign, EXP.xor, EXP.xorAssign: version (IN_LLVM) { - supported = tvec.isintegral(); + return tvec.isIntegral(); } else { - // (u)byte[16]/short[8]/int[4]/long[2] bitwise ops needs SSE2 support ({V}PAND, {V}POR, {V}PXOR) - if (vecsize == 16 && tvec.isintegral() && cpu >= CPU.sse2) - supported = true; - // (u)byte[32]/short[16]/int[8]/long[4] bitwise ops needs AVX2 support (VPAND, VPOR, VPXOR) - else if (vecsize == 32 && tvec.isintegral() && cpu >= CPU.avx2) - supported = true; -} // !IN_LLVM + if (tvec.isIntegral()) + { + // (u)byte[16]/short[8]/int[4]/long[2] bitwise ops needs SSE2 support ({V}PAND, {V}POR, {V}PXOR) + if (vecsize == 16 && cpu >= CPU.sse2) + return true; + // (u)byte[32]/short[16]/int[8]/long[4] bitwise ops needs AVX2 support (VPAND, VPOR, VPXOR) + if (vecsize == 32 && cpu >= CPU.avx2) + return true; + } break; +} // !IN_LLVM case EXP.not: - supported = false; - break; + return false; case EXP.tilde: version (IN_LLVM) { - supported = tvec.isintegral(); + return tvec.isIntegral(); } else { - // (u)byte[16]/short[8]/int[4]/long[2] logical exclusive needs SSE2 support ({V}PXOR) - if (vecsize == 16 && tvec.isintegral() && cpu >= CPU.sse2) - supported = true; - // (u)byte[32]/short[16]/int[8]/long[4] logical exclusive needs AVX2 support (VPXOR) - else if (vecsize == 32 && tvec.isintegral() && cpu >= CPU.avx2) - supported = true; -} // !IN_LLVM + if (tvec.isIntegral()) + { + // (u)byte[16]/short[8]/int[4]/long[2] logical exclusive needs SSE2 support ({V}PXOR) + if (vecsize == 16 && cpu >= CPU.sse2) + return true; + // (u)byte[32]/short[16]/int[8]/long[4] logical exclusive needs AVX2 support (VPXOR) + if (vecsize == 32 && cpu >= CPU.avx2) + return true; + } break; +} // !IN_LLVM case EXP.pow, EXP.powAssign: - supported = false; - break; + return false; default: // import std.stdio : stderr, writeln; // stderr.writeln(op); assert(0, "unhandled op " ~ EXPtoString(cast(EXP)op)); } - return supported; + return false; } /** @@ -1048,7 +1060,7 @@ version (IN_LLVM) TypeTuple toArgTypes(Type t); bool isReturnOnStack(TypeFunction tf, bool needsThis); bool preferPassByRef(Type t); - Expression getTargetInfo(const(char)* name, const ref Loc loc); + Expression getTargetInfo(const(char)* name, Loc loc); bool isCalleeDestroyingArgs(TypeFunction tf); bool libraryObjectMonitors(FuncDeclaration fd, Statement fbody) { return true; } bool supportsLinkerDirective() const; @@ -1068,7 +1080,7 @@ else // !IN_LLVM { import dmd.argtypes_x86 : toArgTypes_x86; import dmd.argtypes_sysv_x64 : toArgTypes_sysv_x64; - if (isX86_64) + if (isX86_64 || isAArch64) { // no argTypes for Win64 yet return isPOSIX ? toArgTypes_sysv_x64(t) : null; @@ -1091,7 +1103,7 @@ else // !IN_LLVM import dmd.argtypes_sysv_x64 : toArgTypes_sysv_x64; import dmd.typesem : castMod; - if (tf.isref) + if (tf.isRef) { //printf(" ref false\n"); return false; // returns a pointer @@ -1116,12 +1128,12 @@ else // !IN_LLVM const sz = tn.size(); Type tns = tn; - if (os == Target.OS.Windows && isX86_64) + if (os == Target.OS.Windows && (isX86_64 || isAArch64)) { // https://msdn.microsoft.com/en-us/library/7572ztz4%28v=vs.100%29.aspx if (tns.ty == TY.Tcomplex32) return true; - if (tns.isscalar()) + if (tns.isScalar()) return false; tns = tns.baseElemOf(); @@ -1150,13 +1162,13 @@ else // !IN_LLVM return true; } } - else if (isX86_64 && isPOSIX) + else if ((isX86_64 || isAArch64) && isPOSIX) { TypeTuple tt = toArgTypes_sysv_x64(tn); if (!tt) return false; // void - else - return !tt.arguments.length; + + return !tt.arguments.length; } Lagain: @@ -1211,9 +1223,9 @@ else // !IN_LLVM goto L2; goto Lagain; } - else if (isX86_64 && sd.numArgTypes() == 0) + else if ((isX86_64 || isAArch64) && sd.numArgTypes() == 0) return true; - else if (sd.isPOD()) + if (sd.isPOD()) { switch (sz) { @@ -1225,7 +1237,7 @@ else // !IN_LLVM return false; // return small structs in regs // (not 3 byte structs!) case 16: - if (os & Target.OS.Posix && isX86_64) + if (os & Target.OS.Posix && (isX86_64 || isAArch64)) return false; break; @@ -1238,17 +1250,17 @@ else // !IN_LLVM } else if (os & Target.OS.Posix && (tf.linkage == LINK.c || tf.linkage == LINK.cpp) && - tns.iscomplex()) + tns.isComplex()) { if (tns.ty == TY.Tcomplex32) return false; // in EDX:EAX, not ST1:ST0 - else - return true; + + return true; } else if (os == Target.OS.Windows && isX86 && tf.linkage == LINK.cpp && - tf.isfloating()) + tf.isFloating()) { /* See DMC++ function exp2_retmethod() * https://github.com/DigitalMars/Compiler/blob/master/dm/src/dmc/dexp2.d#L149 @@ -1274,7 +1286,7 @@ else // !IN_LLVM extern (C++) bool preferPassByRef(Type t) { const size = t.size(); - if (isX86_64) + if (isX86_64 || isAArch64) { if (os == Target.OS.Windows) { @@ -1339,7 +1351,7 @@ else // !IN_LLVM * Returns: * Expression for the requested targetInfo */ - extern (C++) Expression getTargetInfo(const(char)* name, const ref Loc loc) + extern (C++) Expression getTargetInfo(const(char)* name, Loc loc) { import dmd.dmdparams : driverParams; import dmd.expression : IntegerExp, StringExp; @@ -1355,10 +1367,9 @@ else // !IN_LLVM case objectFormat.stringof: if (os == Target.OS.Windows) return stringExp("coff"); - else if (os == Target.OS.OSX) + if (os == Target.OS.OSX) return stringExp("macho"); - else - return stringExp("elf"); + return stringExp("elf"); case floatAbi.stringof: return stringExp("hard"); case cppRuntimeLibrary.stringof: @@ -1436,7 +1447,7 @@ else // !IN_LLVM */ extern (D) bool isXmmSupported() @safe { - return isX86_64 || (isX86 && os == Target.OS.OSX); + return (isX86_64 || isAArch64) || (isX86 && os == Target.OS.OSX); } /** @@ -1458,6 +1469,7 @@ else // !IN_LLVM { uint sz = isXmmSupported() ? 16 : isX86_64 ? 8 : + isAArch64 ? 8 : isX86 ? 4 : 0; assert(sz); return sz; @@ -1523,14 +1535,14 @@ version (IN_LLVM) {} else longsize = 4; else assert(0); - if (target.isX86_64) + if (target.isX86_64 || target.isAArch64) { if (os & (Target.OS.linux | Target.OS.FreeBSD | Target.OS.OpenBSD | Target.OS.DragonFlyBSD | Target.OS.Solaris)) longsize = 8; else if (os == Target.OS.OSX) longsize = 8; } - if (target.isX86_64 && os == Target.OS.Windows) + if ((target.isX86_64 || target.isAArch64) && os == Target.OS.Windows) long_doublesize = 8; else long_doublesize = target.realsize; @@ -1662,8 +1674,8 @@ version (IN_LLVM) {} else */ extern (C++) const(char)* toMangle(Dsymbol s) { - import dmd.cppmangle : toCppMangleItanium; - import dmd.cppmanglewin : toCppMangleMSVC; + import dmd.mangle.cpp : toCppMangleItanium; + import dmd.mangle.cppwin : toCppMangleMSVC; version (IN_LLVM) { @@ -1672,15 +1684,15 @@ version (IN_LLVM) else return toCppMangleItanium(s); } -else +else // !IN_LLVM { if (target.os & (Target.OS.linux | Target.OS.OSX | Target.OS.FreeBSD | Target.OS.OpenBSD | Target.OS.Solaris | Target.OS.DragonFlyBSD)) return toCppMangleItanium(s); if (target.os == Target.OS.Windows) return toCppMangleMSVC(s); - else - assert(0, "fix this"); -} + + assert(0, "fix this"); +} // !IN_LLVM } /** @@ -1692,8 +1704,8 @@ else */ extern (C++) const(char)* typeInfoMangle(ClassDeclaration cd) { - import dmd.cppmangle : cppTypeInfoMangleItanium; - import dmd.cppmanglewin : cppTypeInfoMangleMSVC; + import dmd.mangle.cpp : cppTypeInfoMangleItanium; + import dmd.mangle.cppwin : cppTypeInfoMangleMSVC; version (IN_LLVM) { @@ -1702,15 +1714,15 @@ version (IN_LLVM) else return cppTypeInfoMangleItanium(cd); } -else +else // !IN_LLVM { if (target.os & (Target.OS.linux | Target.OS.OSX | Target.OS.FreeBSD | Target.OS.OpenBSD | Target.OS.Solaris | Target.OS.DragonFlyBSD)) return cppTypeInfoMangleItanium(cd); if (target.os == Target.OS.Windows) return cppTypeInfoMangleMSVC(cd); - else - assert(0, "fix this"); -} + + assert(0, "fix this"); +} // !IN_LLVM } /** @@ -1787,8 +1799,8 @@ else // IN_LLVM: changed from `if (target.os == Target.OS.Windows)` if (isTargetWindowsMSVC()) return (baseClass.structsize + baseClass.alignsize - 1) & ~(baseClass.alignsize - 1); - else - return baseClass.structsize; + + return baseClass.structsize; } } @@ -1803,7 +1815,7 @@ struct TargetObjC version (IN_LLVM) { /* initialized in Target::_init() */ } else extern (D) void initialize(ref const Param params, ref const Target target) @safe { - if (target.os == Target.OS.OSX && target.isX86_64) + if (target.os == Target.OS.OSX && (target.isX86_64 || target.isAArch64)) supported = true; } } diff --git a/dmd/target.h b/dmd/target.h index ad9a3891c6..f5c9aed0ad 100644 --- a/dmd/target.h +++ b/dmd/target.h @@ -1,6 +1,6 @@ /* Compiler implementation of the D programming language - * Copyright (C) 2013-2024 by The D Language Foundation, All Rights Reserved + * Copyright (C) 2013-2025 by The D Language Foundation, All Rights Reserved * written by Iain Buclaw * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. @@ -168,6 +168,7 @@ struct Target DString architectureName; // name of the platform architecture (e.g. X86_64) #if !IN_LLVM CPU cpu; // CPU instruction set to target + d_bool isAArch64; // generate 64 bit Arm code d_bool isX86_64; // generate 64 bit code for x86_64; true by default for 64 bit dmd d_bool isX86; // generate 32 bit Intel x86 code #endif @@ -213,7 +214,7 @@ struct Target // Type sizes and support. unsigned alignsize(Type *type); unsigned fieldalign(Type *type); - Type *va_listType(const Loc &loc, Scope *sc); // get type of va_list + Type *va_listType(Loc loc, Scope *sc); // get type of va_list int isVectorTypeSupported(int sz, Type *type); bool isVectorOpSupported(Type *type, EXP op, Type *t2 = nullptr); // ABI and backend. @@ -221,7 +222,7 @@ struct Target TypeTuple *toArgTypes(Type *t); bool isReturnOnStack(TypeFunction *tf, bool needsThis); bool preferPassByRef(Type *t); - Expression *getTargetInfo(const char* name, const Loc& loc); + Expression *getTargetInfo(const char* name, Loc loc); bool isCalleeDestroyingArgs(TypeFunction* tf); bool libraryObjectMonitors(FuncDeclaration *fd, Statement *fbody); bool supportsLinkerDirective() const; diff --git a/dmd/template.h b/dmd/template.h index ca0bbee15a..af910f4dd9 100644 --- a/dmd/template.h +++ b/dmd/template.h @@ -1,6 +1,6 @@ /* Compiler implementation of the D programming language - * Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * written by Walter Bright * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. @@ -81,13 +81,10 @@ class TemplateDeclaration final : public ScopeDsymbol TemplateDeclaration *syntaxCopy(Dsymbol *) override; bool overloadInsert(Dsymbol *s) override; - bool hasStaticCtorOrDtor() override; const char *kind() const override; - const char *toChars() const override; Visibility visible() override; - TemplateDeclaration *isTemplateDeclaration() override { return this; } bool isDeprecated() const override; bool isOverloadable() const override; @@ -132,14 +129,11 @@ class TemplateParameter : public ASTNode virtual bool declareParameter(Scope *sc) = 0; virtual void print(RootObject *oarg, RootObject *oded) = 0; virtual RootObject *specialization() = 0; - virtual RootObject *defaultArg(const Loc &instLoc, Scope *sc) = 0; + virtual RootObject *defaultArg(Loc instLoc, Scope *sc) = 0; virtual bool hasDefaultArg() = 0; DYNCAST dyncast() const override { return DYNCAST_TEMPLATEPARAMETER; } - /* Create dummy argument based on parameter. - */ - virtual RootObject *dummyArg() = 0; void accept(Visitor *v) override { v->visit(this); } }; @@ -157,9 +151,8 @@ class TemplateTypeParameter : public TemplateParameter bool declareParameter(Scope *sc) override final; void print(RootObject *oarg, RootObject *oded) override final; RootObject *specialization() override final; - RootObject *defaultArg(const Loc &instLoc, Scope *sc) override final; + RootObject *defaultArg(Loc instLoc, Scope *sc) override final; bool hasDefaultArg() override final; - RootObject *dummyArg() override final; void accept(Visitor *v) override { v->visit(this); } }; @@ -189,9 +182,8 @@ class TemplateValueParameter final : public TemplateParameter bool declareParameter(Scope *sc) override; void print(RootObject *oarg, RootObject *oded) override; RootObject *specialization() override; - RootObject *defaultArg(const Loc &instLoc, Scope *sc) override; + RootObject *defaultArg(Loc instLoc, Scope *sc) override; bool hasDefaultArg() override; - RootObject *dummyArg() override; void accept(Visitor *v) override { v->visit(this); } }; @@ -210,9 +202,8 @@ class TemplateAliasParameter final : public TemplateParameter bool declareParameter(Scope *sc) override; void print(RootObject *oarg, RootObject *oded) override; RootObject *specialization() override; - RootObject *defaultArg(const Loc &instLoc, Scope *sc) override; + RootObject *defaultArg(Loc instLoc, Scope *sc) override; bool hasDefaultArg() override; - RootObject *dummyArg() override; void accept(Visitor *v) override { v->visit(this); } }; @@ -227,9 +218,8 @@ class TemplateTupleParameter final : public TemplateParameter bool declareParameter(Scope *sc) override; void print(RootObject *oarg, RootObject *oded) override; RootObject *specialization() override; - RootObject *defaultArg(const Loc &instLoc, Scope *sc) override; + RootObject *defaultArg(Loc instLoc, Scope *sc) override; bool hasDefaultArg() override; - RootObject *dummyArg() override; void accept(Visitor *v) override { v->visit(this); } }; @@ -282,15 +272,12 @@ class TemplateInstance : public ScopeDsymbol TemplateInstance *syntaxCopy(Dsymbol *) override; Dsymbol *toAlias() override final; // resolve real symbol const char *kind() const override; - bool oneMember(Dsymbol *&ps, Identifier *ident) override; - const char *toChars() const override; const char* toPrettyCharsHelper() override final; Identifier *getIdent() override final; bool isDiscardable(); bool needsCodegen(); - TemplateInstance *isTemplateInstance() override final { return this; } void accept(Visitor *v) override { v->visit(this); } }; @@ -301,11 +288,8 @@ class TemplateMixin final : public TemplateInstance TemplateMixin *syntaxCopy(Dsymbol *s) override; const char *kind() const override; - bool oneMember(Dsymbol *&ps, Identifier *ident) override; bool hasPointers() override; - const char *toChars() const override; - TemplateMixin *isTemplateMixin() override { return this; } void accept(Visitor *v) override { v->visit(this); } }; diff --git a/dmd/templateparamsem.d b/dmd/templateparamsem.d index f2dc50ebb7..561181a2cc 100644 --- a/dmd/templateparamsem.d +++ b/dmd/templateparamsem.d @@ -1,12 +1,12 @@ /** * Semantic analysis of template parameters. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/templateparamsem.d, _templateparamsem.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/templateparamsem.d, _templateparamsem.d) * Documentation: https://dlang.org/phobos/dmd_templateparamsem.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/templateparamsem.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/templateparamsem.d */ module dmd.templateparamsem; @@ -170,7 +170,7 @@ RootObject aliasParameterSemantic(Loc loc, Scope* sc, RootObject o, TemplatePara Dsymbol s = ta.toDsymbol(sc); if (s) return s; - else if (TypeInstance ti = ta.isTypeInstance()) + if (TypeInstance ti = ta.isTypeInstance()) { Type t; const errors = global.errors; @@ -183,14 +183,13 @@ RootObject aliasParameterSemantic(Loc loc, Scope* sc, RootObject o, TemplatePara // see https://issues.dlang.org/show_bug.cgi?id=16472 if (t) return t.typeSemantic(loc, sc); - else if (ea) + if (ea) { return eaCTFE(); } - else if (s) + if (s) return s; - else - assert(0); + assert(0); } else return ta.typeSemantic(loc, sc); diff --git a/dmd/templatesem.d b/dmd/templatesem.d index 3737141dd0..1e71cd5e8b 100644 --- a/dmd/templatesem.d +++ b/dmd/templatesem.d @@ -1,12 +1,12 @@ /** * Template semantics. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/templatesem.d, _templatesem.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/templatesem.d, _templatesem.d) * Documentation: https://dlang.org/phobos/dmd_templatesem.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/templatesem.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/templatesem.d */ module dmd.templatesem; @@ -23,7 +23,6 @@ import dmd.dcast; import dmd.dclass; import dmd.declaration; import dmd.dinterpret; -import dmd.dmangle; import dmd.dmodule; import dmd.dscope; import dmd.dsymbol; @@ -111,7 +110,7 @@ void templateDeclarationSemantic(Scope* sc, TemplateDeclaration tempdecl) tempdecl.isstatic = tempdecl.toParent().isModule() || (tempdecl._scope.stc & STC.static_); tempdecl.deprecated_ = !!(sc.stc & STC.deprecated_); - UserAttributeDeclaration.checkGNUABITag(tempdecl, sc.linkage); + checkGNUABITag(tempdecl, sc.linkage); if (!tempdecl.isstatic) { @@ -123,7 +122,7 @@ void templateDeclarationSemantic(Scope* sc, TemplateDeclaration tempdecl) auto paramsym = new ScopeDsymbol(); paramsym.parent = tempdecl.parent; Scope* paramscope = sc.push(paramsym); - paramscope.stc = 0; + paramscope.stc = STC.none; if (global.params.ddoc.doOutput) { @@ -156,7 +155,7 @@ void templateDeclarationSemantic(Scope* sc, TemplateDeclaration tempdecl) /* Calculate TemplateParameter.dependent */ - TemplateParameters tparams = TemplateParameters(1); + auto tparams = TemplateParameters(1); for (size_t i = 0; i < tempdecl.parameters.length; i++) { TemplateParameter tp = (*tempdecl.parameters)[i]; @@ -356,7 +355,7 @@ MATCH matchWithInstance(Scope* sc, TemplateDeclaration td, TemplateInstance ti, // Resolve parameter types and 'auto ref's. tf.inferenceArguments = argumentList; - uint olderrors = global.startGagging(); + const olderrors = global.startGagging(); fd.type = tf.typeSemantic(td.loc, paramscope); global.endGagging(olderrors); if (fd.type.ty != Tfunction) @@ -459,7 +458,7 @@ bool evaluateConstraint(TemplateDeclaration td, TemplateInstance ti, Scope* sc, // (previously, this was immediately before calling evalStaticCondition), so the // semantic pass knows not to issue deprecation warnings for these throw-away decls. // https://issues.dlang.org/show_bug.cgi?id=21831 - scx.flags |= SCOPE.constraint; + scx.inTemplateConstraint = true; assert(!ti.symtab); if (fd) @@ -606,7 +605,7 @@ Scope* createScopeForTemplateParameters(TemplateDeclaration td, TemplateInstance paramscope.tinst = ti; paramscope.minst = sc.minst; paramscope.callsc = sc; - paramscope.stc = 0; + paramscope.stc = STC.none; return paramscope; } @@ -626,7 +625,7 @@ MATCH leastAsSpecialized(Scope* sc, TemplateDeclaration td, TemplateDeclaration enum LOG_LEASTAS = 0; static if (LOG_LEASTAS) { - printf("%s.leastAsSpecialized(%s)\n", toChars(), td2.toChars()); + printf("%s.leastAsSpecialized(%s)\n", td.toChars(), td2.toChars()); } /* This works by taking the template parameters to this template @@ -908,7 +907,7 @@ extern (D) MATCHpair deduceFunctionTemplateMatch(TemplateDeclaration td, Templat // Match attributes of tthis against attributes of fd if (fd.type && !fd.isCtorDeclaration() && !(td._scope.stc & STC.static_)) { - StorageClass stc = td._scope.stc | fd.storage_class2; + STC stc = td._scope.stc | fd.storage_class2; // Propagate parent storage class, https://issues.dlang.org/show_bug.cgi?id=5504 Dsymbol p = td.parent; while (p.isTemplateDeclaration() || p.isTemplateInstance()) @@ -1255,7 +1254,13 @@ extern (D) MATCHpair deduceFunctionTemplateMatch(TemplateDeclaration td, Templat //printf("farg = %s %s\n", farg.type.toChars(), farg.toChars()); RootObject oarg = farg; - if ((fparam.storageClass & STC.ref_) && (!(fparam.storageClass & STC.auto_) || farg.isLvalue())) + + if (farg.isFuncExp()) + { + // When assigning an untyped (void) lambda `x => y` to a `(F)(ref F)` parameter, + // we don't want to deduce type void creating a void parameter + } + else if ((fparam.storageClass & STC.ref_) && (!(fparam.storageClass & STC.auto_) || farg.isLvalue())) { /* Allow expressions that have CT-known boundaries and type [] to match with [dim] */ @@ -1339,10 +1344,10 @@ extern (D) MATCHpair deduceFunctionTemplateMatch(TemplateDeclaration td, Templat * We also save/restore sc.func.flags to avoid messing up * attribute inference in the evaluation. */ - const oldflags = sc.func ? sc.func.flags : 0; + const oldflags = sc.func ? sc.func.saveFlags : 0; auto e = resolveAliasThis(sc, farg, true); if (sc.func) - sc.func.flags = oldflags; + sc.func.restoreFlags(oldflags); if (e) { farg = e; @@ -1359,7 +1364,7 @@ extern (D) MATCHpair deduceFunctionTemplateMatch(TemplateDeclaration td, Templat { // Allow conversion from T[lwr .. upr] to ref T[upr-lwr] } - else if (global.params.rvalueRefParam == FeatureState.enabled) + else if (sc.previews.rvalueRefParam) { // Allow implicit conversion to ref } @@ -1422,9 +1427,9 @@ extern (D) MATCHpair deduceFunctionTemplateMatch(TemplateDeclaration td, Templat Expression e; Type t; Dsymbol s; - Scope *sco; + Scope* sco; - uint errors = global.startGagging(); + const errors = global.startGagging(); /* ref: https://issues.dlang.org/show_bug.cgi?id=11118 * The parameter isn't part of the template * ones, let's try to find it in the @@ -1731,7 +1736,7 @@ FuncDeclaration doHeaderInstantiation(TemplateDeclaration td, TemplateInstance t { // For constructors, emitting return type is necessary for // isReturnIsolated() in functionResolve. - tf.isctor = true; + tf.isCtor = true; Dsymbol parent = td.toParentDecl(); Type tret; @@ -1749,7 +1754,7 @@ FuncDeclaration doHeaderInstantiation(TemplateDeclaration td, TemplateInstance t } tf.next = tret; if (ad && ad.isStructDeclaration()) - tf.isref = 1; + tf.isRef = 1; //printf("tf = %s\n", tf.toChars()); } else @@ -1887,23 +1892,22 @@ void functionResolve(ref MatchAccumulator m, Dsymbol dstart, Loc loc, Scope* sc, { version (none) { - printf("functionResolve() dstart = %s\n", dstart.toChars()); - printf(" tiargs:\n"); - if (tiargs) + printf("functionResolve() dstart: %s (", dstart.toChars()); + for (size_t i = 0; i < (tiargs ? (*tiargs).length : 0); i++) { - for (size_t i = 0; i < tiargs.length; i++) - { - RootObject arg = (*tiargs)[i]; - printf("\t%s\n", arg.toChars()); - } + if (i) printf(", "); + RootObject arg = (*tiargs)[i]; + printf("%s", arg.toChars()); } - printf(" fargs:\n"); - for (size_t i = 0; i < (fargs ? fargs.length : 0); i++) + printf(")("); + for (size_t i = 0; i < (argumentList.arguments ? (*argumentList.arguments).length : 0); i++) { - Expression arg = (*fargs)[i]; - printf("\t%s %s\n", arg.type.toChars(), arg.toChars()); + if (i) printf(", "); + Expression arg = (*argumentList.arguments)[i]; + printf("%s %s", arg.type.toChars(), arg.toChars()); //printf("\tty = %d\n", arg.type.ty); } + printf(")\n"); //printf("stc = %llx\n", dstart._scope.stc); //printf("match:t/f = %d/%d\n", ta_last, m.last); } @@ -1942,7 +1946,7 @@ void functionResolve(ref MatchAccumulator m, Dsymbol dstart, Loc loc, Scope* sc, //printf("fd = %s %s, fargs = %s\n", fd.toChars(), fd.type.toChars(), fargs.toChars()); auto tf = fd.type.isTypeFunction(); - int prop = tf.isproperty ? 1 : 2; + int prop = tf.isProperty ? 1 : 2; if (property == 0) property = prop; else if (property != prop) @@ -1990,7 +1994,7 @@ void functionResolve(ref MatchAccumulator m, Dsymbol dstart, Loc loc, Scope* sc, tf.mod = tthis_fd.mod; } const(char)* failMessage; - MATCH mfa = tf.callMatch(tthis_fd, argumentList, 0, errorHelper, sc); + MATCH mfa = callMatch(fd, tf, tthis_fd, argumentList, 0, errorHelper, sc); //printf("test1: mfa = %d\n", mfa); if (failMessage) errorHelper(failMessage); @@ -2195,7 +2199,7 @@ void functionResolve(ref MatchAccumulator m, Dsymbol dstart, Loc loc, Scope* sc, Type tthis_fd = fd.needThis() && !fd.isCtorDeclaration() ? tthis : null; auto tf = fd.type.isTypeFunction(); - MATCH mfa = tf.callMatch(tthis_fd, argumentList, 0, null, sc); + MATCH mfa = callMatch(fd, tf, tthis_fd, argumentList, 0, null, sc); if (mfa < m.last) return 0; @@ -2297,8 +2301,8 @@ void functionResolve(ref MatchAccumulator m, Dsymbol dstart, Loc loc, Scope* sc, // Disambiguate by tf.callMatch auto tf1 = fd.type.isTypeFunction(); auto tf2 = m.lastf.type.isTypeFunction(); - MATCH c1 = tf1.callMatch(tthis_fd, argumentList, 0, null, sc); - MATCH c2 = tf2.callMatch(tthis_best, argumentList, 0, null, sc); + MATCH c1 = callMatch(fd, tf1, tthis_fd, argumentList, 0, null, sc); + MATCH c2 = callMatch(m.lastf, tf2, tthis_best, argumentList, 0, null, sc); //printf("2: c1 = %d, c2 = %d\n", c1, c2); if (c1 > c2) goto Ltd; if (c1 < c2) goto Ltd_best; @@ -2401,7 +2405,7 @@ void functionResolve(ref MatchAccumulator m, Dsymbol dstart, Loc loc, Scope* sc, if (m.lastf.type.ty == Terror) goto Lerror; auto tf = m.lastf.type.isTypeFunction(); - if (!tf.callMatch(tthis_best, argumentList, 0, null, sc)) + if (callMatch(m.lastf, tf, tthis_best, argumentList, 0, null, sc) == MATCH.nomatch) goto Lnomatch; /* As https://issues.dlang.org/show_bug.cgi?id=3682 shows, @@ -2431,3 +2435,70 @@ void functionResolve(ref MatchAccumulator m, Dsymbol dstart, Loc loc, Scope* sc, m.last = MATCH.nomatch; } } +/* Create dummy argument based on parameter. + */ +private RootObject dummyArg(TemplateParameter tp) +{ + scope v = new DummyArgVisitor(); + tp.accept(v); + return v.result; +} +private extern(C++) class DummyArgVisitor : Visitor +{ + RootObject result; + + alias visit = typeof(super).visit; + override void visit(TemplateTypeParameter ttp) + { + Type t = ttp.specType; + if (t) + { + result = t; + return; + } + // Use this for alias-parameter's too (?) + if (!ttp.tdummy) + ttp.tdummy = new TypeIdentifier(ttp.loc, ttp.ident); + result = ttp.tdummy; + } + + override void visit(TemplateValueParameter tvp) + { + Expression e = tvp.specValue; + if (e) + { + result = e; + return; + } + + // Create a dummy value + auto pe = cast(void*)tvp.valType in tvp.edummies; + if (pe) + { + result = *pe; + return; + } + + e = tvp.valType.defaultInit(Loc.initial); + tvp.edummies[cast(void*)tvp.valType] = e; + result = e; + } + + override void visit(TemplateAliasParameter tap) + { + RootObject s = tap.specAlias; + if (s) + { + result = s; + return; + } + if (!tap.sdummy) + tap.sdummy = new Dsymbol(DSYM.dsymbol); + result = tap.sdummy; + } + + override void visit(TemplateTupleParameter tap) + { + result = null; + } +} diff --git a/dmd/timetrace.d b/dmd/timetrace.d new file mode 100644 index 0000000000..64b260dabf --- /dev/null +++ b/dmd/timetrace.d @@ -0,0 +1,483 @@ +/** +Compilation time tracing, -ftime-trace. + +The time trace profile is output in the Chrome Trace Event Format, described +here: https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/preview + +This file is originally from LDC (the LLVM D compiler). + +Copyright: Copyright (C) 1999-2022 by The D Language Foundation, All Rights Reserved +Authors: Johan Engelen, Max Haughton, Dennis Korpel +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/timetrace.d, common/_timetrace.d) +Documentation: https://dlang.org/phobos/dmd_common_timetrace.html +Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/timetrace.d +*/ +module dmd.timetrace; + +import dmd.location; +import dmd.dsymbol; +import dmd.expression; +import dmd.root.array; +import dmd.common.outbuffer; +import dmd.root.string : toDString; + +// Thread local profiler instance (multithread currently not supported because compiler is single-threaded) +TimeTraceProfiler* timeTraceProfiler = null; + +/** + * Initialize time tracing functionality. + * + * Must be called before any other calls to timeTrace functions. + * + * Params: + * timeGranularityUs = minimum event size in microseconds + * processName = name of this executable + */ +extern (C++, "dmd") +void initializeTimeTrace(uint timeGranularityUs, const(char)* processName) +{ + assert(timeTraceProfiler is null, "Double initialization of timeTraceProfiler"); + timeTraceProfiler = new TimeTraceProfiler(timeGranularityUs, processName); +} + +/** + * Cleanup for time tracing functionality. + * + * After this, no more calls to timeTrace functions can be made. + */ +extern (C++, "dmd") +void deinitializeTimeTrace() +{ + if (timeTraceProfilerEnabled()) + { + object.destroy(timeTraceProfiler); + timeTraceProfiler = null; + } +} + +/** + * Returns: Whether time tracing is enabled. + */ +pragma(inline, true) +extern (C++, "dmd") +bool timeTraceProfilerEnabled() +{ + version (LDC) + { + import ldc.intrinsics : llvm_expect; + + return llvm_expect(timeTraceProfiler !is null, false); + } + else + { + return timeTraceProfiler !is null; + } +} + +/** + * Write all time tracing results so far to JSON, in the Chrome Trace Event Format. + * Params: + * buf = output buffer to write JSON into + */ +extern (C++, "dmd") +void writeTimeTraceProfile(OutBuffer* buf) +{ + timeTraceProfiler.writeToBuffer(*buf); +} + +/** + * Start a new time trace event (C++ interface using upfront C-strings instead of lazy delegates) + * + * Params: + * name_ptr = event name, visible in high level profile view + * detail_ptr = further details, visible when this event is selected + * loc = source location corresponding to this event + */ +extern (C++, "dmd") +void timeTraceBeginEvent(scope const(char)* name_ptr, scope const(char)* detail_ptr, Loc loc) +{ + import dmd.root.rmem : xarraydup; + + assert(timeTraceProfiler); + + timeTraceProfiler.beginScope( + xarraydup(name_ptr.toDString()), + xarraydup(detail_ptr.toDString()), loc + ); +} + +/** + * Start a new time trace event + * + * Details of the event will be passed as delegates to `timeTraceEndEvent` so + * they're only generated when the event is actually written. + * + * Params: + * eventType = what compilation stage the event belongs to + * (redundant with the eventType of `timeTraceEndEvent` but used by GDC) + */ +extern (C++, "dmd") +void timeTraceBeginEvent(TimeTraceEventType eventType) +{ + if (timeTraceProfilerEnabled) + timeTraceProfiler.beginScope(null, null, Loc.initial); +} + +/** + * End a time tracing event, optionally updating the event name and details + * with a delegate. Delegates are used to prevent spending time on string + * generation when an event is too small to be generated anyway. + * + * Params: + * eventType = what compilation stage the event belongs to + * sym = Dsymbol which was analyzed, used to generate 'name' and 'detail' + * e = Expression which was analyzed, used to generate 'name' and 'detail' + * detail = custom lazy string for 'detail' of event + */ +extern (C++, "dmd") +void timeTraceEndEvent(TimeTraceEventType eventType) +{ + if (timeTraceProfilerEnabled) + timeTraceProfiler.endScope(eventType, null, null, Loc.initial); +} + +/// ditto +void timeTraceEndEvent(TimeTraceEventType eventType, Dsymbol sym, scope const(char)[] delegate() detail = null) +{ + if (timeTraceProfilerEnabled) + { + timeTraceProfiler.endScope( + eventType, + () => sym.isImport() ? sym.toPrettyChars().toDString() : sym.toChars().toDString(), + detail ? detail : () => sym.toPrettyChars().toDString(), + sym.loc + ); + } +} + +/// ditto +extern (C++, "dmd") +void timeTraceEndEvent(TimeTraceEventType eventType, Expression e) +{ + if (timeTraceProfilerEnabled) + timeTraceProfiler.endScope(eventType, () => e.toChars().toDString(), + () => e.toChars().toDString(), e.loc); +} + +version (IN_LLVM) +{ + import dmd.func : FuncDeclaration; + + extern (C++, "dmd") + void timeTraceEndEvent(TimeTraceEventType eventType, FuncDeclaration fd) + { + if (timeTraceProfilerEnabled) + timeTraceProfiler.endScope(eventType, () => fd.toChars().toDString(), + () => fd.toPrettyChars().toDString(), fd.loc); + } +} + +/// Identifies which compilation stage the event is associated to +enum TimeTraceEventType +{ + generic, + parseGeneral, + parse, + semaGeneral, + sema1Import, + sema1Module, + sema2, + sema3, + ctfe, + ctfeCall, + codegenGlobal, + codegenModule, + codegenFunction, + link, +} + +/// Names corresponding to `TimeTraceEventType` +private immutable string[] eventPrefixes = [ + "", + "Parsing", + "Parse: Module ", + "Semantic analysis", + "Import ", + "Sema1: Module ", + "Sema2: ", + "Sema3: ", + "Ctfe: ", + "Ctfe: call ", + "Code generation", + "Codegen: module ", + "Codegen: function ", + "Linking", +]; + +/// Integer type holding timer value +private alias TimeTicks = long; + +/// A measurement at a point in time. Used for reporting RAM usage. +private struct CounterEvent +{ + size_t memoryInUse; + ulong allocatedMemory; + size_t numberOfGCCollections; + TimeTicks timepoint; +} + +/// An event with a start and end time +private struct DurationEvent +{ + const(char)[] name = null; + const(char)[] details = null; + Loc loc; + TimeTicks timeBegin; + TimeTicks timeDuration; +} + + +private struct TimeTraceProfiler +{ + import core.time; + + TimeTicks timeGranularity; /// Minimum duration event size + const(char)[] processName; /// Name of the executable being profiled + /// String to identify process and thread (in this case, there's just a single process and thread) + const(char)[] pidtidString = `"pid":101,"tid":101`; + + TimeTicks beginningOfTime; /// Timer value at start of profiling + Array!CounterEvent counterEvents; /// All counter event so far + Array!DurationEvent durationEvents; /// All duration events so far + Array!DurationEvent durationStack; /// Gets pushed to / popped from when an event begins/ends. + + @disable this(); + @disable this(this); + + this(uint timeGranularity_usecs, const(char)* processName) + { + this.timeGranularity = timeGranularity_usecs * (MonoTime.ticksPerSecond() / 1_000_000); + this.processName = processName.toDString(); + this.beginningOfTime = getTimeTicks(); + } + + private TimeTicks getTimeTicks() + { + return MonoTime.currTime().ticks(); + } + + void beginScope(const(char)[] name, const(char)[] details, Loc loc) + { + DurationEvent event; + event.name = name; + event.details = details; + event.loc = loc; + event.timeBegin = getTimeTicks(); + durationStack.push(event); + } + + /// Takes ownership of the string returned by `name` and `details`. + void endScope(TimeTraceEventType eventType, scope const(char)[] delegate() name, scope const(char)[] delegate() details, Loc loc) + { + TimeTicks timeEnd = getTimeTicks(); + + DurationEvent event = durationStack.pop(); + event.timeDuration = timeEnd - event.timeBegin; + if (event.timeDuration >= timeGranularity) + { + // Event passes the logging threshold + if (name) + event.name = eventPrefixes[eventType] ~ name(); + else if (!event.name) + event.name = eventPrefixes[eventType]; + + if (details) + event.details = details(); + if (loc != Loc.initial) + event.loc = loc; + event.timeBegin -= beginningOfTime; + durationEvents.push(event); + counterEvents.push(generateCounterEvent(timeEnd - beginningOfTime)); + } + } + + private CounterEvent generateCounterEvent(TimeTicks timepoint) + { + static import dmd.root.rmem; + + CounterEvent counters; + if (dmd.root.rmem.mem.isGCEnabled) + { + static if (__VERSION__ >= 2085) + { + import core.memory : GC; + + auto stats = GC.stats(); + auto profileStats = GC.profileStats(); + + counters.allocatedMemory = stats.usedSize + stats.freeSize; + counters.memoryInUse = stats.usedSize; + counters.numberOfGCCollections = profileStats.numCollections; + } + } + else + { + counters.allocatedMemory = dmd.root.rmem.heapTotal; + counters.memoryInUse = dmd.root.rmem.heapTotal - dmd.root.rmem.heapleft; + } + counters.timepoint = timepoint; + return counters; + } + + void writeToBuffer(ref OutBuffer buf) + { + // Time is to be output in microseconds + long timescale = MonoTime.ticksPerSecond() / 1_000_000; + + buf.write("{\n\"beginningOfTime\":"); + buf.print(beginningOfTime / timescale); + buf.write(",\n\"traceEvents\": [\n"); + writeMetadataEvents(buf); + writeCounterEvents(buf); + writeDurationEvents(buf); + // Remove the trailing comma (and newline!) to obtain valid JSON. + if (buf[buf.length() - 2] == ',') + { + buf.setsize(buf.length() - 2); + buf.writeByte('\n'); + } + buf.write("]\n}\n"); + } + + private void writeMetadataEvents(ref OutBuffer buf) + { + // {"ph":"M","ts":0,"args":{"name":"bin/ldc2"},"name":"thread_name","pid":0,"tid":0}, + + buf.write(`{"ph":"M","ts":0,"args":{"name":"`); + buf.writeEscapeJSONString(processName); + buf.write(`"},"name":"process_name",`); + buf.write(pidtidString); + buf.write("},\n"); + buf.write(`{"ph":"M","ts":0,"args":{"name":"`); + buf.writeEscapeJSONString(processName); + buf.write(`"},"cat":"","name":"thread_name",`); + buf.write(pidtidString); + buf.write("},\n"); + } + + private void writeCounterEvents(ref OutBuffer buf) + { + // {"ph":"C","name":"ctr","ts":111,"args": {"Allocated_Memory_bytes": 0, "hello": 0}}, + + // Time is to be output in microseconds + long timescale = MonoTime.ticksPerSecond() / 1_000_000; + + foreach (const ref event; counterEvents) + { + buf.write(`{"ph":"C","name":"ctr","ts":`); + buf.print(event.timepoint / timescale); + buf.write(`,"args": {"memoryInUse_bytes":`); + buf.print(event.memoryInUse); + buf.write(`,"allocatedMemory_bytes":`); + buf.print(event.allocatedMemory); + buf.write(`,"GC collections":`); + buf.print(event.numberOfGCCollections); + buf.write("},"); + buf.write(pidtidString); + buf.write("},\n"); + } + } + + private void writeDurationEvents(ref OutBuffer buf) + { + // {"ph":"X","name": "Sema1: somename","ts":111,"dur":222,"loc":"filename.d:123","args": {"detail": "something", "loc":"filename.d:123"},"pid":0,"tid":0} + + void writeLocation(Loc loc) + { + SourceLoc sl = SourceLoc(loc); + if (sl.filename.length > 0) + { + writeEscapeJSONString(buf, sl.filename); + if (sl.line) + { + buf.writeByte(':'); + buf.print(sl.line); + } + } + else + { + buf.write(``); + } + } + + // Time is to be output in microseconds + long timescale = MonoTime.ticksPerSecond() / 1_000_000; + + foreach (event; durationEvents) + { + buf.write(`{"ph":"X","name": "`); + writeEscapeJSONString(buf, event.name); + buf.write(`","ts":`); + buf.print(event.timeBegin / timescale); + buf.write(`,"dur":`); + buf.print(event.timeDuration / timescale); + buf.write(`,"loc":"`); + writeLocation(event.loc); + buf.write(`","args":{"detail": "`); + writeEscapeJSONString(buf, event.details); + // Also output loc data in the "args" field so it shows in trace viewers that do not support the "loc" variable + buf.write(`","loc":"`); + writeLocation(event.loc); + buf.write(`"},`); + buf.write(pidtidString); + buf.write("},\n"); + } + } +} + +/** + * Escape special characters (such as quotes and whitespaces) for a JSON string literal + * Params: + * buf = buffer to write to + * str = string to escape and write as string literal + */ +private void writeEscapeJSONString(ref OutBuffer buf, const(char[]) str) +{ + foreach (char c; str) + { + switch (c) + { + case '\n': + buf.writestring("\\n"); + break; + case '\r': + buf.writestring("\\r"); + break; + case '\t': + buf.writestring("\\t"); + break; + case '\"': + buf.writestring("\\\""); + break; + case '\\': + buf.writestring("\\\\"); + break; + case '\b': + buf.writestring("\\b"); + break; + case '\f': + buf.writestring("\\f"); + break; + default: + if (c < 0x20) + buf.printf("\\u%04x", c); + else + { + // Note that UTF-8 chars pass through here just fine + buf.writeByte(c); + } + break; + } + } +} diff --git a/dmd/timetrace.h b/dmd/timetrace.h new file mode 100644 index 0000000000..01781bdf9a --- /dev/null +++ b/dmd/timetrace.h @@ -0,0 +1,85 @@ + +/* Compiler implementation of the D programming language + * Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved + * written by Walter Bright + * https://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * https://www.boost.org/LICENSE_1_0.txt + * https://github.com/dlang/dmd/blob/master/compiler/src/dmd/timetrace.h + */ + +#pragma once + +#include "dmd/globals.h" + +class Expression; +class FuncDeclaration; +struct OutBuffer; + +enum class TimeTraceEventType +{ + generic, + parseGeneral, + parse, + semaGeneral, + sema1Import, + sema1Module, + sema2, + sema3, + ctfe, + ctfeCall, + codegenGlobal, + codegenModule, + codegenFunction, + link +}; + +namespace dmd +{ + void initializeTimeTrace(unsigned timeGranularityUs, const char *processName); + void deinitializeTimeTrace(); + bool timeTraceProfilerEnabled(); + void writeTimeTraceProfile(OutBuffer *buf); + + void timeTraceBeginEvent(const char *name_ptr, const char *detail_ptr, Loc loc); + void timeTraceBeginEvent(TimeTraceEventType eventType); + + void timeTraceEndEvent(TimeTraceEventType eventType); + void timeTraceEndEvent(TimeTraceEventType eventType, Expression *e); +#if IN_LLVM + void timeTraceEndEvent(TimeTraceEventType eventType, FuncDeclaration *fd); +#endif + + + /// RAII helper class to call the begin and end functions of the time trace + /// profiler. When the object is constructed, it begins the event; and when + /// it is destroyed, it stops it. + /// The strings pointed to are copied (pointers are not stored). + struct TimeTraceScope + { + TimeTraceScope() = delete; + TimeTraceScope(const TimeTraceScope &) = delete; + TimeTraceScope &operator=(const TimeTraceScope &) = delete; + TimeTraceScope(TimeTraceScope &&) = delete; + TimeTraceScope &operator=(TimeTraceScope &&) = delete; + + TimeTraceScope(const char *name, const char *detail = nullptr, Loc loc = Loc()) + : TimeTraceScope(TimeTraceEventType::generic, name, detail, loc) + {} + TimeTraceScope(TimeTraceEventType type, const char *name = nullptr, const char *detail = nullptr, Loc loc = Loc()) + : type(type) + { + if (timeTraceProfilerEnabled()) + timeTraceBeginEvent(name, detail, loc); + } + + ~TimeTraceScope() + { + if (timeTraceProfilerEnabled()) + timeTraceEndEvent(type); + } + + private: + TimeTraceEventType type = TimeTraceEventType::generic; + }; +} diff --git a/dmd/tokens.d b/dmd/tokens.d index da4a3ee209..a10620772a 100644 --- a/dmd/tokens.d +++ b/dmd/tokens.d @@ -3,12 +3,12 @@ * * Specification: $(LINK2 https://dlang.org/spec/lex.html#tokens, Tokens) * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/tokens.d, _tokens.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/tokens.d, _tokens.d) * Documentation: https://dlang.org/phobos/dmd_tokens.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/tokens.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/tokens.d */ module dmd.tokens; @@ -27,6 +27,9 @@ enum TOK : ubyte { reserved, + // if this list changes, update + // tokens.h, ../tests/cxxfrontend.cc and ../../test/unit/lexer/location_offset.d to match + // Other leftParenthesis, rightParenthesis, @@ -45,7 +48,6 @@ enum TOK : ubyte false_, throw_, new_, - delete_, variable, slice, version_, @@ -249,6 +251,7 @@ enum TOK : ubyte wchar_tLiteral, endOfLine, // \n, \r, \u2028, \u2029 whitespace, + rvalue, // C only keywords inline, @@ -425,6 +428,7 @@ enum EXP : ubyte interval, loweredAssignExp, + rvalue, } enum FirstCKeyword = TOK.inline; @@ -432,8 +436,10 @@ enum FirstCKeyword = TOK.inline; // Assert that all token enum members have consecutive values and // that none of them overlap static assert(() { - foreach (idx, enumName; __traits(allMembers, TOK)) { - static if (idx != __traits(getMember, TOK, enumName)) { + foreach (idx, enumName; __traits(allMembers, TOK)) + { + static if (idx != __traits(getMember, TOK, enumName)) + { pragma(msg, "Error: Expected TOK.", enumName, " to be ", idx, " but is ", __traits(getMember, TOK, enumName)); static assert(0); } @@ -454,7 +460,6 @@ private immutable TOK[] keywords = TOK.false_, TOK.cast_, TOK.new_, - TOK.delete_, TOK.throw_, TOK.module_, TOK.pragma_, @@ -556,6 +561,7 @@ private immutable TOK[] keywords = TOK.prettyFunction, TOK.shared_, TOK.immutable_, + TOK.rvalue, // C only keywords TOK.inline, @@ -674,12 +680,12 @@ extern (C++) struct Token TOK.false_: "false", TOK.cast_: "cast", TOK.new_: "new", - TOK.delete_: "delete", TOK.throw_: "throw", TOK.module_: "module", TOK.pragma_: "pragma", TOK.typeof_: "typeof", TOK.typeid_: "typeid", + TOK.rvalue: "__rvalue", TOK.template_: "template", TOK.void_: "void", TOK.int8: "byte", @@ -921,13 +927,18 @@ nothrow: return 0; } - extern(D) void appendInterpolatedPart(const ref OutBuffer buf) { + extern(D) void appendInterpolatedPart(const ref OutBuffer buf) + { appendInterpolatedPart(cast(const(char)*)buf[].ptr, buf.length); } - extern(D) void appendInterpolatedPart(const(char)[] str) { + + extern(D) void appendInterpolatedPart(const(char)[] str) + { appendInterpolatedPart(str.ptr, str.length); } - extern(D) void appendInterpolatedPart(const(char)* ptr, size_t length) { + + extern(D) void appendInterpolatedPart(const(char)* ptr, size_t length) + { assert(value == TOK.interpolated); if (interpolatedSet is null) interpolatedSet = new InterpolatedSet; diff --git a/dmd/tokens.h b/dmd/tokens.h index 929897a3fa..ecbcc02474 100644 --- a/dmd/tokens.h +++ b/dmd/tokens.h @@ -1,6 +1,6 @@ /* Compiler implementation of the D programming language - * Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * written by Walter Bright * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. @@ -54,7 +54,6 @@ enum class TOK : unsigned char false_, throw_, new_, - delete_, variable, slice, version_, @@ -258,6 +257,7 @@ enum class TOK : unsigned char wchar_tLiteral, endOfLine, // \n, \r, \u2028, \u2029 whitespace, + rvalue, // C only keywords inline_, diff --git a/dmd/traits.d b/dmd/traits.d index 5355ff9ed1..193e772dc2 100644 --- a/dmd/traits.d +++ b/dmd/traits.d @@ -3,12 +3,12 @@ * * Specification: $(LINK2 https://dlang.org/spec/traits.html, Traits) * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/traits.d, _traits.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/traits.d, _traits.d) * Documentation: https://dlang.org/phobos/dmd_traits.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/traits.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/traits.d */ module dmd.traits; @@ -26,7 +26,6 @@ import dmd.dclass; import dmd.declaration; import dmd.dimport; import dmd.dinterpret; -import dmd.dmangle; import dmd.dmodule; import dmd.dscope; import dmd.dsymbol; @@ -43,6 +42,7 @@ import dmd.hdrgen; import dmd.id; import dmd.identifier; import dmd.location; +import dmd.mangle : decoToType; import dmd.mtype; import dmd.nogc; import dmd.optimize; @@ -338,7 +338,9 @@ Expression semanticTraits(TraitsExp e, Scope* sc) const save = sc.stc; if (e.ident == Id.isDeprecated) sc.stc |= STC.deprecated_; - if (!TemplateInstance.semanticTiargs(e.loc, sc, e.args, 1)) + Scope* sc2 = sc.startCTFE(); + scope(exit) { sc2.endCTFE(); } + if (!TemplateInstance.semanticTiargs(e.loc, sc2, e.args, 1)) { sc.stc = save; return ErrorExp.get(); @@ -444,23 +446,23 @@ Expression semanticTraits(TraitsExp e, Scope* sc) if (e.ident == Id.isArithmetic) { - return isTypeX(t => t.isintegral() || t.isfloating()); + return isTypeX(t => t.isIntegral() || t.isFloating()); } if (e.ident == Id.isFloating) { - return isTypeX(t => t.isfloating()); + return isTypeX(t => t.isFloating()); } if (e.ident == Id.isIntegral) { - return isTypeX(t => t.isintegral()); + return isTypeX(t => t.isIntegral()); } if (e.ident == Id.isScalar) { - return isTypeX(t => t.isscalar()); + return isTypeX(t => t.isScalar()); } if (e.ident == Id.isUnsigned) { - return isTypeX(t => t.isunsigned()); + return isTypeX(t => t.isUnsigned()); } if (e.ident == Id.isAssociativeArray) { @@ -468,7 +470,7 @@ Expression semanticTraits(TraitsExp e, Scope* sc) } if (e.ident == Id.isDeprecated) { - if (isTypeX(t => t.iscomplex() || t.isimaginary()).toBool().hasValue(true)) + if (isTypeX(t => t.isComplex() || t.isImaginary()).toBool().hasValue(true)) return True(); return isDsymX(t => t.isDeprecated()); } @@ -482,13 +484,19 @@ Expression semanticTraits(TraitsExp e, Scope* sc) } if (e.ident == Id.isAbstractClass) { - return isTypeX(t => t.toBasetype().isTypeClass() && - t.toBasetype().isTypeClass().sym.isAbstract()); + return isTypeX((t) + { + auto c = t.toBasetype().isTypeClass(); + return c && c.sym.isAbstract(); + }); } if (e.ident == Id.isFinalClass) { - return isTypeX(t => t.toBasetype().isTypeClass() && - (t.toBasetype().isTypeClass().sym.storage_class & STC.final_) != 0); + return isTypeX((t) + { + const c = t.toBasetype().isTypeClass(); + return c && (c.sym.storage_class & STC.final_) != 0; + }); } if (e.ident == Id.isTemplate) { @@ -536,7 +544,9 @@ Expression semanticTraits(TraitsExp e, Scope* sc) } return True(); } - if (e.ident == Id.hasCopyConstructor || e.ident == Id.hasPostblit) + if (e.ident == Id.hasCopyConstructor || + e.ident == Id.hasMoveConstructor || + e.ident == Id.hasPostblit) { if (dim != 1) return dimError(1); @@ -554,8 +564,14 @@ Expression semanticTraits(TraitsExp e, Scope* sc) auto ts = tb.isTypeStruct(); if (auto sd = ts ? ts.sym : null) { - return (e.ident == Id.hasPostblit) ? (sd.postblit ? True() : False()) - : (sd.hasCopyCtor ? True() : False()); + bool result; + if (e.ident == Id.hasPostblit) + result = sd.postblit !is null; + else if (e.ident == Id. hasCopyConstructor) + result = sd.hasCopyCtor; + else + result = sd.hasMoveCtor; + return result ? True() : False(); } return False(); } @@ -710,8 +726,7 @@ Expression semanticTraits(TraitsExp e, Scope* sc) if (ClassDeclaration cd = agg.isClassDeclaration()) return cd.com ? True() : False(); - else - return False(); + return False(); } if (e.ident == Id.identifier) { @@ -756,7 +771,9 @@ Expression semanticTraits(TraitsExp e, Scope* sc) return dimError(1); Scope* sc2 = sc.push(); - sc2.flags = sc.flags | SCOPE.noaccesscheck | SCOPE.ignoresymbolvisibility; + sc2.copyFlagsFrom(sc); + sc2.noAccessCheck = true; + sc2.ignoresymbolvisibility = true; bool ok = TemplateInstance.semanticTiargs(e.loc, sc2, e.args, 1); sc2.pop(); if (!ok) @@ -786,13 +803,52 @@ Expression semanticTraits(TraitsExp e, Scope* sc) return se.expressionSemantic(sc); } + if (e.ident == Id.getBitfieldOffset || e.ident == Id.getBitfieldWidth) + { + if (dim != 1) + return dimError(1); + + auto o = (*e.args)[0]; + auto s = getDsymbolWithoutExpCtx(o); + if (!s) + { + error(e.loc, "bitfield symbol expected not `%s`", o.toChars()); + return ErrorExp.get(); + } + + auto vd = s.toAlias.isVarDeclaration(); + if (!vd || !(vd.storage_class & STC.field)) + { + error(e.loc, "bitfield symbol expected not %s `%s`", s.kind, s.toPrettyChars); + return ErrorExp.get(); + } + + uint fieldWidth; + uint bitOffset; + if (auto bf = vd.isBitFieldDeclaration()) + { + fieldWidth = bf.fieldWidth; + bitOffset = bf.bitOffset; + } + else // just a regular field + { + const sz = size(vd.type); + assert(sz < uint.max / 8); // overflow check + fieldWidth = cast(uint)sz * 8; + bitOffset = 0; + } + uint value = e.ident == Id.getBitfieldOffset ? bitOffset : fieldWidth; + return new IntegerExp(e.loc, value, Type.tuns32); + } if (e.ident == Id.getProtection || e.ident == Id.getVisibility) { if (dim != 1) return dimError(1); Scope* sc2 = sc.push(); - sc2.flags = sc.flags | SCOPE.noaccesscheck | SCOPE.ignoresymbolvisibility; + sc2.copyFlagsFrom(sc); + sc2.noAccessCheck = true; + sc2.ignoresymbolvisibility = true; bool ok = TemplateInstance.semanticTiargs(e.loc, sc2, e.args, 1); sc2.pop(); if (!ok) @@ -1023,7 +1079,8 @@ Expression semanticTraits(TraitsExp e, Scope* sc) doSemantic: // ignore symbol visibility and disable access checks for these traits Scope* scx = sc.push(); - scx.flags |= SCOPE.ignoresymbolvisibility | SCOPE.noaccesscheck; + scx.ignoresymbolvisibility = true; + scx.noAccessCheck = true; scope (exit) scx.pop(); if (e.ident == Id.hasMember) @@ -1045,7 +1102,7 @@ Expression semanticTraits(TraitsExp e, Scope* sc) e.ident == Id.getVirtualMethods || e.ident == Id.getOverloads) { - uint errors = global.errors; + const errors = global.errors; Expression eorig = ex; ex = ex.expressionSemantic(scx); if (errors < global.errors) @@ -1466,7 +1523,7 @@ Expression semanticTraits(TraitsExp e, Scope* sc) if (!fparams.parameters) return ErrorExp.get(); - StorageClass stc; + STC stc; // Set stc to storage class of the ith parameter auto ex = isExpression((*e.args)[1]); @@ -1753,11 +1810,15 @@ Expression semanticTraits(TraitsExp e, Scope* sc) foreach (o; *e.args) { - uint errors = global.startGagging(); + const errors = global.startGagging(); Scope* sc2 = sc.push(); sc2.tinst = null; sc2.minst = null; // this is why code for these are not emitted to object file - sc2.flags = (sc.flags & ~(SCOPE.ctfe | SCOPE.condition)) | SCOPE.compile | SCOPE.fullinst; + sc2.copyFlagsFrom(sc); + sc2.ctfe = false; + sc2.condition = false; + sc2.traitsCompiles = true; + sc2.fullinst = true; bool err = false; @@ -1784,7 +1845,7 @@ Expression semanticTraits(TraitsExp e, Scope* sc) if (sc2.func && sc2.func.type.isTypeFunction()) { const tf = sc2.func.type.isTypeFunction(); - err |= tf.isnothrow && canThrow(ex, sc2.func, null); + err |= tf.isNothrow && canThrow(ex, sc2.func, null); } ex = checkGC(sc2, ex); if (ex.op == EXP.error) @@ -2002,9 +2063,9 @@ version (IN_LLVM) return dimError(1); auto arg0 = (*e.args)[0]; Dsymbol s = getDsymbolWithoutExpCtx(arg0); - if (!s || !s.loc.isValid()) + if (!s || !s.loc.isValid() || s.isModule()) { - error(e.loc, "can only get the location of a symbol, not `%s`", arg0.toChars()); + error(e.loc, "can only get the location of a symbol, not `%s`", s ? s.toPrettyChars() : arg0.toChars()); return ErrorExp.get(); } diff --git a/dmd/typesem.d b/dmd/typesem.d index 3933d7dfdd..d4c7a5865d 100644 --- a/dmd/typesem.d +++ b/dmd/typesem.d @@ -1,12 +1,12 @@ /** * Semantic analysis for D types. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/typesem.d, _typesem.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/typesem.d, _typesem.d) * Documentation: https://dlang.org/phobos/dmd_typesem.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/typesem.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/typesem.d */ module dmd.typesem; @@ -28,7 +28,6 @@ import dmd.declaration; import dmd.denum; import dmd.dimport; import dmd.dinterpret; -import dmd.dmangle; import dmd.dmodule; import dmd.dscope; import dmd.dstruct; @@ -53,6 +52,8 @@ import dmd.initsem; import dmd.location; import dmd.visitor; import dmd.mtype; +import dmd.mangle; +import dmd.nogc; import dmd.objc; import dmd.opover; import dmd.optimize; @@ -82,7 +83,7 @@ import dmd.tokens; * ps = set if s[oindex] is a Dsymbol, otherwise null * oindex = index into s */ -private void resolveTupleIndex(const ref Loc loc, Scope* sc, Dsymbol s, out Expression pe, out Type pt, out Dsymbol ps, RootObject oindex) +private void resolveTupleIndex(Loc loc, Scope* sc, Dsymbol s, out Expression pe, out Type pt, out Dsymbol ps, RootObject oindex) { auto tup = s.isTupleDeclaration(); @@ -152,7 +153,7 @@ private void resolveTupleIndex(const ref Loc loc, Scope* sc, Dsymbol s, out Expr * ps = set if symbol otherwise null * typeid = set if in TypeidExpression https://dlang.org/spec/expression.html#TypeidExpression */ -private void resolveHelper(TypeQualified mt, const ref Loc loc, Scope* sc, Dsymbol s, Dsymbol scopesym, +private void resolveHelper(TypeQualified mt, Loc loc, Scope* sc, Dsymbol s, Dsymbol scopesym, out Expression pe, out Type pt, out Dsymbol ps, bool intypeid = false) { version (none) @@ -229,13 +230,13 @@ private void resolveHelper(TypeQualified mt, const ref Loc loc, Scope* sc, Dsymb } Type t = s.getType(); // type symbol, type alias, or type tuple? - uint errorsave = global.errors; + const errorsave = global.errors; SearchOptFlags flags = t is null ? SearchOpt.localsOnly : SearchOpt.ignorePrivateImports; Dsymbol sm = s.searchX(loc, sc, id, flags); if (sm) { - if (!(sc.flags & SCOPE.ignoresymbolvisibility) && !symbolIsVisible(sc, sm)) + if (!sc.ignoresymbolvisibility && !symbolIsVisible(sc, sm)) { .error(loc, "`%s` is not visible from module `%s`", sm.toPrettyChars(), sc._module.toChars()); sm = null; @@ -387,7 +388,7 @@ private void resolveHelper(TypeQualified mt, const ref Loc loc, Scope* sc, Dsymb * Returns: * symbol found, NULL if not */ -private Dsymbol searchX(Dsymbol dsym, const ref Loc loc, Scope* sc, RootObject id, SearchOptFlags flags) +private Dsymbol searchX(Dsymbol dsym, Loc loc, Scope* sc, RootObject id, SearchOptFlags flags) { //printf("Dsymbol::searchX(this=%p,%s, ident='%s')\n", this, toChars(), ident.toChars()); Dsymbol s = dsym.toAlias(); @@ -473,12 +474,12 @@ bool isCopyable(Type t) * Otherwise, when the type has const/inout indirections, returns 1. * * Params: - * isref = if true, check `ref t`; otherwise, check just `t` + * isRef = if true, check `ref t`; otherwise, check just `t` * t = the type that is being checked */ -int mutabilityOfType(bool isref, Type t) +int mutabilityOfType(bool isRef, Type t) { - if (isref) + if (isRef) { if (t.mod & MODFlags.immutable_) return 2; @@ -614,13 +615,13 @@ Expression typeToExpression(Type t) * loc = The source location. * sc = scope of the type */ -extern (D) bool checkComplexTransition(Type type, const ref Loc loc, Scope* sc) +extern (D) bool checkComplexTransition(Type type, Loc loc, Scope* sc) { if (sc.isDeprecated()) return false; // Don't complain if we're inside a template constraint // https://issues.dlang.org/show_bug.cgi?id=21831 - if (sc.flags & SCOPE.constraint) + if (sc.inTemplateConstraint) return false; Type t = type.baseElemOf(); @@ -631,9 +632,9 @@ extern (D) bool checkComplexTransition(Type type, const ref Loc loc, Scope* sc) if (t.ty == Tenum && !(cast(TypeEnum)t).sym.memtype) return false; - if (t.isimaginary() || t.iscomplex()) + if (t.isImaginary() || t.isComplex()) { - if (sc.flags & SCOPE.Cfile) + if (sc.inCfile) return true; // complex/imaginary not deprecated in C code Type rt; switch (t.ty) @@ -660,7 +661,7 @@ extern (D) bool checkComplexTransition(Type type, const ref Loc loc, Scope* sc) // Deprecated in 2.097 - Can be made an error from 2.117. // The deprecation period is longer than usual as `cfloat`, // `cdouble`, and `creal` were quite widely used. - if (t.iscomplex()) + if (t.isComplex()) { deprecation(loc, "use of complex type `%s` is deprecated, use `std.complex.Complex!(%s)` instead", type.toChars(), rt.toChars()); @@ -680,6 +681,7 @@ extern (D) bool checkComplexTransition(Type type, const ref Loc loc, Scope* sc) * 'args' are being matched to function type 'tf' * Determine match level. * Params: + * fd = function being called, if a symbol * tf = function type * tthis = type of `this` pointer, null if not member function * argumentList = arguments to function call @@ -689,9 +691,10 @@ extern (D) bool checkComplexTransition(Type type, const ref Loc loc, Scope* sc) * Returns: * MATCHxxxx */ -extern (D) MATCH callMatch(TypeFunction tf, Type tthis, ArgumentList argumentList, int flag = 0, void delegate(const(char)*) scope errorHelper = null, Scope* sc = null) +extern (D) MATCH callMatch(FuncDeclaration fd, TypeFunction tf, Type tthis, ArgumentList argumentList, + int flag = 0, void delegate(const(char)*) scope errorHelper = null, Scope* sc = null) { - //printf("TypeFunction::callMatch() %s\n", tf.toChars()); + //printf("callMatch() fd: %s, tf: %s\n", fd ? fd.ident.toChars() : "null", toChars(tf)); MATCH match = MATCH.exact; // assume exact match ubyte wildmatch = 0; @@ -752,13 +755,14 @@ extern (D) MATCH callMatch(TypeFunction tf, Type tthis, ArgumentList argumentLis } const(char)* failMessage; const(char)** pMessage = errorHelper ? &failMessage : null; - auto resolvedArgs = tf.resolveNamedArgs(argumentList, pMessage); + OutBuffer buf; + auto resolvedArgs = tf.resolveNamedArgs(argumentList, errorHelper ? &buf : null); Expression[] args; if (!resolvedArgs) { - if (failMessage) + if (buf.length) { - errorHelper(failMessage); + errorHelper(buf.peekChars()); return MATCH.nomatch; } @@ -818,7 +822,12 @@ extern (D) MATCH callMatch(TypeFunction tf, Type tthis, ArgumentList argumentLis Expression arg = args[u]; if (!arg) continue; // default argument - m = argumentMatchParameter(tf, p, arg, wildmatch, flag, sc, pMessage); + m = argumentMatchParameter(fd, tf, p, arg, wildmatch, flag, sc, pMessage); + if (failMessage) + { + buf.reset(); + buf.writestring(failMessage); + } } else if (p.defaultArg) continue; @@ -845,15 +854,17 @@ extern (D) MATCH callMatch(TypeFunction tf, Type tthis, ArgumentList argumentLis errorHelper(failMessage); return MATCH.nomatch; } - if (pMessage && u >= args.length) - *pMessage = tf.getMatchError("missing argument for parameter #%d: `%s`", - u + 1, parameterToChars(p, tf, false)); - // If an error happened previously, `pMessage` was already filled - else if (pMessage && !*pMessage) - *pMessage = tf.getParamError(args[u], p); - if (errorHelper) - errorHelper(*pMessage); + { + if (u >= args.length) + TypeFunction.getMatchError(buf, "missing argument for parameter #%d: `%s`", + u + 1, parameterToChars(p, tf, false)); + // If an error happened previously, `pMessage` was already filled + else if (buf.length == 0) + buf.writestring(tf.getParamError(args[u], p)); + + errorHelper(buf.peekChars()); + } return MATCH.nomatch; } if (m < match) @@ -863,7 +874,9 @@ extern (D) MATCH callMatch(TypeFunction tf, Type tthis, ArgumentList argumentLis if (errorHelper && !parameterList.varargs && args.length > nparams) { // all parameters had a match, but there are surplus args - errorHelper(tf.getMatchError("expected %d argument(s), not %d", nparams, args.length)); + OutBuffer buf2; + TypeFunction.getMatchError(buf2, "expected %d argument(s), not %d", nparams, args.length); + errorHelper(buf2.extractChars()); return MATCH.nomatch; } //printf("match = %d\n", match); @@ -876,14 +889,15 @@ extern (D) MATCH callMatch(TypeFunction tf, Type tthis, ArgumentList argumentLis * * This is done by seeing if a call to the copy constructor can be made: * ``` - * typeof(tprm) __copytmp; - * copytmp.__copyCtor(arg); + * typeof(tprm) __copytemp; + * copytemp.__copyCtor(arg); * ``` */ private extern(D) bool isCopyConstructorCallable (StructDeclaration argStruct, Expression arg, Type tprm, Scope* sc, const(char)** pMessage) { - auto tmp = new VarDeclaration(arg.loc, tprm, Identifier.generateId("__copytmp"), null); + //printf("isCopyConstructorCallable() argStruct: %s arg: %s tprm: %s\n", argStruct.toChars(), toChars(arg), toChars(tprm)); + auto tmp = new VarDeclaration(arg.loc, tprm, Identifier.generateId("__copytemp"), null); tmp.storage_class = STC.rvalue | STC.temp | STC.ctfe; tmp.dsymbolSemantic(sc); Expression ve = new VarExp(arg.loc, tmp); @@ -893,66 +907,74 @@ private extern(D) bool isCopyConstructorCallable (StructDeclaration argStruct, if (dmd.expressionsem.trySemantic(e, sc)) return true; - if (pMessage) + if (!pMessage) + return false; + + /* https://issues.dlang.org/show_bug.cgi?id=22202 + * + * If a function was deduced by semantic on the CallExp, + * it means that resolveFuncCall completed succesfully. + * Therefore, there exists a callable copy constructor, + * however, it cannot be called because scope constraints + * such as purity, safety or nogc. + */ + OutBuffer buf; + auto callExp = e.isCallExp(); + + bool nocpctor() { - /* https://issues.dlang.org/show_bug.cgi?id=22202 - * - * If a function was deduced by semantic on the CallExp, - * it means that resolveFuncCall completed succesfully. - * Therefore, there exists a callable copy constructor, - * however, it cannot be called because scope constraints - * such as purity, safety or nogc. - */ - OutBuffer buf; - auto callExp = e.isCallExp(); - if (auto f = callExp.f) - { - char[] s; - if (!f.isPure && sc.func.setImpure()) - s ~= "pure "; - if (!f.isSafe() && !f.isTrusted() && sc.setUnsafe()) - s ~= "@safe "; - if (!f.isNogc && sc.func.setGC(arg.loc, null)) - s ~= "nogc "; - if (f.isDisabled() && !f.isGenerated()) - { - /* https://issues.dlang.org/show_bug.cgi?id=24301 - * Copy constructor is explicitly disabled - */ - buf.printf("`%s` copy constructor cannot be used because it is annotated with `@disable`", - f.type.toChars()); - } - else if (s) - { - s[$-1] = '\0'; - buf.printf("`%s` copy constructor cannot be called from a `%s` context", f.type.toChars(), s.ptr); - } - else if (f.isGenerated() && f.isDisabled()) - { - /* https://issues.dlang.org/show_bug.cgi?id=23097 - * Compiler generated copy constructor failed. - */ - buf.printf("generating a copy constructor for `struct %s` failed, therefore instances of it are uncopyable", - argStruct.toChars()); - } - else - { - /* Although a copy constructor may exist, no suitable match was found. - * i.e: `inout` constructor creates `const` object, not mutable. - * Fallback to using the original generic error before https://issues.dlang.org/show_bug.cgi?id=22202. - */ - goto Lnocpctor; - } - } - else - { - Lnocpctor: - buf.printf("`struct %s` does not define a copy constructor for `%s` to `%s` copies", - argStruct.toChars(), arg.type.toChars(), tprm.toChars()); - } + buf.printf("`struct %s` does not define a copy constructor for `%s` to `%s` copies", + argStruct.toChars(), arg.type.toChars(), tprm.toChars()); + *pMessage = buf.extractChars(); + return false; + } + + auto f = callExp.f; + if (!f) + return nocpctor(); + if (f.isDisabled() && !f.isGenerated()) + { + /* https://issues.dlang.org/show_bug.cgi?id=24301 + * Copy constructor is explicitly disabled + */ + buf.printf("`%s` copy constructor cannot be used because it is annotated with `@disable`", + f.type.toChars()); *pMessage = buf.extractChars(); + return false; } + + 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); + if (bpure | bsafe | bnogc) + { + const nullptr = "".ptr; + buf.printf("`%s` copy constructor cannot be called from a `%s%s%s` context", + f.type.toChars(), + bpure ? "pure " .ptr : nullptr, + bsafe ? "@safe ".ptr : nullptr, + bnogc ? "nogc" .ptr : nullptr); + } + else if (f.isGenerated() && f.isDisabled()) + { + /* https://issues.dlang.org/show_bug.cgi?id=23097 + * Compiler generated copy constructor failed. + */ + buf.printf("generating a copy constructor for `struct %s` failed, therefore instances of it are uncopyable", + argStruct.toChars()); + } + else + { + /* Although a copy constructor may exist, no suitable match was found. + * i.e: `inout` constructor creates `const` object, not mutable. + * Fallback to using the original generic error before https://issues.dlang.org/show_bug.cgi?id=22202. + */ + return nocpctor(); + } + + *pMessage = buf.extractChars(); + return false; } @@ -961,25 +983,28 @@ private extern(D) bool isCopyConstructorCallable (StructDeclaration argStruct, * * This function is called by `TypeFunction.callMatch` while iterating over * the list of parameter. Here we check if `arg` is a match for `p`, - * which is mostly about checking if `arg.type` converts to `p`'s type + * which is mostly about checking if `arg.type` converts to type of `p` * and some check about value reference. * * Params: + * fd = the function being called if symbol, null if not * tf = The `TypeFunction`, only used for error reporting * p = The parameter of `tf` being matched * arg = Argument being passed (bound) to `p` * wildmatch = Wild (`inout`) matching level, derived from the full argument list - * flag = A non-zero value means we're doing a partial ordering check + * flag = A non-zero value means we are doing a partial ordering check * (no value semantic check) * sc = Scope we are in * pMessage = A buffer to write the error in, or `null` * * Returns: Whether `trailingArgs` match `p`. */ -private extern(D) MATCH argumentMatchParameter (TypeFunction tf, Parameter p, +private extern(D) MATCH argumentMatchParameter (FuncDeclaration fd, TypeFunction tf, Parameter p, Expression arg, ubyte wildmatch, int flag, Scope* sc, const(char)** pMessage) { - //printf("arg: %s, type: %s\n", arg.toChars(), arg.type.toChars()); + static if (0) + printf("argumentMatchParameter() sc: %p, fd: %s, tf: %s, p: %s, arg: %s, arg.type: %s\n", + sc, fd ? fd.ident.toChars() : "null", tf.toChars(), parameterToChars(p, tf, false), arg.toChars(), arg.type.toChars()); MATCH m; Type targ = arg.type; Type tprm = wildmatch ? p.type.substWildTo(wildmatch) : p.type; @@ -994,18 +1019,47 @@ private extern(D) MATCH argumentMatchParameter (TypeFunction tf, Parameter p, else { const isRef = p.isReference(); - StructDeclaration argStruct, prmStruct; - // first look for a copy constructor - if (arg.isLvalue() && !isRef && targ.ty == Tstruct && tprm.ty == Tstruct) + StructDeclaration argStruct, prmStruct; + if (targ.ty == Tstruct && tprm.ty == Tstruct) { // if the argument and the parameter are of the same unqualified struct type argStruct = (cast(TypeStruct)targ).sym; prmStruct = (cast(TypeStruct)tprm).sym; + + /* if both a copy constructor and move constructor exist, then match + * the lvalue to the copy constructor only and the rvalue to the move constructor + * only + */ + if (argStruct == prmStruct && fd) + { + if (auto cfd = fd.isCtorDeclaration()) + { + /* Get struct that constructor is making + */ + + auto t1 = cfd.type.toBasetype(); + auto t2 = t1.nextOf(); + auto t3 = t2.isTypeStruct(); + if (t3) + { + auto ctorStruct = t3.sym; +// StructDeclaration ctorStruct = cfd.type.toBasetype().nextOf().isTypeStruct().sym; + + if (prmStruct == ctorStruct && ctorStruct.hasCopyCtor && ctorStruct.hasMoveCtor) + { + if (cfd.isCpCtor && !arg.isLvalue()) + return MATCH.nomatch; // copy constructor is only for lvalues + if (cfd.isMoveCtor && arg.isLvalue()) + return MATCH.nomatch; // move constructor is only for rvalues + } + } + } + } } // check if the copy constructor may be called to copy the argument - if (argStruct && argStruct == prmStruct && argStruct.hasCopyCtor) + if (arg.isLvalue() && !isRef && argStruct && argStruct == prmStruct && argStruct.hasCopyCtor) { if (!isCopyConstructorCallable(argStruct, arg, tprm, sc, pMessage)) return MATCH.nomatch; @@ -1014,106 +1068,107 @@ private extern(D) MATCH argumentMatchParameter (TypeFunction tf, Parameter p, else { import dmd.dcast : cimplicitConvTo; - m = (sc && sc.flags & SCOPE.Cfile) ? arg.cimplicitConvTo(tprm) : arg.implicitConvTo(tprm); + m = (sc && sc.inCfile) ? arg.cimplicitConvTo(tprm) : arg.implicitConvTo(tprm); } } // Non-lvalues do not match ref or out parameters - if (p.isReference()) - { - // https://issues.dlang.org/show_bug.cgi?id=13783 - // Don't use toBasetype() to handle enum types. - Type ta = targ; - Type tp = tprm; - //printf("fparam[%d] ta = %s, tp = %s\n", u, ta.toChars(), tp.toChars()); + if (!p.isReference()) + return m; - if (m && !arg.isLvalue()) - { - if (p.storageClass & STC.out_) - { - if (pMessage) *pMessage = tf.getParamError(arg, p); - return MATCH.nomatch; - } + // https://issues.dlang.org/show_bug.cgi?id=13783 + // Don't use toBasetype() to handle enum types. + Type ta = targ; + Type tp = tprm; + //printf("fparam[%d] ta = %s, tp = %s\n", u, ta.toChars(), tp.toChars()); - if (arg.op == EXP.string_ && tp.ty == Tsarray) - { - if (ta.ty != Tsarray) - { - Type tn = tp.nextOf().castMod(ta.nextOf().mod); - dinteger_t dim = (cast(StringExp)arg).len; - ta = tn.sarrayOf(dim); - } - } - else if (arg.op == EXP.slice && tp.ty == Tsarray) - { - // Allow conversion from T[lwr .. upr] to ref T[upr-lwr] - if (ta.ty != Tsarray) - { - Type tn = ta.nextOf(); - dinteger_t dim = (cast(TypeSArray)tp).dim.toUInteger(); - ta = tn.sarrayOf(dim); - } - } - else if (p.storageClass & STC.constscoperef) - { - // Allow converting a literal to an `in` which is `ref` - if (arg.op == EXP.arrayLiteral && tp.ty == Tsarray) - { - Type tn = tp.nextOf(); - dinteger_t dim = (cast(TypeSArray)tp).dim.toUInteger(); - ta = tn.sarrayOf(dim); - } + if (m && !arg.isLvalue()) + { + if (p.storageClass & STC.out_) + { + if (pMessage) *pMessage = tf.getParamError(arg, p); + return MATCH.nomatch; + } - // Need to make this a rvalue through a temporary - m = MATCH.convert; - } - else if (global.params.rvalueRefParam != FeatureState.enabled || - p.storageClass & STC.out_ || - !arg.type.isCopyable()) // can't copy to temp for ref parameter + if (arg.op == EXP.string_ && tp.ty == Tsarray) + { + if (ta.ty != Tsarray) { - if (pMessage) *pMessage = tf.getParamError(arg, p); - return MATCH.nomatch; + Type tn = tp.nextOf().castMod(ta.nextOf().mod); + dinteger_t dim = (cast(StringExp)arg).len; + ta = tn.sarrayOf(dim); } - else + } + else if (arg.op == EXP.slice && tp.ty == Tsarray) + { + // Allow conversion from T[lwr .. upr] to ref T[upr-lwr] + if (ta.ty != Tsarray) { - /* in functionParameters() we'll convert this - * rvalue into a temporary - */ - m = MATCH.convert; + Type tn = ta.nextOf(); + dinteger_t dim = (cast(TypeSArray)tp).dim.toUInteger(); + ta = tn.sarrayOf(dim); } } - - /* If the match is not already perfect or if the arg - is not a lvalue then try the `alias this` chain - see https://issues.dlang.org/show_bug.cgi?id=15674 - and https://issues.dlang.org/show_bug.cgi?id=21905 - */ - if (ta != tp || !arg.isLvalue()) + else if (p.storageClass & STC.constscoperef) { - Type firsttab = ta.toBasetype(); - while (1) + // Allow converting a literal to an `in` which is `ref` + if (arg.op == EXP.arrayLiteral && tp.ty == Tsarray) { - Type tab = ta.toBasetype(); - Type tat = tab.aliasthisOf(); - if (!tat || !tat.implicitConvTo(tprm)) - break; - if (tat == tab || tat == firsttab) - break; - ta = tat; + Type tn = tp.nextOf(); + dinteger_t dim = (cast(TypeSArray)tp).dim.toUInteger(); + ta = tn.sarrayOf(dim); } - } - /* A ref variable should work like a head-const reference. - * e.g. disallows: - * ref T <- an lvalue of const(T) argument - * ref T[dim] <- an lvalue of const(T[dim]) argument - */ - if (!ta.constConv(tp)) + // Need to make this a rvalue through a temporary + m = MATCH.convert; + } + else if (!(sc && sc.previews.rvalueRefParam) || + p.storageClass & STC.out_ || + !arg.type.isCopyable()) // can't copy to temp for ref parameter { if (pMessage) *pMessage = tf.getParamError(arg, p); return MATCH.nomatch; } + else + { + /* in functionParameters() we'll convert this + * rvalue into a temporary + */ + m = MATCH.convert; + } } + + /* If the match is not already perfect or if the arg + is not a lvalue then try the `alias this` chain + see https://issues.dlang.org/show_bug.cgi?id=15674 + and https://issues.dlang.org/show_bug.cgi?id=21905 + */ + if (ta != tp || !arg.isLvalue()) + { + Type firsttab = ta.toBasetype(); + while (1) + { + Type tab = ta.toBasetype(); + Type tat = tab.aliasthisOf(); + if (!tat || !tat.implicitConvTo(tprm)) + break; + if (tat == tab || tat == firsttab) + break; + ta = tat; + } + } + + /* A ref variable should work like a head-const reference. + * e.g. disallows: + * ref T <- an lvalue of const(T) argument + * ref T[dim] <- an lvalue of const(T[dim]) argument + */ + if (!ta.constConv(tp)) + { + if (pMessage) *pMessage = tf.getParamError(arg, p); + return MATCH.nomatch; + } + return m; } @@ -1132,7 +1187,7 @@ private const(char)* getParamError(TypeFunction tf, Expression arg, Parameter pa // only mention rvalue if it's relevant const rv = !arg.isLvalue() && par.isReference(); buf.printf("cannot pass %sargument `%s` of type `%s` to parameter `%s`", - rv ? "rvalue ".ptr : "".ptr, arg.toChars(), at, + rv ? "rvalue ".ptr : "".ptr, arg.toErrMsg(), at, parameterToChars(par, tf, qual)); return buf.extractChars(); } @@ -1164,8 +1219,12 @@ private extern(D) MATCH matchTypeSafeVarArgs(TypeFunction tf, Parameter p, if (sz != trailingArgs.length) { if (pMessage) - *pMessage = tf.getMatchError("expected %llu variadic argument(s), not %zu", + { + OutBuffer buf; + TypeFunction.getMatchError(buf, "expected %llu variadic argument(s), not %zu", sz, trailingArgs.length); + *pMessage = buf.extractChars(); + } return MATCH.nomatch; } goto case Tarray; @@ -1306,7 +1365,7 @@ uinteger_t size(Type t) return size(t, Loc.initial); } -uinteger_t size(Type t, const ref Loc loc) +uinteger_t size(Type t, Loc loc) { uinteger_t visitType(Type t) @@ -1447,6 +1506,163 @@ uinteger_t size(Type t, const ref Loc loc) } } +/******************************* + * Determine if converting 'this' to 'to' is an identity operation, + * a conversion to const operation, or the types aren't the same. + * Returns: + * MATCH.exact 'this' == 'to' + * MATCH.constant 'to' is const + * MATCH.nomatch conversion to mutable or invariant + */ +MATCH constConv(Type from, Type to) +{ + MATCH visitType(Type from) + { + //printf("Type::constConv(this = %s, to = %s)\n", from.toChars(), to.toChars()); + if (from.equals(to)) + return MATCH.exact; + if (from.ty == to.ty && MODimplicitConv(from.mod, to.mod)) + return MATCH.constant; + return MATCH.nomatch; + } + + MATCH visitNext(TypeNext from) + { + //printf("TypeNext::constConv from = %s, to = %s\n", from.toChars(), to.toChars()); + if (from.equals(to)) + return MATCH.exact; + + if (!(from.ty == to.ty && MODimplicitConv(from.mod, to.mod))) + return MATCH.nomatch; + + Type tn = to.nextOf(); + if (!(tn && from.next.ty == tn.ty)) + return MATCH.nomatch; + + MATCH m; + if (to.isConst()) // whole tail const conversion + { + // Recursive shared level check + m = from.next.constConv(tn); + if (m == MATCH.exact) + m = MATCH.constant; + } + else + { + //printf("\tnext => %s, to.next => %s\n", from.next.toChars(), tn.toChars()); + m = from.next.equals(tn) ? MATCH.constant : MATCH.nomatch; + } + return m; + } + + MATCH visitSArray(TypeSArray from) + { + if (auto tsa = to.isTypeSArray()) + { + if (!from.dim.equals(tsa.dim)) + return MATCH.nomatch; + } + return visitNext(from); + } + + MATCH visitAArray(TypeAArray from) + { + if (auto taa = to.isTypeAArray()) + { + MATCH mindex = from.index.constConv(taa.index); + MATCH mkey = from.next.constConv(taa.next); + // Pick the worst match + return mkey < mindex ? mkey : mindex; + } + return visitType(from); + } + + MATCH visitPointer(TypePointer from) + { + if (from.next.ty == Tfunction) + { + if (to.nextOf() && from.next.equals((cast(TypeNext)to).next)) + return visitType(from); + else + return MATCH.nomatch; + } + return visitNext(from); + } + + MATCH visitFunction(TypeFunction from) + { + // Attributes need to match exactly, otherwise it's an implicit conversion + if (from.ty != to.ty || !from.attributesEqual(cast(TypeFunction) to)) + return MATCH.nomatch; + + return visitNext(from); + } + + MATCH visitStruct(TypeStruct from) + { + if (from.equals(to)) + return MATCH.exact; + if (from.ty == to.ty && from.sym == (cast(TypeStruct)to).sym && MODimplicitConv(from.mod, to.mod)) + return MATCH.constant; + return MATCH.nomatch; + } + + MATCH visitEnum(TypeEnum from) + { + if (from.equals(to)) + return MATCH.exact; + if (from.ty == to.ty && from.sym == (cast(TypeEnum)to).sym && MODimplicitConv(from.mod, to.mod)) + return MATCH.constant; + return MATCH.nomatch; + } + + MATCH visitClass(TypeClass from) + { + if (from.equals(to)) + return MATCH.exact; + if (from.ty == to.ty && from.sym == (cast(TypeClass)to).sym && MODimplicitConv(from.mod, to.mod)) + return MATCH.constant; + + /* Conversion derived to const(base) + */ + int offset = 0; + if (to.isBaseOf(from, &offset) && offset == 0 && MODimplicitConv(from.mod, to.mod)) + { + // Disallow: + // derived to base + // inout(derived) to inout(base) + if (!to.isMutable() && !to.isWild()) + return MATCH.convert; + } + + return MATCH.nomatch; + } + + MATCH visitNoreturn(TypeNoreturn from) + { + // Either another noreturn or conversion to any type + return from.implicitConvTo(to); + } + + switch(from.ty) + { + default: return visitType(from); + case Tsarray: return visitSArray(from.isTypeSArray()); + case Taarray: return visitAArray(from.isTypeAArray()); + case Treference: + case Tdelegate: + case Tslice: + case Tarray: return visitNext(cast(TypeNext)from); + case Tpointer: return visitPointer(from.isTypePointer()); + case Tfunction: return visitFunction(from.isTypeFunction()); + case Tstruct: return visitStruct(from.isTypeStruct()); + case Tenum: return visitEnum(from.isTypeEnum()); + case Tclass: return visitClass(from.isTypeClass()); + case Tnoreturn: return visitNoreturn(from.isTypeNoreturn()); + } +} + + /****************************************** * Perform semantic analysis on a type. * Params: @@ -1457,7 +1673,7 @@ uinteger_t size(Type t, const ref Loc loc) * `Type` with completed semantic analysis, `Terror` if errors * were encountered */ -Type typeSemantic(Type type, const ref Loc loc, Scope* sc) +Type typeSemantic(Type type, Loc loc, Scope* sc) { static Type error() { @@ -1481,7 +1697,7 @@ Type typeSemantic(Type type, const ref Loc loc, Scope* sc) Type visitComplex(TypeBasic t) { - if (!(sc.flags & SCOPE.Cfile)) + if (!sc.inCfile) return visitType(t); auto tc = getComplexLibraryType(loc, sc, t.ty); @@ -1655,7 +1871,7 @@ Type typeSemantic(Type type, const ref Loc loc, Scope* sc) default: break; } - if (tbn.isscope()) + if (tbn.isScopeClass()) { .error(loc, "cannot have array of scope `%s`", tbn.toChars()); return error(); @@ -1689,7 +1905,7 @@ Type typeSemantic(Type type, const ref Loc loc, Scope* sc) default: break; } - if (tn.isscope()) + if (tn.isScopeClass()) { .error(loc, "cannot have array of scope `%s`", tn.toChars()); return error(); @@ -1809,7 +2025,7 @@ Type typeSemantic(Type type, const ref Loc loc, Scope* sc) } else if (sd.xeq == sd.xerreq) { - if (search_function(sd, Id.eq)) + if (search_function(sd, Id.opEquals)) { .error(loc, "%sAA key type `%s` does not have `bool opEquals(ref const %s) const`", s, sd.toChars(), sd.toChars()); } @@ -1821,7 +2037,7 @@ Type typeSemantic(Type type, const ref Loc loc, Scope* sc) } else if (!sd.xhash) { - if (search_function(sd, Id.eq)) + if (search_function(sd, Id.opEquals)) { .error(loc, "%sAA key type `%s` should have `extern (D) size_t toHash() const nothrow @safe` if `opEquals` defined", s, sd.toChars()); } @@ -1859,9 +2075,9 @@ Type typeSemantic(Type type, const ref Loc loc, Scope* sc) __gshared FuncDeclaration fcmp = null; __gshared FuncDeclaration fhash = null; if (!feq) - feq = search_function(ClassDeclaration.object, Id.eq).isFuncDeclaration(); + feq = search_function(ClassDeclaration.object, Id.opEquals).isFuncDeclaration(); if (!fcmp) - fcmp = search_function(ClassDeclaration.object, Id.cmp).isFuncDeclaration(); + fcmp = search_function(ClassDeclaration.object, Id.opCmp).isFuncDeclaration(); if (!fhash) fhash = search_function(ClassDeclaration.object, Id.tohash).isFuncDeclaration(); assert(fcmp && feq && fhash); @@ -1895,7 +2111,7 @@ Type typeSemantic(Type type, const ref Loc loc, Scope* sc) default: break; } - if (mtype.next.isscope()) + if (mtype.next.isScopeClass()) { .error(loc, "cannot have array of scope `%s`", mtype.next.toChars()); return error(); @@ -1995,23 +2211,23 @@ Type typeSemantic(Type type, const ref Loc loc, Scope* sc) if (sc.stc & STC.pure_) tf.purity = PURE.fwdref; if (sc.stc & STC.nothrow_) - tf.isnothrow = true; + tf.isNothrow = true; if (sc.stc & STC.nogc) - tf.isnogc = true; + tf.isNogc = true; if (sc.stc & STC.ref_) - tf.isref = true; + tf.isRef = true; if (sc.stc & STC.return_) - tf.isreturn = true; + tf.isReturn = true; if (sc.stc & STC.returnScope) - tf.isreturnscope = true; + tf.isReturnScope = true; if (sc.stc & STC.returninferred) - tf.isreturninferred = true; + tf.isReturnInferred = true; if (sc.stc & STC.scope_) tf.isScopeQual = true; if (sc.stc & STC.scopeinferred) - tf.isscopeinferred = true; + tf.isScopeInferred = true; -// if (tf.isreturn && !tf.isref) +// if (tf.isReturn && !tf.isRef) // tf.isScopeQual = true; // return by itself means 'return scope' if (tf.trust == TRUST.default_) @@ -2025,9 +2241,9 @@ Type typeSemantic(Type type, const ref Loc loc, Scope* sc) } if (sc.stc & STC.property) - tf.isproperty = true; + tf.isProperty = true; if (sc.stc & STC.live) - tf.islive = true; + tf.isLive = true; tf.linkage = sc.linkage; if (tf.linkage == LINK.system) @@ -2061,7 +2277,7 @@ Type typeSemantic(Type type, const ref Loc loc, Scope* sc) tf.next = tf.next.typeSemantic(loc, sc); sc = sc.pop(); errors |= tf.checkRetType(loc); - if (tf.next.isscope() && !tf.isctor) + if (tf.next.isScopeClass() && !tf.isCtor) { .error(loc, "functions cannot return `scope %s`", tf.next.toChars()); errors = true; @@ -2069,9 +2285,9 @@ Type typeSemantic(Type type, const ref Loc loc, Scope* sc) if (tf.next.hasWild()) wildreturn = true; - if (tf.isreturn && !tf.isref && !tf.next.hasPointers()) + if (tf.isReturn && !tf.isRef && !tf.next.hasPointers()) { - tf.isreturn = false; + tf.isReturn = false; } } @@ -2113,14 +2329,13 @@ Type typeSemantic(Type type, const ref Loc loc, Scope* sc) { const(char)* errTxt = fparam.storageClass & STC.ref_ ? "ref" : "out"; .error(e.loc, "expression `%s` of type `%s` is not implicitly convertible to type `%s %s` of parameter `%s`", - e.toChars(), e.type.toChars(), errTxt, fparam.type.toChars(), fparam.toChars()); + e.toErrMsg(), e.type.toChars(), errTxt, fparam.type.toChars(), fparam.toChars()); } e = e.implicitCastTo(sc, fparam.type); // default arg must be an lvalue if (isRefOrOut && !isAuto && - !(fparam.storageClass & STC.constscoperef) && - global.params.rvalueRefParam != FeatureState.enabled) + !(fparam.storageClass & STC.constscoperef) && !sc.previews.rvalueRefParam) e = e.toLvalue(sc, "create default argument for `ref` / `out` parameter from"); fparam.defaultArg = e; @@ -2133,7 +2348,7 @@ Type typeSemantic(Type type, const ref Loc loc, Scope* sc) /* Create a scope for evaluating the default arguments for the parameters */ Scope* argsc = sc.push(); - argsc.stc = 0; // don't inherit storage class + argsc.stc = STC.none; // don't inherit storage class argsc.visibility = Visibility(Visibility.Kind.public_); argsc.func = null; @@ -2189,12 +2404,12 @@ Type typeSemantic(Type type, const ref Loc loc, Scope* sc) // https://issues.dlang.org/show_bug.cgi?id=12744 // If the storage classes of narg // conflict with the ones in fparam, it's ignored. - StorageClass stc = fparam.storageClass | narg.storageClass; - StorageClass stc1 = fparam.storageClass & (STC.ref_ | STC.out_ | STC.lazy_); - StorageClass stc2 = narg.storageClass & (STC.ref_ | STC.out_ | STC.lazy_); + STC stc = fparam.storageClass | narg.storageClass; + STC stc1 = fparam.storageClass & (STC.ref_ | STC.out_ | STC.lazy_); + STC stc2 = narg.storageClass & (STC.ref_ | STC.out_ | STC.lazy_); if (stc1 && stc2 && stc1 != stc2) { - OutBuffer buf1; stcToBuffer(buf1, stc1 | ((stc1 & STC.ref_) ? (fparam.storageClass & STC.auto_) : 0)); + OutBuffer buf1; stcToBuffer(buf1, stc1 | ((stc1 & STC.ref_) ? (fparam.storageClass & STC.auto_) : STC.none)); OutBuffer buf2; stcToBuffer(buf2, stc2); .error(loc, "incompatible parameter storage classes `%s` and `%s`", @@ -2278,10 +2493,17 @@ Type typeSemantic(Type type, const ref Loc loc, Scope* sc) errors = true; } - const bool isTypesafeVariadic = i + 1 == dim && - tf.parameterList.varargs == VarArg.typesafe && - (t.isTypeDArray() || t.isTypeClass()); - if (isTypesafeVariadic) + const bool isTypesafeVariadic = i + 1 == dim && tf.parameterList.varargs == VarArg.typesafe; + const bool isStackAllocatedVariadic = isTypesafeVariadic && (t.isTypeDArray() || t.isTypeClass()); + + if (isTypesafeVariadic && t.isTypeClass()) + { + // Deprecated in 2.111, kept as a legacy feature for compatibility (currently no plan to turn it into an error) + .deprecation(loc, "typesafe variadic parameters with a `class` type (`%s %s...`) are deprecated", + t.isTypeClass().sym.ident.toChars(), fparam.toChars()); + } + + if (isStackAllocatedVariadic) { /* typesafe variadic arguments are constructed on the stack, so must be `scope` */ @@ -2294,7 +2516,7 @@ Type typeSemantic(Type type, const ref Loc loc, Scope* sc) { if (!(fparam.storageClass & STC.scope_)) fparam.storageClass |= STC.scope_ | STC.scopeinferred; // 'return' implies 'scope' - if (tf.isref) + if (tf.isRef) { } else if (tf.next && !tf.next.hasPointers() && tf.next.toBasetype().ty != Tvoid) @@ -2303,7 +2525,7 @@ Type typeSemantic(Type type, const ref Loc loc, Scope* sc) } } - if (isTypesafeVariadic) + if (isStackAllocatedVariadic) { /* This is because they can be constructed on the stack * https://dlang.org/spec/function.html#typesafe_variadic_functions @@ -2452,14 +2674,14 @@ Type typeSemantic(Type type, const ref Loc loc, Scope* sc) tf.isInOutParam = (wildparams & 1) != 0; tf.isInOutQual = (wildparams & 2) != 0; - if (tf.isproperty && (tf.parameterList.varargs != VarArg.none || tf.parameterList.length > 2)) + if (tf.isProperty && (tf.parameterList.varargs != VarArg.none || tf.parameterList.length > 2)) { .error(loc, "properties can only have zero, one, or two parameter"); errors = true; } if (tf.parameterList.varargs == VarArg.variadic && tf.linkage != LINK.d && tf.parameterList.length == 0 && - !(sc.flags & SCOPE.Cfile)) + !sc.inCfile) { .error(loc, "variadic functions with non-D linkage must have at least one parameter"); errors = true; @@ -2521,53 +2743,49 @@ Type typeSemantic(Type type, const ref Loc loc, Scope* sc) //printf("\tit's a type %d, %s, %s\n", t.ty, t.toChars(), t.deco); return t.addMod(mtype.mod); } - else - { - if (s) - { - auto td = s.isTemplateDeclaration; - if (td && td.onemember && td.onemember.isAggregateDeclaration) - .error(loc, "template %s `%s` is used as a type without instantiation" - ~ "; to instantiate it use `%s!(arguments)`", - s.kind, s.toPrettyChars, s.ident.toChars); - else - .error(loc, "%s `%s` is used as a type", s.kind, s.toPrettyChars); - //assert(0); - } - else if (e.op == EXP.variable) // special case: variable is used as a type - { - /* - N.B. This branch currently triggers for the following code - template test(x* x) - { - } - i.e. the compiler prints "variable x is used as a type" - which isn't a particularly good error message (x is a variable?). - */ - Dsymbol varDecl = mtype.toDsymbol(sc); - const(Loc) varDeclLoc = varDecl.getLoc(); - Module varDeclModule = varDecl.getModule(); //This can be null - - .error(loc, "variable `%s` is used as a type", mtype.toChars()); - //Check for null to avoid https://issues.dlang.org/show_bug.cgi?id=22574 - if ((varDeclModule !is null) && varDeclModule != sc._module) // variable is imported + if (s) + { + auto td = s.isTemplateDeclaration; + if (td && td.onemember && td.onemember.isAggregateDeclaration) + .error(loc, "template %s `%s` is used as a type without instantiation" + ~ "; to instantiate it use `%s!(arguments)`", + s.kind, s.toPrettyChars, s.ident.toChars); + else + .error(loc, "%s `%s` is used as a type", s.kind, s.toPrettyChars); + //assert(0); + } + else if (e.op == EXP.variable) // special case: variable is used as a type + { + /* + N.B. This branch currently triggers for the following code + template test(x* x) { - const(Loc) varDeclModuleImportLoc = varDeclModule.getLoc(); - .errorSupplemental( - varDeclModuleImportLoc, - "variable `%s` is imported here from: `%s`", - varDecl.toChars, - varDeclModule.toPrettyChars, - ); + } + i.e. the compiler prints "variable x is used as a type" + which isn't a particularly good error message (x is a variable?). + */ + Dsymbol varDecl = mtype.toDsymbol(sc); + Module varDeclModule = varDecl.getModule(); //This can be null - .errorSupplemental(varDeclLoc, "variable `%s` is declared here", varDecl.toChars); + .error(loc, "variable `%s` is used as a type", mtype.toChars()); + //Check for null to avoid https://issues.dlang.org/show_bug.cgi?id=22574 + if ((varDeclModule !is null) && varDeclModule != sc._module) // variable is imported + { + .errorSupplemental( + varDeclModule.loc, + "variable `%s` is imported here from: `%s`", + varDecl.toChars, + varDeclModule.toPrettyChars, + ); } - else - .error(loc, "`%s` is used as a type", mtype.toChars()); - return error(); + + .errorSupplemental(varDecl.loc, "variable `%s` is declared here", varDecl.toChars); } + else + .error(loc, "`%s` is used as a type", mtype.toChars()); + return error(); } Type visitInstance(TypeInstance mtype) @@ -2767,8 +2985,14 @@ Type typeSemantic(Type type, const ref Loc loc, Scope* sc) Type visitTag(TypeTag mtype) { //printf("TypeTag.semantic() %s\n", mtype.toChars()); - Type returnType(Type t) - { + Type returnType(TypeTag tt) + { + Type t = tt.resolved; + // To make const checking work, the const STC needs to be added: + // t = t.resolved.addSTC(mtype.mod.ModToStc); + // However, this currently fails compilable/test22875.i + // Apparently there's some aliasing going on, where mutable + // versions of the type also get const applied to them. return t.deco ? t : t.merge(); } @@ -2776,7 +3000,7 @@ Type typeSemantic(Type type, const ref Loc loc, Scope* sc) { /* struct S s, *p; */ - return returnType(mtype.resolved.addSTC(mtype.mod)); + return returnType(mtype); } /* Find the current scope by skipping tag scopes. @@ -2849,7 +3073,7 @@ Type typeSemantic(Type type, const ref Loc loc, Scope* sc) { mtype.id = Identifier.generateId("__tag"[]); declareTag(); - return returnType(mtype.resolved.addSTC(mtype.mod)); + return returnType(mtype); } /* look for pre-existing declaration @@ -2862,7 +3086,7 @@ Type typeSemantic(Type type, const ref Loc loc, Scope* sc) if (mtype.tok == TOK.enum_ && !mtype.members) .error(mtype.loc, "`enum %s` is incomplete without members", mtype.id.toChars()); // C11 6.7.2.3-3 declareTag(); - return returnType(mtype.resolved.addSTC(mtype.mod)); + return returnType(mtype); } /* A redeclaration only happens if both declarations are in @@ -2917,7 +3141,7 @@ Type typeSemantic(Type type, const ref Loc loc, Scope* sc) else { /* struct S { int a; }; - * struct S *s; + * struct S* s; */ } mtype.resolved = sd.type; @@ -2948,21 +3172,21 @@ Type typeSemantic(Type type, const ref Loc loc, Scope* sc) mtype.tok == TOK.struct_ && s.isStructDeclaration()) { /* struct S; - * { struct S *s; } + * { struct S* s; } */ mtype.resolved = s.isStructDeclaration().type; } else { /* union S; - * { struct S *s; } + * { struct S* s; } */ .error(mtype.loc, "redeclaring `%s %s` as `%s %s`", s.kind(), s.toChars(), Token.toChars(mtype.tok), mtype.id.toChars()); declareTag(); } } - return returnType(mtype.resolved.addSTC(mtype.mod)); + return returnType(mtype); } switch (type.ty) @@ -2994,7 +3218,7 @@ Type typeSemantic(Type type, const ref Loc loc, Scope* sc) } } -Type trySemantic(Type type, const ref Loc loc, Scope* sc) +Type trySemantic(Type type, Loc loc, Scope* sc) { //printf("+trySemantic(%s) %d\n", toChars(), global.errors); @@ -3042,9 +3266,19 @@ Type merge(Type type) case Tsarray: // prevents generating the mangle if the array dim is not yet known - if (!type.isTypeSArray().dim.isIntegerExp()) - return type; - goto default; + if (auto ie = type.isTypeSArray().dim.isIntegerExp()) + { + // After TypeSemantic, the length is always converted to size_t, but the parser + // usually generates regular integer types (e.g. in cast(const ubyte[2])) which + // it may try to merge, which then leads to failing implicit conversions as 2LU != 2 + // according to Expression.equals. Only merge array types with size_t lengths for now. + // https://github.com/dlang/dmd/issues/21179 + if (ie.type != Type.tsize_t) + return type; + + goto default; + } + return type; case Tenum: break; @@ -3061,37 +3295,36 @@ Type merge(Type type) } //printf("merge(%s)\n", toChars()); - if (!type.deco) - { - OutBuffer buf; - buf.reserve(32); + if (type.deco) + return type; - mangleToBuffer(type, buf); + OutBuffer buf; + buf.reserve(32); - auto sv = type.stringtable.update(buf[]); - if (sv.value) - { - Type t = sv.value; - debug - { - import core.stdc.stdio; - if (!t.deco) - printf("t = %s\n", t.toChars()); - } - assert(t.deco); - //printf("old value, deco = '%s' %p\n", t.deco, t.deco); - return t; - } - else + mangleToBuffer(type, buf); + + auto sv = type.stringtable.update(buf[]); + if (sv.value) + { + Type t = sv.value; + debug { - Type t = stripDefaultArgs(type); - sv.value = t; - type.deco = t.deco = cast(char*)sv.toDchars(); - //printf("new value, deco = '%s' %p\n", t.deco, t.deco); - return t; + import core.stdc.stdio; + if (!t.deco) + printf("t = %s\n", t.toChars()); } + assert(t.deco); + //printf("old value, deco = '%s' %p\n", t.deco, t.deco); + return t; + } + else + { + Type t = stripDefaultArgs(type); + sv.value = t; + type.deco = t.deco = cast(char*)sv.toDchars(); + //printf("new value, deco = '%s' %p\n", t.deco, t.deco); + return t; } - return type; } /************************************* @@ -3130,7 +3363,7 @@ Type merge2(Type type) * Returns: * expression representing the property, or null if not a property and (flag & 1) */ -Expression getProperty(Type t, Scope* scope_, const ref Loc loc, Identifier ident, int flag, +Expression getProperty(Type t, Scope* scope_, Loc loc, Identifier ident, int flag, Expression src = null) { Expression visitType(Type mt) @@ -3145,14 +3378,14 @@ Expression getProperty(Type t, Scope* scope_, const ref Loc loc, Identifier iden const sz = mt.size(loc); if (sz == SIZE_INVALID) return ErrorExp.get(); - e = new IntegerExp(loc, sz, Type.tsize_t); + return new IntegerExp(loc, sz, Type.tsize_t); } else if (ident == Id.__xalignof) { const explicitAlignment = mt.alignment(); const naturalAlignment = mt.alignsize(); const actualAlignment = (explicitAlignment.isDefault() ? naturalAlignment : explicitAlignment.get()); - e = new IntegerExp(loc, actualAlignment, Type.tsize_t); + return new IntegerExp(loc, actualAlignment, Type.tsize_t); } else if (ident == Id._init) { @@ -3162,13 +3395,14 @@ Expression getProperty(Type t, Scope* scope_, const ref Loc loc, Identifier iden { e.isStructLiteralExp().useStaticInit = true; } + return e; } else if (ident == Id._mangleof) { if (!mt.deco) { error(loc, "forward reference of type `%s.mangleof`", mt.toChars()); - e = ErrorExp.get(); + return ErrorExp.get(); } else { @@ -3177,6 +3411,7 @@ Expression getProperty(Type t, Scope* scope_, const ref Loc loc, Identifier iden sc.eSink = global.errorSink; e = e.expressionSemantic(&sc); } + return e; } else if (ident == Id.stringof) { @@ -3185,53 +3420,98 @@ Expression getProperty(Type t, Scope* scope_, const ref Loc loc, Identifier iden Scope sc; sc.eSink = global.errorSink; e = e.expressionSemantic(&sc); + return e; } else if (flag && mt != Type.terror) { return null; } + + Dsymbol s = null; + if (mt.ty == Tstruct || mt.ty == Tclass || mt.ty == Tenum) + s = mt.toDsymbol(null); + if (s) + s = s.search_correct(ident); + if (s && !symbolIsVisible(scope_, s)) + s = null; + + if (mt == Type.terror) + return ErrorExp.get(); + + if (s) + error(loc, "no property `%s` for type `%s`, did you mean `%s`?", ident.toChars(), mt.toChars(), s.toPrettyChars()); + else if (ident == Id.opCall && mt.ty == Tclass) + error(loc, "no property `%s` for type `%s`, did you mean `new %s`?", ident.toChars(), mt.toChars(), mt.toPrettyChars()); + + else if (const n = importHint(ident.toString())) + error(loc, "no property `%s` for type `%s`, perhaps `import %.*s;` is needed?", ident.toChars(), mt.toChars(), cast(int)n.length, n.ptr); else { - Dsymbol s = null; - if (mt.ty == Tstruct || mt.ty == Tclass || mt.ty == Tenum) - s = mt.toDsymbol(null); - if (s) - s = s.search_correct(ident); - if (s && !symbolIsVisible(scope_, s)) - s = null; - if (mt != Type.terror) - { - if (s) - error(loc, "no property `%s` for type `%s`, did you mean `%s`?", ident.toChars(), mt.toChars(), s.toPrettyChars()); - else if (ident == Id.call && mt.ty == Tclass) - error(loc, "no property `%s` for type `%s`, did you mean `new %s`?", ident.toChars(), mt.toChars(), mt.toPrettyChars()); - - else if (const n = importHint(ident.toString())) - error(loc, "no property `%s` for type `%s`, perhaps `import %.*s;` is needed?", ident.toChars(), mt.toChars(), cast(int)n.length, n.ptr); - else + if (src) + { + error(loc, "no property `%s` for `%s` of type `%s`", + ident.toChars(), src.toChars(), mt.toPrettyChars(true)); + auto s2 = scope_.search_correct(ident); + // UFCS + if (s2 && s2.isFuncDeclaration) { - if (src) - error(loc, "no property `%s` for `%s` of type `%s`", ident.toChars(), src.toChars(), mt.toPrettyChars(true)); + if (s2.ident == ident) + { + errorSupplemental(s2.loc, "cannot call %s `%s` with UFCS because it is not declared at module scope", + s2.kind(), s2.toChars()); + } else - error(loc, "no property `%s` for type `%s`", ident.toChars(), mt.toPrettyChars(true)); + errorSupplemental(s2.loc, "did you mean %s `%s`?", + s2.kind(), s2.toChars()); + } + else if (src.type.ty == Tpointer) + { + // structPtr.field + auto tn = (cast(TypeNext) src.type).nextOf(); + if (auto as = tn.isAggregate()) + { + if (auto s3 = as.search_correct(ident)) + { + errorSupplemental(s3.loc, "did you mean %s `%s`?", + s3.kind(), s3.toChars()); + } + } + } + } + else + error(loc, "no property `%s` for type `%s`", ident.toChars(), mt.toPrettyChars(true)); + + if (auto dsym = mt.toDsymbol(scope_)) + { + if (auto sym = dsym.isAggregateDeclaration()) + { + if (!sym.members) + { + errorSupplemental(sym.loc, "`%s %s` is opaque and has no members.", sym.kind, mt.toPrettyChars(true)); + return ErrorExp.get(); + } - if (auto dsym = mt.toDsymbol(scope_)) + if (auto fd = search_function(sym, Id.opDispatch)) { - if (auto sym = dsym.isAggregateDeclaration()) + if (auto td = fd.isTemplateDeclaration()) { - if (auto fd = search_function(sym, Id.opDispatch)) - errorSupplemental(loc, "potentially malformed `opDispatch`. Use an explicit instantiation to get a better error message"); - else if (!sym.members) - errorSupplemental(sym.loc, "`%s %s` is opaque and has no members.", sym.kind, mt.toPrettyChars(true)); + e = mt.defaultInitLiteral(loc); + auto se = new StringExp(e.loc, ident.toString()); + auto tiargs = new Objects(); + tiargs.push(se); + auto dti = new DotTemplateInstanceExp(e.loc, e, Id.opDispatch, tiargs); + dti.ti.tempdecl = td; + dti.dotTemplateSemanticProp(scope_, DotExpFlag.none); + return ErrorExp.get(); } - errorSupplemental(dsym.loc, "%s `%s` defined here", - dsym.kind, dsym.toChars()); } } + errorSupplemental(dsym.loc, "%s `%s` defined here", + dsym.kind, dsym.toChars()); } - e = ErrorExp.get(); } - return e; + + return ErrorExp.get(); } Expression visitError(TypeError) @@ -3253,7 +3533,7 @@ Expression getProperty(Type t, Scope* scope_, const ref Loc loc, Identifier iden Expression floatValue(real_t r) { - if (mt.isreal() || mt.isimaginary()) + if (mt.isReal() || mt.isImaginary()) return new RealExp(loc, r, mt); else { @@ -3592,7 +3872,7 @@ private void resolveExp(Expression exp, out Type t, out Expression e, out Dsymbo * ps = is set if t is a symbol * intypeid = true if in type id */ -void resolve(Type mt, const ref Loc loc, Scope* sc, out Expression pe, out Type pt, out Dsymbol ps, bool intypeid = false) +void resolve(Type mt, Loc loc, Scope* sc, out Expression pe, out Type pt, out Dsymbol ps, bool intypeid = false) { void returnExp(Expression e) { @@ -3899,7 +4179,7 @@ void resolve(Type mt, const ref Loc loc, Scope* sc, out Expression pe, out Type // compile time sequences are valid types !mt.exp.type.isTypeTuple()) { - if (!(sc.flags & SCOPE.Cfile) && // in (extended) C typeof may be used on types as with sizeof + if (!sc.inCfile && // in (extended) C typeof may be used on types as with sizeof mt.exp.checkType()) goto Lerr; @@ -4228,6 +4508,10 @@ void resolve(Type mt, const ref Loc loc, Scope* sc, out Expression pe, out Type */ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag flag) { + enum LOGDOTEXP = false; + if (LOGDOTEXP) + printf("dotExp()\n"); + Expression visitType(Type mt) { VarDeclaration v = null; @@ -4494,7 +4778,7 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag { if (e.op == EXP.type) { - error(e.loc, "`%s` is not an expression", e.toChars()); + error(e.loc, "`%s` is not an expression", e.toErrMsg()); return ErrorExp.get(); } else if (mt.dim.toUInteger() < 1 && checkUnsafeDotExp(sc, e, ident, flag)) @@ -4543,7 +4827,7 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag } if (e.op == EXP.type && (ident == Id.length || ident == Id.ptr)) { - error(e.loc, "`%s` is not an expression", e.toChars()); + error(e.loc, "`%s` is not an expression", e.toErrMsg()); return ErrorExp.get(); } if (ident == Id.length) @@ -4593,8 +4877,8 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag fd_aaLen = FuncDeclaration.genCfunc(fparams, Type.tsize_t, Id.aaLen); TypeFunction tf = fd_aaLen.type.toTypeFunction(); tf.purity = PURE.const_; - tf.isnothrow = true; - tf.isnogc = false; + tf.isNothrow = true; + tf.isNogc = false; } Expression ev = new VarExp(e.loc, fd_aaLen, false); e = new CallExp(e.loc, ev, e); @@ -4646,7 +4930,7 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag /*************************************** * `ident` was not found as a member of `mt`. - * Attempt to use overloaded opDot(), overloaded opDispatch(), or `alias this`. + * Attempt to use overloaded opDispatch() or `alias this`. * If that fails, forward to visitType(). * Params: * mt = class or struct @@ -4702,21 +4986,6 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag ident != Id.postblit && ident != Id.__xpostblit) { - /* Look for overloaded opDot() to see if we should forward request - * to it. - */ - if (auto fd = search_function(sym, Id.opDot)) - { - /* Rewrite e.ident as: - * e.opDot().ident - */ - e = build_overload(e.loc, sc, e, null, fd); - // @@@DEPRECATED_2.110@@@. - // Deprecated in 2.082, made an error in 2.100. - error(e.loc, "`opDot` is obsolete. Use `alias this`"); - return ErrorExp.get(); - } - /* Look for overloaded opDispatch to see if we should forward request * to it. */ @@ -4740,7 +5009,7 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag * e.g. * template opDispatch(name) if (isValid!name) { ... } */ - uint errors = gagError ? global.startGagging() : 0; + const errors = gagError ? global.startGagging() : 0; e = dti.dotTemplateSemanticProp(sc, DotExpFlag.none); if (gagError && global.endGagging(errors)) e = null; @@ -4758,7 +5027,7 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag */ auto die = new DotIdExp(e.loc, alias_e, ident); - auto errors = gagError ? 0 : global.startGagging(); + const errors = gagError ? 0 : global.startGagging(); auto exp = die.dotIdSemanticProp(sc, gagError); if (!gagError) { @@ -4788,7 +5057,7 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag assert(e.op != EXP.dot); // https://issues.dlang.org/show_bug.cgi?id=14010 - if (!(sc.flags & SCOPE.Cfile) && ident == Id._mangleof) + if (!sc.inCfile && ident == Id._mangleof) { return mt.getProperty(sc, e.loc, ident, flag & 1); } @@ -4800,7 +5069,7 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag /* Create a TupleExp out of the fields of the struct e: * (e.field0, e.field1, e.field2, ...) */ - e = e.expressionSemantic(sc); // do this before turning on noaccesscheck + e = e.expressionSemantic(sc); // do this before turning on noAccessCheck if (!mt.sym.determineFields()) { @@ -4830,26 +5099,59 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag e = new TupleExp(e.loc, e0, exps); Scope* sc2 = sc.push(); - sc2.flags |= SCOPE.noaccesscheck; + sc2.noAccessCheck = true; e = e.expressionSemantic(sc2); sc2.pop(); return e; } - immutable flags = sc.flags & SCOPE.ignoresymbolvisibility ? SearchOpt.ignoreVisibility : 0; + immutable flags = sc.ignoresymbolvisibility ? SearchOpt.ignoreVisibility : 0; s = mt.sym.search(e.loc, ident, flags | SearchOpt.ignorePrivateImports); L1: if (!s) { return noMember(mt, sc, e, ident, flag); } - if (!(sc.flags & SCOPE.ignoresymbolvisibility) && !symbolIsVisible(sc, s)) + if (!sc.ignoresymbolvisibility && !symbolIsVisible(sc, s)) { return noMember(mt, sc, e, ident, flag); } // check before alias resolution; the alias itself might be deprecated! - if (s.isAliasDeclaration) + if (auto ad = s.isAliasDeclaration) + { s.checkDeprecated(e.loc, sc); + + // Fix for https://github.com/dlang/dmd/issues/20610 + if (ad.originalType) + { + if (auto tid = ad.originalType.isTypeIdentifier()) + { + if (tid.idents.length) + { + static if (0) + { + printf("TypeStruct::dotExp(e = '%s', ident = '%s')\n", e.toChars(), ident.toChars()); + printf("AliasDeclaration: %s\n", ad.toChars()); + if (ad.aliassym) + printf("aliassym: %s\n", ad.aliassym.toChars()); + printf("tid type: %s\n", toChars(tid)); + } + /* Rewrite e.s as e.(tid.ident).(tid.idents) + */ + Expression die = new DotIdExp(e.loc, e, tid.ident); + foreach (id; tid.idents) // maybe use typeToExpressionHelper() + die = new DotIdExp(e.loc, die, cast(Identifier)id); + /* Ambiguous syntax, only way to disambiguate it to try it + */ + die = dmd.expressionsem.trySemantic(die, sc); + if (die && die.isDotVarExp()) // shrink wrap around DotVarExp() + { + return die; + } + } + } + } + } s = s.toAlias(); if (auto em = s.isEnumMember()) @@ -4952,7 +5254,7 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag Declaration d = s.isDeclaration(); if (!d) { - error(e.loc, "`%s.%s` is not a declaration", e.toChars(), ident.toChars()); + error(e.loc, "`%s.%s` is not a declaration", e.toErrMsg(), ident.toChars()); return ErrorExp.get(); } @@ -5080,7 +5382,7 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag /* Create a TupleExp */ - e = e.expressionSemantic(sc); // do this before turning on noaccesscheck + e = e.expressionSemantic(sc); // do this before turning on noAccessCheck mt.sym.size(e.loc); // do semantic of type @@ -5110,13 +5412,13 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag e = new TupleExp(e.loc, e0, exps); Scope* sc2 = sc.push(); - sc2.flags |= SCOPE.noaccesscheck; + sc2.noAccessCheck = true; e = e.expressionSemantic(sc2); sc2.pop(); return e; } - SearchOptFlags flags = sc.flags & SCOPE.ignoresymbolvisibility ? SearchOpt.ignoreVisibility : SearchOpt.all; + SearchOptFlags flags = sc.ignoresymbolvisibility ? SearchOpt.ignoreVisibility : SearchOpt.all; s = mt.sym.search(e.loc, ident, flags | SearchOpt.ignorePrivateImports); L1: @@ -5269,7 +5571,7 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag return noMember(mt, sc, e, ident, flag & 1); } - if (!(sc.flags & SCOPE.ignoresymbolvisibility) && !symbolIsVisible(sc, s)) + if (!sc.ignoresymbolvisibility && !symbolIsVisible(sc, s)) { return noMember(mt, sc, e, ident, flag); } @@ -5385,7 +5687,7 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag Declaration d = s.isDeclaration(); if (!d) { - error(e.loc, "`%s.%s` is not a declaration", e.toChars(), ident.toChars()); + error(e.loc, "`%s.%s` is not a declaration", e.toErrMsg(), ident.toChars()); return ErrorExp.get(); } @@ -5544,6 +5846,74 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag } } +// if initializer is 0 +bool isZeroInit(Type t, Loc loc) +{ + bool visitType(Type _) + { + return false; // assume not + } + + bool visitBasic(TypeBasic t) + { + switch (t.ty) + { + case Tchar: + case Twchar: + case Tdchar: + case Timaginary32: + case Timaginary64: + case Timaginary80: + case Tfloat32: + case Tfloat64: + case Tfloat80: + case Tcomplex32: + case Tcomplex64: + case Tcomplex80: + return false; // no + default: + return true; // yes + } + } + + bool visitVector(TypeVector t) + { + return t.basetype.isZeroInit(loc); + } + + bool visitSArray(TypeSArray t) + { + return t.next.isZeroInit(loc); + } + + bool visitStruct(TypeStruct t) + { + // Determine zeroInit here, as this can be called before semantic2 + t.sym.determineSize(t.sym.loc); + return t.sym.zeroInit; + } + + bool visitEnum(TypeEnum t) + { + return t.sym.getDefaultValue(loc).toBool().hasValue(false); + } + + switch(t.ty) + { + default: return t.isTypeBasic() ? visitBasic(cast(TypeBasic)t) : visitType(t); + case Tvector: return visitVector(t.isTypeVector()); + case Tsarray: return visitSArray(t.isTypeSArray()); + case Taarray: + case Tarray: + case Treference: + case Tdelegate: + case Tclass: + case Tpointer: return true; + case Tstruct: return visitStruct(t.isTypeStruct()); + case Tenum: return visitEnum(t.isTypeEnum()); + } +} + /************************ * Get the default initialization expression for a type. @@ -5555,7 +5925,7 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag * Returns: * The initialization expression for the type. */ -Expression defaultInit(Type mt, const ref Loc loc, const bool isCfile = false) +Expression defaultInit(Type mt, Loc loc, const bool isCfile = false) { Expression visitBasic(TypeBasic mt) { @@ -5772,7 +6142,7 @@ Dsymbol toDsymbol(Type type, Scope* sc) Dsymbol visitIdentifier(TypeIdentifier type) { - //printf("TypeIdentifier::toDsymbol('%s')\n", toChars()); + //printf("TypeIdentifier::toDsymbol('%s')\n", toChars(type)); if (!sc) return null; @@ -5784,7 +6154,6 @@ Dsymbol toDsymbol(Type type, Scope* sc) s = t.toDsymbol(sc); if (e) s = getDsymbol(e); - return s; } @@ -5837,7 +6206,7 @@ Dsymbol toDsymbol(Type type, Scope* sc) /************************************ * Add storage class modifiers to type. */ -Type addStorageClass(Type type, StorageClass stc) +Type addStorageClass(Type type, STC stc) { Type visitType(Type t) { @@ -5863,43 +6232,43 @@ Type addStorageClass(Type type, StorageClass stc) //printf("addStorageClass(%llx) %d\n", stc, (stc & STC.scope_) != 0); TypeFunction t = visitType(tf_src).toTypeFunction(); if ((stc & STC.pure_ && !t.purity) || - (stc & STC.nothrow_ && !t.isnothrow) || - (stc & STC.nogc && !t.isnogc) || + (stc & STC.nothrow_ && !t.isNothrow) || + (stc & STC.nogc && !t.isNogc) || (stc & STC.scope_ && !t.isScopeQual) || (stc & STC.safe && t.trust < TRUST.trusted)) { // Klunky to change these - auto tf = new TypeFunction(t.parameterList, t.next, t.linkage, 0); + auto tf = new TypeFunction(t.parameterList, t.next, t.linkage, STC.none); tf.mod = t.mod; tf.inferenceArguments = tf_src.inferenceArguments; tf.purity = t.purity; - tf.isnothrow = t.isnothrow; - tf.isnogc = t.isnogc; - tf.isproperty = t.isproperty; - tf.isref = t.isref; - tf.isreturn = t.isreturn; - tf.isreturnscope = t.isreturnscope; + tf.isNothrow = t.isNothrow; + tf.isNogc = t.isNogc; + tf.isProperty = t.isProperty; + tf.isRef = t.isRef; + tf.isReturn = t.isReturn; + tf.isReturnScope = t.isReturnScope; tf.isScopeQual = t.isScopeQual; - tf.isreturninferred = t.isreturninferred; - tf.isscopeinferred = t.isscopeinferred; + tf.isReturnInferred = t.isReturnInferred; + tf.isScopeInferred = t.isScopeInferred; tf.trust = t.trust; tf.isInOutParam = t.isInOutParam; tf.isInOutQual = t.isInOutQual; - tf.isctor = t.isctor; + tf.isCtor = t.isCtor; if (stc & STC.pure_) tf.purity = PURE.fwdref; if (stc & STC.nothrow_) - tf.isnothrow = true; + tf.isNothrow = true; if (stc & STC.nogc) - tf.isnogc = true; + tf.isNogc = true; if (stc & STC.safe) tf.trust = TRUST.safe; if (stc & STC.scope_) { tf.isScopeQual = true; if (stc & STC.scopeinferred) - tf.isscopeinferred = true; + tf.isScopeInferred = true; } tf.deco = tf.merge().deco; @@ -5932,7 +6301,7 @@ Type addStorageClass(Type type, StorageClass stc) * Complex!float, Complex!double, Complex!real or null for error */ -Type getComplexLibraryType(const ref Loc loc, Scope* sc, TY ty) +Type getComplexLibraryType(Loc loc, Scope* sc, TY ty) { // singleton __gshared Type complex_float; @@ -6000,7 +6369,7 @@ Type getComplexLibraryType(const ref Loc loc, Scope* sc, TY ty) * Returns: * An enum value of either `Covariant.yes` or a reason it's not covariant. */ -Covariant covariant(Type src, Type t, StorageClass* pstc = null, bool cppCovariant = false) +Covariant covariant(Type src, Type t, STC* pstc = null, bool cppCovariant = false) { version (none) { @@ -6010,8 +6379,8 @@ Covariant covariant(Type src, Type t, StorageClass* pstc = null, bool cppCovaria printf("mod = %x, %x\n", src.mod, t.mod); } if (pstc) - *pstc = 0; - StorageClass stc = 0; + *pstc = STC.none; + STC stc = STC.none; bool notcovariant = false; @@ -6071,7 +6440,7 @@ Covariant covariant(Type src, Type t, StorageClass* pstc = null, bool cppCovaria goto Ldistinct; } Lcov: - notcovariant |= !fparam1.isCovariant(t1.isref, fparam2); + notcovariant |= !fparam1.isCovariant(t1.isRef, fparam2); /* https://issues.dlang.org/show_bug.cgi?id=23135 * extern(C++) mutable parameters are not covariant with const. @@ -6131,7 +6500,7 @@ Covariant covariant(Type src, Type t, StorageClass* pstc = null, bool cppCovaria } else if (t1n.ty == t2n.ty && t1n.implicitConvTo(t2n)) { - if (t1.isref && t2.isref) + if (t1.isRef && t2.isRef) { // Treat like pointers to t1n and t2n if (t1n.constConv(t2n) < MATCH.constant) @@ -6156,31 +6525,31 @@ Covariant covariant(Type src, Type t, StorageClass* pstc = null, bool cppCovaria goto Lnotcovariant; Lcovariant: - if (t1.isref != t2.isref) + if (t1.isRef != t2.isRef) goto Lnotcovariant; - if (!t1.isref && (t1.isScopeQual || t2.isScopeQual)) + if (!t1.isRef && (t1.isScopeQual || t2.isScopeQual)) { - StorageClass stc1 = t1.isScopeQual ? STC.scope_ : 0; - StorageClass stc2 = t2.isScopeQual ? STC.scope_ : 0; - if (t1.isreturn) + STC stc1 = t1.isScopeQual ? STC.scope_ : STC.none; + STC stc2 = t2.isScopeQual ? STC.scope_ : STC.none; + if (t1.isReturn) { stc1 |= STC.return_; if (!t1.isScopeQual) stc1 |= STC.ref_; } - if (t2.isreturn) + if (t2.isReturn) { stc2 |= STC.return_; if (!t2.isScopeQual) stc2 |= STC.ref_; } - if (!Parameter.isCovariantScope(t1.isref, stc1, stc2)) + if (!Parameter.isCovariantScope(t1.isRef, stc1, stc2)) goto Lnotcovariant; } // We can subtract 'return ref' from 'this', but cannot add it - else if (t1.isreturn && !t2.isreturn) + else if (t1.isReturn && !t2.isReturn) goto Lnotcovariant; /* https://issues.dlang.org/show_bug.cgi?id=23135 @@ -6213,10 +6582,10 @@ Lcovariant: if (!t1.purity && t2.purity) stc |= STC.pure_; - if (!t1.isnothrow && t2.isnothrow) + if (!t1.isNothrow && t2.isNothrow) stc |= STC.nothrow_; - if (!t1.isnogc && t2.isnogc) + if (!t1.isNogc && t2.isNogc) stc |= STC.nogc; /* Can convert safe/trusted to system @@ -6260,7 +6629,7 @@ Lnotcovariant: * Returns: * storage class with STC.scope_ or STC.return_ OR'd in */ -StorageClass parameterStorageClass(TypeFunction tf, Type tthis, Parameter p, VarDeclarations* outerVars = null, +STC parameterStorageClass(TypeFunction tf, Type tthis, Parameter p, VarDeclarations* outerVars = null, bool indirect = false) { //printf("parameterStorageClass(p: %s)\n", p.toChars()); @@ -6275,7 +6644,7 @@ StorageClass parameterStorageClass(TypeFunction tf, Type tthis, Parameter p, Var /* If haven't inferred the return type yet, can't infer storage classes */ - if (!tf.nextOf() || !tf.isnothrow()) + if (!tf.nextOf() || !tf.isNothrow()) return stc; tf.purityLevel(); @@ -6355,7 +6724,7 @@ StorageClass parameterStorageClass(TypeFunction tf, Type tthis, Parameter p, Var // Check escaping through return value Type tret = tf.nextOf().toBasetype(); - if (tf.isref || tret.hasPointers()) + if (tf.isRef || tret.hasPointers()) { return stc | STC.scope_ | STC.return_ | STC.returnScope; } @@ -6393,30 +6762,32 @@ Type pointerTo(Type type) { if (type.ty == Terror) return type; - if (!type.pto) + auto mcache = type.getMcache(); + if (!mcache.pto) { Type t = new TypePointer(type); if (type.ty == Tfunction) { t.deco = t.merge().deco; - type.pto = t; + mcache.pto = t; } else - type.pto = t.merge(); + mcache.pto = t.merge(); } - return type.pto; + return mcache.pto; } Type referenceTo(Type type) { if (type.ty == Terror) return type; - if (!type.rto) + auto mcache = type.getMcache(); + if (!mcache.rto) { Type t = new TypeReference(type); - type.rto = t.merge(); + mcache.rto = t.merge(); } - return type.rto; + return mcache.rto; } // Make corresponding static array type without semantic @@ -6434,12 +6805,13 @@ Type arrayOf(Type type) { if (type.ty == Terror) return type; - if (!type.arrayof) + auto mcache = type.getMcache(); + if (!mcache.arrayof) { Type t = new TypeDArray(type); - type.arrayof = t.merge(); + mcache.arrayof = t.merge(); } - return type.arrayof; + return mcache.arrayof; } /******************************** @@ -6691,6 +7063,39 @@ Type sharedWildConstOf(Type type) return t; } +Type nakedOf(Type type) +{ + //printf("Type::nakedOf() %p, %s\n", type, type.toChars()); + if (type.mod == 0) + return type; + if (type.mcache) with(type.mcache) + { + // the cache has the naked type at the "identity" position, try to find it + if (cto && cto.mod == 0) + return cto; + if (ito && ito.mod == 0) + return ito; + if (sto && sto.mod == 0) + return sto; + if (scto && scto.mod == 0) + return scto; + if (wto && wto.mod == 0) + return wto; + if (wcto && wcto.mod == 0) + return wcto; + if (swto && swto.mod == 0) + return swto; + if (swcto && swcto.mod == 0) + return swcto; + } + Type t = type.nullAttributes(); + t.mod = 0; + t = t.merge(); + t.fixTo(type); + //printf("\t%p %s\n", t, t.toChars()); + return t; +} + Type unqualify(Type type, uint m) { Type t = type.mutableOf().unSharedOf(); @@ -6952,21 +7357,21 @@ Type substWildTo(Type type, uint mod) // Similar to TypeFunction.syntaxCopy; auto t = new TypeFunction(ParameterList(params, tf.parameterList.varargs), tret, tf.linkage); t.mod = ((tf.mod & MODFlags.wild) ? (tf.mod & ~MODFlags.wild) | MODFlags.const_ : tf.mod); - t.isnothrow = tf.isnothrow; - t.isnogc = tf.isnogc; + t.isNothrow = tf.isNothrow; + t.isNogc = tf.isNogc; t.purity = tf.purity; - t.isproperty = tf.isproperty; - t.isref = tf.isref; - t.isreturn = tf.isreturn; - t.isreturnscope = tf.isreturnscope; + t.isProperty = tf.isProperty; + t.isRef = tf.isRef; + t.isReturn = tf.isReturn; + t.isReturnScope = tf.isReturnScope; t.isScopeQual = tf.isScopeQual; - t.isreturninferred = tf.isreturninferred; - t.isscopeinferred = tf.isscopeinferred; + t.isReturnInferred = tf.isReturnInferred; + t.isScopeInferred = tf.isScopeInferred; t.isInOutParam = false; t.isInOutQual = false; t.trust = tf.trust; t.inferenceArguments = tf.inferenceArguments; - t.isctor = tf.isctor; + t.isCtor = tf.isCtor; return t.merge(); } @@ -7133,11 +7538,173 @@ bool isRecursiveAliasThis(ref Type att, Type t) auto tb = t.toBasetype(); if (att && tb.equivalent(att)) return true; - else if (!att && tb.checkAliasThisRec()) + if (!att && tb.checkAliasThisRec()) att = tb; return false; } +MATCH implicitConvToWithoutAliasThis(TypeStruct from, Type to) +{ + //printf("TypeStruct::implicitConvToWithoutAliasThis(%s => %s)\n", toChars(), to.toChars()); + + auto tos = to.isTypeStruct(); + if (!(tos && from.sym == tos.sym)) + return MATCH.nomatch; + + if (from.mod == to.mod) + return MATCH.exact; + + if (MODimplicitConv(from.mod, to.mod)) + return MATCH.constant; + + /* Check all the fields. If they can all be converted, + * allow the conversion. + */ + MATCH m = MATCH.constant; + uint offset = ~0; // must never match a field offset + foreach (v; from.sym.fields[]) + { + /* Why are we only looking at the first member of a union? + * The check should check for overlap of v with the previous field, + * not just starting at the same point + */ + if (!global.params.fixImmutableConv && v.offset == offset) // v is at same offset as previous field + continue; // ignore + + Type tvf = v.type.addMod(from.mod); // from type + Type tvt = v.type.addMod(to.mod); // to type + + // field match + MATCH mf = tvf.implicitConvTo(tvt); + //printf("\t%s => %s, match = %d\n", v.type.toChars(), tvt.toChars(), mf); + + if (mf == MATCH.nomatch) + return MATCH.nomatch; + if (mf < m) // if field match is worse + m = mf; + offset = v.offset; + } + return m; +} + +MATCH implicitConvToWithoutAliasThis(TypeClass from, Type to) +{ + ClassDeclaration cdto = to.isClassHandle(); + MATCH m = constConv(from, to); + if (m > MATCH.nomatch) + return m; + + if (cdto && cdto.isBaseOf(from.sym, null) && MODimplicitConv(from.mod, to.mod)) + { + //printf("'to' is base\n"); + return MATCH.convert; + } + return MATCH.nomatch; +} + +MATCH implicitConvToThroughAliasThis(TypeClass from, Type to) +{ + MATCH m; + if (from.sym.aliasthis && !(from.att & AliasThisRec.tracing)) + { + if (auto ato = aliasthisOf(from)) + { + from.att = cast(AliasThisRec)(from.att | AliasThisRec.tracing); + m = ato.implicitConvTo(to); + from.att = cast(AliasThisRec)(from.att & ~AliasThisRec.tracing); + } + } + return m; +} + +MATCH implicitConvToThroughAliasThis(TypeStruct from, Type to) +{ + auto tos = to.isTypeStruct(); + if (!(tos && from.sym == tos.sym) && + from.sym.aliasthis && + !(from.att & AliasThisRec.tracing)) + { + if (auto ato = aliasthisOf(from)) + { + from.att = cast(AliasThisRec)(from.att | AliasThisRec.tracing); + MATCH m = ato.implicitConvTo(to); + from.att = cast(AliasThisRec)(from.att & ~AliasThisRec.tracing); + return m; + } + } + return MATCH.nomatch; +} + +/******************************************* + * Compute number of elements for a (possibly multidimensional) static array, + * or 1 for other types. + * Params: + * t = static array type + * loc = for error message + * Returns: + * number of elements, uint.max on overflow + */ +uint numberOfElems(Type t, Loc loc) +{ + //printf("Type::numberOfElems()\n"); + uinteger_t n = 1; + Type tb = t; + while ((tb = tb.toBasetype()).ty == Tsarray) + { + bool overflow = false; + n = mulu(n, (cast(TypeSArray)tb).dim.toUInteger(), overflow); + if (overflow || n >= uint.max) + { + error(loc, "static array `%s` size overflowed to %llu", t.toChars(), cast(ulong)n); + return uint.max; + } + tb = (cast(TypeSArray)tb).next; + } + return cast(uint)n; +} + +bool checkRetType(TypeFunction tf, Loc loc) +{ + Type tb = tf.next.toBasetype(); + if (tb.ty == Tfunction) + { + error(loc, "functions cannot return a function"); + tf.next = Type.terror; + } + if (tb.ty == Ttuple) + { + error(loc, "functions cannot return a sequence (use `std.typecons.Tuple`)"); + tf.next = Type.terror; + } + if (!tf.isRef && (tb.ty == Tstruct || tb.ty == Tsarray)) + { + if (auto ts = tb.baseElemOf().isTypeStruct()) + { + if (!ts.sym.members) + { + error(loc, "functions cannot return opaque type `%s` by value", tb.toChars()); + tf.next = Type.terror; + } + } + } + if (tb.ty == Terror) + return true; + return false; +} + +/// Returns: whether `t` is a struct/class/enum without a body +bool isOpaqueType(Type t) +{ + if (auto te = t.isTypeEnum()) + return te.sym.members is null; + if (auto ts = t.isTypeStruct()) + return ts.sym.members is null; + if (auto tc = t.isTypeClass()) + return tc.sym.members is null; + return false; +} + + /******************************* Private *****************************************/ private: @@ -7259,8 +7826,7 @@ Type stripDefaultArgs(Type t) { foreach (i, p; *parameters) { - Parameter ps = stripParameter(p); - if (ps) + if (Parameter ps = stripParameter(p)) { // Replace params with a copy we can modify Parameters* nparams = new Parameters(parameters.length); @@ -7337,11 +7903,11 @@ Type stripDefaultArgs(Type t) * Returns: * corresponding value of .max/.min */ -Expression getMaxMinValue(EnumDeclaration ed, const ref Loc loc, Identifier id) +Expression getMaxMinValue(EnumDeclaration ed, Loc loc, Identifier id) { //printf("EnumDeclaration::getMaxValue()\n"); - static Expression pvalToResult(Expression e, const ref Loc loc) + static Expression pvalToResult(Expression e, Loc loc) { if (e.op != EXP.error) { @@ -7376,7 +7942,7 @@ Expression getMaxMinValue(EnumDeclaration ed, const ref Loc loc, Identifier id) .error(loc, "%s `%s` is opaque and has no `.%s`", ed.kind, ed.toPrettyChars, id.toChars(), id.toChars()); return errorReturn(); } - if (!(ed.memtype && ed.memtype.isintegral())) + if (!(ed.memtype && ed.memtype.isIntegral())) { .error(loc, "%s `%s` has no `.%s` property because base type `%s` is not an integral type", ed.kind, ed.toPrettyChars, id.toChars(), id.toChars(), ed.memtype ? ed.memtype.toChars() : ""); @@ -7448,7 +8014,7 @@ Expression getMaxMinValue(EnumDeclaration ed, const ref Loc loc, Identifier id) * Return: * null if error, else RootObject AST as parsed */ -RootObject compileTypeMixin(TypeMixin tm, ref const Loc loc, Scope* sc) +RootObject compileTypeMixin(TypeMixin tm, Loc loc, Scope* sc) { OutBuffer buf; if (expressionsToString(buf, sc, tm.exps, tm.loc, null, true)) @@ -7459,9 +8025,9 @@ RootObject compileTypeMixin(TypeMixin tm, ref const Loc loc, Scope* sc) buf.writeByte(0); const str = buf.extractSlice()[0 .. len]; const bool doUnittests = global.params.parsingUnittestsRequired(); - auto locm = adjustLocForMixin(str, loc, global.params.mixinOut); - scope p = new Parser!ASTCodegen(locm, sc._module, str, false, global.errorSink, &global.compileEnv, doUnittests); - p.transitionIn = global.params.v.vin; + scope p = new Parser!ASTCodegen(sc._module, str, false, global.errorSink, &global.compileEnv, doUnittests); + adjustLocForMixin(str, loc, *p.baseLoc, global.params.mixinOut); + p.linnum = p.baseLoc.startLine; p.nextToken(); //printf("p.loc.linnum = %d\n", p.loc.linnum); diff --git a/dmd/typinf.d b/dmd/typinf.d index 7666d356b6..43ec5717fd 100644 --- a/dmd/typinf.d +++ b/dmd/typinf.d @@ -1,12 +1,12 @@ /** * Generate `TypeInfo` objects, which are needed for run-time introspection of types. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/typinf.d, _typinf.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/typinf.d, _typinf.d) * Documentation: https://dlang.org/phobos/dmd_typinf.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/typinf.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/typinf.d */ module dmd.typinf; @@ -22,6 +22,7 @@ import dmd.expression; import dmd.globals; import dmd.location; import dmd.mtype; +import dmd.typesem; import core.stdc.stdio; /**************************************************** @@ -35,14 +36,14 @@ import core.stdc.stdio; * Returns: * true if `TypeInfo` was generated and needs compiling to object file */ -bool genTypeInfo(Expression e, const ref Loc loc, Type torig, Scope* sc) +bool genTypeInfo(Expression e, Loc loc, Type torig, Scope* sc) { // printf("genTypeInfo() %s\n", torig.toChars()); // Even when compiling without `useTypeInfo` (e.g. -betterC) we should // still be able to evaluate `TypeInfo` at compile-time, just not at runtime. // https://issues.dlang.org/show_bug.cgi?id=18472 - if (!sc || !(sc.flags & SCOPE.ctfe)) + if (!sc || !sc.ctfe) { if (!global.params.useTypeInfo) { @@ -67,6 +68,9 @@ bool genTypeInfo(Expression e, const ref Loc loc, Type torig, Scope* sc) import dmd.typesem : merge2; Type t = torig.merge2(); // do this since not all Type's are merge'd + if (t.ty == Taarray) + t = makeNakedAssociativeArray(cast(TypeAArray)t); + bool needsCodegen = false; if (!t.vtinfo) { @@ -79,7 +83,7 @@ bool genTypeInfo(Expression e, const ref Loc loc, Type torig, Scope* sc) else if (t.isWild()) t.vtinfo = TypeInfoWildDeclaration.create(t); else - t.vtinfo = getTypeInfoDeclaration(t); + t.vtinfo = getTypeInfoDeclaration(t, sc); assert(t.vtinfo); version (IN_LLVM) @@ -110,7 +114,7 @@ else * Returns: * The type of the `TypeInfo` object associated with `t` */ -extern (C++) Type getTypeInfoType(const ref Loc loc, Type t, Scope* sc) +extern (C++) Type getTypeInfoType(Loc loc, Type t, Scope* sc) { assert(t.ty != Terror); if (genTypeInfo(null, loc, t, sc)) @@ -122,7 +126,7 @@ extern (C++) Type getTypeInfoType(const ref Loc loc, Type t, Scope* sc) return t.vtinfo.type; } -private TypeInfoDeclaration getTypeInfoDeclaration(Type t) +private TypeInfoDeclaration getTypeInfoDeclaration(Type t, Scope* sc) { //printf("Type::getTypeInfoDeclaration() %s\n", t.toChars()); switch (t.ty) @@ -134,7 +138,7 @@ private TypeInfoDeclaration getTypeInfoDeclaration(Type t) case Tsarray: return TypeInfoStaticArrayDeclaration.create(t); case Taarray: - return TypeInfoAssociativeArrayDeclaration.create(t); + return getTypeInfoAssocArrayDeclaration(cast(TypeAArray)t, sc); case Tstruct: return TypeInfoStructDeclaration.create(t); case Tvector: @@ -158,6 +162,73 @@ private TypeInfoDeclaration getTypeInfoDeclaration(Type t) } } +/****************************************** + * Instantiate TypeInfoAssociativeArrayDeclaration and fill + * the entry with TypeInfo_AssociativeArray.Entry!(t.index, t.next) + * + * Params: + * t = TypeAArray to generate TypeInfo_AssociativeArray for + * sc = context + * Returns: + * a TypeInfoAssociativeArrayDeclaration with field entry initialized + */ +TypeInfoDeclaration getTypeInfoAssocArrayDeclaration(TypeAArray t, Scope* sc) +{ + import dmd.arraytypes; + import dmd.expressionsem; + import dmd.id; + + assert(sc); // must not be called in the code generation phase + + auto ti = TypeInfoAssociativeArrayDeclaration.create(t); + t.vtinfo = ti; // assign it early to avoid recursion in expressionSemantic + Loc loc = t.loc; + auto tiargs = new Objects(); + tiargs.push(t.index); // always called with naked types + tiargs.push(t.next); + + Expression id = new IdentifierExp(loc, Id.empty); + id = new DotIdExp(loc, id, Id.object); + id = new DotIdExp(loc, id, Id.TypeInfo_AssociativeArray); + auto tempinst = new DotTemplateInstanceExp(loc, id, Id.Entry, tiargs); + auto e = expressionSemantic(tempinst, sc); + assert(e.type); + ti.entry = e.type; + if (auto ts = ti.entry.isTypeStruct()) + { +version (IN_LLVM) {} else +{ + ts.sym.requestTypeInfo = true; +} + if (auto tmpl = ts.sym.isInstantiated()) + tmpl.minst = sc._module.importedFrom; // ensure it get's emitted + } + getTypeInfoType(loc, ti.entry, sc); + assert(ti.entry.vtinfo); + + return ti; +} + +/****************************************** + * Find or create a TypeAArray with index and next without + * any head modifiers, tail `inout` is replaced with `const` + * + * Params: + * t = TypeAArray to convert + * Returns: + * t = found type + */ +Type makeNakedAssociativeArray(TypeAArray t) +{ + Type tindex = t.index.toBasetype().nakedOf().substWildTo(MODFlags.const_); + Type tnext = t.next.toBasetype().nakedOf().substWildTo(MODFlags.const_); + if (tindex == t.index && tnext == t.next) + return t; + + t = new TypeAArray(tnext, tindex); + return t.merge(); +} + version (IN_LLVM) { // LDC handles TypeInfo emission in the codegen layer diff --git a/dmd/typinf.h b/dmd/typinf.h index 6414ecdd36..c34494da2f 100644 --- a/dmd/typinf.h +++ b/dmd/typinf.h @@ -1,6 +1,6 @@ /* Compiler implementation of the D programming language - * Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * written by Walter Bright * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. @@ -14,12 +14,16 @@ class Expression; class Type; +class TypeAArray; +class TypeInfoDeclaration; struct Scope; namespace dmd { - bool genTypeInfo(Expression *e, const Loc &loc, Type *torig, Scope *sc); + bool genTypeInfo(Expression *e, Loc loc, Type *torig, Scope *sc); bool isSpeculativeType(Type *t); bool builtinTypeInfo(Type *t); + Type *makeNakedAssociativeArray(TypeAArray *t); + TypeInfoDeclaration *getTypeInfoAssocArrayDeclaration(TypeAArray *t, Scope *sc); } -Type *getTypeInfoType(const Loc &loc, Type *t, Scope *sc); +Type *getTypeInfoType(Loc loc, Type *t, Scope *sc); diff --git a/dmd/utils.d b/dmd/utils.d index 9228ba69b1..bfa197aca3 100644 --- a/dmd/utils.d +++ b/dmd/utils.d @@ -1,12 +1,12 @@ /** * This module defines some utility functions for DMD. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/utils.d, _utils.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/utils.d, _utils.d) * Documentation: https://dlang.org/phobos/dmd_utils.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/utils.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/utils.d */ module dmd.utils; @@ -124,7 +124,7 @@ bool ensurePathToNameExists(Loc loc, const(char)[] name) * buf = Buffer to write the escaped path to * fname = Path to escape */ -void escapePath(OutBuffer* buf, const(char)* fname) +void escapePath(OutBuffer* buf, const(char)* fname) pure { while (1) { @@ -145,78 +145,6 @@ void escapePath(OutBuffer* buf, const(char)* fname) } } -/** - * Takes a path, and make it compatible with GNU Makefile format. - * - * GNU make uses a weird quoting scheme for white space. - * A space or tab preceded by 2N+1 backslashes represents N backslashes followed by space; - * a space or tab preceded by 2N backslashes represents N backslashes at the end of a file name; - * and backslashes in other contexts should not be doubled. - * - * Params: - * buf = Buffer to write the escaped path to - * fname = Path to escape - */ -void writeEscapedMakePath(ref OutBuffer buf, const(char)* fname) -{ - uint slashes; - - while (*fname) - { - switch (*fname) - { - case '\\': - slashes++; - break; - case '$': - buf.writeByte('$'); - goto default; - case ' ': - case '\t': - while (slashes--) - buf.writeByte('\\'); - goto case; - case '#': - buf.writeByte('\\'); - goto default; - case ':': - // ':' not escaped on Windows because it can - // create problems with absolute paths (e.g. C:\Project) - version (Windows) {} - else - { - buf.writeByte('\\'); - } - goto default; - default: - slashes = 0; - break; - } - - buf.writeByte(*fname); - fname++; - } -} - -/// -unittest -{ - version (Windows) - { - enum input = `C:\My Project\file#4$.ext`; - enum expected = `C:\My\ Project\file\#4$$.ext`; - } - else - { - enum input = `/foo\bar/weird$.:name#\ with spaces.ext`; - enum expected = `/foo\bar/weird$$.\:name\#\\\ with\ spaces.ext`; - } - - OutBuffer buf; - buf.writeEscapedMakePath(input); - assert(buf[] == expected); -} - /** * Convert string to integer. * @@ -309,7 +237,7 @@ bool parseDigits(T)(ref T val, const(char)[] p, const T max = T.max) * size = 1 for ubyte[], 2 for ushort[], 4 for uint[], 8 for ulong[] * Returns: copy of `data`, with bytes shuffled if compiled for `version(LittleEndian)` */ -ubyte[] arrayCastBigEndian(const ubyte[] data, size_t size) +ubyte[] arrayCastBigEndian(const ubyte[] data, size_t size) @safe { ubyte[] impl(T)() { diff --git a/dmd/version.h b/dmd/version.h index dd83fd67df..1200b2e5ee 100644 --- a/dmd/version.h +++ b/dmd/version.h @@ -1,6 +1,6 @@ /* Compiler implementation of the D programming language - * Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * written by Walter Bright * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. @@ -15,25 +15,17 @@ class DebugSymbol final : public Dsymbol { public: - unsigned level; - DebugSymbol *syntaxCopy(Dsymbol *) override; - const char *toChars() const override; const char *kind() const override; - DebugSymbol *isDebugSymbol() override; void accept(Visitor *v) override { v->visit(this); } }; class VersionSymbol final : public Dsymbol { public: - unsigned level; - VersionSymbol *syntaxCopy(Dsymbol *) override; - const char *toChars() const override; const char *kind() const override; - VersionSymbol *isVersionSymbol() override; void accept(Visitor *v) override { v->visit(this); } }; diff --git a/dmd/visitor.h b/dmd/visitor.h index ab5cba6f7d..94fd29402f 100644 --- a/dmd/visitor.h +++ b/dmd/visitor.h @@ -1,6 +1,6 @@ /* Compiler implementation of the D programming language - * Copyright (C) 2013-2024 by The D Language Foundation, All Rights Reserved + * Copyright (C) 2013-2025 by The D Language Foundation, All Rights Reserved * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. * https://www.boost.org/LICENSE_1_0.txt diff --git a/dmd/foreachvar.d b/dmd/visitor/foreachvar.d similarity index 96% rename from dmd/foreachvar.d rename to dmd/visitor/foreachvar.d index 53b3c041d1..80611d6f26 100644 --- a/dmd/foreachvar.d +++ b/dmd/visitor/foreachvar.d @@ -1,15 +1,15 @@ /** * Utility to visit every variable in an expression. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/foreachvar.d, _foreachvar.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/visitor/foreachvar.d, _foreachvar.d) * Documentation: https://dlang.org/phobos/dmd_foreachvar.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/foreachvar.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/visitor/foreachvar.d */ -module dmd.foreachvar; +module dmd.visitor.foreachvar; import core.stdc.stdio; import core.stdc.stdlib; @@ -24,7 +24,6 @@ import dmd.dstruct; import dmd.dsymbol; import dmd.dsymbolsem; import dmd.dtemplate; -import dmd.errors; import dmd.expression; import dmd.func; import dmd.id; @@ -32,13 +31,13 @@ import dmd.identifier; import dmd.init; import dmd.initsem; import dmd.mtype; -import dmd.postordervisitor; import dmd.printast; import dmd.root.array; import dmd.rootobject; import dmd.statement; import dmd.tokens; import dmd.visitor; +import dmd.visitor.postorder; /********************************************* * Visit each Expression in e, and call dgVar() on each variable declared in it. diff --git a/dmd/visitor.d b/dmd/visitor/package.d similarity index 95% rename from dmd/visitor.d rename to dmd/visitor/package.d index 9082909ba2..50b5a54520 100644 --- a/dmd/visitor.d +++ b/dmd/visitor/package.d @@ -1,23 +1,21 @@ /** * Provides a visitor class visiting all AST nodes present in the compiler. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/visitor.d, _visitor.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/visitor/package.d, _visitor.d) * Documentation: https://dlang.org/phobos/dmd_visitor.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/visitor.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/visitor/package.d */ module dmd.visitor; import dmd.astcodegen; -import dmd.astenums; -import dmd.parsetimevisitor; import dmd.tokens; -import dmd.transitivevisitor; -import dmd.expression; import dmd.rootobject; +import dmd.visitor.parsetime; +import dmd.visitor.transitive; /** * Classic Visitor class which implements visit methods for all the AST @@ -151,10 +149,11 @@ extern (C++) class SemanticTimeTransitiveVisitor : SemanticTimePermissiveVisitor { // CTFE can generate struct literals that contain an AddrExp pointing to themselves, // need to avoid infinite recursion. - if (!(e.stageflags & stageToCBuffer)) + alias flag = ASTCodegen.StructLiteralExp.StageFlags.toCBuffer; + if (!(e.stageflags & flag)) { const old = e.stageflags; - e.stageflags |= stageToCBuffer; + e.stageflags |= flag; foreach (el; *e.elements) if (el) el.accept(this); @@ -250,7 +249,7 @@ extern (C++) class SemanticTimeTransitiveVisitor : SemanticTimePermissiveVisitor override void visit(ASTCodegen.LoweredAssignExp e) { e.lowering.accept(this); - visit(cast(AssignExp)e); + visit(cast(ASTCodegen.AssignExp)e); } } diff --git a/dmd/parsetimevisitor.d b/dmd/visitor/parsetime.d similarity index 99% rename from dmd/parsetimevisitor.d rename to dmd/visitor/parsetime.d index c03f78d4de..914ca413c7 100644 --- a/dmd/parsetimevisitor.d +++ b/dmd/visitor/parsetime.d @@ -7,7 +7,7 @@ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/parsetimevisitor.d */ -module dmd.parsetimevisitor; +module dmd.visitor.parsetime; /** Basic and dumm visitor which implements a visit method for each AST node * implemented in AST. This visitor is the parent of strict, transitive diff --git a/dmd/permissivevisitor.d b/dmd/visitor/permissive.d similarity index 93% rename from dmd/permissivevisitor.d rename to dmd/visitor/permissive.d index 5d7f3fcba2..ef1f279002 100644 --- a/dmd/permissivevisitor.d +++ b/dmd/visitor/permissive.d @@ -5,9 +5,9 @@ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/permissivevisitor.d */ -module dmd.permissivevisitor; +module dmd.visitor.permissive; -import dmd.parsetimevisitor; +import dmd.visitor.parsetime; /** PermissiveVisitor overrides all the visit methods in the parent class * that assert(0) in order to facilitate the traversal of subsets of the AST. diff --git a/dmd/visitor/postorder.d b/dmd/visitor/postorder.d new file mode 100644 index 0000000000..22549da45d --- /dev/null +++ b/dmd/visitor/postorder.d @@ -0,0 +1,318 @@ +/** + * A depth-first visitor for expressions and statements. + * + * 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/visitor/postorder.d, _apply.d) + * Documentation: https://dlang.org/phobos/dmd_apply.html + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/visitor/postorder.d + */ + +module dmd.visitor.postorder; + +import dmd.dtemplate; +import dmd.expression; +import dmd.root.array; +import dmd.statement; +import dmd.visitor; + +bool walkPostorder(Expression e, StoppableVisitor v) +{ + scope PostorderExpressionVisitor pv = new PostorderExpressionVisitor(v); + e.accept(pv); + return v.stop; +} + +bool walkPostorder(Statement s, StoppableVisitor v) +{ + scope PostorderStatementVisitor pv = new PostorderStatementVisitor(v); + s.accept(pv); + return v.stop; +} + +private: +/************************************** + * An Expression tree walker that will visit each Expression e in the tree, + * in depth-first evaluation order, and call fp(e,param) on it. + * fp() signals whether the walking continues with its return value: + * Returns: + * 0 continue + * 1 done + * It's a bit slower than using virtual functions, but more encapsulated and less brittle. + * Creating an iterator for this would be much more complex. + */ +extern (C++) final class PostorderExpressionVisitor : StoppableVisitor +{ + alias visit = typeof(super).visit; +public: + StoppableVisitor v; + + extern (D) this(StoppableVisitor v) scope @safe + { + this.v = v; + } + + bool doCond(Expression e) + { + if (!stop && e) + e.accept(this); + return stop; + } + + extern(D) bool doCond(Expression[] e) + { + for (size_t i = 0; i < e.length && !stop; i++) + doCond(e[i]); + return stop; + } + + bool applyTo(Expression e) + { + e.accept(v); + stop = v.stop; + return true; + } + + override void visit(Expression e) + { + applyTo(e); + } + + override void visit(NewExp e) + { + //printf("NewExp::apply(): %s\n", toChars()); + doCond(e.placement) || doCond(e.thisexp) || doCond(e.arguments.peekSlice()) || applyTo(e); + } + + override void visit(NewAnonClassExp e) + { + //printf("NewAnonClassExp::apply(): %s\n", toChars()); + doCond(e.placement) || doCond(e.thisexp) || doCond(e.arguments.peekSlice()) || applyTo(e); + } + + override void visit(TypeidExp e) + { + doCond(isExpression(e.obj)) || applyTo(e); + } + + override void visit(UnaExp e) + { + doCond(e.e1) || applyTo(e); + } + + override void visit(BinExp e) + { + doCond(e.e1) || doCond(e.e2) || applyTo(e); + } + + override void visit(AssertExp e) + { + //printf("CallExp::apply(apply_fp_t fp, void *param): %s\n", toChars()); + doCond(e.e1) || doCond(e.msg) || applyTo(e); + } + + override void visit(CallExp e) + { + //printf("CallExp::apply(apply_fp_t fp, void *param): %s\n", toChars()); + doCond(e.e1) || doCond(e.arguments.peekSlice()) || applyTo(e); + } + + override void visit(ArrayExp e) + { + //printf("ArrayExp::apply(apply_fp_t fp, void *param): %s\n", toChars()); + doCond(e.e1) || doCond(e.arguments.peekSlice()) || applyTo(e); + } + + override void visit(SliceExp e) + { + doCond(e.e1) || doCond(e.lwr) || doCond(e.upr) || applyTo(e); + } + + override void visit(ArrayLiteralExp e) + { + doCond(e.basis) || doCond(e.elements.peekSlice()) || applyTo(e); + } + + override void visit(AssocArrayLiteralExp e) + { + doCond(e.keys.peekSlice()) || doCond(e.values.peekSlice()) || applyTo(e); + } + + override void visit(StructLiteralExp e) + { + if (e.stageflags & StructLiteralExp.StageFlags.apply) + return; + const old = e.stageflags; + e.stageflags |= StructLiteralExp.StageFlags.apply; + doCond(e.elements.peekSlice()) || applyTo(e); + e.stageflags = old; + } + + override void visit(TupleExp e) + { + doCond(e.e0) || doCond(e.exps.peekSlice()) || applyTo(e); + } + + override void visit(CondExp e) + { + doCond(e.econd) || doCond(e.e1) || doCond(e.e2) || applyTo(e); + } +} + +/************************************** + * A Statement tree walker that will visit each Statement s in the tree, + * in depth-first evaluation order, and call fp(s,param) on it. + * fp() signals whether the walking continues with its return value: + * Returns: + * 0 continue + * 1 done + * It's a bit slower than using virtual functions, but more encapsulated and less brittle. + * Creating an iterator for this would be much more complex. + */ +extern (C++) final class PostorderStatementVisitor : StoppableVisitor +{ + alias visit = typeof(super).visit; +public: + StoppableVisitor v; + + extern (D) this(StoppableVisitor v) scope @safe + { + this.v = v; + } + + bool doCond(Statement s) + { + if (!stop && s) + s.accept(this); + return stop; + } + + bool applyTo(Statement s) + { + s.accept(v); + stop = v.stop; + return true; + } + + override void visit(Statement s) + { + applyTo(s); + } + + override void visit(PeelStatement s) + { + doCond(s.s) || applyTo(s); + } + + override void visit(CompoundStatement s) + { + for (size_t i = 0; i < s.statements.length; i++) + if (doCond((*s.statements)[i])) + return; + applyTo(s); + } + + override void visit(UnrolledLoopStatement s) + { + for (size_t i = 0; i < s.statements.length; i++) + if (doCond((*s.statements)[i])) + return; + applyTo(s); + } + + override void visit(ScopeStatement s) + { + doCond(s.statement) || applyTo(s); + } + + override void visit(WhileStatement s) + { + doCond(s._body) || applyTo(s); + } + + override void visit(DoStatement s) + { + doCond(s._body) || applyTo(s); + } + + override void visit(ForStatement s) + { + doCond(s._init) || doCond(s._body) || applyTo(s); + } + + override void visit(ForeachStatement s) + { + doCond(s._body) || applyTo(s); + } + + override void visit(ForeachRangeStatement s) + { + doCond(s._body) || applyTo(s); + } + + override void visit(IfStatement s) + { + doCond(s.ifbody) || doCond(s.elsebody) || applyTo(s); + } + + override void visit(PragmaStatement s) + { + doCond(s._body) || applyTo(s); + } + + override void visit(SwitchStatement s) + { + doCond(s._body) || applyTo(s); + } + + override void visit(CaseStatement s) + { + doCond(s.statement) || applyTo(s); + } + + override void visit(DefaultStatement s) + { + doCond(s.statement) || applyTo(s); + } + + override void visit(SynchronizedStatement s) + { + doCond(s._body) || applyTo(s); + } + + override void visit(WithStatement s) + { + doCond(s._body) || applyTo(s); + } + + override void visit(TryCatchStatement s) + { + if (doCond(s._body)) + return; + for (size_t i = 0; i < s.catches.length; i++) + if (doCond((*s.catches)[i].handler)) + return; + applyTo(s); + } + + override void visit(TryFinallyStatement s) + { + doCond(s._body) || doCond(s.finalbody) || applyTo(s); + } + + override void visit(ScopeGuardStatement s) + { + doCond(s.statement) || applyTo(s); + } + + override void visit(DebugStatement s) + { + doCond(s.statement) || applyTo(s); + } + + override void visit(LabelStatement s) + { + doCond(s.statement) || applyTo(s); + } +} diff --git a/dmd/statement_rewrite_walker.d b/dmd/visitor/statement_rewrite_walker.d similarity index 93% rename from dmd/statement_rewrite_walker.d rename to dmd/visitor/statement_rewrite_walker.d index 221c5021c5..25e4c736bb 100644 --- a/dmd/statement_rewrite_walker.d +++ b/dmd/visitor/statement_rewrite_walker.d @@ -1,15 +1,15 @@ /** * Provides a visitor for statements that allows rewriting the currently visited node. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/statement_rewrite_walker.d, _statement_rewrite_walker.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/visitor/statement_rewrite_walker.d, _statement_rewrite_walker.d) * Documentation: https://dlang.org/phobos/dmd_statement_rewrite_walker.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/statement_rewrite_walker.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/visitor/statement_rewrite_walker.d */ -module dmd.statement_rewrite_walker; +module dmd.visitor.statement_rewrite_walker; import core.stdc.stdio; diff --git a/dmd/strictvisitor.d b/dmd/visitor/strict.d similarity index 99% rename from dmd/strictvisitor.d rename to dmd/visitor/strict.d index ab87b3f9d7..be763df74a 100644 --- a/dmd/strictvisitor.d +++ b/dmd/visitor/strict.d @@ -3,9 +3,9 @@ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/strictvisitor.d */ -module dmd.strictvisitor; +module dmd.visitor.strict; -import dmd.parsetimevisitor; +import dmd.visitor.parsetime; /** The StrictVisitor asserts 0 an all visiting functions in order to * make sure that all the nodes are visited. diff --git a/dmd/transitivevisitor.d b/dmd/visitor/transitive.d similarity index 98% rename from dmd/transitivevisitor.d rename to dmd/visitor/transitive.d index bf1d38e61c..89c23320b9 100644 --- a/dmd/transitivevisitor.d +++ b/dmd/visitor/transitive.d @@ -3,10 +3,10 @@ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/transitivevisitor.d */ -module dmd.transitivevisitor; +module dmd.visitor.transitive; import dmd.astenums; -import dmd.permissivevisitor; +import dmd.visitor.permissive; import dmd.tokens; import dmd.rootobject; @@ -26,7 +26,7 @@ extern(C++) class ParseTimeTransitiveVisitor(AST) : PermissiveVisitor!AST * is used for semantic time AST node traversal, so in order to not duplicate the code, * the template mixin is used. */ -package mixin template ParseVisitMethods(AST) +package(dmd.visitor) mixin template ParseVisitMethods(AST) { import dmd.root.array; @@ -153,8 +153,8 @@ package mixin template ParseVisitMethods(AST) override void visit(AST.ForeachRangeStatement s) { //printf("Visiting ForeachRangeStatement\n"); - if (s.prm.type) - visitType(s.prm.type); + if (s.param.type) + visitType(s.param.type); s.lwr.accept(this); s.upr.accept(this); if (s._body) @@ -174,8 +174,8 @@ package mixin template ParseVisitMethods(AST) override void visit(AST.IfStatement s) { //printf("Visiting IfStatement\n"); - if (s.prm && s.prm.type) - visitType(s.prm.type); + if (s.param && s.param.type) + visitType(s.param.type); s.condition.accept(this); s.ifbody.accept(this); if (s.elsebody) @@ -854,7 +854,7 @@ package mixin template ParseVisitMethods(AST) override void visit(AST.FuncLiteralDeclaration f) { //printf("Visiting FuncLiteralDeclaration\n"); - if (f.type.ty == Terror) + if (f.type.isTypeError()) return; auto tf = f.type.isTypeFunction(); if (!f.inferRetType && tf.next) @@ -994,6 +994,8 @@ package mixin template ParseVisitMethods(AST) override void visit(AST.NewExp e) { //printf("Visiting NewExp\n"); + if (e.placement) + e.placement.accept(this); if (e.thisexp) e.thisexp.accept(this); visitType(e.newtype); @@ -1003,6 +1005,8 @@ package mixin template ParseVisitMethods(AST) override void visit(AST.NewAnonClassExp e) { //printf("Visiting NewAnonClassExp\n"); + if (e.placement) + e.placement.accept(this); if (e.thisexp) e.thisexp.accept(this); visitArgs(e.arguments.peekSlice()); diff --git a/dmd/vsoptions.d b/dmd/vsoptions.d index 7c67b29fb0..d01eb336b1 100644 --- a/dmd/vsoptions.d +++ b/dmd/vsoptions.d @@ -1,12 +1,12 @@ /** * When compiling on Windows with the Microsoft toolchain, try to detect the Visual Studio setup. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * 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/src/dmd/link.d, _vsoptions.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/vsoptions.d, _vsoptions.d) * Documentation: https://dlang.org/phobos/dmd_vsoptions.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/vsoptions.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/vsoptions.d */ module dmd.vsoptions; diff --git a/dmd/vsoptions.h b/dmd/vsoptions.h index 155a139f9c..f087e50e60 100644 --- a/dmd/vsoptions.h +++ b/dmd/vsoptions.h @@ -1,6 +1,6 @@ /* Compiler implementation of the D programming language - * Copyright (C) 2009-2024 by The D Language Foundation, All Rights Reserved + * Copyright (C) 2009-2025 by The D Language Foundation, All Rights Reserved * written by Walter Bright * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. diff --git a/driver/archiver.cpp b/driver/archiver.cpp index 9da4850afe..14c91d810c 100644 --- a/driver/archiver.cpp +++ b/driver/archiver.cpp @@ -10,8 +10,8 @@ #include "dmd/errors.h" #include "dmd/globals.h" #include "dmd/target.h" +#include "dmd/timetrace.h" #include "driver/cl_options.h" -#include "driver/timetrace.h" #include "driver/tool.h" #include "gen/logger.h" #if LDC_LLVM_VER < 1700 @@ -280,7 +280,7 @@ static std::string gStaticLibPath; int createStaticLibrary() { Logger::println("*** Creating static library ***"); - ::TimeTraceScope timeScope("Create static library"); + dmd::TimeTraceScope timeScope("Create static library"); const bool isTargetMSVC = global.params.targetTriple->isWindowsMSVCEnvironment(); diff --git a/driver/cl_helpers.cpp b/driver/cl_helpers.cpp index 72a1fe7446..b326a4cfc8 100644 --- a/driver/cl_helpers.cpp +++ b/driver/cl_helpers.cpp @@ -67,4 +67,12 @@ void StringsAdapter::push_back(const char *cstr) { arrp->push(mem.xstrdup(cstr)); } +void ImportPathsAdapter::push_back(const char *cstr) { + if (!cstr || !*cstr) { + error(Loc(), "Expected argument to '-%s'", name); + } + + arrp->push(ImportPathInfo(mem.xstrdup(cstr))); +} + } // namespace opts diff --git a/driver/cl_helpers.h b/driver/cl_helpers.h index a942fdb546..514a8b2c11 100644 --- a/driver/cl_helpers.h +++ b/driver/cl_helpers.h @@ -179,4 +179,20 @@ class StringsAdapter { void push_back(const std::string &str) { push_back(str.c_str()); } }; + +class ImportPathsAdapter { + const char *name; + Array *arrp; + +public: + ImportPathsAdapter(const char *name_, Array &arr) { + name = name_; + arrp = &arr; + assert(name); + } + + void push_back(const char *cstr); + + void push_back(const std::string &str) { push_back(str.c_str()); } +}; } diff --git a/driver/cl_options.cpp b/driver/cl_options.cpp index 6e6b498bf1..2303001d3c 100644 --- a/driver/cl_options.cpp +++ b/driver/cl_options.cpp @@ -146,11 +146,10 @@ static cl::opt cl::desc("Show errors from speculative compiles such as " "__traits(compiles,...)")); -static cl::opt printErrorContext( +cl::opt printErrorContext( "verrors-context", cl::ZeroOrMore, - cl::location(global.params.v.printErrorContext), cl::desc( - "Show error messages with the context of the erroring source line")); + "Show error messages with the context of the erroring source line (including caret)")); static cl::opt verrorStyle( "verror-style", cl::ZeroOrMore, cl::location(global.params.v.messageStyle), @@ -161,7 +160,12 @@ static cl::opt verrorStyle( "'file(line[,column]): message' (default)"), clEnumValN(MessageStyle::gnu, "gnu", "'file:line[:column]: message', conforming to the GNU " - "standard used by gcc and clang")), + "standard used by gcc and clang"), + clEnumValN( + MessageStyle::sarif, "sarif", + "Generate JSON output conforming to the SARIF (Static Analysis " + "Results Interchange Format) standard, useful for integration with " + "tools like GitHub and other SARIF readers")), cl::init(MessageStyle::digitalmars)); static cl::opt @@ -171,7 +175,7 @@ static cl::opt "each error (0 means unlimited)")); static cl::opt warnings( - cl::desc("Warnings:"), cl::ZeroOrMore, cl::location(global.params.warnings), + cl::desc("Warnings:"), cl::ZeroOrMore, cl::location(global.params.useWarnings), cl::values( clEnumValN(DIAGNOSTICerror, "w", "Enable warnings as errors (compilation will halt)"), @@ -198,7 +202,9 @@ static cl::opt cplusplus( clEnumValN(CppStdRevisionCpp17, "c++17", "Sets `__traits(getTargetInfo, \"cppStd\")` to `201703`"), clEnumValN(CppStdRevisionCpp20, "c++20", - "Sets `__traits(getTargetInfo, \"cppStd\")` to `202002`"))); + "Sets `__traits(getTargetInfo, \"cppStd\")` to `202002`"), + clEnumValN(CppStdRevisionCpp23, "c++23", + "Sets `__traits(getTargetInfo, \"cppStd\")` to `202302`"))); static cl::opt debugInfo( cl::desc("Generating debug information:"), cl::ZeroOrMore, @@ -358,14 +364,17 @@ static cl::opt addMain( cl::desc( "Add default main() if not present already (e.g. for unittesting)")); -// -d-debug is a bit messy, it has 3 modes: -// -d-debug=ident, -d-debug=level and -d-debug (without argument) -// The last one represents `-d-debug=1`, so it needs some special handling: +// -d-debug is a bit messy, it has 2 modes: +// -d-debug=ident and -d-debug (without argument) +// The last one needs some special handling: std::vector debugArgs; - struct D_DebugStorage { void push_back(const std::string &str) { - debugArgs.push_back(str.empty() ? "1" : str); + if (str.empty()) { + global.params.debugEnabled = true; + } else { + debugArgs.push_back(str); + } } }; @@ -670,18 +679,19 @@ cl::opt coverageIncrement( "Don't read, just set counter to 1"))); // Compilation time tracing options -cl::opt fTimeTrace( - "ftime-trace", cl::ZeroOrMore, +static cl::opt fTimeTrace( + "ftime-trace", cl::ZeroOrMore, cl::location(global.params.timeTrace), cl::desc("Turn on time profiler. Generates JSON file " "based on the output filename (also see --ftime-trace-file).")); -cl::opt fTimeTraceGranularity( - "ftime-trace-granularity", cl::ZeroOrMore, cl::init(500), +static cl::opt fTimeTraceGranularity( + "ftime-trace-granularity", cl::ZeroOrMore, + cl::location(global.params.timeTraceGranularityUs), cl::desc( "Minimum time granularity (in microseconds) traced by time profiler")); cl::opt -fTimeTraceFile("ftime-trace-file", - cl::desc("Specify time trace file destination"), - cl::value_desc("filename")); + fTimeTraceFile("ftime-trace-file", + cl::desc("Specify time trace file destination"), + cl::value_desc("filename")); cl::opt ltoMode( "flto", cl::ZeroOrMore, cl::desc("Set LTO mode, requires linker support"), diff --git a/driver/cl_options.h b/driver/cl_options.h index da5e8d9906..01596ccc8e 100644 --- a/driver/cl_options.h +++ b/driver/cl_options.h @@ -77,6 +77,7 @@ extern cl::list linkerSwitches; extern cl::list ccSwitches; extern cl::list cppSwitches; extern cl::list includeModulePatterns; +extern cl::opt printErrorContext; extern cl::opt m32bits; extern cl::opt m64bits; @@ -120,9 +121,7 @@ enum class CoverageIncrement extern cl::opt coverageIncrement; // Compilation time tracing options -extern cl::opt fTimeTrace; extern cl::opt fTimeTraceFile; -extern cl::opt fTimeTraceGranularity; // LTO options enum LTOKind { diff --git a/driver/codegenerator.cpp b/driver/codegenerator.cpp index 6c211a5968..0c695cc4a7 100644 --- a/driver/codegenerator.cpp +++ b/driver/codegenerator.cpp @@ -152,7 +152,7 @@ struct InlineAsmDiagnosticHandler : public llvm::DiagnosticHandler { if (DI.getKind() == llvm::SourceMgr::DK_Error || DI.getSeverity() == llvm::DS_Error) { ++global.errors; - } else if (global.params.warnings == DIAGNOSTICerror && + } else if (global.params.useWarnings == DIAGNOSTICerror && (DI.getKind() == llvm::SourceMgr::DK_Warning || DI.getSeverity() == llvm::DS_Warning)) { ++global.warnings; diff --git a/driver/cpreprocessor.cpp b/driver/cpreprocessor.cpp index a3c5c41293..765d91959a 100644 --- a/driver/cpreprocessor.cpp +++ b/driver/cpreprocessor.cpp @@ -1,8 +1,8 @@ #include "driver/cpreprocessor.h" #include "dmd/errors.h" +#include "dmd/timetrace.h" #include "driver/cl_options.h" -#include "driver/timetrace.h" #include "driver/tool.h" #include "gen/irstate.h" #include "llvm/Support/FileSystem.h" @@ -16,11 +16,16 @@ const char *getPathToImportc_h(const Loc &loc) { // importc.h should be next to object.d static const char *cached = nullptr; if (!cached) { - cached = FileName::searchPath(global.path, "importc.h", false); + cached = FileName::searchPath(global.importPaths, "importc.h", false); if (!cached) { error(loc, "cannot find \"importc.h\" along import path"); fatal(); } + +#ifdef _WIN32 + // if the path to importc.h is relative, cl.exe (but not clang-cl) treats it as relative to the .c file! + cached = FileName::toAbsolute(cached); +#endif } return cached; } @@ -76,7 +81,7 @@ FileName getOutputPath(const Loc &loc, const char *csrcfile) { FileName runCPreprocessor(FileName csrcfile, const Loc &loc, OutBuffer &defines) { - TimeTraceScope timeScope("Preprocess C file", csrcfile.toChars()); + dmd::TimeTraceScope timeScope("Preprocess C file", csrcfile.toChars(), loc); const char *importc_h = getPathToImportc_h(loc); diff --git a/driver/ldmd.cpp b/driver/ldmd.cpp index c86623babd..7b5e24ba11 100644 --- a/driver/ldmd.cpp +++ b/driver/ldmd.cpp @@ -186,7 +186,12 @@ Where:\n\ #if 0 " -fPIE generate position independent executables\n" #endif -" -g add symbolic debug info\n\ +" -ftime-trace turn on compile time profiler, generate JSON file with results\n\ + -ftime-trace-granularity=\n\ + minimum time granularity (in microseconds) traced by time profiler (default: 500)\n\ + -ftime-trace-file=\n\ + specify output file for -ftime-trace\n\ + -g add symbolic debug info\n\ -gdwarf= add DWARF symbolic debug info\n\ -gf emit debug info for all referenced types\n\ -gs always emit stack frame\n" @@ -241,7 +246,8 @@ Where:\n\ -o- do not write object file\n\ -od= write object & library files to directory\n\ -of= name output file to filename\n\ - -op preserve source path for output files\n" + -op preserve source path for output files\n\ + -oq write object files with fully qualified file names\n" #if 0 " -os= sets target operating system to \n" #endif @@ -270,12 +276,13 @@ Where:\n\ -vasm generate additional textual assembly files (*.s)\n\ -vcolumns print character (column) numbers in diagnostics\n\ -vdmd print the underlying LDC command line\n\ - -verror-style=[digitalmars|gnu]\n\ + -verror-style=[digitalmars|gnu|sarif]\n\ set the style for file/line number annotations on compiler messages\n\ -verror-supplements=\n\ limit the number of supplemental messages for each error (0 means unlimited)\n\ - -verrors= limit the number of error messages (0 means unlimited)\n\ - -verrors=context show error messages with the context of the erroring source line\n\ + -verrors= limit the number of error/deprecation messages (0 means unlimited)\n\ + -verrors=[context|simple]\n\ + set the verbosity of error messages\n\ -verrors=spec show errors from speculative compiles such as __traits(compiles,...)\n\ --version print compiler version and exit\n\ -version= compile in version code >= level\n\ @@ -517,7 +524,12 @@ void translateArgs(const llvm::SmallVectorImpl &ldmdArgs, } } else if (strcmp(p + 1, "fPIE") == 0) { goto Lnot_in_ldc; - } else if (strcmp(p + 1, "map") == 0) { + } + /* -ftime-trace + * -ftime-trace-granularity + * -ftime-trace-file + */ + else if (strcmp(p + 1, "map") == 0) { goto Lnot_in_ldc; } else if (strcmp(p + 1, "multiobj") == 0) { goto Lnot_in_ldc; @@ -537,6 +549,8 @@ void translateArgs(const llvm::SmallVectorImpl &ldmdArgs, goto Lnot_in_ldc; } else if (strcmp(p + 1, "gt") == 0) { error("use -profile instead of -gt\n"); + } else if (strcmp(p + 1, "arm") == 0) { + ldcArgs.push_back("-march=aarch64"); } /* -m32 * -m64 @@ -573,9 +587,11 @@ void translateArgs(const llvm::SmallVectorImpl &ldmdArgs, else if (startsWith(p + 1, "verrors")) { if (p[8] == '=' && isdigit(static_cast(p[9]))) { ldcArgs.push_back(p); - } else if (startsWith(p + 9, "spec")) { + } else if (strcmp(p + 9, "spec") == 0) { ldcArgs.push_back("-verrors-spec"); - } else if (startsWith(p + 9, "context")) { + } else if (strcmp(p + 9, "simple") == 0) { + ldcArgs.push_back("-verrors-context=false"); + } else if (strcmp(p + 9, "context") == 0) { ldcArgs.push_back("-verrors-context"); } else { goto Lerror; @@ -617,6 +633,7 @@ void translateArgs(const llvm::SmallVectorImpl &ldmdArgs, * -od * -of * -op + * -oq */ else if (strcmp(p + 1, "o") == 0) { error("-o no longer supported, use -of or -od"); @@ -677,21 +694,9 @@ void translateArgs(const llvm::SmallVectorImpl &ldmdArgs, else if (startsWith(p + 1, "debug") && p[6] != 'l') { // Parse: // -debug - // -debug=number // -debug=identifier if (p[6] == '=') { - if (isdigit(static_cast(p[7]))) { - long level; - errno = 0; - char *end; - level = strtol(p + 7, &end, 10); - if (*end || errno || level > INT_MAX) { - goto Lerror; - } - ldcArgs.push_back(concat("-d-debug=", static_cast(level))); - } else { - ldcArgs.push_back(concat("-d-debug=", p + 7)); - } + ldcArgs.push_back(concat("-d-debug=", p + 7)); } else if (p[6]) { goto Lerror; } else { @@ -699,21 +704,9 @@ void translateArgs(const llvm::SmallVectorImpl &ldmdArgs, } } else if (startsWith(p + 1, "version")) { // Parse: - // -version=number // -version=identifier if (p[8] == '=') { - if (isdigit(static_cast(p[9]))) { - long level; - errno = 0; - char *end; - level = strtol(p + 9, &end, 10); - if (*end || errno || level > INT_MAX) { - goto Lerror; - } - ldcArgs.push_back(concat("-d-version=", static_cast(level))); - } else { - ldcArgs.push_back(concat("-d-version=", p + 9)); - } + ldcArgs.push_back(concat("-d-version=", p + 9)); } else { goto Lerror; } diff --git a/driver/linker.cpp b/driver/linker.cpp index bd309e83b7..65e0a099e0 100644 --- a/driver/linker.cpp +++ b/driver/linker.cpp @@ -11,8 +11,8 @@ #include "dmd/errors.h" #include "dmd/target.h" +#include "dmd/timetrace.h" #include "driver/cl_options.h" -#include "driver/timetrace.h" #include "driver/tool.h" #include "gen/llvm.h" #include "gen/logger.h" @@ -289,7 +289,7 @@ static std::string gExePath; int linkObjToBinary() { Logger::println("*** Linking executable ***"); - TimeTraceScope timeScope("Linking executable"); + dmd::TimeTraceScope timeScope(TimeTraceEventType::link); // remember output path for later gExePath = getOutputPath(); @@ -321,7 +321,7 @@ void deleteExeFile() { ////////////////////////////////////////////////////////////////////////////// int runProgram() { - TimeTraceScope timeScope("Run user program"); + dmd::TimeTraceScope timeScope("Run user program"); assert(!gExePath.empty()); diff --git a/driver/main.cpp b/driver/main.cpp index a23dd2a146..f9173bbe13 100644 --- a/driver/main.cpp +++ b/driver/main.cpp @@ -21,6 +21,7 @@ #include "dmd/root/rmem.h" #include "dmd/scope.h" #include "dmd/target.h" +#include "dmd/timetrace.h" #include "driver/args.h" #include "driver/cache.h" #include "driver/cl_helpers.h" @@ -36,7 +37,6 @@ #include "driver/linker.h" #include "driver/plugins.h" #include "driver/targetmachine.h" -#include "driver/timetrace.h" #include "gen/abi/abi.h" #include "gen/irstate.h" #include "gen/ldctraits.h" @@ -90,8 +90,8 @@ void generateJson(Modules *modules); using namespace dmd; using namespace opts; -static StringsAdapter impPathsStore("I", global.params.imppath); -static cl::list +static ImportPathsAdapter impPathsStore("I", global.params.imppath); +static cl::list importPaths("I", cl::desc("Look for imports also in "), cl::value_desc("directory"), cl::location(impPathsStore), cl::Prefix); @@ -141,26 +141,13 @@ void printVersion(llvm::raw_ostream &OS) { // Helper function to handle -d-debug=* and -d-version=* template -void processVersions(const std::vector &list, const char *type, - unsigned &globalLevel) { +void processVersions(const std::vector &list, const char *type) { for (const auto &i : list) { - const char *value = i.c_str(); - if (isdigit(value[0])) { - errno = 0; - char *end; - long level = strtol(value, &end, 10); - if (*end || errno || level > INT_MAX) { - error(Loc(), "Invalid %s level: %s", type, i.c_str()); - } else { - globalLevel = static_cast(level); - } + char *cstr = mem.xstrdup(i.c_str()); + if (Identifier::isValidIdentifier(cstr)) { + Condition::addGlobalIdent(cstr); } else { - char *cstr = mem.xstrdup(value); - if (Identifier::isValidIdentifier(cstr)) { - Condition::addGlobalIdent(cstr); - } else { - error(Loc(), "Invalid %s identifier or level: '%s'", type, cstr); - } + error(Loc(), "Invalid %s identifier: '%s'", type, cstr); } } } @@ -389,13 +376,15 @@ void parseCommandLine(Strings &sourceFiles) { global.params.makeDeps.name = opts::dupPathString(makeDeps); } + global.params.timeTraceFile = fTimeTraceFile.c_str(); + #if _WIN32 - const auto toWinPaths = [](Strings &paths) { - for (auto &path : paths) - path = opts::dupPathString(path).ptr; - }; - toWinPaths(global.params.imppath); - toWinPaths(global.params.fileImppath); + for (auto &info : global.params.imppath) { + info.path = opts::dupPathString(info.path).ptr; + } + for (auto &path : global.params.fileImppath) { + path = opts::dupPathString(path).ptr; + } #endif for (const auto &field : jsonFields) { @@ -422,11 +411,16 @@ void parseCommandLine(Strings &sourceFiles) { global.params.outputSourceLocations = true; } + if (printErrorContext.getNumOccurrences() != 0) { + global.params.v.errorPrintMode = printErrorContext + ? ErrorPrintMode::printErrorContext + : ErrorPrintMode::simpleError; + } + opts::initializeSanitizerOptionsFromCmdline(); - processVersions(debugArgs, "debug", global.params.debuglevel); - processVersions(versions, "version", - global.params.versionlevel); + processVersions(debugArgs, "debug"); + processVersions(versions, "version"); for (const auto &id : transitions) parseTransitionOption(global.params, id.c_str()); @@ -481,7 +475,7 @@ void parseCommandLine(Strings &sourceFiles) { global.params.run = true; if (!runargs.empty()) { if (runargs[0] == "-") { - sourceFiles.push("__stdin.d"); + global.params.readStdin = true; } else { char const *name = runargs[0].c_str(); char const *ext = FileName::ext(name); @@ -510,8 +504,11 @@ void parseCommandLine(Strings &sourceFiles) { sourceFiles.reserve(fileList.size()); for (const auto &file : fileList) { if (!file.empty()) { - sourceFiles.push(file == "-" ? "__stdin.d" - : opts::dupPathString(file).ptr); + if (file == "-") { + global.params.readStdin = true; + } else { + sourceFiles.push(opts::dupPathString(file).ptr); + } } } @@ -1145,10 +1142,6 @@ int cppmain() { fatal(); } - if (opts::fTimeTrace) { - initializeTimeTrace(opts::fTimeTraceGranularity, 0, opts::allArguments[0]); - } - // Set up the TargetMachine. const auto arch = getArchStr(); if ((m32bits || m64bits) && (!arch.empty() || !mTargetTriple.empty())) { @@ -1226,20 +1219,12 @@ int cppmain() { loadAllPlugins(); - int status; - { - TimeTraceScope timeScope("ExecuteCompiler"); - status = mars_tryMain(global.params, files); - } + const int status = mars_tryMain(global.params, files); // try to remove the temp objects dir if created for -cleanup-obj if (!tempObjectsDir.empty()) llvm::sys::fs::remove(tempObjectsDir); - std::string fTimeTraceFile = opts::fTimeTraceFile; - writeTimeTraceProfile(fTimeTraceFile.empty() ? "" : fTimeTraceFile.c_str()); - deinitializeTimeTrace(); - llvm::llvm_shutdown(); return status; @@ -1248,7 +1233,7 @@ int cppmain() { void codegenModules(Modules &modules) { // Generate one or more object/IR/bitcode files/dcompute kernels. if (global.params.obj && !modules.empty()) { - TimeTraceScope timeScope("Codegen all modules"); + dmd::TimeTraceScope timeScope(TimeTraceEventType::codegenGlobal); #if LDC_MLIR_ENABLED mlir::MLIRContext mlircontext; @@ -1279,11 +1264,10 @@ void codegenModules(Modules &modules) { const auto atCompute = hasComputeAttr(m); if (atCompute == DComputeCompileFor::hostOnly || atCompute == DComputeCompileFor::hostAndDevice) { - TimeTraceScope timeScope( - ("Codegen module " + llvm::SmallString<20>(m->toChars())) - .str() - .c_str(), - m->loc); + dmd::TimeTraceScope timeScope( + TimeTraceEventType::codegenModule, + (llvm::Twine("Codegen: module ") + m->toChars()).str().c_str(), + m->toChars(), m->loc); #if LDC_MLIR_ENABLED if (global.params.output_mlir == OUTPUTFLAGset) cg.emitMLIR(m); @@ -1309,13 +1293,14 @@ void codegenModules(Modules &modules) { } if (!computeModules.empty()) { - TimeTraceScope timeScope("Codegen DCompute device modules"); + dmd::TimeTraceScope timeScope("Codegen DCompute device modules"); for (auto &mod : computeModules) { - TimeTraceScope timeScope(("Codegen DCompute device module " + - llvm::SmallString<20>(mod->toChars())) - .str() - .c_str(), - mod->loc); + dmd::TimeTraceScope timeScope( + TimeTraceEventType::codegenModule, + (llvm::Twine("Codegen DCompute: device module ") + mod->toChars()) + .str() + .c_str(), + mod->toChars(), mod->loc); dccg.emit(mod); } } @@ -1327,7 +1312,7 @@ void codegenModules(Modules &modules) { } { - TimeTraceScope timeScope("Prune object file cache"); + dmd::TimeTraceScope timeScope("Prune object file cache"); cache::pruneCache(); } diff --git a/driver/timetrace.d b/driver/timetrace.d deleted file mode 100644 index 5ed7cb798c..0000000000 --- a/driver/timetrace.d +++ /dev/null @@ -1,485 +0,0 @@ -//===-- driver/timetrace.d ----------------------------------------*- D -*-===// -// -// LDC – the LLVM D compiler -// -// This file is distributed under the BSD-style LDC license. See the LICENSE -// file for details. -// -//===----------------------------------------------------------------------===// -// -// Compilation time tracing, --ftime-trace. -// -// The time trace profile is output in the Chrome Trace Event Format, described -// here: https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/preview -// -//===----------------------------------------------------------------------===// - -module driver.timetrace; - -import dmd.errors; -import dmd.globals; -import dmd.location; -import dmd.root.array; -import dmd.root.file; -import dmd.common.outbuffer; -import dmd.root.string : toDString; - -// Thread local profiler instance (multithread currently not supported because compiler is single-threaded) -TimeTraceProfiler* timeTraceProfiler = null; - -// processName pointer is captured -extern(C++) -void initializeTimeTrace(uint timeGranularity, uint memoryGranularity, const(char)* processName) -{ - assert(timeTraceProfiler is null, "Double initialization of timeTraceProfiler"); - timeTraceProfiler = new TimeTraceProfiler(timeGranularity, memoryGranularity, processName); -} - -extern(C++) -void deinitializeTimeTrace() -{ - if (timeTraceProfilerEnabled()) - { - object.destroy(timeTraceProfiler); - timeTraceProfiler = null; - } -} - -pragma(inline, true) -extern(C++) -bool timeTraceProfilerEnabled() -{ - version (LDC) - { - import ldc.intrinsics: llvm_expect; - return llvm_expect(timeTraceProfiler !is null, false); - } - else - { - return timeTraceProfiler !is null; - } -} - -const(char)[] getTimeTraceProfileFilename(const(char)* filename_cstr) -{ - const(char)[] filename; - if (filename_cstr) - { - filename = filename_cstr.toDString(); - } - if (filename.length == 0) - { - if (global.params.objfiles.length) - { - filename = global.params.objfiles[0].toDString() ~ ".time-trace"; - } - else - { - filename = "out.time-trace"; - } - } - return filename; -} - -extern(C++) -void writeTimeTraceProfile(const(char)* filename_cstr) -{ - if (!timeTraceProfiler) - return; - - const filename = getTimeTraceProfileFilename(filename_cstr); - - OutBuffer buf; - timeTraceProfiler.writeToBuffer(&buf); - if (filename == "-") - { - // Write to stdout - import core.stdc.stdio : fwrite, stdout; - size_t n = fwrite(buf[].ptr, 1, buf.length, stdout); - if (n != buf.length) - { - error(Loc.initial, "Error writing --ftime-trace profile to stdout"); - fatal(); - } - } - else if (!File.write(filename, buf[])) - { - error(Loc.initial, "Error writing --ftime-trace profile: could not open '%*.s'", cast(int) filename.length, filename.ptr); - fatal(); - } -} - -// Pointers should not be stored, string copies must be made. -extern(C++) -void timeTraceProfilerBegin(const(char)* name_ptr, const(char)* detail_ptr, Loc loc) -{ - import dmd.root.rmem : xarraydup; - import core.stdc.string : strdup; - - assert(timeTraceProfiler); - - timeTraceProfiler.beginScope(xarraydup(name_ptr.toDString()), - xarraydup(detail_ptr.toDString()), loc); -} - -extern(C++) -void timeTraceProfilerEnd() -{ - assert(timeTraceProfiler); - timeTraceProfiler.endScope(); -} - - - -struct TimeTraceProfiler -{ - import core.time; - alias long TimeTicks; - - TimeTicks timeGranularity; - uint memoryGranularity; - const(char)[] processName; - const(char)[] pidtid_string = `"pid":101,"tid":101`; - - TimeTicks beginningOfTime; - Array!CounterEvent counterEvents; - Array!DurationEvent durationEvents; - Array!DurationEvent durationStack; - - struct CounterEvent - { - size_t memoryInUse; - ulong allocatedMemory; - size_t numberOfGCCollections; - TimeTicks timepoint; - } - struct DurationEvent - { - const(char)[] name; - const(char)[] details; - Loc loc; - TimeTicks timeBegin; - TimeTicks timeDuration; - } - - @disable this(); - @disable this(this); - - this(uint timeGranularity_usecs, uint memoryGranularity, const(char)* processName) - { - this.timeGranularity = timeGranularity_usecs * (MonoTime.ticksPerSecond() / 1_000_000); - this.memoryGranularity = memoryGranularity; - this.processName = processName.toDString(); - this.beginningOfTime = getTimeTicks(); - } - - TimeTicks getTimeTicks() - { - return MonoTime.currTime().ticks(); - } - - void beginScope(const(char)[] name, const(char)[] details, Loc loc) - { - DurationEvent event; - event.name = name; - event.details = details; - event.loc = loc; - event.timeBegin = getTimeTicks(); - durationStack.push(event); - - //counterEvents.push(generateCounterEvent(event.timeBegin)); - } - - void endScope() - { - TimeTicks timeEnd = getTimeTicks(); - - DurationEvent event = durationStack.pop(); - event.timeDuration = timeEnd - event.timeBegin; - if (event.timeDuration >= timeGranularity) - { - // Event passes the logging threshold - event.timeBegin -= beginningOfTime; - durationEvents.push(event); - counterEvents.push(generateCounterEvent(timeEnd-beginningOfTime)); - } - } - - /// Takes ownership of the string returned by `details`. - void endScopeUpdateDetails(scope const(char)[] delegate() details) - { - TimeTicks timeEnd = getTimeTicks(); - - DurationEvent event = durationStack.pop(); - event.timeDuration = timeEnd - event.timeBegin; - if (event.timeDuration >= timeGranularity) - { - // Event passes the logging threshold - event.details = details(); - event.timeBegin -= beginningOfTime; - durationEvents.push(event); - counterEvents.push(generateCounterEvent(timeEnd-beginningOfTime)); - } - } - - - CounterEvent generateCounterEvent(TimeTicks timepoint) - { - static import dmd.root.rmem; - CounterEvent counters; - if (dmd.root.rmem.mem.isGCEnabled) - { - static if (__VERSION__ >= 2085) - { - import core.memory : GC; - auto stats = GC.stats(); - auto profileStats = GC.profileStats(); - - counters.allocatedMemory = stats.usedSize + stats.freeSize; - counters.memoryInUse = stats.usedSize; - counters.numberOfGCCollections = profileStats.numCollections; - } - } - else - { - counters.allocatedMemory = dmd.root.rmem.heaptotal; - counters.memoryInUse = dmd.root.rmem.heaptotal - dmd.root.rmem.heapleft; - } - counters.timepoint = timepoint; - return counters; - } - - void writeToBuffer(OutBuffer* buf) - { - writePrologue(buf); - writeEvents(buf); - writeEpilogue(buf); - } - - void writePrologue(OutBuffer* buf) - { - // Time is to be output in microseconds - long timescale = MonoTime.ticksPerSecond() / 1_000_000; - - buf.write("{\n\"beginningOfTime\":"); - buf.print(beginningOfTime / timescale); - buf.write(",\n\"traceEvents\": [\n"); - } - - void writeEpilogue(OutBuffer* buf) - { - buf.write("]\n}\n"); - } - - void writeEvents(OutBuffer* buf) - { - writeMetadataEvents(buf); - writeCounterEvents(buf); - writeDurationEvents(buf); - // Remove the trailing comma (and newline!) to obtain valid JSON. - if ((*buf)[buf.length()-2] == ',') - { - buf.setsize(buf.length()-2); - buf.writeByte('\n'); - } - } - - void writeMetadataEvents(OutBuffer* buf) - { - // {"ph":"M","ts":0,"args":{"name":"bin/ldc2"},"name":"thread_name","pid":0,"tid":0}, - - buf.write(`{"ph":"M","ts":0,"args":{"name":"`); - buf.writeEscapeJSONString(processName); - buf.write(`"},"name":"process_name",`); - buf.write(pidtid_string); - buf.write("},\n"); - buf.write(`{"ph":"M","ts":0,"args":{"name":"`); - buf.writeEscapeJSONString(processName); - buf.write(`"},"cat":"","name":"thread_name",`); - buf.write(pidtid_string); - buf.write("},\n"); - } - - void writeCounterEvents(OutBuffer* buf) - { - // {"ph":"C","name":"ctr","ts":111,"args": {"Allocated_Memory_bytes": 0, "hello": 0}}, - - // Time is to be output in microseconds - long timescale = MonoTime.ticksPerSecond() / 1_000_000; - - foreach (const ref event; counterEvents) - { - buf.write(`{"ph":"C","name":"ctr","ts":`); - buf.print(event.timepoint / timescale); - buf.write(`,"args": {"memoryInUse_bytes":`); - buf.print(event.memoryInUse); - buf.write(`,"allocatedMemory_bytes":`); - buf.print(event.allocatedMemory); - buf.write(`,"GC collections":`); - buf.print(event.numberOfGCCollections); - buf.write("},"); - buf.write(pidtid_string); - buf.write("},\n"); - } - } - - void writeDurationEvents(OutBuffer* buf) - { - // {"ph":"X","name": "Sema1: somename","ts":111,"dur":222,"loc":"filename.d:123","args": {"detail": "something", "loc":"filename.d:123"},"pid":0,"tid":0} - - void writeLocation(Loc loc) - { - if (loc.filename()) - { - writeEscapeJSONString(buf, loc.filename().toDString()); - if (loc.linnum()) - { - buf.writeByte(':'); - buf.print(loc.linnum()); - } - } - else - { - buf.write(``); - } - } - - // Time is to be output in microseconds - long timescale = MonoTime.ticksPerSecond() / 1_000_000; - - foreach (event; durationEvents) - { - buf.write(`{"ph":"X","name": "`); - writeEscapeJSONString(buf, event.name); - buf.write(`","ts":`); - buf.print(event.timeBegin / timescale); - buf.write(`,"dur":`); - buf.print(event.timeDuration / timescale); - buf.write(`,"loc":"`); - writeLocation(event.loc); - buf.write(`","args":{"detail": "`); - writeEscapeJSONString(buf, event.details); - // Also output loc data in the "args" field so it shows in trace viewers that do not support the "loc" variable - buf.write(`","loc":"`); - writeLocation(event.loc); - buf.write(`"},`); - buf.write(pidtid_string); - buf.write("},\n"); - } - } -} - - -/// RAII helper class to call the begin and end functions of the time trace -/// profiler. When the object is constructed, it begins the section; and when -/// it is destroyed, it stops it. -struct TimeTraceScope -{ - @disable this(); - @disable this(this); - - this(lazy string name, Loc loc = Loc()) - { - if (timeTraceProfilerEnabled()) - { - assert(timeTraceProfiler); - timeTraceProfiler.beginScope(name.dup, "", loc); - } - } - this(lazy string name, lazy string detail, Loc loc = Loc()) - { - if (timeTraceProfilerEnabled()) - { - assert(timeTraceProfiler); - timeTraceProfiler.beginScope(name.dup, detail.dup, loc); - } - } - /// Takes ownership of string returned by `detail`. - this(lazy string name, scope const(char)[] delegate() detail, Loc loc = Loc()) - { - if (timeTraceProfilerEnabled()) - { - assert(timeTraceProfiler); - timeTraceProfiler.beginScope(name.dup, detail(), loc); - } - } - - ~this() - { - if (timeTraceProfilerEnabled()) - timeTraceProfiler.endScope(); - } -} - -/// RAII helper class to call the begin and end functions of the time trace -/// profiler. When the object is constructed, it begins the section; and when -/// it is destroyed, it stops it. -/// Delays string evaluation (via delegate) until the object is destroyed and the delegate -/// is only called when the event passes the granularity threshold. -struct TimeTraceScopeDelayedDetail -{ - @disable this(); - @disable this(this); - - const(char)[] delegate() details_dlg; - - /// Takes ownership of string returned by `detail`. - /// `detail` is stored in the struct, but does not escape the lifetime of the struct object. - this(lazy string name, scope const(char)[] delegate() detail, Loc loc = Loc()) scope @system - { - if (timeTraceProfilerEnabled()) - { - assert(timeTraceProfiler); - details_dlg = detail; - timeTraceProfiler.beginScope(name.dup, "", loc); - } - } - - ~this() - { - if (timeTraceProfilerEnabled()) - timeTraceProfiler.endScopeUpdateDetails(details_dlg); - } -} - -private void writeEscapeJSONString(OutBuffer* buf, const(char[]) str) -{ - foreach (char c; str) - { - switch (c) - { - case '\n': - buf.writestring("\\n"); - break; - case '\r': - buf.writestring("\\r"); - break; - case '\t': - buf.writestring("\\t"); - break; - case '\"': - buf.writestring("\\\""); - break; - case '\\': - buf.writestring("\\\\"); - break; - case '\b': - buf.writestring("\\b"); - break; - case '\f': - buf.writestring("\\f"); - break; - default: - if (c < 0x20) - buf.printf("\\u%04x", c); - else - { - // Note that UTF-8 chars pass through here just fine - buf.writeByte(c); - } - break; - } - } -} - - diff --git a/driver/timetrace.h b/driver/timetrace.h deleted file mode 100644 index 3c177cc770..0000000000 --- a/driver/timetrace.h +++ /dev/null @@ -1,62 +0,0 @@ -//===-- driver/timetrace.h --------------------------------------*- C++ -*-===// -// -// LDC – the LLVM D compiler -// -// This file is distributed under the BSD-style LDC license. See the LICENSE -// file for details. -// -//===----------------------------------------------------------------------===// -// -// Compilation time tracing, --ftime-trace. -// Main implementation is in D, this C++ source is for interfacing. -// -//===----------------------------------------------------------------------===// - -#pragma once - -#include "dmd/globals.h" -#include - -// Forward declarations to functions implemented in D -void initializeTimeTrace(unsigned timeGranularity, unsigned memoryGranularity, - const char *processName); -void deinitializeTimeTrace(); -void writeTimeTraceProfile(const char *filename_cstr); -void timeTraceProfilerBegin(const char *name_ptr, const char *detail_ptr, Loc loc); -void timeTraceProfilerEnd(); -bool timeTraceProfilerEnabled(); - - -/// RAII helper class to call the begin and end functions of the time trace -/// profiler. When the object is constructed, it begins the section; and when -/// it is destroyed, it stops it. -/// The strings pointed to are copied (pointers are not stored). -struct TimeTraceScope { - TimeTraceScope() = delete; - TimeTraceScope(const TimeTraceScope &) = delete; - TimeTraceScope &operator=(const TimeTraceScope &) = delete; - TimeTraceScope(TimeTraceScope &&) = delete; - TimeTraceScope &operator=(TimeTraceScope &&) = delete; - - TimeTraceScope(const char *name, Loc loc = Loc()) { - if (timeTraceProfilerEnabled()) - timeTraceProfilerBegin(name, "", loc); - } - TimeTraceScope(const char *name, const char *detail, Loc loc = Loc()) { - if (timeTraceProfilerEnabled()) - timeTraceProfilerBegin(name, detail, loc); - } - TimeTraceScope(const char *name, std::function detail, Loc loc = Loc()) { - if (timeTraceProfilerEnabled()) - timeTraceProfilerBegin(name, detail().c_str(), loc); - } - TimeTraceScope(std::function name, std::function detail, Loc loc = Loc()) { - if (timeTraceProfilerEnabled()) - timeTraceProfilerBegin(name().c_str(), detail().c_str(), loc); - } - - ~TimeTraceScope() { - if (timeTraceProfilerEnabled()) - timeTraceProfilerEnd(); - } -}; diff --git a/driver/timetrace_sema.d b/driver/timetrace_sema.d deleted file mode 100644 index f606b6180b..0000000000 --- a/driver/timetrace_sema.d +++ /dev/null @@ -1,238 +0,0 @@ -//===-- driver/timetrace_sema.d ------------------------------------*- D -*-===// -// -// LDC – the LLVM D compiler -// -// This file is distributed under the BSD-style LDC license. See the LICENSE -// file for details. -// -//===----------------------------------------------------------------------===// -// -// Visitors for adding timetracing code to DMD's semantic analysis visitors. -// -//===----------------------------------------------------------------------===// - -module driver.timetrace_sema; - -import dmd.aggregate; -import dmd.aliasthis; -import dmd.arraytypes; -import dmd.astcodegen; -import dmd.attrib; -import dmd.blockexit; -import dmd.clone; -import dmd.compiler; -import dmd.dcast; -import dmd.dclass; -import dmd.declaration; -import dmd.denum; -import dmd.dimport; -import dmd.dinterpret; -import dmd.dmangle; -import dmd.dmodule; -import dmd.dscope; -import dmd.dstruct; -import dmd.dsymbol; -import dmd.dtemplate; -import dmd.dversion; -import dmd.errors; -import dmd.escape; -import dmd.expression; -import dmd.expressionsem; -import dmd.func; -import dmd.globals; -import dmd.id; -import dmd.identifier; -import dmd.init; -import dmd.initsem; -import dmd.hdrgen; -import dmd.mtype; -import dmd.nogc; -import dmd.nspace; -import dmd.objc; -import dmd.opover; -import dmd.parse; -import dmd.root.filename; -import dmd.common.outbuffer; -import dmd.root.rmem; -import dmd.rootobject; -import dmd.semantic2; -import dmd.semantic3; -import dmd.sideeffect; -import dmd.statementsem; -import dmd.staticassert; -import dmd.tokens; -import dmd.root.utf; -import dmd.utils; -import dmd.statement; -import dmd.target; -import dmd.templateparamsem; -import dmd.typesem; -import dmd.visitor; -import driver.timetrace; - -/// Time tracing visitor for semantic analysis. Only valid to instantiate and use this visitor if time tracing is enabled. -extern(C++) final class SemanticTimeTraceVisitor(SemaVisitor) : Visitor -{ - static assert(checkFirstOverridesAllSecondOverrides!(typeof(this), SemaVisitor)); - - import driver.timetrace, std.format, std.conv; - - SemaVisitor semavisitor; - - @disable this(); - - static if (SemaVisitor.stringof == "DsymbolSemanticVisitor") - enum pretext = "Sema1: "; - else static if (SemaVisitor.stringof == "Semantic2Visitor") - enum pretext = "Sema2: "; - else static if (SemaVisitor.stringof == "Semantic3Visitor") - enum pretext = "Sema3: "; - else - static assert (false, "did not recognize SemaVisitor ==" ~ SemaVisitor); - - this(SemaVisitor semavisitor) - { - this.semavisitor = semavisitor; - } - - alias visit = Visitor.visit; - - override void visit(Dsymbol dsym) { semavisitor.visit(dsym); } - override void visit(ScopeDsymbol dsym) { semavisitor.visit(dsym); } - override void visit(Declaration dsym) { semavisitor.visit(dsym); } - - override void visit(AliasThis dsym) { semavisitor.visit(dsym); } - - override void visit(AliasDeclaration dsym) { semavisitor.visit(dsym); } - - override void visit(VarDeclaration dsym) { semavisitor.visit(dsym); } - - override void visit(TypeInfoDeclaration dsym) { semavisitor.visit(dsym); } - - override void visit(Import imp) - { - auto timeScope = TimeTraceScope(text(pretext ~ "Import ", imp.id.toChars()), imp.toPrettyChars().to!string, imp.loc); - semavisitor.visit(imp); - } - - override void visit(AttribDeclaration atd) { semavisitor.visit(atd); } - - override void visit(DeprecatedDeclaration dd) { semavisitor.visit(dd); } - - override void visit(AlignDeclaration ad) { semavisitor.visit(ad); } - - override void visit(AggregateDeclaration ad) { semavisitor.visit(ad); } - - override void visit(AnonDeclaration scd) { semavisitor.visit(scd); } - - override void visit(PragmaDeclaration pd) { semavisitor.visit(pd); } - - override void visit(StaticIfDeclaration sid) { semavisitor.visit(sid); } - - override void visit(StaticForeachDeclaration sfd) { semavisitor.visit(sfd); } - - override void visit(MixinDeclaration md) { semavisitor.visit(md); } - - override void visit(CPPNamespaceDeclaration ns) { semavisitor.visit(ns); } - - override void visit(UserAttributeDeclaration uad) { semavisitor.visit(uad); } - - override void visit(StaticAssert sa) { semavisitor.visit(sa); } - - override void visit(DebugSymbol ds) { semavisitor.visit(ds); } - - override void visit(VersionSymbol vs) { semavisitor.visit(vs); } - - override void visit(AliasAssign aa) { semavisitor.visit(aa); } - - override void visit(CAsmDeclaration cad) { semavisitor.visit(cad); } - - override void visit(Package pkg) { semavisitor.visit(pkg); } - - override void visit(Module m) { - auto timeScope = TimeTraceScope(text(pretext ~ "Module ", m.toPrettyChars()), m.loc); - semavisitor.visit(m); - } - - override void visit(EnumDeclaration ed) { semavisitor.visit(ed); } - - override void visit(EnumMember em) { semavisitor.visit(em); } - - override void visit(TemplateDeclaration tempdecl) { semavisitor.visit(tempdecl); } - - override void visit(TemplateInstance ti) { semavisitor.visit(ti); } - - override void visit(TemplateMixin tm) { semavisitor.visit(tm); } - - override void visit(Nspace ns) { semavisitor.visit(ns); } - - override void visit(FuncDeclaration funcdecl) { - auto timeScope = TimeTraceScope(text(pretext ~ "Func ", funcdecl.toChars()), funcdecl.toPrettyChars().to!string, funcdecl.loc); - semavisitor.visit(funcdecl); - } - - override void visit(CtorDeclaration ctd) { semavisitor.visit(ctd); } - - override void visit(PostBlitDeclaration pbd) { semavisitor.visit(pbd); } - - override void visit(DtorDeclaration dd) { semavisitor.visit(dd); } - - override void visit(StaticCtorDeclaration scd) { semavisitor.visit(scd); } - - override void visit(StaticDtorDeclaration sdd) { semavisitor.visit(sdd); } - - override void visit(InvariantDeclaration invd) { semavisitor.visit(invd); } - - override void visit(UnitTestDeclaration utd) { semavisitor.visit(utd); } - - override void visit(NewDeclaration nd) { semavisitor.visit(nd); } - - override void visit(StructDeclaration sd) { semavisitor.visit(sd); } - - override void visit(ClassDeclaration cldec) { semavisitor.visit(cldec); } - - override void visit(InterfaceDeclaration idec) { semavisitor.visit(idec); } - - override void visit(TupleDeclaration td) { semavisitor.visit(td); } - - override void visit(BitFieldDeclaration bfd) { semavisitor.visit(bfd); } -} - -// Note: this is not a very careful check, but is hopefully good enough to trigger upon relevant changes to the DMD frontend. -// If the parent of First and Second already contains an override -// function and Second overrides it, this function always returns true even if First does not override it. -private bool checkFirstOverridesAllSecondOverrides(First, Second)() { - // Due to access rights limits of __traits(derivedMembers,...) we require a newer dlang version to do the check - static if (__VERSION__ >= 2086) - { - foreach (der2; __traits(derivedMembers, Second)) - { - foreach (mem2; __traits(getOverloads, Second, der2)) - { - if (!__traits(isOverrideFunction, mem2)) - continue; - else - { - bool found = false; - foreach (der1; __traits(derivedMembers, First)) - { - foreach (mem1; __traits(getOverloads, First, der1)) - { - if (!__traits(isOverrideFunction, mem1)) - continue; - else if (__traits(getVirtualIndex, mem1) == __traits(getVirtualIndex, mem2)) - { - found = true; - break; - } - } - if (found) break; - } - assert (found, mem2.mangleof ~ " needs override"); - if (!found) return false; - } - } - } - } - return true; -} diff --git a/driver/toobj.cpp b/driver/toobj.cpp index 0902167b6f..5b5a5ec169 100644 --- a/driver/toobj.cpp +++ b/driver/toobj.cpp @@ -10,10 +10,10 @@ #include "driver/toobj.h" #include "dmd/errors.h" +#include "dmd/timetrace.h" #include "driver/cl_options.h" #include "driver/cache.h" #include "driver/targetmachine.h" -#include "driver/timetrace.h" #include "driver/tool.h" #include "gen/irstate.h" #include "gen/logger.h" @@ -344,7 +344,7 @@ void writeModule(llvm::Module *m, const char *filename) { const bool useIR2ObjCache = !opts::cacheDir.empty() && outputObj && !doLTO; llvm::SmallString<32> moduleHash; if (useIR2ObjCache) { - ::TimeTraceScope timeScope("Check object cache", filename); + dmd::TimeTraceScope timeScope("Check object cache", filename); llvm::SmallString<128> cacheDir(opts::cacheDir.c_str()); llvm::sys::fs::make_absolute(cacheDir); opts::cacheDir = cacheDir.c_str(); @@ -363,12 +363,12 @@ void writeModule(llvm::Module *m, const char *filename) { // run LLVM optimization passes { - ::TimeTraceScope timeScope("Optimize", filename); + dmd::TimeTraceScope timeScope("Optimize", filename); ldc_optimize_module(m, gTargetMachine); } if (global.params.dllimport != DLLImport::none) { - ::TimeTraceScope timeScope("dllimport relocation", filename); + dmd::TimeTraceScope timeScope("dllimport relocation", filename); runDLLImportRelocationPass(*gTargetMachine, *m); } @@ -382,7 +382,7 @@ void writeModule(llvm::Module *m, const char *filename) { } // Everything beyond this point is writing file(s) to disk. - ::TimeTraceScope timeScope("Write file(s)", filename); + dmd::TimeTraceScope timeScope("Write file(s)", filename); // make sure the output directory exists const auto directory = llvm::sys::path::parent_path(filename); diff --git a/gen/abi/abi.cpp b/gen/abi/abi.cpp index d0bbf9bfb3..e62eeda99f 100644 --- a/gen/abi/abi.cpp +++ b/gen/abi/abi.cpp @@ -113,7 +113,7 @@ bool TargetABI::isAggregate(Type *t) { // by runtime functions, for which we don't apply the rewrites yet // when calling them return ty == TY::Tstruct || ty == TY::Tsarray || - /*ty == TY::Tarray ||*/ ty == TY::Tdelegate || t->iscomplex(); + /*ty == TY::Tarray ||*/ ty == TY::Tdelegate || t->isComplex(); } bool TargetABI::isPOD(Type *t, bool excludeStructsWithCtor) { diff --git a/gen/abi/loongarch64.cpp b/gen/abi/loongarch64.cpp index 2f64104118..a5f446fca1 100644 --- a/gen/abi/loongarch64.cpp +++ b/gen/abi/loongarch64.cpp @@ -86,7 +86,7 @@ struct HardfloatRewrite : BaseBitcastABIRewrite { LLType *t[2]; for (unsigned i = 0; i < 2; ++i) { t[i] = - flat.fields[i]->isfloating() + flat.fields[i]->isFloating() ? DtoType(flat.fields[i]) : LLIntegerType::get(gIR->context(), size(flat.fields[i]) * 8); } @@ -111,8 +111,8 @@ struct LoongArch64TargetABI : TargetABI { if (result.length <= 0) return false; if (result.length == 1) - return result.fields[0]->isfloating(); - return result.fields[0]->isfloating() || result.fields[1]->isfloating(); + return result.fields[0]->isFloating(); + return result.fields[0]->isFloating() || result.fields[1]->isFloating(); } public: @@ -143,7 +143,7 @@ struct LoongArch64TargetABI : TargetABI { } Type *ty = arg.type->toBasetype(); - if (ty->isintegral() && (ty->ty == TY::Tint32 || ty->ty == TY::Tuns32 || + if (ty->isIntegral() && (ty->ty == TY::Tint32 || ty->ty == TY::Tuns32 || ty->ty == TY::Tdchar)) { // In the LP64D ABI, both int32 and unsigned int32 are stored in // general-purpose registers as proper sign extensions of their diff --git a/gen/abi/ppc.cpp b/gen/abi/ppc.cpp index 51a0ae4824..e938a7d0ef 100644 --- a/gen/abi/ppc.cpp +++ b/gen/abi/ppc.cpp @@ -70,8 +70,8 @@ struct PPCTargetABI : TargetABI { compositeToArray32.applyTo(arg); } } - } else if (ty->isintegral()) { - arg.attrs.addAttribute(ty->isunsigned() ? LLAttribute::ZExt + } else if (ty->isIntegral()) { + arg.attrs.addAttribute(ty->isUnsigned() ? LLAttribute::ZExt : LLAttribute::SExt); } } diff --git a/gen/abi/ppc64le.cpp b/gen/abi/ppc64le.cpp index ab839ef912..f4efa502c4 100644 --- a/gen/abi/ppc64le.cpp +++ b/gen/abi/ppc64le.cpp @@ -51,8 +51,8 @@ struct PPC64LETargetABI : TargetABI { } else { compositeToArray64.applyTo(arg); } - } else if (ty->isintegral() && !ty->isTypeVector()) { - arg.attrs.addAttribute(ty->isunsigned() ? LLAttribute::ZExt + } else if (ty->isIntegral() && !ty->isTypeVector()) { + arg.attrs.addAttribute(ty->isUnsigned() ? LLAttribute::ZExt : LLAttribute::SExt); } } diff --git a/gen/abi/riscv64.cpp b/gen/abi/riscv64.cpp index 674f24b34c..f606cb6314 100644 --- a/gen/abi/riscv64.cpp +++ b/gen/abi/riscv64.cpp @@ -93,8 +93,8 @@ bool requireHardfloatRewrite(Type *ty) { if (result.length <= 0) return false; if (result.length == 1) - return result.fields[0].ty->isfloating(); - return result.fields[0].ty->isfloating() || result.fields[1].ty->isfloating(); + return result.fields[0].ty->isFloating(); + return result.fields[0].ty->isFloating() || result.fields[1].ty->isFloating(); } struct HardfloatRewrite : ABIRewrite { @@ -139,7 +139,7 @@ struct HardfloatRewrite : ABIRewrite { assert(flat.length == 2); LLType *t[2]; for (unsigned i = 0; i < 2; ++i) { - t[i] = flat.fields[i].ty->isfloating() + t[i] = flat.fields[i].ty->isFloating() ? DtoType(flat.fields[i].ty) : LLIntegerType::get(gIR->context(), size(flat.fields[i].ty) * 8); diff --git a/gen/abi/wasm.cpp b/gen/abi/wasm.cpp index f42056b13e..496753daef 100644 --- a/gen/abi/wasm.cpp +++ b/gen/abi/wasm.cpp @@ -31,7 +31,7 @@ Type *getSingleWrappedScalarType(Type *t) { return getSingleWrappedScalarType(tsa->nextOf()); } - return t->isscalar() + return t->isScalar() // some more pointers: || t->ty == TY::Tclass || t->ty == TY::Taarray ? t diff --git a/gen/abi/x86-64.cpp b/gen/abi/x86-64.cpp index 67100b3b1b..de84cdef5f 100644 --- a/gen/abi/x86-64.cpp +++ b/gen/abi/x86-64.cpp @@ -383,7 +383,7 @@ const char *X86_64TargetABI::objcMsgSendFunc(Type *ret, IrFuncTy &fty, bool dire return directcall ? "objc_msgSendSuper_stret" : "objc_msgSend_stret"; } // float, double, long double return - if (ret && ret->isfloating()) { + if (ret && ret->isFloating()) { return ret->ty == TY::Tcomplex80 ? "objc_msgSend_fp2ret" : "objc_msgSend_fpret"; } return directcall ? "objc_msgSendSuper" : "objc_msgSend"; diff --git a/gen/abi/x86.cpp b/gen/abi/x86.cpp index a8912bfc62..1f8358a8b0 100644 --- a/gen/abi/x86.cpp +++ b/gen/abi/x86.cpp @@ -109,7 +109,7 @@ struct X86TargetABI : TargetABI { return false; // complex numbers - if (rt->iscomplex()) { + if (rt->isComplex()) { // extern(D): let LLVM return them directly as LL aggregates if (externD) return false; @@ -206,7 +206,7 @@ struct X86TargetABI : TargetABI { } else { Type *firstTy = first.type->toBasetype(); auto sz = size(firstTy); - if (!firstTy->isfloating() && (sz == 1 || sz == 2 || sz == 4)) { + if (!firstTy->isFloating() && (sz == 1 || sz == 2 || sz == 4)) { // rewrite aggregates as integers to make inreg work if (firstTy->ty == TY::Tstruct || firstTy->ty == TY::Tsarray) { integerRewrite.applyTo(first); diff --git a/gen/arrays.cpp b/gen/arrays.cpp index ad120451c3..9d448144e4 100644 --- a/gen/arrays.cpp +++ b/gen/arrays.cpp @@ -624,7 +624,7 @@ DSliceValue *DtoNewDynArray(const Loc &loc, Type *arrayType, DValue *dim, return DtoNullValue(arrayType, loc)->isSlice(); // get runtime function - bool zeroInit = eltType->isZeroInit(); + bool zeroInit = isZeroInit(eltType); const char *fnname = defaultInit ? (zeroInit ? "_d_newarrayT" : "_d_newarrayiT") : "_d_newarrayU"; diff --git a/gen/asm-x86.h b/gen/asm-x86.h index db148f39dd..6d53d0b555 100644 --- a/gen/asm-x86.h +++ b/gen/asm-x86.h @@ -2343,7 +2343,7 @@ struct AsmProcessor { if (sc->func->isNaked()) { switch (type) { case Arg_Integer: - if (e->type->isunsigned()) { + if (e->type->isUnsigned()) { insnTemplate << "$" << e->toUInteger(); } else { #ifndef ASM_X86_64 @@ -3232,7 +3232,7 @@ struct AsmProcessor { operand->hasNumber = 1; } } else { - if (v && v->type->isscalar()) { + if (v && v->type->isScalar()) { // DMD doesn't check Tcomplex*, and counts Tcomplex32 as // Tfloat64 TY ty = v->type->toBasetype()->ty; diff --git a/gen/asmstmt.cpp b/gen/asmstmt.cpp index 9ad20c47a3..d616f69549 100644 --- a/gen/asmstmt.cpp +++ b/gen/asmstmt.cpp @@ -102,8 +102,6 @@ Statement *asmSemantic(AsmStatement *s, Scope *sc) { return nullptr; } - sc->func->hasReturnExp |= 8; - // GCC-style asm starts with a string literal or a `(` if (s->tokens->value == TOK::string_ || s->tokens->value == TOK::leftParenthesis) { @@ -112,7 +110,7 @@ Statement *asmSemantic(AsmStatement *s, Scope *sc) { } // this is DMD-style asm - sc->func->hasReturnExp |= 32; + sc->func->hasInlineAsm(true); const auto caseSensitive = s->caseSensitive; @@ -164,7 +162,7 @@ void AsmStatement_toIR(InlineAsmStatement *stmt, IRState *irs) { LOG_SCOPE; // sanity check - assert((irs->func()->decl->hasReturnExp & 40) == 40); + assert(irs->func()->decl->hasInlineAsm()); // get asm block IRAsmBlock *asmblock = irs->asmBlock; diff --git a/gen/binops.cpp b/gen/binops.cpp index 80064ecfd2..a353d2322d 100644 --- a/gen/binops.cpp +++ b/gen/binops.cpp @@ -153,7 +153,7 @@ DValue *binAdd(const Loc &loc, Type *type, DValue *lhs, Expression *rhs, Type *rhsType = rhs->type->toBasetype(); if (lhsType != rhsType && lhsType->ty == TY::Tpointer && - rhsType->isintegral()) { + rhsType->isIntegral()) { Logger::println("Adding integer to pointer"); return emitPointerOffset(loc, lhs, rhs, false, type, loadLhsAfterRhs); } @@ -162,7 +162,7 @@ DValue *binAdd(const Loc &loc, Type *type, DValue *lhs, Expression *rhs, if (type->ty == TY::Tnull) return DtoNullValue(type, loc); - if (type->iscomplex()) + if (type->isComplex()) return DtoComplexAdd(loc, type, rvals.lhs, rvals.rhs); LLValue *l = DtoRVal(DtoCast(loc, rvals.lhs, type)); @@ -171,7 +171,7 @@ DValue *binAdd(const Loc &loc, Type *type, DValue *lhs, Expression *rhs, if (auto aa = isAssociativeArrayAndNull(type, l, r)) return aa; - LLValue *res = (type->isfloating() ? gIR->ir->CreateFAdd(l, r) + LLValue *res = (type->isFloating() ? gIR->ir->CreateFAdd(l, r) : gIR->ir->CreateAdd(l, r)); return new DImValue(type, res); @@ -185,7 +185,7 @@ DValue *binMin(const Loc &loc, Type *type, DValue *lhs, Expression *rhs, Type *rhsType = rhs->type->toBasetype(); if (lhsType != rhsType && lhsType->ty == TY::Tpointer && - rhsType->isintegral()) { + rhsType->isIntegral()) { Logger::println("Subtracting integer from pointer"); return emitPointerOffset(loc, lhs, rhs, true, type, loadLhsAfterRhs); } @@ -207,7 +207,7 @@ DValue *binMin(const Loc &loc, Type *type, DValue *lhs, Expression *rhs, if (type->ty == TY::Tnull) return DtoNullValue(type, loc); - if (type->iscomplex()) + if (type->isComplex()) return DtoComplexMin(loc, type, rvals.lhs, rvals.rhs); LLValue *l = DtoRVal(DtoCast(loc, rvals.lhs, type)); @@ -216,7 +216,7 @@ DValue *binMin(const Loc &loc, Type *type, DValue *lhs, Expression *rhs, if (auto aa = isAssociativeArrayAndNull(type, l, r)) return aa; - LLValue *res = (type->isfloating() ? gIR->ir->CreateFSub(l, r) + LLValue *res = (type->isFloating() ? gIR->ir->CreateFSub(l, r) : gIR->ir->CreateSub(l, r)); return new DImValue(type, res); @@ -228,12 +228,12 @@ DValue *binMul(const Loc &loc, Type *type, DValue *lhs, Expression *rhs, bool loadLhsAfterRhs) { auto rvals = evalSides(lhs, rhs, loadLhsAfterRhs); - if (type->iscomplex()) + if (type->isComplex()) return DtoComplexMul(loc, type, rvals.lhs, rvals.rhs); LLValue *l = DtoRVal(DtoCast(loc, rvals.lhs, type)); LLValue *r = DtoRVal(DtoCast(loc, rvals.rhs, type)); - LLValue *res = (type->isfloating() ? gIR->ir->CreateFMul(l, r) + LLValue *res = (type->isFloating() ? gIR->ir->CreateFMul(l, r) : gIR->ir->CreateMul(l, r)); return new DImValue(type, res); @@ -245,13 +245,13 @@ DValue *binDiv(const Loc &loc, Type *type, DValue *lhs, Expression *rhs, bool loadLhsAfterRhs) { auto rvals = evalSides(lhs, rhs, loadLhsAfterRhs); - if (type->iscomplex()) + if (type->isComplex()) return DtoComplexDiv(loc, type, rvals.lhs, rvals.rhs); LLValue *l = DtoRVal(DtoCast(loc, rvals.lhs, type)); LLValue *r = DtoRVal(DtoCast(loc, rvals.rhs, type)); LLValue *res; - if (type->isfloating()) { + if (type->isFloating()) { res = gIR->ir->CreateFDiv(l, r); } else if (!isLLVMUnsigned(type)) { res = gIR->ir->CreateSDiv(l, r); @@ -268,13 +268,13 @@ DValue *binMod(const Loc &loc, Type *type, DValue *lhs, Expression *rhs, bool loadLhsAfterRhs) { auto rvals = evalSides(lhs, rhs, loadLhsAfterRhs); - if (type->iscomplex()) + if (type->isComplex()) return DtoComplexMod(loc, type, rvals.lhs, rvals.rhs); LLValue *l = DtoRVal(DtoCast(loc, rvals.lhs, type)); LLValue *r = DtoRVal(DtoCast(loc, rvals.rhs, type)); LLValue *res; - if (type->isfloating()) { + if (type->isFloating()) { res = gIR->ir->CreateFRem(l, r); } else if (!isLLVMUnsigned(type)) { res = gIR->ir->CreateSRem(l, r); @@ -344,14 +344,14 @@ LLValue *DtoBinNumericEquals(const Loc &loc, DValue *lhs, DValue *rhs, EXP op) { assert(op == EXP::equal || op == EXP::notEqual || op == EXP::identity || op == EXP::notIdentity); Type *t = lhs->type->toBasetype(); - assert(t->isfloating()); + assert(t->isFloating()); Logger::println("numeric equality"); LLValue *res = nullptr; - if (t->iscomplex()) { + if (t->isComplex()) { Logger::println("complex"); res = DtoComplexEquals(loc, op, lhs, rhs); - } else if (t->isfloating()) { + } else if (t->isFloating()) { Logger::println("floating"); res = DtoBinFloatsEquals(loc, lhs, rhs, op); } diff --git a/gen/classes.cpp b/gen/classes.cpp index 51a7a554f3..4ac7ef8978 100644 --- a/gen/classes.cpp +++ b/gen/classes.cpp @@ -80,17 +80,20 @@ DValue *DtoNewClass(const Loc &loc, TypeClass *tc, NewExp *newexp) { // allocate LLValue *mem; bool doInit = true; - if (newexp->onstack) { + if (newexp->placement) { + mem = DtoLVal(newexp->placement); + } else if (newexp->onstack) { mem = DtoRawAlloca(irClass->getLLStructType(), tc->sym->alignsize, ".newclass_alloca"); + } else if (global.params.ehnogc && newexp->thrownew) { + // _d_newThrowable template lowering + assert(newexp->lowering); + mem = DtoRVal(newexp->lowering); + doInit = false; } else { - const bool useEHAlloc = global.params.ehnogc && newexp->thrownew; - llvm::Function *fn = getRuntimeFunction( - loc, gIR->module, useEHAlloc ? "_d_newThrowable" : "_d_allocclass"); + llvm::Function *fn = getRuntimeFunction(loc, gIR->module, "_d_allocclass"); LLConstant *ci = irClass->getClassInfoSymbol(); - mem = gIR->CreateCallOrInvoke( - fn, ci, useEHAlloc ? ".newthrowable" : ".newclass_gc"); - doInit = !useEHAlloc; + mem = gIR->CreateCallOrInvoke(fn, ci, ".newclass_gc"); } // init @@ -249,7 +252,7 @@ DValue *DtoCastClass(const Loc &loc, DValue *val, Type *_to) { return new DImValue(_to, gIR->ir->CreateICmpNE(llval, zero)); } // class -> integer - if (to->isintegral()) { + if (to->isIntegral()) { IF_LOG Logger::println("to %s", to->toChars()); // get class ptr diff --git a/gen/complex.cpp b/gen/complex.cpp index af060af79d..dec5140b1c 100644 --- a/gen/complex.cpp +++ b/gen/complex.cpp @@ -139,9 +139,9 @@ void DtoGetComplexParts(const Loc &loc, Type *to, DValue *val, DValue *&re, Type *t = val->type->toBasetype(); - if (t->iscomplex()) { + if (t->isComplex()) { DValue *v = DtoCastComplex(loc, val, to); - if (to->iscomplex()) { + if (to->isComplex()) { if (v->isLVal()) { LLValue *reVal = DtoGEP(DtoType(v->type), DtoLVal(v), 0u, 0, ".re_part"); LLValue *imVal = DtoGEP(DtoType(v->type), DtoLVal(v), 0, 1, ".im_part"); @@ -158,13 +158,13 @@ void DtoGetComplexParts(const Loc &loc, Type *to, DValue *val, DValue *&re, } else { DtoGetComplexParts(loc, to, v, re, im); } - } else if (t->isimaginary()) { + } else if (t->isImaginary()) { re = nullptr; im = DtoCastFloat(loc, val, baseimty); - } else if (t->isfloating()) { + } else if (t->isFloating()) { re = DtoCastFloat(loc, val, baserety); im = nullptr; - } else if (t->isintegral()) { + } else if (t->isIntegral()) { re = DtoCastInt(loc, val, baserety); im = nullptr; } else { @@ -436,7 +436,7 @@ LLValue *DtoComplexEquals(const Loc &loc, EXP op, DValue *lhs, DValue *rhs) { DValue *DtoCastComplex(const Loc &loc, DValue *val, Type *_to) { Type *to = _to->toBasetype(); Type *vty = val->type->toBasetype(); - if (to->iscomplex()) { + if (to->isComplex()) { if (size(vty) == size(to)) { return val; } @@ -456,7 +456,7 @@ DValue *DtoCastComplex(const Loc &loc, DValue *val, Type *_to) { LLValue *pair = DtoAggrPair(DtoType(_to), re, im); return new DImValue(_to, pair); } - if (to->isimaginary()) { + if (to->isImaginary()) { // FIXME: this loads both values, even when we only need one LLValue *v = DtoRVal(val); LLValue *impart = gIR->ir->CreateExtractValue(v, 1, ".im_part"); @@ -481,7 +481,7 @@ DValue *DtoCastComplex(const Loc &loc, DValue *val, Type *_to) { return new DImValue( _to, DtoComplexEquals(loc, EXP::notEqual, val, DtoNullValue(vty))); } - if (to->isfloating() || to->isintegral()) { + if (to->isFloating() || to->isIntegral()) { // FIXME: this loads both values, even when we only need one LLValue *v = DtoRVal(val); LLValue *repart = gIR->ir->CreateExtractValue(v, 0, ".re_part"); diff --git a/gen/declarations.cpp b/gen/declarations.cpp index 9613d190bc..e8c3a4ad5a 100644 --- a/gen/declarations.cpp +++ b/gen/declarations.cpp @@ -382,7 +382,7 @@ class CodegenVisitor : public Visitor { ////////////////////////////////////////////////////////////////////////// void visit(AttribDeclaration *decl) override { - Dsymbols *d = decl->include(nullptr); + Dsymbols *d = include(decl, nullptr); if (d) { for (auto s : *d) { diff --git a/gen/dibuilder.cpp b/gen/dibuilder.cpp index 5843c98e58..cabbe23ff6 100644 --- a/gen/dibuilder.cpp +++ b/gen/dibuilder.cpp @@ -353,7 +353,7 @@ DIType DIBuilder::CreateEnumType(TypeEnum *type) { // just emit a typedef for non-integral base types auto tb = type->toBasetype(); - if (!tb->isintegral()) { + if (!tb->isIntegral()) { auto tbase = CreateTypeDescription(tb); return DBuilder.createTypedef(tbase, name, file, lineNumber, scope); } @@ -509,7 +509,7 @@ void DIBuilder::AddStaticMembers(AggregateDeclaration *ad, DIFile file, std::function visitMembers = [&](Dsymbols *members) { for (auto s : *members) { if (auto attrib = s->isAttribDeclaration()) { - if (Dsymbols *d = attrib->include(nullptr)) + if (Dsymbols *d = include(attrib, nullptr)) visitMembers(d); } else if (auto tmixin = s->isTemplateMixin()) { // FIXME: static variables inside a template mixin need to be put inside @@ -739,7 +739,7 @@ DISubroutineType DIBuilder::CreateFunctionType(Type *type, }; // the first 'param' is the return value - pushParam(t->next, t->isref()); + pushParam(t->next, t->isRef()); // then the implicit 'this'/context pointer if (fd) { @@ -822,7 +822,7 @@ DIType DIBuilder::CreateTypeDescription(Type *t, bool voidToUbyte) { return CreateEnumType(te); if (auto tv = t->isTypeVector()) return CreateVectorType(tv); - if (t->isintegral() || t->isfloating()) + if (t->isIntegral() || t->isFloating()) return CreateBasicType(t); if (auto tp = t->isTypePointer()) return CreatePointerType(tp); diff --git a/gen/dvalue.cpp b/gen/dvalue.cpp index bdad72263b..a3eb510a08 100644 --- a/gen/dvalue.cpp +++ b/gen/dvalue.cpp @@ -182,7 +182,7 @@ DRValue *DBitFieldLValue::getRVal() { LLValue *v = gIR->ir->CreateAlignedLoad(intType, ptr, llvm::MaybeAlign(1)); // TODO: byte-swap v for big-endian targets? - if (bf->type->isunsigned()) { + if (bf->type->isUnsigned()) { if (auto n = bf->bitOffset) v = gIR->ir->CreateLShr(v, n); const auto mask = llvm::APInt::getLowBitsSet(sizeInBits, bf->fieldWidth); diff --git a/gen/function-inlining.cpp b/gen/function-inlining.cpp index 4e51a4ee48..f7cc9ccbc4 100644 --- a/gen/function-inlining.cpp +++ b/gen/function-inlining.cpp @@ -158,7 +158,7 @@ bool defineAsExternallyAvailable(FuncDeclaration &fdecl) { IF_LOG Logger::println("Potential inlining candidate"); - if (fdecl.semanticRun < PASS::semantic3) { + if (fdecl.semanticRun() < PASS::semantic3) { IF_LOG Logger::println("Do semantic analysis"); LOG_SCOPE @@ -180,7 +180,7 @@ bool defineAsExternallyAvailable(FuncDeclaration &fdecl) { IF_LOG Logger::println("Errors occured during semantic analysis."); return false; } - assert(fdecl.semanticRun >= PASS::semantic3done); + assert(fdecl.semanticRun() >= PASS::semantic3done); } // FuncDeclaration::naked is set by the AsmParser during semantic3 analysis, diff --git a/gen/functions.cpp b/gen/functions.cpp index 19cb709cef..8890bb2ea6 100644 --- a/gen/functions.cpp +++ b/gen/functions.cpp @@ -22,10 +22,10 @@ #include "dmd/statement.h" #include "dmd/target.h" #include "dmd/template.h" +#include "dmd/timetrace.h" #include "driver/cl_options.h" #include "driver/cl_options_instrumentation.h" #include "driver/cl_options_sanitizers.h" -#include "driver/timetrace.h" #include "gen/abi/abi.h" #include "gen/arrays.h" #include "gen/classes.h" @@ -98,7 +98,7 @@ llvm::FunctionType *DtoFunctionType(Type *type, IrFuncTy &irFty, Type *thistype, newIrFty.ret = new IrFuncTyArg(Type::tint32, false); } else { Type *rt = f->next; - const bool byref = f->isref() && rt->toBasetype()->ty != TY::Tvoid; + const bool byref = f->isRef() && rt->toBasetype()->ty != TY::Tvoid; llvm::AttrBuilder attrs(getGlobalContext()); if (!byref && abi->returnInArg(f, fd && fd->needThis())) { @@ -140,13 +140,13 @@ llvm::FunctionType *DtoFunctionType(Type *type, IrFuncTy &irFty, Type *thistype, } bool hasObjCSelector = false; - if (fd && fd->_linkage == LINK::objc) { + if (fd && fd->_linkage() == LINK::objc) { auto ftype = (TypeFunction*)fd->type; if (fd->objc.selector) { hasObjCSelector = true; } else if (fd->parent->isClassDeclaration()) { - if(fd->isFinal() || ftype->isproperty()) { + if(fd->isFinal() || ftype->isProperty()) { // HACK: Ugly hack, but final functions for some reason don't actually declare a selector. // However, this does make it more flexible. @@ -362,7 +362,7 @@ void DtoResolveFunction(FuncDeclaration *fdecl, const bool willDeclare) { } else if (tempdecl->llvmInternal == LLVMinline_ir) { Logger::println("magic inline ir found"); assert(fdecl->llvmInternal == LLVMinline_ir); - fdecl->_linkage = LINK::c; + fdecl->_linkage(LINK::c); Type *type = fdecl->type; assert(type->ty == TY::Tfunction); static_cast(type)->linkage = LINK::c; @@ -432,8 +432,6 @@ void applyTargetMachineAttributes(llvm::Function &func, opts::setFunctionAttributes(cpu, features, func); if (opts::fFastMath) // -ffast-math[=true] overrides -enable-unsafe-fp-math func.addFnAttr("unsafe-fp-math", "true"); - if (!func.hasFnAttribute("frame-pointer")) // not explicitly set by user - func.addFnAttr("frame-pointer", isOptimizationEnabled() ? "none" : "all"); } void applyXRayAttributes(FuncDeclaration &fdecl, llvm::Function &func) { @@ -448,6 +446,23 @@ void applyXRayAttributes(FuncDeclaration &fdecl, llvm::Function &func) { } } +// Keep frame pointers by default with enabled optimizations? +// The logic is very loosely based on clang's +// `useFramePointerForTargetByDefault()`, as well as backtrace test results for +// druntime-test-exceptions-release. +bool keepFramePointersByDefault() { + const auto &triple = *global.params.targetTriple; + if (triple.isAndroid()) + return true; + if (triple.isAArch64()) + return !triple.isOSWindows(); + if (triple.getArch() == llvm::Triple::x86) + return triple.isOSWindows(); // required for druntime backtraces + if (triple.getArch() == llvm::Triple::x86_64) + return !(triple.isOSWindows() || triple.isGNUEnvironment()); + return false; +} + void onlyOneMainCheck(FuncDeclaration *fd) { if (!fd->fbody) // multiple *declarations* are fine return; @@ -514,7 +529,7 @@ void DtoDeclareFunction(FuncDeclaration *fdecl, const bool willDefine) { } else if (defineOnDeclare(fdecl, /*isFunction=*/true)) { Logger::println("Function is inside a linkonce_odr template, will be " "defined after declaration."); - if (fdecl->semanticRun < PASS::semantic3done) { + if (fdecl->semanticRun() < PASS::semantic3done) { Logger::println("Function hasn't had sema3 run yet, running it now."); const bool semaSuccess = functionSemantic3(fdecl); (void)semaSuccess; @@ -635,7 +650,7 @@ void DtoDeclareFunction(FuncDeclaration *fdecl, const bool willDefine) { } else { if (fdecl->inlining == PINLINE::always) { // If the function contains DMD-style inline assembly. - if (fdecl->hasReturnExp & 32) { + if (fdecl->hasInlineAsm()) { // The presence of DMD-style inline assembly in a function causes that // function to become never-inline. So, if this function contains DMD-style // inline assembly we'll emit an error as it can't be made always-inline. @@ -955,16 +970,10 @@ void emulateWeakAnyLinkageForMSVC(IrFunction *irFunc, LINK linkage) { } // anonymous namespace void DtoDefineFunction(FuncDeclaration *fd, bool linkageAvailableExternally) { - TimeTraceScope timeScope([fd]() { - std::string name("Codegen func "); - name += fd->toChars(); - return name; - }, - [fd]() { - std::string detail = fd->toPrettyChars(); - return detail; - }, - fd->loc); + dmd::timeTraceBeginEvent(TimeTraceEventType::codegenFunction); + SCOPE_EXIT { + dmd::timeTraceEndEvent(TimeTraceEventType::codegenFunction, fd); + }; IF_LOG Logger::println("DtoDefineFunction(%s): %s", fd->toPrettyChars(), fd->loc.toChars()); @@ -997,7 +1006,7 @@ void DtoDefineFunction(FuncDeclaration *fd, bool linkageAvailableExternally) { return; } - if (fd->semanticRun == PASS::semanticdone) { + if (fd->semanticRun() == PASS::semanticdone) { // This function failed semantic3() with errors but the errors were gagged. // In contrast to DMD we immediately bail out here, since other parts of // the codegen expect irFunc to be set for defined functions. @@ -1047,7 +1056,7 @@ void DtoDefineFunction(FuncDeclaration *fd, bool linkageAvailableExternally) { // nested context creation code. FuncDeclaration *parent = fd; while ((parent = getParentFunc(parent))) { - if (parent->semanticRun != PASS::semantic3done || + if (parent->semanticRun() != PASS::semantic3done || parent->hasSemantic3Errors()) { IF_LOG Logger::println( "Ignoring nested function with unanalyzed parent."); @@ -1060,7 +1069,7 @@ void DtoDefineFunction(FuncDeclaration *fd, bool linkageAvailableExternally) { assert(fd->ident != Id::empty); - if (fd->semanticRun != PASS::semantic3done) { + if (fd->semanticRun() != PASS::semantic3done) { error(fd->loc, "Internal Compiler Error: function not fully analyzed; " "previous unreported errors compiling `%s`?", @@ -1180,6 +1189,17 @@ void DtoDefineFunction(FuncDeclaration *fd, bool linkageAvailableExternally) { func->addFnAttr("use-sample-profile"); } + if (fd->hasInlineAsm()) { + // disable frame-pointer-elimination for functions with DMD-style inline asm + func->addFnAttr("frame-pointer", "all"); + } else if (!func->hasFnAttribute("frame-pointer")) { + // not explicitly set by user + func->addFnAttr("frame-pointer", + isOptimizationEnabled() && !keepFramePointersByDefault() + ? "none" + : "all"); + } + llvm::BasicBlock *beginbb = llvm::BasicBlock::Create(gIR->context(), "", func); @@ -1216,12 +1236,6 @@ void DtoDefineFunction(FuncDeclaration *fd, bool linkageAvailableExternally) { emitDMDStyleFunctionTrace(*gIR, fd, funcGen); } - // disable frame-pointer-elimination for functions with DMD-style inline asm - if (fd->hasReturnExp & 32) { - func->addFnAttr( - llvm::Attribute::get(gIR->context(), "frame-pointer", "all")); - } - // give the 'this' parameter (an lvalue) storage and debug info if (irFty.arg_this) { LLValue *thisvar = irFunc->thisArg; diff --git a/gen/llvmhelpers.cpp b/gen/llvmhelpers.cpp index c426202859..b91fa2be72 100644 --- a/gen/llvmhelpers.cpp +++ b/gen/llvmhelpers.cpp @@ -418,7 +418,7 @@ void DtoAssign(const Loc &loc, DValue *lhs, DValue *rhs, EXP op, Logger::cout() << "r : " << *r << '\n'; } DtoStore(r, l); - } else if (t->iscomplex()) { + } else if (t->isComplex()) { LLValue *dst = DtoLVal(lhs); LLValue *src = DtoRVal(DtoCast(loc, rhs, lhs->type)); DtoStore(src, dst); @@ -461,7 +461,7 @@ DValue *DtoNullValue(Type *type, Loc loc) { LLType *lltype = DtoType(basetype); // complex, needs to be first since complex are also floating - if (basetype->iscomplex()) { + if (basetype->isComplex()) { LLType *basefp = DtoComplexBaseType(basetype); LLValue *res = DtoAggrPair(DtoType(type), LLConstant::getNullValue(basefp), LLConstant::getNullValue(basefp)); @@ -469,7 +469,7 @@ DValue *DtoNullValue(Type *type, Loc loc) { } // integer, floating, pointer, assoc array, delegate and class have no special // representation - if (basetype->isintegral() || basetype->isfloating() || + if (basetype->isIntegral() || basetype->isFloating() || basety == TY::Tpointer || basety == TY::Tnull || basety == TY::Tclass || basety == TY::Tdelegate || basety == TY::Taarray) { return new DNullValue(type, LLConstant::getNullValue(lltype)); @@ -493,7 +493,7 @@ DValue *DtoCastInt(const Loc &loc, DValue *val, Type *_to) { Type *to = _to->toBasetype(); Type *from = val->type->toBasetype(); - assert(from->isintegral()); + assert(from->isIntegral()); LLValue *rval = DtoRVal(val); if (rval->getType() == tolltype) { @@ -506,7 +506,7 @@ DValue *DtoCastInt(const Loc &loc, DValue *val, Type *_to) { if (to->ty == TY::Tbool) { LLValue *zero = LLConstantInt::get(rval->getType(), 0, false); rval = gIR->ir->CreateICmpNE(rval, zero); - } else if (to->isintegral()) { + } else if (to->isIntegral()) { if (fromsz < tosz || from->ty == TY::Tbool) { IF_LOG Logger::cout() << "cast to: " << *tolltype << '\n'; if (isLLVMUnsigned(from) || from->ty == TY::Tbool) { @@ -519,10 +519,10 @@ DValue *DtoCastInt(const Loc &loc, DValue *val, Type *_to) { } else { rval = DtoBitCast(rval, tolltype); } - } else if (to->iscomplex()) { + } else if (to->isComplex()) { return DtoComplex(loc, to, val); - } else if (to->isfloating()) { - if (from->isunsigned()) { + } else if (to->isFloating()) { + if (from->isUnsigned()) { rval = new llvm::UIToFPInst(rval, tolltype, "", gIR->scopebb()); } else { rval = new llvm::SIToFPInst(rval, tolltype, "", gIR->scopebb()); @@ -561,7 +561,7 @@ DValue *DtoCastPtr(const Loc &loc, DValue *val, Type *to) { LLValue *src = DtoRVal(val); LLValue *zero = LLConstant::getNullValue(src->getType()); rval = gIR->ir->CreateICmpNE(src, zero); - } else if (totype->isintegral()) { + } else if (totype->isIntegral()) { rval = new llvm::PtrToIntInst(DtoRVal(val), tolltype, "", gIR->scopebb()); } else { error(loc, "invalid cast from `%s` to `%s`", val->type->toChars(), @@ -581,7 +581,7 @@ DValue *DtoCastFloat(const Loc &loc, DValue *val, Type *to) { Type *totype = to->toBasetype(); Type *fromtype = val->type->toBasetype(); - assert(fromtype->isfloating()); + assert(fromtype->isFloating()); size_t fromsz = size(fromtype); size_t tosz = size(totype); @@ -592,9 +592,9 @@ DValue *DtoCastFloat(const Loc &loc, DValue *val, Type *to) { rval = DtoRVal(val); LLValue *zero = LLConstant::getNullValue(rval->getType()); rval = gIR->ir->CreateFCmpUNE(rval, zero); - } else if (totype->iscomplex()) { + } else if (totype->isComplex()) { return DtoComplex(loc, to, val); - } else if (totype->isfloating()) { + } else if (totype->isFloating()) { if (fromsz == tosz) { rval = DtoRVal(val); assert(rval->getType() == tolltype); @@ -607,8 +607,8 @@ DValue *DtoCastFloat(const Loc &loc, DValue *val, Type *to) { to->toChars()); fatal(); } - } else if (totype->isintegral()) { - if (totype->isunsigned()) { + } else if (totype->isIntegral()) { + if (totype->isUnsigned()) { rval = new llvm::FPToUIInst(DtoRVal(val), tolltype, "", gIR->scopebb()); } else { rval = new llvm::FPToSIInst(DtoRVal(val), tolltype, "", gIR->scopebb()); @@ -712,16 +712,16 @@ DValue *DtoCast(const Loc &loc, DValue *val, Type *to) { LOG_SCOPE; if (fromtype->ty == TY::Tvector) { - // First, handle vector types (which can also be isintegral()). + // First, handle vector types (which can also be isIntegral()). return DtoCastVector(loc, val, to); } - if (fromtype->isintegral()) { + if (fromtype->isIntegral()) { return DtoCastInt(loc, val, to); } - if (fromtype->iscomplex()) { + if (fromtype->isComplex()) { return DtoCastComplex(loc, val, to); } - if (fromtype->isfloating()) { + if (fromtype->isFloating()) { return DtoCastFloat(loc, val, to); } @@ -975,7 +975,7 @@ DValue *DtoDeclarationExp(Dsymbol *declaration) { } else if (AttribDeclaration *a = declaration->isAttribDeclaration()) { Logger::println("AttribDeclaration"); // choose the right set in case this is a conditional declaration - if (auto d = a->include(nullptr)) { + if (auto d = include(a, nullptr)) { for (unsigned i = 0; i < d->length; ++i) { DtoDeclarationExp((*d)[i]); } @@ -1240,9 +1240,9 @@ static char *DtoOverloadedIntrinsicName(TemplateInstance *ti, Type *T = static_cast(ti->tdtypes[0]); char prefix; - if (T->isfloating() && !T->iscomplex()) { + if (T->isFloating() && !T->isComplex()) { prefix = 'f'; - } else if (T->isintegral()) { + } else if (T->isIntegral()) { prefix = 'i'; } else { error(ti->loc, "%s `%s` has invalid template parameter for intrinsic: `%s`", @@ -1365,7 +1365,7 @@ bool isSpecialRefVar(VarDeclaration *vd) { //////////////////////////////////////////////////////////////////////////////// bool isLLVMUnsigned(Type *t) { - return t->isunsigned() || t->ty == TY::Tpointer; + return t->isUnsigned() || t->ty == TY::Tpointer; } //////////////////////////////////////////////////////////////////////////////// @@ -1800,7 +1800,7 @@ FuncDeclaration *getParentFunc(Dsymbol *sym) { if (auto fld = fd->isFuncLiteralDeclaration()) { if (fld->tok == TOK::function_) return nullptr; - } else if (fd->isStatic() || (!fd->isThis() && fd->_linkage != LINK::d)) { + } else if (fd->isStatic() || (!fd->isThis() && fd->_linkage() != LINK::d)) { return nullptr; } } diff --git a/gen/mangling.cpp b/gen/mangling.cpp index fd7932ab5d..5ca9e3c77c 100644 --- a/gen/mangling.cpp +++ b/gen/mangling.cpp @@ -100,7 +100,7 @@ std::string hashSymbolName(llvm::StringRef name, Dsymbol *symb) { std::string getIRMangledName(FuncDeclaration *fdecl, LINK link) { std::string mangledName = mangleExact(fdecl); - if (fdecl->adFlags & 4) { // nounderscore + if (fdecl->noUnderscore()) { mangledName.insert(0, "\1"); } diff --git a/gen/modules.cpp b/gen/modules.cpp index 05580a35d2..06a8f3bbee 100644 --- a/gen/modules.cpp +++ b/gen/modules.cpp @@ -22,8 +22,8 @@ #include "dmd/statement.h" #include "dmd/target.h" #include "dmd/template.h" +#include "dmd/timetrace.h" #include "driver/cl_options_instrumentation.h" -#include "driver/timetrace.h" #include "gen/abi/abi.h" #include "gen/arrays.h" #include "gen/functions.h" @@ -426,7 +426,7 @@ void addModuleFlags(llvm::Module &m) { } // anonymous namespace void codegenModule(IRState *irs, Module *m) { - TimeTraceScope timeScope("Generate IR", m->toChars(), m->loc); + dmd::TimeTraceScope timeScope("Generate IR", m->toChars(), m->loc); assert(!irs->dmodule && "irs->module not null, codegen already in progress?!"); diff --git a/gen/naked.cpp b/gen/naked.cpp index 4d9ed26cc0..481438664c 100644 --- a/gen/naked.cpp +++ b/gen/naked.cpp @@ -270,16 +270,16 @@ void emitABIReturnAsmStmt(IRAsmBlock *asmblock, const Loc &loc, // x86 if (triple.getArch() == llvm::Triple::x86) { - if (rt->isintegral() || rt->ty == TY::Tpointer || rt->ty == TY::Tclass || + if (rt->isIntegral() || rt->ty == TY::Tpointer || rt->ty == TY::Tclass || rt->ty == TY::Taarray) { if (size(rt) == 8) { as->out.c = "=A,"; } else { as->out.c = "={ax},"; } - } else if (rt->isfloating()) { - if (rt->iscomplex()) { - if (fdecl->_linkage == LINK::d) { + } else if (rt->isFloating()) { + if (rt->isComplex()) { + if (fdecl->_linkage() == LINK::d) { // extern(D) always returns on the FPU stack as->out.c = "={st},={st(1)},"; asmblock->retn = 2; @@ -334,10 +334,10 @@ void emitABIReturnAsmStmt(IRAsmBlock *asmblock, const Loc &loc, // x86_64 else if (triple.getArch() == llvm::Triple::x86_64) { - if (rt->isintegral() || rt->ty == TY::Tpointer || rt->ty == TY::Tclass || + if (rt->isIntegral() || rt->ty == TY::Tpointer || rt->ty == TY::Tclass || rt->ty == TY::Taarray) { as->out.c = "={ax},"; - } else if (rt->isfloating()) { + } else if (rt->isFloating()) { const bool isWin64 = triple.isOSWindows(); if (rt == Type::tcomplex80 && !isWin64) { @@ -358,7 +358,7 @@ void emitABIReturnAsmStmt(IRAsmBlock *asmblock, const Loc &loc, as->out.c = "={xmm0},"; asmblock->retty = LLType::getDoubleTy(gIR->context()); } - } else if (rt->iscomplex()) { + } else if (rt->isComplex()) { if (isWin64) { // Win64: cdouble and creal are returned via sret // don't add anything! diff --git a/gen/objcgen.cpp b/gen/objcgen.cpp index 8414fb428c..2eb8ccff01 100644 --- a/gen/objcgen.cpp +++ b/gen/objcgen.cpp @@ -720,7 +720,7 @@ ObjcMethodInfo *ObjCState::getMethod(FuncDeclaration *decl) { } // Skip functions not marked as extern(Objective-C). - if (decl->_linkage != LINK::objc) + if (decl->_linkage() != LINK::objc) return nullptr; methods[decl] = { /*.decl =*/ decl }; diff --git a/gen/runtime.cpp b/gen/runtime.cpp index 2a809b3417..a00649f934 100644 --- a/gen/runtime.cpp +++ b/gen/runtime.cpp @@ -609,10 +609,6 @@ static void buildRuntimeModule() { createFwdDecl(LINK::c, objectTy, {"_d_newclass", "_d_allocclass"}, {classInfoTy}, {STCconst}); - // Throwable _d_newThrowable(const ClassInfo ci) - createFwdDecl(LINK::c, throwableTy, {"_d_newThrowable"}, {classInfoTy}, - {STCconst}); - // void _d_delarray_t(void[]* p, const TypeInfo_Struct ti) createFwdDecl(LINK::c, voidTy, {"_d_delarray_t"}, {voidArrayPtrTy, structTypeInfoTy}, {0, STCconst}); @@ -801,10 +797,10 @@ static void buildRuntimeModule() { ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// - // void invariant._d_invariant(Object o) + // void rt.invariant_._d_invariant(Object o) { - static const std::string mangle = - getIRMangledFuncName("_D9invariant12_d_invariantFC6ObjectZv", LINK::d); + static const std::string mangle = getIRMangledFuncName( + "_D2rt10invariant_12_d_invariantFC6ObjectZv", LINK::d); createFwdDecl(LINK::d, voidTy, {mangle}, {objectTy}); } diff --git a/gen/statements.cpp b/gen/statements.cpp index 267306fa9f..b1a97ad7ad 100644 --- a/gen/statements.cpp +++ b/gen/statements.cpp @@ -204,7 +204,7 @@ class ToIRVisitor : public Visitor { } else if (funcType->getReturnType()->isVoidTy()) { // if the IR function's return type is void (but not the D one), it uses // sret - assert(!f->type->isref()); + assert(!f->type->isRef()); LLValue *sretPointer = f->sretArg; assert(sretPointer); @@ -241,7 +241,7 @@ class ToIRVisitor : public Visitor { } DValue *dval = nullptr; // call postblit if necessary - if (!f->type->isref()) { + if (!f->type->isRef()) { dval = toElem(stmt->exp); LLValue *vthis = (DtoIsInMemoryOnly(dval->type) ? DtoLVal(dval) : DtoRVal(dval)); @@ -1433,13 +1433,13 @@ class ToIRVisitor : public Visitor { irs->DBuilder.EmitBlockStart(stmt->loc); // evaluate lwr/upr - assert(stmt->lwr->type->isintegral()); + assert(stmt->lwr->type->isIntegral()); LLValue *lower = DtoRVal(toElemDtor(stmt->lwr)); - assert(stmt->upr->type->isintegral()); + assert(stmt->upr->type->isIntegral()); LLValue *upper = DtoRVal(toElemDtor(stmt->upr)); // handle key - assert(stmt->key->type->isintegral()); + assert(stmt->key->type->isIntegral()); LLValue *keyval = DtoRawVarDeclaration(stmt->key); LLType *keytype = DtoType(stmt->key->type); // store initial value in key diff --git a/gen/target.cpp b/gen/target.cpp index 7068c19b9c..7b599398d1 100644 --- a/gen/target.cpp +++ b/gen/target.cpp @@ -287,7 +287,7 @@ unsigned Target::alignsize(Type *type) { */ unsigned Target::fieldalign(Type *type) { return DtoAlignment(type); } -Type *Target::va_listType(const Loc &loc, Scope *sc) { +Type *Target::va_listType(Loc loc, Scope *sc) { if (!tvalist) tvalist = typeSemantic(gABI->vaListType(), loc, sc); return tvalist; @@ -343,12 +343,12 @@ TypeTuple *Target::toArgTypes(Type *t) { } bool Target::isReturnOnStack(TypeFunction *tf, bool needsThis) { - return !tf->isref() && gABI->returnInArg(tf, needsThis); + return !tf->isRef() && gABI->returnInArg(tf, needsThis); } bool Target::preferPassByRef(Type *t) { return gABI->preferPassByRef(t); } -Expression *Target::getTargetInfo(const char *name_, const Loc &loc) { +Expression *Target::getTargetInfo(const char *name_, Loc loc) { const llvm::StringRef name(name_); const auto &triple = *global.params.targetTriple; diff --git a/gen/tocall.cpp b/gen/tocall.cpp index 9267e13214..b53c307944 100644 --- a/gen/tocall.cpp +++ b/gen/tocall.cpp @@ -916,7 +916,7 @@ DValue *DtoCallFunction(const Loc &loc, Type *resulttype, DValue *fnval, // call the function llvm::CallBase *call = gIR->funcGen().callOrInvoke(callable, callableTy, args, - "", tf->isnothrow()); + "", tf->isNothrow()); // PGO: Insert instrumentation or attach profile metadata at indirect call // sites. @@ -932,7 +932,7 @@ DValue *DtoCallFunction(const Loc &loc, Type *resulttype, DValue *fnval, LLValue *retllval = irFty.arg_sret ? args[sretArgIndex] : static_cast(call); bool retValIsLVal = - (tf->isref() && returnTy != TY::Tvoid) || (irFty.arg_sret != nullptr); + (tf->isRef() && returnTy != TY::Tvoid) || (irFty.arg_sret != nullptr); if (!retValIsLVal) { // let the ABI transform the return value back @@ -952,7 +952,7 @@ DValue *DtoCallFunction(const Loc &loc, Type *resulttype, DValue *fnval, returntype->toChars(), rbase->toChars()); switch (rbase->ty) { case TY::Tarray: - if (tf->isref()) { + if (tf->isRef()) { retllval = DtoBitCast(retllval, DtoType(pointerTo(rbase))); } else { retllval = DtoSlicePaint(retllval, DtoType(rbase)); @@ -960,7 +960,7 @@ DValue *DtoCallFunction(const Loc &loc, Type *resulttype, DValue *fnval, break; case TY::Tsarray: - if (nextbase->ty == TY::Tvector && !tf->isref()) { + if (nextbase->ty == TY::Tvector && !tf->isRef()) { if (!retValIsLVal) { // static arrays need to be dumped to memory; use vector alignment retllval = @@ -975,7 +975,7 @@ DValue *DtoCallFunction(const Loc &loc, Type *resulttype, DValue *fnval, case TY::Tclass: case TY::Taarray: case TY::Tpointer: - if (tf->isref()) { + if (tf->isRef()) { retllval = DtoBitCast(retllval, DtoType(pointerTo(rbase))); } else { retllval = DtoBitCast(retllval, DtoType(rbase)); @@ -983,7 +983,7 @@ DValue *DtoCallFunction(const Loc &loc, Type *resulttype, DValue *fnval, break; case TY::Tstruct: - if (nextbase->ty == TY::Taarray && !tf->isref()) { + if (nextbase->ty == TY::Taarray && !tf->isRef()) { // In the D2 frontend, the associative array type and its // object.AssociativeArray representation are used // interchangably in some places. However, AAs are returned diff --git a/gen/toconstelem.cpp b/gen/toconstelem.cpp index c215d5769a..60ba2afe6c 100644 --- a/gen/toconstelem.cpp +++ b/gen/toconstelem.cpp @@ -118,7 +118,7 @@ class ToConstElemVisitor : public Visitor { } else { assert(llvm::isa(t)); result = LLConstantInt::get(t, static_cast(e->getInteger()), - !e->type->isunsigned()); + !e->type->isUnsigned()); assert(result); IF_LOG Logger::cout() << "value = " << *result << '\n'; } @@ -190,7 +190,7 @@ class ToConstElemVisitor : public Visitor { // very similar to emitPointerOffset() in binops.cpp LLConstant *tryEmitPointerOffset(BinExp *e, bool negateOffset) { Type *t1b = e->e1->type->toBasetype(); - if (t1b->ty != TY::Tpointer || !e->e2->type->isintegral()) + if (t1b->ty != TY::Tpointer || !e->e2->type->isIntegral()) return nullptr; Type *const pointeeType = t1b->nextOf(); @@ -545,7 +545,7 @@ class ToConstElemVisitor : public Visitor { e->toChars(), e->type ? e->type->toChars() : "(null)"); LOG_SCOPE; - if (e->useStaticInit) { + if (e->useStaticInit()) { DtoResolveStruct(e->sd); result = getIrAggr(e->sd)->getDefaultInit(); } else { diff --git a/gen/toir.cpp b/gen/toir.cpp index c1dc6d5362..7c74386849 100644 --- a/gen/toir.cpp +++ b/gen/toir.cpp @@ -571,7 +571,7 @@ class ToElemVisitor : public Visitor { // for struct `.init` assignment. if (lhs->isLVal() && e->op == EXP::assign) { if (auto sle = e->e2->isStructLiteralExp()) { - if (sle->useStaticInit) { + if (sle->useStaticInit()) { if (toInPlaceConstruction(lhs->isLVal(), sle)) return; } @@ -809,7 +809,7 @@ class ToElemVisitor : public Visitor { // Only emit this extra code from -O2. // This optimization is only valid for D class method calls (not C++). bool canEmitVTableUnchangedAssumption = - dfnval && dfnval->func && (dfnval->func->_linkage == LINK::d) && + dfnval && dfnval->func && (dfnval->func->_linkage() == LINK::d) && (optLevel() >= 2); if (dfnval && dfnval->func) { @@ -937,7 +937,7 @@ class ToElemVisitor : public Visitor { } // Casts are also "optimized into" SymOffExp by the frontend. - LLValue *llVal = (e->type->toBasetype()->isintegral() + LLValue *llVal = (e->type->toBasetype()->isIntegral() ? p->ir->CreatePtrToInt(offsetValue, DtoType(e->type)) : DtoBitCast(offsetValue, DtoType(e->type))); result = new DImValue(e->type, llVal); @@ -1335,7 +1335,7 @@ class ToElemVisitor : public Visitor { LLValue *eval = nullptr; - if (t->isintegral() || t->ty == TY::Tpointer || t->ty == TY::Tnull) { + if (t->isIntegral() || t->ty == TY::Tpointer || t->ty == TY::Tnull) { llvm::ICmpInst::Predicate icmpPred; tokToICmpPred(e->op, isLLVMUnsigned(t), &icmpPred, &eval); @@ -1351,7 +1351,7 @@ class ToElemVisitor : public Visitor { } eval = p->ir->CreateICmp(icmpPred, a, b); } - } else if (t->isfloating()) { + } else if (t->isFloating()) { llvm::FCmpInst::Predicate cmpop; switch (e->op) { case EXP::lessThan: @@ -1438,7 +1438,7 @@ class ToElemVisitor : public Visitor { // the Tclass catches interface comparisons, regular // class equality should be rewritten as a.opEquals(b) by this time - if (t->isintegral() || t->ty == TY::Tpointer || t->ty == TY::Tclass || + if (t->isIntegral() || t->ty == TY::Tpointer || t->ty == TY::Tclass || t->ty == TY::Tnull) { Logger::println("integral or pointer or interface"); llvm::ICmpInst::Predicate cmpop; @@ -1462,7 +1462,7 @@ class ToElemVisitor : public Visitor { Logger::cout() << "rv: " << *rv << '\n'; } eval = p->ir->CreateICmp(cmpop, lv, rv); - } else if (t->isfloating()) { // includes iscomplex + } else if (t->isFloating()) { // includes iscomplex eval = DtoBinNumericEquals(e->loc, l, r, e->op); } else if (t->ty == TY::Tsarray || t->ty == TY::Tarray) { Logger::println("static or dynamic array"); @@ -1504,10 +1504,10 @@ class ToElemVisitor : public Visitor { Type *e1type = e->e1->type->toBasetype(); Type *e2type = e->e2->type->toBasetype(); - if (e1type->isintegral()) { - assert(e2type->isintegral()); + if (e1type->isIntegral()) { + assert(e2type->isIntegral()); LLValue *one = - LLConstantInt::get(val->getType(), 1, !e2type->isunsigned()); + LLConstantInt::get(val->getType(), 1, !e2type->isUnsigned()); if (e->op == EXP::plusPlus) { post = llvm::BinaryOperator::CreateAdd(val, one, "", p->scopebb()); } else if (e->op == EXP::minusMinus) { @@ -1518,8 +1518,8 @@ class ToElemVisitor : public Visitor { LLConstant *offset = e->op == EXP::plusPlus ? DtoConstUint(1) : DtoConstInt(-1); post = DtoGEP1(DtoMemType(dv->type->nextOf()), val, offset, "", p->scopebb()); - } else if (e1type->iscomplex()) { - assert(e2type->iscomplex()); + } else if (e1type->isComplex()) { + assert(e2type->isComplex()); LLValue *one = LLConstantFP::get(DtoComplexBaseType(e1type), 1.0); LLValue *re, *im; DtoGetComplexParts(e->loc, e1type, dv, re, im); @@ -1529,8 +1529,8 @@ class ToElemVisitor : public Visitor { re = llvm::BinaryOperator::CreateFSub(re, one, "", p->scopebb()); } DtoComplexSet(DtoType(dv->type), lval, re, im); - } else if (e1type->isfloating()) { - assert(e2type->isfloating()); + } else if (e1type->isFloating()) { + assert(e2type->isFloating()); LLValue *one = DtoConstFP(e1type, ldouble(1.0)); if (e->op == EXP::plusPlus) { post = llvm::BinaryOperator::CreateFAdd(val, one, "", p->scopebb()); @@ -1543,7 +1543,7 @@ class ToElemVisitor : public Visitor { // The real part of the complex number has already been updated, skip the // store - if (!e1type->iscomplex()) { + if (!e1type->isComplex()) { DtoStore(post, lval); } result = new DImValue(e->type, val); @@ -1573,6 +1573,7 @@ class ToElemVisitor : public Visitor { // new dynamic array else if (ntype->ty == TY::Tarray) { IF_LOG Logger::println("new dynamic array: %s", e->newtype->toChars()); + assert(!e->placement); assert(e->argprefix == NULL); // get dim assert(e->arguments); @@ -1597,9 +1598,14 @@ class ToElemVisitor : public Visitor { TypeStruct *ts = static_cast(ntype); - // allocate (via _d_newitemT template lowering) - assert(e->lowering); - LLValue *mem = DtoRVal(e->lowering); + LLValue *mem; + if (e->placement) { + mem = DtoLVal(e->placement); + } else { + // allocate (via _d_newitemT template lowering) + assert(e->lowering); + mem = DtoRVal(e->lowering); + } if (!e->member && e->arguments) { IF_LOG Logger::println("Constructing using literal"); @@ -1629,6 +1635,7 @@ class ToElemVisitor : public Visitor { } // new AA else if (auto taa = ntype->isTypeAArray()) { + assert(!e->placement); LLFunction *func = getRuntimeFunction(e->loc, gIR->module, "_aaNew"); LLValue *aaTypeInfo = DtoTypeInfoOf(e->loc, stripModifiers(taa)); LLValue *aa = gIR->CreateCallOrInvoke(func, aaTypeInfo, "aa"); @@ -1639,9 +1646,13 @@ class ToElemVisitor : public Visitor { IF_LOG Logger::println("basic type on heap: %s\n", e->newtype->toChars()); assert(e->argprefix == NULL); - // allocate - LLValue *mem = DtoNew(e->loc, e->newtype); - DLValue tmpvar(e->newtype, mem); + LLValue *mem; + if (e->placement) { + mem = DtoLVal(e->placement); + } else { + // allocate + mem = DtoNew(e->loc, e->newtype); + } Expression *exp = nullptr; if (!e->arguments || e->arguments->length == 0) { @@ -1655,6 +1666,7 @@ class ToElemVisitor : public Visitor { } // try to construct it in-place + DLValue tmpvar(e->newtype, mem); if (!toInPlaceConstruction(&tmpvar, exp)) DtoAssign(e->loc, &tmpvar, toElem(exp), EXP::blit); @@ -1823,8 +1835,8 @@ class ToElemVisitor : public Visitor { Logger::println("calling class invariant"); - const auto fnMangle = - getIRMangledFuncName("_D9invariant12_d_invariantFC6ObjectZv", LINK::d); + const auto fnMangle = getIRMangledFuncName( + "_D2rt10invariant_12_d_invariantFC6ObjectZv", LINK::d); const auto fn = getRuntimeFunction(e->loc, gIR->module, fnMangle.c_str()); const auto arg = DtoRVal(cond); @@ -1999,7 +2011,7 @@ class ToElemVisitor : public Visitor { // We need to actually codegen the function here, as literals are not // added to the module member list. - if (e->func->semanticRun == PASS::semantic3done) { + if (e->func->semanticRun() == PASS::semantic3done) { Dsymbol *owner = e->func->toParent(); while (!owner->isTemplateInstance() && owner->toParent()) { owner = owner->toParent(); @@ -2050,7 +2062,7 @@ class ToElemVisitor : public Visitor { assert(lv->getType() == rv->getType()); } eval = DtoDelegateEquals(e->op, lv, rv); - } else if (t1->isfloating()) { // includes iscomplex + } else if (t1->isFloating()) { // includes iscomplex eval = DtoBinNumericEquals(e->loc, l, r, e->op); } else if (t1->ty == TY::Tpointer || t1->ty == TY::Tclass) { LLValue *lv = DtoRVal(l); @@ -2174,14 +2186,14 @@ class ToElemVisitor : public Visitor { DRValue *dval = toElem(e->e1)->getRVal(); - if (e->type->iscomplex()) { + if (e->type->isComplex()) { result = DtoComplexNeg(e->loc, e->type, dval); return; } LLValue *val = DtoRVal(dval); - if (e->type->isintegral()) { + if (e->type->isIntegral()) { val = p->ir->CreateNeg(val, "negval"); } else { val = p->ir->CreateFNeg(val, "negval"); @@ -2392,7 +2404,7 @@ class ToElemVisitor : public Visitor { e->type->toChars()); LOG_SCOPE; - if (e->useStaticInit) { + if (e->useStaticInit()) { StructDeclaration *sd = e->sd; DtoResolveStruct(sd); diff --git a/gen/tollvm.cpp b/gen/tollvm.cpp index 8b96fc6b1e..63465ed887 100644 --- a/gen/tollvm.cpp +++ b/gen/tollvm.cpp @@ -49,8 +49,8 @@ bool DtoIsInMemoryOnly(Type *type) { void DtoAddExtendAttr(Type *type, llvm::AttrBuilder &attrs) { type = type->toBasetype(); - if (type->isintegral() && type->ty != TY::Tvector && size(type) <= 2) { - attrs.addAttribute(type->isunsigned() ? LLAttribute::ZExt + if (type->isIntegral() && type->ty != TY::Tvector && size(type) <= 2) { + attrs.addAttribute(type->isUnsigned() ? LLAttribute::ZExt : LLAttribute::SExt); } } @@ -139,7 +139,7 @@ LLType *DtoType(Type *t) { */ IF_LOG { Logger::println("Aggregate with multiple Types detected: %s (%s)", - ad->toPrettyChars(), ad->locToChars()); + ad->toPrettyChars(), ad->loc.toChars()); LOG_SCOPE; Logger::println("Existing deco: %s", adType->deco); Logger::println("Mismatching deco: %s", t->deco); diff --git a/gen/trycatchfinally.cpp b/gen/trycatchfinally.cpp index 460b1ba334..fea4389d46 100644 --- a/gen/trycatchfinally.cpp +++ b/gen/trycatchfinally.cpp @@ -112,7 +112,7 @@ void TryCatchScope::emitCatchBodies(IRState &irs, llvm::Value *ehPtrSlot) { * exception handler. At some point should try to do better. */ FuncDeclaration *fdend = - FuncDeclaration::genCfunc(nullptr, Type::tvoid, "__cxa_end_catch"); + dmd::genCfunc(nullptr, Type::tvoid, "__cxa_end_catch"); Expression *efunc = VarExp::create(Loc(), fdend); Expression *ecall = CallExp::create(Loc(), efunc); ecall->type = Type::tvoid; diff --git a/gen/typinf.cpp b/gen/typinf.cpp index c1a5f18b9a..9caaf4e3d9 100644 --- a/gen/typinf.cpp +++ b/gen/typinf.cpp @@ -148,7 +148,7 @@ class DefineVisitor : public Visitor { // void[] init // the array is null if the default initializer is zero - if (!sd->members || decl->tinfo->isZeroInit(decl->loc)) { + if (!sd->members || isZeroInit(decl->tinfo, decl->loc)) { b.push_null_void_array(); } // otherwise emit a void[] with the default initializer @@ -231,6 +231,9 @@ class DefineVisitor : public Visitor { // key typeinfo b.push_typeinfo(tc->index); + // entry typeinfo (key-value pair) + b.push_typeinfo(decl->entry); + // finish b.finalize(gvar); } diff --git a/ir/iraggr.cpp b/ir/iraggr.cpp index a19867af54..86d6c78d5f 100644 --- a/ir/iraggr.cpp +++ b/ir/iraggr.cpp @@ -156,7 +156,7 @@ add_zeros(llvm::SmallVectorImpl &constants, LLConstant *IrAggr::getDefaultInitializer(VarDeclaration *field) { // Issue 9057 workaround caused by issue 14666 fix, see DMD upstream // commit 069f570005. - if (field->_init && field->semanticRun < PASS::semantic2done && + if (field->_init && field->semanticRun() < PASS::semantic2done && field->_scope) { semantic2(field, field->_scope); } diff --git a/ir/irclass.cpp b/ir/irclass.cpp index daf7f72742..b98b002251 100644 --- a/ir/irclass.cpp +++ b/ir/irclass.cpp @@ -240,7 +240,7 @@ LLConstant *IrClass::getVtblInit() { c = DtoCallee(fd); - if (cd->isFuncHidden(fd) && !fd->isFuture()) { + if (isFuncHidden(cd, fd) && !fd->isFuture()) { // fd is hidden from the view of this class. If fd overlaps with any // function in the vtbl[], issue error. for (size_t j = cd->vtblOffset(); j < n; j++) { diff --git a/ir/irstruct.cpp b/ir/irstruct.cpp index 1b0f4003c0..c69c615c2f 100644 --- a/ir/irstruct.cpp +++ b/ir/irstruct.cpp @@ -87,7 +87,7 @@ LLConstant *IrStruct::getTypeInfoInit() { const bool isOpaque = !sd->members; // make sure xtoHash/xopEquals/xopCmp etc. are semantically analyzed - if (!isOpaque && sd->semanticRun < PASS::semantic3done) { + if (!isOpaque && sd->semanticRun() < PASS::semantic3done) { Logger::println( "Struct hasn't had semantic3 yet, calling semanticTypeInfoMembers()"); semanticTypeInfoMembers(sd); @@ -134,7 +134,7 @@ LLConstant *IrStruct::getTypeInfoInit() { b.push_null_void_array(); } else { llvm::Constant *initPtr; - if (ts->isZeroInit(Loc())) { + if (isZeroInit(ts)) { initPtr = getNullPtr(); } else { initPtr = getInitSymbol(); diff --git a/ir/irvar.cpp b/ir/irvar.cpp index fda8c5fd96..aefb49b3a8 100644 --- a/ir/irvar.cpp +++ b/ir/irvar.cpp @@ -90,7 +90,7 @@ void IrGlobal::declare() { if (!V->isThreadlocal()) { // implicitly include extern(D) globals with -dllimport useDLLImport = - (V->isExport() || V->_linkage == LINK::d) && dllimportDataSymbol(V); + (V->isExport() || V->_linkage() == LINK::d) && dllimportDataSymbol(V); } } diff --git a/packaging/dlang-tools_version b/packaging/dlang-tools_version index e9acfb34f9..9045b79495 100644 --- a/packaging/dlang-tools_version +++ b/packaging/dlang-tools_version @@ -1 +1 @@ -v2.110.0 \ No newline at end of file +v2.111.0 \ No newline at end of file diff --git a/packaging/dub_version b/packaging/dub_version index 365cbf9b1a..1eb0982074 100644 --- a/packaging/dub_version +++ b/packaging/dub_version @@ -1 +1 @@ -v1.39.0 \ No newline at end of file +v1.40.0 \ No newline at end of file diff --git a/packaging/reggae_version b/packaging/reggae_version index 2b6fb638fa..3061e9e5a2 100644 --- a/packaging/reggae_version +++ b/packaging/reggae_version @@ -1 +1 @@ -c8fb508ec6b042a21723c807acfa768cfb691383 \ No newline at end of file +v0.12.0 \ No newline at end of file diff --git a/runtime/CMakeLists.txt b/runtime/CMakeLists.txt index b486a514bb..43d9c62148 100644 --- a/runtime/CMakeLists.txt +++ b/runtime/CMakeLists.txt @@ -53,7 +53,7 @@ set(CMAKE_INSTALL_LIBDIR ${CMAKE_INSTALL_PREFIX}/lib${LIB_SUFFIX}) set(DRUNTIME_EXTRA_FLAGS ) set(DRUNTIME_EXTRA_UNITTEST_FLAGS -d-version=CoreUnittest -unittest -checkaction=context -linkonce-templates) set(PHOBOS2_EXTRA_FLAGS ) -set(PHOBOS2_EXTRA_UNITTEST_FLAGS -d-version=StdUnittest -unittest -linkonce-templates) +set(PHOBOS2_EXTRA_UNITTEST_FLAGS -d-version=StdUnittest -unittest -checkaction=context -linkonce-templates) # Shadow the D_FLAGS cache variable by a regular variable containing all base D flags set(D_FLAGS ${D_FLAGS} ${D_EXTRA_FLAGS}) @@ -170,8 +170,6 @@ set(JITRT_DIR ${PROJECT_SOURCE_DIR}/jit-rt CACHE PATH "jit runtime root director # druntime D parts file(GLOB_RECURSE DRUNTIME_D ${RUNTIME_DIR}/src/*.d) list(REMOVE_ITEM DRUNTIME_D ${RUNTIME_DIR}/src/test_runner.d) -# remove unsupported etc/linux/memoryerror.d (see issue #1915) -list(REMOVE_ITEM DRUNTIME_D ${RUNTIME_DIR}/src/etc/linux/memoryerror.d) # remove some modules in rt/ list(REMOVE_ITEM DRUNTIME_D ${RUNTIME_DIR}/src/rt/alloca.d @@ -182,7 +180,6 @@ list(REMOVE_ITEM DRUNTIME_D ${RUNTIME_DIR}/src/rt/sections_osx_x86.d ${RUNTIME_DIR}/src/rt/sections_osx_x86_64.d ${RUNTIME_DIR}/src/rt/sections_solaris.d - ${RUNTIME_DIR}/src/rt/sections_win32.d ) # only include core/sys/ modules matching the platform file(GLOB_RECURSE DRUNTIME_D_BIONIC ${RUNTIME_DIR}/src/core/sys/bionic/*.d) @@ -234,7 +231,7 @@ endif() # druntime ASM parts set(DRUNTIME_ASM) if("${TARGET_SYSTEM}" MATCHES "UNIX") - list(APPEND DRUNTIME_ASM ${RUNTIME_DIR}/src/core/threadasm.S ${RUNTIME_DIR}/src/ldc/eh_asm.S) + list(APPEND DRUNTIME_ASM ${RUNTIME_DIR}/src/core/thread/fiber/switch_context_asm.S ${RUNTIME_DIR}/src/ldc/eh_asm.S) endif() if(PHOBOS2_DIR) @@ -1072,10 +1069,6 @@ macro(file_to_module_name file_name out_module_name) # The logical module name for package.d files doesn't include the # trailing .package part. string(REPLACE ".package" "" module ${module}) - - # rt.invariant doesn't have a module declaration, presumably because - # invariant is a keyword. - string(REPLACE "rt.invariant" "invariant" ${out_module_name} ${module}) endmacro() function(add_tests d_files runner target_suffix) diff --git a/runtime/DRuntimeIntegrationTests.cmake b/runtime/DRuntimeIntegrationTests.cmake index fbb38e9840..719e5848a3 100644 --- a/runtime/DRuntimeIntegrationTests.cmake +++ b/runtime/DRuntimeIntegrationTests.cmake @@ -28,7 +28,7 @@ if(MULTILIB AND "${TARGET_SYSTEM}" MATCHES "APPLE") set(druntime_path "${CMAKE_BINARY_DIR}/lib${LIB_SUFFIX}/libdruntime-ldc.a") endif() else() - set(shared_druntime_path "$") + set(shared_druntime_path "$") if(${BUILD_SHARED_LIBS} STREQUAL "ON") set(druntime_path ${shared_druntime_path}) else() @@ -37,13 +37,7 @@ else() endif() string(REPLACE ";" " " dflags_base "${D_EXTRA_FLAGS}") - string(REPLACE ";" " " cflags_base "${RT_CFLAGS}") -if("${TARGET_SYSTEM}" MATCHES "MSVC") - set(cflags_base "${cflags_base} /Wall") -else() - set(cflags_base "${cflags_base} -Wall -Wl,-rpath,${CMAKE_BINARY_DIR}/lib${LIB_SUFFIX}") -endif() set(linkdl "") if("${TARGET_SYSTEM}" MATCHES "Linux") @@ -63,6 +57,10 @@ else() list(REMOVE_ITEM testnames uuid) endif() +if(TARGET_SYSTEM MATCHES "musl") + set(musl "IS_MUSL=1") +endif() + foreach(name ${testnames}) foreach(build debug release) set(druntime_path_build ${druntime_path}) @@ -83,11 +81,8 @@ foreach(name ${testnames}) CC=${CMAKE_C_COMPILER} CXX=${CMAKE_CXX_COMPILER} DRUNTIME=${druntime_path_build} DRUNTIMESO=${shared_druntime_path_build} CFLAGS_BASE=${cflags_base} DFLAGS_BASE=${dflags_base} ${linkdl} + IN_LDC=1 ${musl} ) set_tests_properties(${fullname} PROPERTIES DEPENDS clean-${fullname}) endforeach() endforeach() - -# HACK: there's a race condition for the debug/release coverage tests -# (temporary in-place modification of source file) -set_tests_properties(druntime-test-coverage-release PROPERTIES DEPENDS druntime-test-coverage-debug) diff --git a/runtime/druntime/Makefile b/runtime/druntime/Makefile index df9543950f..2c022c6997 100644 --- a/runtime/druntime/Makefile +++ b/runtime/druntime/Makefile @@ -314,6 +314,12 @@ $(DOC_OUTPUT_DIR)/core_thread.html : import/core/thread/package.d $(DMD) $(DOC_OUTPUT_DIR)/core_thread_%.html : import/core/thread/%.d $(DMD) $(DMD) $(DDOCFLAGS) -Df$@ project.ddoc $(DOCFMT) $< +$(DOC_OUTPUT_DIR)/core_thread_fiber.html : import/core/thread/fiber/package.d $(DMD) + $(DMD) $(DDOCFLAGS) -Df$@ project.ddoc $(DOCFMT) $< + +$(DOC_OUTPUT_DIR)/core_thread_fiber_%.html : import/core/thread/fiber/%.d $(DMD) + $(DMD) $(DDOCFLAGS) -Df$@ project.ddoc $(DOCFMT) $< + $(DOC_OUTPUT_DIR)/core_builtins.html : import/core/builtins.d $(DMD) $(DMD) $(DDOCFLAGS) -Df$@ project.ddoc $(DOCFMT) $< @@ -387,7 +393,7 @@ $(ROOT)/errno_c$(DOTOBJ) : src/core/stdc/errno.c $(DMD) @mkdir -p $(dir $@) $(DMD) -c $(DFLAGS) -I. -P=-I. $< -of$@ -$(ROOT)/threadasm$(DOTOBJ) : src/core/threadasm.S +$(ROOT)/threadasm$(DOTOBJ) : src/core/thread/fiber/switch_context_asm.S @mkdir -p $(dir $@) $(CC) -c $(CFLAGS) $< -o$@ @@ -421,7 +427,7 @@ HAS_ADDITIONAL_TESTS:=$(shell test -d test && echo 1) ifeq ($(HAS_ADDITIONAL_TESTS),1) ADDITIONAL_TESTS:=test/init_fini test/exceptions test/coverage test/profile test/cycles test/allocations test/typeinfo \ test/aa test/cpuid test/gc test/hash test/lifetime test/shared \ - test/thread test/unittest test/imports test/betterc test/stdcpp test/config test/traits + test/thread test/unittest test/imports test/betterc test/stdcpp test/config test/traits test/importc_compare ifeq (windows,$(OS)) ADDITIONAL_TESTS+=test/uuid else @@ -473,7 +479,7 @@ TESTS_EXTRACTOR=$(ROOT)/tests_extractor$(DOTEXE) BETTERCTESTS_DIR=$(ROOT)/betterctests # macro that returns the module name given the src path -moduleName=$(subst rt.invariant,invariant,$(subst object_,object,$(subst /,.,$(1)))) +moduleName=$(subst /,.,$(1)) $(ROOT)/unittest/% : $(ROOT)/unittest/test_runner$(DOTEXE) @mkdir -p $(dir $@) diff --git a/runtime/druntime/src/__importc_builtins.di b/runtime/druntime/src/__importc_builtins.di index 6779f89fc1..312e16c62c 100644 --- a/runtime/druntime/src/__importc_builtins.di +++ b/runtime/druntime/src/__importc_builtins.di @@ -3,10 +3,10 @@ * 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-2024 + * 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.d) + * Source: $(DRUNTIMESRC __importc_builtins.di) */ @@ -118,6 +118,12 @@ version (DigitalMars) 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; diff --git a/runtime/druntime/src/core/attribute.d b/runtime/druntime/src/core/attribute.d index 9f56908d7a..8ec5b403b7 100644 --- a/runtime/druntime/src/core/attribute.d +++ b/runtime/druntime/src/core/attribute.d @@ -13,6 +13,9 @@ * Makes an Objective-C interface method optional.) * $(TROW $(LREF selector), Objective-C, * Attaches an Objective-C selector to a method.) + * $(TROW $(LREF standalone),, + * Marks a shared module constructor as not depending on any + * other module constructor being run first.) * $(TROW $(LREF weak),, * Specifies that a global symbol should be emitted with weak linkage.) * ) diff --git a/runtime/druntime/src/core/bitop.d b/runtime/druntime/src/core/bitop.d index 9a429627e1..20f29d8754 100644 --- a/runtime/druntime/src/core/bitop.d +++ b/runtime/druntime/src/core/bitop.d @@ -924,6 +924,11 @@ private int softPopcnt(N)(N x) pure return cast(int) x; } +version (DigitalMars) version (AArch64) +{ + int _popcnt(ulong x) pure; +} + version (DigitalMars) version (AnyX86) { /** diff --git a/runtime/druntime/src/core/builtins.d b/runtime/druntime/src/core/builtins.d index 1ed80f7d94..4e0c6d4d62 100644 --- a/runtime/druntime/src/core/builtins.d +++ b/runtime/druntime/src/core/builtins.d @@ -51,3 +51,66 @@ version (LDC) /// Writes `s` to `stderr` during CTFE (does nothing at runtime). void __ctfeWrite(scope const(char)[] s) @nogc @safe pure nothrow {} + +version (GNU) +{ + /// https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html#index-_005f_005fbuiltin_005fexpect + alias expect = __builtin_expect; + /// https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html#index-_005f_005fbuiltin_005ftrap + alias trap = __builtin_trap; +} +else version (LDC) +{ + /// https://llvm.org/docs/LangRef.html#llvm-expect-intrinsic + alias expect = llvm_expect; + debug + /// https://llvm.org/docs/LangRef.html#llvm-debugtrap-intrinsic + alias trap = llvm_debugtrap; + else + /// https://llvm.org/docs/LangRef.html#llvm-trap-intrinsic + alias trap = llvm_trap; +} +else version (DigitalMars) +{ + pragma(inline, true) + T expect(T)(T val, T expected) if (__traits(isIntegral, T)) + { + return val; + } + + /// Execute target dependent trap instruction, if supported. + /// Otherwise, abort execution. + pragma(inline, true) + void trap() + { + debug + { + version(D_InlineAsm_X86) + asm nothrow @nogc pure @trusted { int 3; } + } + assert(0); + } +} + +/// Provide static branch and value hints for the LDC/GDC compilers. +/// DMD ignores these hints. +pragma(inline, true) bool likely()(bool b) { return !!expect(b, true); } +/// ditto +pragma(inline, true) bool unlikely()(bool b) { return !!expect(b, false); } + +/// +@nogc nothrow pure @safe unittest +{ + int x = 12; + + expect(x, 12); + + if (likely(x > 0)) + { + // ... + } + else if (unlikely(x == int.min)) + { + // ... + } +} diff --git a/runtime/druntime/src/core/cpuid.d b/runtime/druntime/src/core/cpuid.d index 62edbac34f..4c2eb2adc0 100644 --- a/runtime/druntime/src/core/cpuid.d +++ b/runtime/druntime/src/core/cpuid.d @@ -1038,9 +1038,9 @@ bool hasCPUID() void cpuidX86() { - datacache[0].size = 8; - datacache[0].associativity = 2; - datacache[0].lineSize = 32; + datacache[0].size = 8; + datacache[0].associativity = 2; + datacache[0].lineSize = 32; } } diff --git a/runtime/druntime/src/core/demangle.d b/runtime/druntime/src/core/demangle.d index 6b4b1f77b6..6d28f1ed2b 100644 --- a/runtime/druntime/src/core/demangle.d +++ b/runtime/druntime/src/core/demangle.d @@ -21,8 +21,12 @@ else version (TVOS) else version (WatchOS) version = Darwin; -debug(trace) import core.stdc.stdio : printf; -debug(info) import core.stdc.stdio : printf; +debug (trace) debug = needPrintf; +debug (info) debug = needPrintf; + +debug (needPrintf) +private int printf(Args...)(scope const char* fmt, scope const Args args) + => __ctfe ? 0 : imported!"core.stdc.stdio".printf(fmt, args); extern (C) alias CXX_DEMANGLER = char* function (const char* mangled_name, char* output_buffer, @@ -1754,13 +1758,10 @@ pure @safe: if (parseMangledNameArg()) continue; - else - { - dst.len = l; - pos = p; - brp = b; - debug(trace) printf( "not a mangled name arg\n" ); - } + dst.len = l; + pos = p; + brp = b; + debug(trace) printf( "not a mangled name arg\n" ); } if ( isDigit( front ) && isDigit( peek( 1 ) ) ) { @@ -2128,7 +2129,7 @@ pure @safe: } name = dst[beg .. nameEnd]; - debug(info) printf( "name (%.*s)\n", cast(int) name.length, name.ptr ); + debug(info) printf( "name (%.*s)\n", cast(int) name.length, name.getSlice.ptr ); if ( 'M' == front ) popFront(); // has 'this' pointer @@ -3194,9 +3195,7 @@ private struct BufSlice size_t from; size_t to; - @safe: - pure: - nothrow: + @safe pure nothrow: @disable this(); @@ -3216,7 +3215,7 @@ private struct BufSlice this.to = to; } - invariant() + invariant { if (buf is null) { diff --git a/runtime/druntime/src/core/gc/config.d b/runtime/druntime/src/core/gc/config.d index 258183fd50..af077be77b 100644 --- a/runtime/druntime/src/core/gc/config.d +++ b/runtime/druntime/src/core/gc/config.d @@ -7,8 +7,8 @@ module core.gc.config; -import core.stdc.stdio; import core.internal.parseoptions; +import core.stdc.stdio : printf; __gshared Config config; diff --git a/runtime/druntime/src/core/gc/gcinterface.d b/runtime/druntime/src/core/gc/gcinterface.d index fa3d859ce3..c4e78bae78 100644 --- a/runtime/druntime/src/core/gc/gcinterface.d +++ b/runtime/druntime/src/core/gc/gcinterface.d @@ -190,4 +190,84 @@ interface GC * GC.stats().allocatedInCurrentThread, but faster. */ ulong allocatedInCurrentThread() nothrow; + + // ARRAY FUNCTIONS + /** + * Get the current used capacity of an array block. + * + * Note that this is only needed if you are about to change the array used + * size and need to deal with the memory that is about to go away. For + * appending or shrinking arrays that have no destructors, you probably + * don't need this function. + * + * Params: + * ptr = The pointer to check. This can be an interior pointer, but if it + * is beyond the end of the used space, the return value may not be + * valid. + * atomic = The value is fetched atomically (for shared arrays) + * Returns: + * Current array slice, or null if the pointer does not point to a valid + * appendable GC block. + */ + void[] getArrayUsed(void *ptr, bool atomic = false) nothrow; + + /** + * Expand the array used size in place. + * + * Used for appending and expanding the length of the array slice. If the + * operation can be performed without reallocating, the function succeeds. + * Newly expanded data is not initialized. Slices that do not point at + * expandable GC blocks cannot be affected, and this function will always + * return false. + * + * Params: + * slice = the slice to attempt expanding in place. + * newUsed = the size that should be stored as used. + * atomic = if true, the array may be shared between threads, and this + * operation should be done atomically. + * Returns: true if successful. + */ + bool expandArrayUsed(void[] slice, size_t newUsed, bool atomic = false) nothrow @safe; + + /** + * Expand the array capacity in place. + * + * Used for reserving space that can be used for appending. If the + * operation can be performed without reallocating, the function succeeds. + * The used size is not changed. Slices that do not point at expandable GC + * blocks cannot be affected, and this function will always return zero. + * + * Params: + * slice = the slice to attempt reserving capacity for. + * request = the requested size to expand to. Includes the existing data. + * Passing a value less than the current array size will result in no + * changes, but will return the current capacity. + * atomic = The array may be shared between threads, and this operation + * should be done atomically. + * + * Returns: + * Resulting capacity size or 0 if the operation could not be performed. + */ + size_t reserveArrayCapacity(void[] slice, size_t request, bool atomic = false) nothrow @safe; + + /** + * Shrink used space of a slice in place. + * + * Unlike the other array functions, the array slice passed in is the + * target slice, and the existing used space is passed separately. This is + * to discourage code that ends up with a slice to dangling valid data. + * + * If slice.ptr[0 .. existingUsed] does not point to the end of a valid GC + * appendable slice, then the operation fails. + * + * Params: + * slice = The proposed valid slice data. + * existingUsed = The amount of data in the block (starting at slice.ptr) + * that is currently valid in the array. If this amount does not match + * the current used size, the operation fails. + * atomic = The slice may be shared between threads, and the operation + * should be atomic. + * Returns: true if successful. + */ + bool shrinkArrayUsed(void[] slice, size_t existingUsed, bool atomic = false) nothrow; } diff --git a/runtime/druntime/src/core/int128.d b/runtime/druntime/src/core/int128.d index 12731fca90..90a10b71d2 100644 --- a/runtime/druntime/src/core/int128.d +++ b/runtime/druntime/src/core/int128.d @@ -951,7 +951,7 @@ version (unittest) { version (none) { - import core.stdc.stdio; + import core.stdc.stdio : printf; @trusted void print(Cent c) diff --git a/runtime/druntime/src/core/internal/abort.d b/runtime/druntime/src/core/internal/abort.d index 6942f7e37d..c0adde99dd 100644 --- a/runtime/druntime/src/core/internal/abort.d +++ b/runtime/druntime/src/core/internal/abort.d @@ -6,7 +6,7 @@ module core.internal.abort; */ void abort(scope string msg, scope string filename = __FILE__, size_t line = __LINE__) @nogc nothrow @safe { - import core.stdc.stdlib: c_abort = abort; + import core.stdc.stdlib : c_abort = abort; // use available OS system calls to print the message to stderr version (Posix) { diff --git a/runtime/druntime/src/core/internal/array/appending.d b/runtime/druntime/src/core/internal/array/appending.d index ba34727a30..1b2b78ea57 100644 --- a/runtime/druntime/src/core/internal/array/appending.d +++ b/runtime/druntime/src/core/internal/array/appending.d @@ -52,7 +52,7 @@ version (D_ProfileGC) /** * TraceGC wrapper around $(REF _d_arrayappendT, core,internal,array,appending). */ - ref Tarr _d_arrayappendcTXTrace(Tarr : T[], T)(string file, int line, string funcname, return ref scope Tarr px, size_t n) @trusted + ref Tarr _d_arrayappendcTXTrace(Tarr : T[], T)(return ref scope Tarr px, size_t n, string file = __FILE__, int line = __LINE__, string funcname = __FUNCTION__) @trusted { version (D_TypeInfo) { @@ -115,7 +115,7 @@ version (D_ProfileGC) /** * TraceGC wrapper around $(REF _d_arrayappendT, core,internal,array,appending). */ - ref Tarr _d_arrayappendTTrace(Tarr : T[], T)(string file, int line, string funcname, return ref scope Tarr x, scope Tarr y) @trusted + ref Tarr _d_arrayappendTTrace(Tarr : T[], T)(return ref scope Tarr x, scope Tarr y, string file = __FILE__, int line = __LINE__, string funcname = __FUNCTION__) @trusted { version (D_TypeInfo) { diff --git a/runtime/druntime/src/core/internal/array/casting.d b/runtime/druntime/src/core/internal/array/casting.d index 4366da829c..54e7463ca4 100644 --- a/runtime/druntime/src/core/internal/array/casting.d +++ b/runtime/druntime/src/core/internal/array/casting.d @@ -136,7 +136,7 @@ TTo[] __ArrayCast(TFrom, TTo)(return scope TFrom[] from) @nogc pure @trusted if (msg != expected) { - import core.stdc.stdio; + import core.stdc.stdio : printf; printf("Expected: |%.*s|\n", cast(int) expected.length, expected.ptr); printf("Actual : |%.*s|\n", cast(int) msg.length, msg.ptr); assert(false); diff --git a/runtime/druntime/src/core/internal/array/concatenation.d b/runtime/druntime/src/core/internal/array/concatenation.d index 4a05b50fcf..13ec5fad40 100644 --- a/runtime/druntime/src/core/internal/array/concatenation.d +++ b/runtime/druntime/src/core/internal/array/concatenation.d @@ -48,7 +48,7 @@ Tret _d_arraycatnTX(Tret, Tarr...)(auto ref Tarr froms) @trusted { // TODO: forward file, line, name from _d_arraycatnTXTrace _d_arraysetlengthTImpl!(typeof(res))._d_arraysetlengthTTrace( - __FILE__, __LINE__, "_d_arraycatnTX", res, totalLen); + res, totalLen, __FILE__, __LINE__, __FUNCTION__); } else { @@ -62,7 +62,7 @@ Tret _d_arraycatnTX(Tret, Tarr...)(auto ref Tarr froms) @trusted { size_t i = 0; foreach (ref from; froms) - static if (is (typeof(from) : T)) + static if (is(typeof(from) : T)) copyEmplace(cast(T) from, res[i++]); else { @@ -178,7 +178,7 @@ version (D_ProfileGC) /** * TraceGC wrapper around $(REF _d_arraycatnTX, core,internal,array,concatenation). */ - Tret _d_arraycatnTXTrace(Tret, Tarr...)(string file, int line, string funcname, scope auto ref Tarr froms) @trusted + Tret _d_arraycatnTXTrace(Tret, Tarr...)(scope auto ref Tarr froms, string file = __FILE__, int line = __LINE__, string funcname = __FUNCTION__) @trusted { version (D_TypeInfo) { diff --git a/runtime/druntime/src/core/internal/array/construction.d b/runtime/druntime/src/core/internal/array/construction.d index 8f0323a054..40e5a61bbd 100644 --- a/runtime/druntime/src/core/internal/array/construction.d +++ b/runtime/druntime/src/core/internal/array/construction.d @@ -13,7 +13,7 @@ import core.internal.traits : Unqual; debug(PRINTF) { - import core.stdc.stdio; + import core.stdc.stdio : printf; } /** @@ -46,9 +46,8 @@ Tarr _d_arrayctor(Tarr : T[], T)(return scope Tarr to, scope Tarr from, char* ma import core.lifetime : copyEmplace; import core.stdc.string : memcpy; import core.stdc.stdint : uintptr_t; - debug(PRINTF) import core.stdc.stdio : printf; - debug(PRINTF) printf("_d_arrayctor(from = %p,%d) size = %d\n", from.ptr, from.length, T.sizeof); + debug(PRINTF) printf("_d_arrayctor(from = %p,%zd) size = %zd\n", from.ptr, from.length, T.sizeof); void[] vFrom = (cast(void*) from.ptr)[0..from.length]; void[] vTo = (cast(void*) to.ptr)[0..to.length]; @@ -349,7 +348,7 @@ T[] _d_newarrayU(T)(size_t length, bool isShared=false) @trusted { import core.exception : onOutOfMemoryError; import core.internal.traits : Unqual; - import core.internal.array.utils : __arrayStart, __setArrayAllocLength, __arrayAlloc; + import core.internal.array.utils : __arrayAlloc; alias UnqT = Unqual!T; @@ -395,16 +394,11 @@ Loverflow: assert(0); Lcontinue: - auto info = __arrayAlloc!UnqT(arraySize); - if (!info.base) + auto arr = __arrayAlloc!UnqT(arraySize); + if (!arr.ptr) goto Loverflow; - debug(PRINTF) printf("p = %p\n", info.base); - - auto arrstart = __arrayStart(info); - - __setArrayAllocLength!UnqT(info, arraySize, isShared); - - return (cast(T*) arrstart)[0 .. length]; + debug(PRINTF) printf("p = %p\n", arr.ptr); + return (cast(T*) arr.ptr)[0 .. length]; } /// ditto @@ -476,7 +470,7 @@ version (D_ProfileGC) /** * TraceGC wrapper around $(REF _d_newitemT, core,lifetime). */ - T[] _d_newarrayTTrace(T)(string file, int line, string funcname, size_t length, bool isShared) @trusted + T[] _d_newarrayTTrace(T)(size_t length, bool isShared, string file = __FILE__, int line = __LINE__, string funcname = __FUNCTION__) @trusted { version (D_TypeInfo) { @@ -513,7 +507,7 @@ version (D_ProfileGC) */ Tarr _d_newarraymTX(Tarr : U[], T, U)(size_t[] dims, bool isShared=false) @trusted { - debug(PRINTF) printf("_d_newarraymTX(dims.length = %d)\n", dims.length); + debug(PRINTF) printf("_d_newarraymTX(dims.length = %zd)\n", dims.length); if (dims.length == 0) return null; @@ -522,7 +516,7 @@ Tarr _d_newarraymTX(Tarr : U[], T, U)(size_t[] dims, bool isShared=false) @trust void[] __allocateInnerArray(size_t[] dims) { - import core.internal.array.utils : __arrayStart, __setArrayAllocLength, __arrayAlloc; + import core.internal.array.utils : __arrayAlloc; auto dim = dims[0]; @@ -530,24 +524,22 @@ Tarr _d_newarraymTX(Tarr : U[], T, U)(size_t[] dims, bool isShared=false) @trust if (dims.length == 1) { auto r = _d_newarrayT!UnqT(dim, isShared); - return *cast(void[]*)(&r); + return *cast(void[]*)&r; } auto allocSize = (void[]).sizeof * dim; // the array-of-arrays holds pointers! Don't use UnqT here! - auto info = __arrayAlloc!(void[])(allocSize); - __setArrayAllocLength!(void[])(info, allocSize, isShared); - auto p = __arrayStart(info)[0 .. dim]; + auto arr = __arrayAlloc!(void[])(allocSize); foreach (i; 0..dim) { - (cast(void[]*)p.ptr)[i] = __allocateInnerArray(dims[1..$]); + (cast(void[]*)arr.ptr)[i] = __allocateInnerArray(dims[1..$]); } - return p; + return arr.ptr[0 .. dim]; } auto result = __allocateInnerArray(dims); - debug(PRINTF) printf("result = %llx\n", result.ptr); + debug(PRINTF) printf("result = %p\n", result.ptr); return (cast(U*) result.ptr)[0 .. dims[0]]; } @@ -580,6 +572,21 @@ unittest } } +// Test 3-level array allocation (this uses different code paths). +unittest +{ + int[][][] a = _d_newarraymTX!(int[][][], int)([3, 4, 5]); + int[5] zeros = 0; + + assert(a.length == 3); + foreach(l2; a) + { + assert(l2.length == 4); + foreach(l3; l2) + assert(l3 == zeros[]); + } +} + // https://issues.dlang.org/show_bug.cgi?id=24436 @system unittest { @@ -595,7 +602,7 @@ version (D_ProfileGC) /** * TraceGC wrapper around $(REF _d_newarraymT, core,internal,array,construction). */ - Tarr _d_newarraymTXTrace(Tarr : U[], T, U)(string file, int line, string funcname, size_t[] dims, bool isShared=false) @trusted + Tarr _d_newarraymTXTrace(Tarr : U[], T, U)(size_t[] dims, bool isShared=false, string file = __FILE__, int line = __LINE__, string funcname = __FUNCTION__) @trusted { version (D_TypeInfo) { diff --git a/runtime/druntime/src/core/internal/array/duplication.d b/runtime/druntime/src/core/internal/array/duplication.d index 9df84893bb..b5222b3b17 100644 --- a/runtime/druntime/src/core/internal/array/duplication.d +++ b/runtime/druntime/src/core/internal/array/duplication.d @@ -327,7 +327,7 @@ U[] _dup(T, U)(T[] a) if (!__traits(isPOD, T)) { if (l != 0xDEADBEEF) { - import core.stdc.stdio; + import core.stdc.stdio : fflush, printf, stdout; printf("Unexpected value: %lld\n", l); fflush(stdout); assert(false); diff --git a/runtime/druntime/src/core/internal/array/utils.d b/runtime/druntime/src/core/internal/array/utils.d index 89ce6ca218..deaae3f95d 100644 --- a/runtime/druntime/src/core/internal/array/utils.d +++ b/runtime/druntime/src/core/internal/array/utils.d @@ -12,24 +12,8 @@ module core.internal.array.utils; import core.internal.traits : Parameters; import core.memory : GC; -alias BlkInfo = GC.BlkInfo; alias BlkAttr = GC.BlkAttr; -private -{ - enum : size_t - { - PAGESIZE = 4096, - BIGLENGTHMASK = ~(PAGESIZE - 1), - SMALLPAD = 1, - MEDPAD = ushort.sizeof, - LARGEPREFIX = 16, // 16 bytes padding at the front of the array - LARGEPAD = LARGEPREFIX + 1, - MAXSMALLSIZE = 256-SMALLPAD, - MAXMEDSIZE = (PAGESIZE / 2) - MEDPAD - } -} - auto gcStatsPure() nothrow pure { import core.memory : GC; @@ -69,18 +53,6 @@ version (D_ProfileGC) string name = } ~ "`" ~ Type ~ "`;" ~ q{ // FIXME: use rt.tracegc.accumulator when it is accessable in the future. - version (tracegc) - } ~ "{\n" ~ q{ - import core.stdc.stdio; - - printf("%sTrace file = '%.*s' line = %d function = '%.*s' type = %.*s\n", - } ~ "\"" ~ Hook ~ "\".ptr," ~ q{ - file.length, file.ptr, - line, - funcname.length, funcname.ptr, - name.length, name.ptr - ); - } ~ "}\n" ~ q{ ulong currentlyAllocated = gcStatsPure().allocatedInCurrentThread; scope(exit) @@ -115,7 +87,7 @@ version (D_ProfileGC) * purity, and throwabilty checks. To prevent breaking existing code, this function template * is temporarily declared `@trusted pure` until the implementation can be brought up to modern D expectations. */ - auto _d_HookTraceImpl(T, alias Hook, string errorMessage)(string file, int line, string funcname, Parameters!Hook parameters) @trusted pure + auto _d_HookTraceImpl(T, alias Hook, string errorMessage)(Parameters!Hook parameters, string file = __FILE__, int line = __LINE__, string funcname = __FUNCTION__) @trusted pure { version (D_TypeInfo) { @@ -156,218 +128,31 @@ template isPostblitNoThrow(T) { } /** - * Clear padding that might not be zeroed by the GC (it assumes it is within the - * requested size from the start, but it is actually at the end of the allocated - * block). - * - * Params: - * info = array allocation data - * arrSize = size of the array in bytes - * padSize = size of the padding in bytes - */ -void __arrayClearPad()(ref BlkInfo info, size_t arrSize, size_t padSize) nothrow pure -{ - import core.stdc.string; - if (padSize > MEDPAD && !(info.attr & BlkAttr.NO_SCAN) && info.base) - { - if (info.size < PAGESIZE) - memset(info.base + arrSize, 0, padSize); - else - memset(info.base, 0, LARGEPREFIX); - } -} - -/** - * Allocate an array memory block by applying the proper padding and assigning - * block attributes if not inherited from the existing block. + * Allocate a memory block with appendable capabilities for array usage. * * Params: * arrSize = size of the allocated array in bytes * Returns: - * `BlkInfo` with allocation metadata + * `void[]` matching requested size on success, `null` on failure. */ -BlkInfo __arrayAlloc(T)(size_t arrSize) @trusted +void[] __arrayAlloc(T)(size_t arrSize) @trusted { - import core.checkedint; import core.lifetime : TypeInfoSize; import core.internal.traits : hasIndirections; enum typeInfoSize = TypeInfoSize!T; BlkAttr attr = BlkAttr.APPENDABLE; - size_t padSize = arrSize > MAXMEDSIZE ? - LARGEPAD : - ((arrSize > MAXSMALLSIZE ? MEDPAD : SMALLPAD) + typeInfoSize); - - bool overflow; - auto paddedSize = addu(arrSize, padSize, overflow); - - if (overflow) - return BlkInfo(); - /* `extern(C++)` classes don't have a classinfo pointer in their vtable, * so the GC can't finalize them. */ static if (typeInfoSize) - attr |= BlkAttr.STRUCTFINAL | BlkAttr.FINALIZE; + attr |= BlkAttr.FINALIZE; static if (!hasIndirections!T) attr |= BlkAttr.NO_SCAN; - auto bi = GC.qalloc(paddedSize, attr, typeid(T)); - __arrayClearPad(bi, arrSize, padSize); - return bi; -} - -/** - * Get the start of the array for the given block. - * - * Params: - * info = array metadata - * Returns: - * pointer to the start of the array - */ -void *__arrayStart()(return scope BlkInfo info) nothrow pure -{ - return info.base + ((info.size & BIGLENGTHMASK) ? LARGEPREFIX : 0); -} - -/** - * Set the allocated length of the array block. This is called when an array - * is appended to or its length is set. - * - * The allocated block looks like this for blocks < PAGESIZE: - * `|elem0|elem1|elem2|...|elemN-1|emptyspace|N*elemsize|` - * - * The size of the allocated length at the end depends on the block size: - * a block of 16 to 256 bytes has an 8-bit length. - * a block with 512 to pagesize/2 bytes has a 16-bit length. - * - * For blocks >= pagesize, the length is a size_t and is at the beginning of the - * block. The reason we have to do this is because the block can extend into - * more pages, so we cannot trust the block length if it sits at the end of the - * block, because it might have just been extended. If we can prove in the - * future that the block is unshared, we may be able to change this, but I'm not - * sure it's important. - * - * In order to do put the length at the front, we have to provide 16 bytes - * buffer space in case the block has to be aligned properly. In x86, certain - * SSE instructions will only work if the data is 16-byte aligned. In addition, - * we need the sentinel byte to prevent accidental pointers to the next block. - * Because of the extra overhead, we only do this for page size and above, where - * the overhead is minimal compared to the block size. - * - * So for those blocks, it looks like: - * `|N*elemsize|padding|elem0|elem1|...|elemN-1|emptyspace|sentinelbyte|`` - * - * where `elem0` starts 16 bytes after the first byte. - */ -bool __setArrayAllocLength(T)(ref BlkInfo info, size_t newLength, bool isShared, size_t oldLength = ~0) -{ - import core.atomic; - import core.lifetime : TypeInfoSize; - - size_t typeInfoSize = TypeInfoSize!T; - - if (info.size <= 256) - { - import core.checkedint; - - bool overflow; - auto newLengthPadded = addu(newLength, - addu(SMALLPAD, typeInfoSize, overflow), - overflow); - - if (newLengthPadded > info.size || overflow) - // new size does not fit inside block - return false; - - auto length = cast(ubyte *)(info.base + info.size - typeInfoSize - SMALLPAD); - if (oldLength != ~0) - { - if (isShared) - { - return cas(cast(shared)length, cast(ubyte)oldLength, cast(ubyte)newLength); - } - else - { - if (*length == cast(ubyte)oldLength) - *length = cast(ubyte)newLength; - else - return false; - } - } - else - { - // setting the initial length, no cas needed - *length = cast(ubyte)newLength; - } - if (typeInfoSize) - { - auto typeInfo = cast(TypeInfo*)(info.base + info.size - size_t.sizeof); - *typeInfo = cast()typeid(T); - } - } - else if (info.size < PAGESIZE) - { - if (newLength + MEDPAD + typeInfoSize > info.size) - // new size does not fit inside block - return false; - auto length = cast(ushort *)(info.base + info.size - typeInfoSize - MEDPAD); - if (oldLength != ~0) - { - if (isShared) - { - return cas(cast(shared)length, cast(ushort)oldLength, cast(ushort)newLength); - } - else - { - if (*length == oldLength) - *length = cast(ushort)newLength; - else - return false; - } - } - else - { - // setting the initial length, no cas needed - *length = cast(ushort)newLength; - } - if (typeInfoSize) - { - auto typeInfo = cast(TypeInfo*)(info.base + info.size - size_t.sizeof); - *typeInfo = cast()typeid(T); - } - } - else - { - if (newLength + LARGEPAD > info.size) - // new size does not fit inside block - return false; - auto length = cast(size_t *)(info.base); - if (oldLength != ~0) - { - if (isShared) - { - return cas(cast(shared)length, cast(size_t)oldLength, cast(size_t)newLength); - } - else - { - if (*length == oldLength) - *length = newLength; - else - return false; - } - } - else - { - // setting the initial length, no cas needed - *length = newLength; - } - if (typeInfoSize) - { - auto typeInfo = cast(TypeInfo*)(info.base + size_t.sizeof); - *typeInfo = cast()typeid(T); - } - } - return true; // resize succeeded + auto ptr = GC.malloc(arrSize, attr, typeid(T)); + if (ptr) + return ptr[0 .. arrSize]; + return null; } diff --git a/runtime/druntime/src/core/internal/atomic.d b/runtime/druntime/src/core/internal/atomic.d index 09eb779da9..b872f54855 100644 --- a/runtime/druntime/src/core/internal/atomic.d +++ b/runtime/druntime/src/core/internal/atomic.d @@ -10,7 +10,7 @@ module core.internal.atomic; -import core.atomic : MemoryOrder, has128BitCAS; +import core.atomic : has128BitCAS, MemoryOrder; version (LDC) { @@ -1090,7 +1090,7 @@ else version (GNU) { static if (GNU_Thread_Model == ThreadModel.Posix) { - import core.sys.posix.pthread; + import core.sys.posix.sys.types : pthread_mutex_t, pthread_mutexattr_t; alias atomicMutexHandle = pthread_mutex_t; pragma(mangle, "pthread_mutex_init") int fakePureMutexInit(pthread_mutex_t*, pthread_mutexattr_t*); @@ -1099,7 +1099,7 @@ else version (GNU) } else static if (GNU_Thread_Model == ThreadModel.Win32) { - import core.sys.windows.winbase; + import core.sys.windows.winbase : CRITICAL_SECTION; alias atomicMutexHandle = CRITICAL_SECTION; pragma(mangle, "InitializeCriticalSection") int fakePureMutexInit(CRITICAL_SECTION*); @@ -1161,7 +1161,7 @@ else version (GNU) // Internal static mutex reference. private AtomicMutex* _getAtomicMutex() @trusted @nogc nothrow { - __gshared static AtomicMutex mutex; + __gshared AtomicMutex mutex; return &mutex; } diff --git a/runtime/druntime/src/core/internal/backtrace/dwarf.d b/runtime/druntime/src/core/internal/backtrace/dwarf.d index 05dd414b73..5da9894800 100644 --- a/runtime/druntime/src/core/internal/backtrace/dwarf.d +++ b/runtime/druntime/src/core/internal/backtrace/dwarf.d @@ -44,7 +44,7 @@ * Copyright: Copyright Digital Mars 2015 - 2015. * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). * Authors: Yazan Dabain, Sean Kelly - * Source: $(DRUNTIMESRC rt/backtrace/dwarf.d) + * Source: $(DRUNTIMESRC core/internal/backtrace/dwarf.d) */ module core.internal.backtrace.dwarf; @@ -422,8 +422,6 @@ version (Darwin) { */ void resolveAddresses(const(ubyte)[] debugLineSectionData, Location[] locations, size_t baseAddress) @nogc nothrow { - debug(DwarfDebugMachine) import core.stdc.stdio; - size_t numberOfLocationsFound = 0; const(ubyte)[] dbg = debugLineSectionData; @@ -1127,12 +1125,12 @@ LineNumberProgram readLineNumberProgram(ref const(ubyte)[] data) @nogc nothrow foreach (ref sf; lp.sourceFiles) { if (sf.dirIndex > lp.includeDirectories.length) - printf("\t- Out of bound directory! (%llu): %.*s\n", + printf("\t- Out of bound directory! (%zu): %.*s\n", sf.dirIndex, cast(int) sf.file.length, sf.file.ptr); else if (sf.dirIndex > 0) { const dir = lp.includeDirectories[sf.dirIndex - 1]; - printf("\t- (Dir:%llu:%.*s/)%.*s\n", sf.dirIndex, + printf("\t- (Dir:%zu:%.*s/)%.*s\n", sf.dirIndex, cast(int) dir.length, dir.ptr, cast(int) sf.file.length, sf.file.ptr); } diff --git a/runtime/druntime/src/core/internal/backtrace/elf.d b/runtime/druntime/src/core/internal/backtrace/elf.d index 07d6769479..1a235537bb 100644 --- a/runtime/druntime/src/core/internal/backtrace/elf.d +++ b/runtime/druntime/src/core/internal/backtrace/elf.d @@ -6,34 +6,34 @@ * Copyright: Copyright Digital Mars 2015 - 2018. * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). * Authors: Yazan Dabain - * Source: $(DRUNTIMESRC rt/backtrace/elf.d) + * Source: $(DRUNTIMESRC core/internal/backtrace/elf.d) */ module core.internal.backtrace.elf; version (linux) { - import core.sys.linux.elf; + import core.sys.linux.elf : SHF_COMPRESSED, ET_DYN; version = LinuxOrBSD; } else version (FreeBSD) { - import core.sys.freebsd.sys.elf; + import core.sys.freebsd.sys.elf : SHF_COMPRESSED, ET_DYN; version = LinuxOrBSD; } else version (DragonFlyBSD) { - import core.sys.dragonflybsd.sys.elf; + import core.sys.dragonflybsd.sys.elf : SHF_COMPRESSED, ET_DYN; version = LinuxOrBSD; } else version (OpenBSD) { - import core.sys.openbsd.sys.elf; + import core.sys.openbsd.sys.elf : SHF_COMPRESSED, ET_DYN; version = LinuxOrBSD; } else version (Solaris) { - import core.sys.solaris.sys.elf; + import core.sys.solaris.sys.elf : SHF_COMPRESSED, ET_DYN; version = LinuxOrBSD; } diff --git a/runtime/druntime/src/core/internal/backtrace/libunwind.d b/runtime/druntime/src/core/internal/backtrace/libunwind.d index 8b69280e80..d59a393054 100644 --- a/runtime/druntime/src/core/internal/backtrace/libunwind.d +++ b/runtime/druntime/src/core/internal/backtrace/libunwind.d @@ -33,7 +33,7 @@ version (DRuntime_Use_Libunwind): // mechanism for Windows, so the bindings haven't been brought in yet. version (Posix): -import core.stdc.inttypes; +import core.stdc.inttypes : uintptr_t; extern(C): @nogc: @@ -163,5 +163,10 @@ else version (RISCV64) // 32 is not supported enum _LIBUNWIND_CONTEXT_SIZE = 64; enum _LIBUNWIND_CURSOR_SIZE = 76; } +else version (LoongArch64) +{ + enum _LIBUNWIND_CONTEXT_SIZE = 65; + enum _LIBUNWIND_CURSOR_SIZE = 77; +} else static assert(0, "Platform not supported"); diff --git a/runtime/druntime/src/core/internal/backtrace/macho.d b/runtime/druntime/src/core/internal/backtrace/macho.d index ed10bc827b..ff6a31b7fa 100644 --- a/runtime/druntime/src/core/internal/backtrace/macho.d +++ b/runtime/druntime/src/core/internal/backtrace/macho.d @@ -4,7 +4,7 @@ * Copyright: Copyright Jacob Carlborg 2018. * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) * Authors: Jacob Carlborg - * Source: $(DRUNTIMESRC rt/backtrace/macho.d) + * Source: $(DRUNTIMESRC core/internal/backtrace/macho.d) */ module core.internal.backtrace.macho; @@ -20,9 +20,8 @@ else version (WatchOS) version (Darwin): import core.stdc.config : c_ulong; -import core.sys.darwin.crt_externs; -import core.sys.darwin.mach.getsect; -import core.sys.darwin.mach.loader; +import core.sys.darwin.crt_externs : _NSGetMachExecuteHeader; +import core.sys.darwin.mach.getsect : mach_header_64, getsectiondata; struct Image { diff --git a/runtime/druntime/src/core/internal/backtrace/unwind.d b/runtime/druntime/src/core/internal/backtrace/unwind.d index b31c909ada..ea299a2223 100644 --- a/runtime/druntime/src/core/internal/backtrace/unwind.d +++ b/runtime/druntime/src/core/internal/backtrace/unwind.d @@ -10,7 +10,7 @@ */ module core.internal.backtrace.unwind; -import core.stdc.stdint; +import core.stdc.stdint : intptr_t, uintptr_t; version (ARM) { diff --git a/runtime/druntime/src/core/internal/convert.d b/runtime/druntime/src/core/internal/convert.d index 92eb243ec1..7bac8359a7 100644 --- a/runtime/druntime/src/core/internal/convert.d +++ b/runtime/druntime/src/core/internal/convert.d @@ -695,7 +695,7 @@ const(ubyte)[] toUbyte(T)(const ref scope T val) if (__traits(isIntegral, T) && } else { - return (cast(const(ubyte)*)(&val))[0 .. T.sizeof]; + return (cast(const(ubyte)*)&val)[0 .. T.sizeof]; } } else if (__ctfe) @@ -715,7 +715,7 @@ const(ubyte)[] toUbyte(T)(const ref scope T val) if (__traits(isIntegral, T) && } else { - return (cast(const(ubyte)*)(&val))[0 .. T.sizeof]; + return (cast(const(ubyte)*)&val)[0 .. T.sizeof]; } } diff --git a/runtime/druntime/src/core/internal/dassert.d b/runtime/druntime/src/core/internal/dassert.d index 8d03c8fda3..d8ea22d18c 100644 --- a/runtime/druntime/src/core/internal/dassert.d +++ b/runtime/druntime/src/core/internal/dassert.d @@ -14,7 +14,7 @@ * * Copyright: D Language Foundation 2018 - 2020 * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/druntime/src/core/internal/dassert.d, _dassert.d) + * Source: $(DRUNTIMESRC core/internal/_dassert.d) * Documentation: https://dlang.org/phobos/core_internal_dassert.html */ module core.internal.dassert; diff --git a/runtime/druntime/src/core/internal/elf/dl.d b/runtime/druntime/src/core/internal/elf/dl.d index 409a8bd998..963b4249a3 100644 --- a/runtime/druntime/src/core/internal/elf/dl.d +++ b/runtime/druntime/src/core/internal/elf/dl.d @@ -13,32 +13,32 @@ module core.internal.elf.dl; version (linux) { - import core.sys.linux.link; + import core.sys.linux.link : dl_iterate_phdr, dl_phdr_info, ElfW; version = LinuxOrBSD; } else version (FreeBSD) { - import core.sys.freebsd.sys.link_elf; + import core.sys.freebsd.sys.link_elf : _rtld_addr_phdr, dl_iterate_phdr, dl_phdr_info, ElfW; version = LinuxOrBSD; } else version (DragonFlyBSD) { - import core.sys.dragonflybsd.sys.link_elf; + import core.sys.dragonflybsd.sys.link_elf : _rtld_addr_phdr, dl_iterate_phdr, dl_phdr_info, ElfW; version = LinuxOrBSD; } else version (NetBSD) { - import core.sys.netbsd.sys.link_elf; + import core.sys.netbsd.sys.link_elf : dl_iterate_phdr, dl_phdr_info, ElfW; version = LinuxOrBSD; } else version (OpenBSD) { - import core.sys.openbsd.sys.link_elf; + import core.sys.openbsd.sys.link_elf : dl_iterate_phdr, dl_phdr_info, ElfW; version = LinuxOrBSD; } else version (Solaris) { - import core.sys.solaris.link; + import core.sys.solaris.link : dl_iterate_phdr, dl_phdr_info, ElfW; version = LinuxOrBSD; } @@ -146,7 +146,9 @@ struct SharedObject char[] getPath(size_t N)(ref char[N] buffer) const if (N > 1) { - import core.stdc.stdio, core.stdc.string, core.sys.posix.unistd; + import core.stdc.stdio : fclose, fgets, fopen, snprintf, sscanf; + import core.stdc.string : strlen; + import core.sys.posix.unistd : getpid; char[N + 128] lineBuffer = void; @@ -219,7 +221,7 @@ version (Linux_Use_GNU) { const(char)* getprogname() { - import core.sys.linux.errno; + import core.sys.linux.errno : program_invocation_name; return program_invocation_name; } } @@ -230,7 +232,7 @@ else // Bionic, BSDs unittest { - import core.stdc.stdio; + import core.stdc.stdio : printf; char[512] buffer = void; foreach (object; SharedObjects) diff --git a/runtime/druntime/src/core/internal/elf/io.d b/runtime/druntime/src/core/internal/elf/io.d index 1598f5f4bb..80c9415e14 100644 --- a/runtime/druntime/src/core/internal/elf/io.d +++ b/runtime/druntime/src/core/internal/elf/io.d @@ -6,48 +6,48 @@ * Copyright: Copyright Digital Mars 2015 - 2018. * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). * Authors: Yazan Dabain, Martin Kinkelin - * Source: $(DRUNTIMESRC core/elf/io.d) + * Source: $(DRUNTIMESRC core/internal/elf/io.d) */ module core.internal.elf.io; version (Posix): -import core.memory : pageSize; import core.lifetime : move; -import core.stdc.stdlib : malloc, free; -import core.sys.posix.fcntl; -import core.sys.posix.sys.mman; -import core.sys.posix.unistd; +import core.memory : pageSize; +import core.stdc.stdlib : free, malloc; +import core.sys.posix.fcntl : O_RDONLY, open; +import core.sys.posix.sys.mman : MAP_FAILED, MAP_PRIVATE, mmap, munmap, PROT_READ; +import core.sys.posix.unistd : close, lseek, readlink; version (linux) { - import core.sys.linux.link; + import core.sys.linux.link : ElfW; version = LinuxOrBSD; } else version (FreeBSD) { - import core.sys.freebsd.sys.link_elf; + import core.sys.freebsd.sys.link_elf : ElfW; version = LinuxOrBSD; } else version (DragonFlyBSD) { - import core.sys.dragonflybsd.sys.link_elf; + import core.sys.dragonflybsd.sys.link_elf : ElfW; version = LinuxOrBSD; } else version (NetBSD) { - import core.sys.netbsd.sys.link_elf; + import core.sys.netbsd.sys.link_elf : ElfW; version = LinuxOrBSD; } else version (OpenBSD) { - import core.sys.openbsd.sys.link_elf; + import core.sys.openbsd.sys.link_elf : ElfW; version = LinuxOrBSD; } else version (Solaris) { - import core.sys.solaris.link; + import core.sys.solaris.link : ElfW; version = LinuxOrBSD; } @@ -374,9 +374,6 @@ private struct MMapRegion @nogc nothrow: -version (OpenBSD) -private extern(C) const(char)* getprogname(); - /// Returns the path to the process' executable as newly allocated C string /// (free() when done), or null on error. version (LinuxOrBSD) @@ -403,23 +400,24 @@ char* thisExePath() { // there's apparently no proper way :/ import core.stdc.string : strdup; + import core.sys.openbsd.stdlib : getprogname; return strdup(getprogname()); } else { version (DragonFlyBSD) { - import core.sys.dragonflybsd.sys.sysctl : sysctl, CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME; + import core.sys.dragonflybsd.sys.sysctl : CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, sysctl; int[4] mib = [CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1]; } else version (FreeBSD) { - import core.sys.freebsd.sys.sysctl : sysctl, CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME; + import core.sys.freebsd.sys.sysctl : CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, sysctl; int[4] mib = [CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1]; } else version (NetBSD) { - import core.sys.netbsd.sys.sysctl : sysctl, CTL_KERN, KERN_PROC_ARGS, KERN_PROC_PATHNAME; + import core.sys.netbsd.sys.sysctl : CTL_KERN, KERN_PROC_ARGS, KERN_PROC_PATHNAME, sysctl; int[4] mib = [CTL_KERN, KERN_PROC_ARGS, -1, KERN_PROC_PATHNAME]; } else diff --git a/runtime/druntime/src/core/internal/execinfo.d b/runtime/druntime/src/core/internal/execinfo.d index 73cff9ecbf..9c468d9256 100644 --- a/runtime/druntime/src/core/internal/execinfo.d +++ b/runtime/druntime/src/core/internal/execinfo.d @@ -53,24 +53,24 @@ version (LDC) version (FreeBSD) pragma(lib, "execinfo"); version (linux) { version (CRuntime_Glibc) - import _execinfo = core.sys.linux.execinfo; + import _execinfo = core.sys.linux.execinfo : backtrace, backtrace_symbols, backtrace_symbols_fd; else version (CRuntime_UClibc) - import _execinfo = core.sys.linux.execinfo; + import _execinfo = core.sys.linux.execinfo : backtrace, backtrace_symbols, backtrace_symbols_fd; else version (_extExecinfo) - import _execinfo = core.sys.linux.execinfo; + import _execinfo = core.sys.linux.execinfo : backtrace, backtrace_symbols, backtrace_symbols_fd; } else version (Darwin) - import _execinfo = core.sys.darwin.execinfo; + import _execinfo = core.sys.darwin.execinfo : backtrace, backtrace_symbols, backtrace_symbols_fd; else version (FreeBSD) - import _execinfo = core.sys.freebsd.execinfo; + import _execinfo = core.sys.freebsd.execinfo : backtrace, backtrace_symbols, backtrace_symbols_fd; else version (NetBSD) - import _execinfo = core.sys.netbsd.execinfo; + import _execinfo = core.sys.netbsd.execinfo : backtrace, backtrace_symbols, backtrace_symbols_fd; else version (OpenBSD) - import _execinfo = core.sys.openbsd.execinfo; + import _execinfo = core.sys.openbsd.execinfo : backtrace, backtrace_symbols, backtrace_symbols_fd; else version (DragonFlyBSD) - import _execinfo = core.sys.dragonflybsd.execinfo; + import _execinfo = core.sys.dragonflybsd.execinfo : backtrace, backtrace_symbols, backtrace_symbols_fd; else version (Solaris) - import _execinfo = core.sys.solaris.execinfo; + import _execinfo = core.sys.solaris.execinfo : backtrace, backtrace_symbols, backtrace_symbols_fd; /// Indicates the availability of backtrace functions enum bool hasExecinfo = is(_execinfo == module); diff --git a/runtime/druntime/src/core/internal/gc/bits.d b/runtime/druntime/src/core/internal/gc/bits.d index aa94e40764..7aeb2e2d24 100644 --- a/runtime/druntime/src/core/internal/gc/bits.d +++ b/runtime/druntime/src/core/internal/gc/bits.d @@ -10,9 +10,9 @@ module core.internal.gc.bits; import core.internal.gc.os : os_mem_map, os_mem_unmap, HaveFork; import core.bitop; -import core.stdc.string; -import core.stdc.stdlib; import core.exception : onOutOfMemoryError; +import core.stdc.stdlib : calloc, free; +import core.stdc.string : memcpy, memset; // use version gcbitsSingleBitOperation to disable optimizations that use // word operands on bulk operation copyRange, setRange, clrRange, etc. @@ -134,7 +134,7 @@ struct GCBits } else { - auto pos = i >> BITS_SHIFT; + const pos = i >> BITS_SHIFT; auto pdata = cast(shared)(data + pos); auto mask = BITS_1 << (i & BITS_MASK); auto state = *pdata; diff --git a/runtime/druntime/src/core/internal/gc/blkcache.d b/runtime/druntime/src/core/internal/gc/blkcache.d new file mode 100644 index 0000000000..07027f56eb --- /dev/null +++ b/runtime/druntime/src/core/internal/gc/blkcache.d @@ -0,0 +1,257 @@ +/** +BlkInfo thread-local cache. Used for array appending in the conservative GC to avoid the lock when possible. + +Note: this used to be in rt.lifetime, but was moved here to allow GCs to take over array operations. +*/ +module core.internal.gc.blkcache; + +import core.memory; +import core.attribute; + +debug (PRINTF) import core.stdc.stdio : printf; + +alias BlkInfo = GC.BlkInfo; +alias BlkAttr = GC.BlkAttr; + +/** + cache for the lookup of the block info + */ +private enum N_CACHE_BLOCKS = 8; + +// note this is TLS, so no need to sync. +BlkInfo *__blkcache_storage; + +static if (N_CACHE_BLOCKS == 1) +{ + version=single_cache; +} +else +{ + //version=simple_cache; // uncomment to test simple cache strategy + //version=random_cache; // uncomment to test random cache strategy + + // ensure N_CACHE_BLOCKS is power of 2. + static assert(!((N_CACHE_BLOCKS - 1) & N_CACHE_BLOCKS)); + + version (random_cache) + { + int __nextRndNum = 0; + } + int __nextBlkIdx; +} + +@property BlkInfo *__blkcache() nothrow @nogc +{ + if (!__blkcache_storage) + { + import core.stdc.stdlib : calloc; + import core.thread.threadbase; + auto tBase = ThreadBase.getThis(); + if (tBase is null) + // if we don't have a thread object, this is a detached thread, and + // this won't be properly maintained by the GC. + return null; + + // allocate the block cache for the first time + immutable size = BlkInfo.sizeof * N_CACHE_BLOCKS; + // use C alloc, because this may become a detached thread, and the GC + // would then clean up the cache without zeroing this pointer. + __blkcache_storage = cast(BlkInfo*) calloc(size, 1); + tBase.tlsGCData = __blkcache_storage; + } + return __blkcache_storage; +} + +// free the allocation on thread exit. +@standalone static ~this() +{ + if (__blkcache_storage) + { + import core.stdc.stdlib : free; + import core.thread.threadbase; + auto tBase = ThreadBase.getThis(); + if (tBase !is null) + tBase.tlsGCData = null; + free(__blkcache_storage); + __blkcache_storage = null; + } +} + +/** + * Indicates whether an address has been marked by the GC. + */ +enum IsMarked : int +{ + no, /// Address is not marked. + yes, /// Address is marked. + unknown, /// Address is not managed by the GC. +} + +alias IsMarkedDg = IsMarked delegate(void* addr) nothrow; /// The isMarked callback function. + +// we expect this to be called with the lock in place +void processGCMarks(void* data, scope IsMarkedDg isMarked) nothrow +{ + if (!data) + return; + + auto cache = cast(BlkInfo*) data; + // called after the mark routine to eliminate block cache data when it + // might be ready to sweep + + debug(PRINTF) printf("processing GC Marks, %p\n", cache); + debug(PRINTF) foreach (i; 0 .. N_CACHE_BLOCKS) + { + printf("cache entry %d has base ptr %p\tsize %zd\tflags %x\n", i, cache[i].base, cache[i].size, cache[i].attr); + } + auto cache_end = cache + N_CACHE_BLOCKS; + for (;cache < cache_end; ++cache) + { + if (cache.base != null && isMarked(cache.base) == IsMarked.no) + { + debug(PRINTF) printf("clearing cache entry at %p\n", cache.base); + cache.base = null; // clear that data. + } + } +} + +unittest +{ + // Bugzilla 10701 - segfault in GC + ubyte[] result; result.length = 4096; + GC.free(result.ptr); + GC.collect(); +} + +/** + Get the cached block info of an interior pointer. Returns null if the + interior pointer's block is not cached. + + NOTE: The following note was not valid, but is retained for historical + purposes. The data cannot be cleared because the stack contains a + reference to the affected block (e.g. through `interior`). Therefore, + the element will not be collected, and the data will remain valid. + + ORIGINAL: The base ptr in this struct can be cleared asynchronously by the GC, + so any use of the returned BlkInfo should copy it and then check the + base ptr of the copy before actually using it. + */ +BlkInfo *__getBlkInfo(void *interior) nothrow @nogc +{ + BlkInfo *ptr = __blkcache; + if (ptr is null) + // if for some reason we don't have a cache, return null. + return null; + version (single_cache) + { + if (ptr.base && ptr.base <= interior && (interior - ptr.base) < ptr.size) + return ptr; + return null; // not in cache. + } + else version (simple_cache) + { + foreach (i; 0..N_CACHE_BLOCKS) + { + if (ptr.base && ptr.base <= interior && (interior - ptr.base) < ptr.size) + return ptr; + ptr++; + } + } + else + { + // try to do a smart lookup, using __nextBlkIdx as the "head" + auto curi = ptr + __nextBlkIdx; + for (auto i = curi; i >= ptr; --i) + { + if (i.base && i.base <= interior && cast(size_t)(interior - i.base) < i.size) + return i; + } + + for (auto i = ptr + N_CACHE_BLOCKS - 1; i > curi; --i) + { + if (i.base && i.base <= interior && cast(size_t)(interior - i.base) < i.size) + return i; + } + } + return null; // not in cache. +} + +void __insertBlkInfoCache(BlkInfo bi, BlkInfo *curpos) nothrow @nogc +{ + auto cache = __blkcache; + if (cache is null) + // no cache to use. + return; + + version (single_cache) + { + *cache = bi; + return; + } + else + { + version (simple_cache) + { + if (curpos) + *curpos = bi; + else + { + // note, this is a super-simple algorithm that does not care about + // most recently used. It simply uses a round-robin technique to + // cache block info. This means that the ordering of the cache + // doesn't mean anything. Certain patterns of allocation may + // render the cache near-useless. + cache[__nextBlkIdx] = bi; + __nextBlkIdx = (__nextBlkIdx+1) & (N_CACHE_BLOCKS - 1); + } + } + else version (random_cache) + { + // strategy: if the block currently is in the cache, move the + // current block index to the a random element and evict that + // element. + if (!curpos) + { + __nextBlkIdx = (__nextRndNum = 1664525 * __nextRndNum + 1013904223) & (N_CACHE_BLOCKS - 1); + curpos = cache + __nextBlkIdx; + } + else + { + __nextBlkIdx = curpos - cache; + } + *curpos = bi; + } + else + { + // + // strategy: If the block currently is in the cache, swap it with + // the head element. Otherwise, move the head element up by one, + // and insert it there. + // + if (!curpos) + { + __nextBlkIdx = (__nextBlkIdx+1) & (N_CACHE_BLOCKS - 1); + curpos = cache + __nextBlkIdx; + } + else if (curpos !is cache + __nextBlkIdx) + { + *curpos = cache[__nextBlkIdx]; + curpos = cache + __nextBlkIdx; + } + *curpos = bi; + } + } +} + +debug(PRINTF) +{ + extern(C) void printArrayCache() + { + auto ptr = __blkcache; + printf("CACHE: \n"); + foreach (i; 0 .. N_CACHE_BLOCKS) + { + printf(" %d\taddr:% .8p\tsize:% .10zd\tflags:% .8x\n", i, ptr[i].base, ptr[i].size, ptr[i].attr); + } + } +} diff --git a/runtime/druntime/src/core/internal/gc/blockmeta.d b/runtime/druntime/src/core/internal/gc/blockmeta.d new file mode 100644 index 0000000000..75d11b79e6 --- /dev/null +++ b/runtime/druntime/src/core/internal/gc/blockmeta.d @@ -0,0 +1,360 @@ +/** + Functions to manipulate metadata in-block. + + functionality was moved from rt.lifetime + */ +module core.internal.gc.blockmeta; + +import core.memory; + +alias BlkInfo = GC.BlkInfo; +alias BlkAttr = GC.BlkAttr; + +enum : size_t +{ + PAGESIZE = 4096, + BIGLENGTHMASK = ~(PAGESIZE - 1), + SMALLPAD = 1, + MEDPAD = ushort.sizeof, + LARGEPREFIX = 16, // 16 bytes padding at the front of the array + LARGEPAD = LARGEPREFIX + 1, + MAXSMALLSIZE = 256-SMALLPAD, + MAXMEDSIZE = (PAGESIZE / 2) - MEDPAD +} + +// size used to store the TypeInfo at the end of an allocation for structs that have a destructor +size_t structTypeInfoSize(const TypeInfo ti) pure nothrow @nogc +{ + if (ti && typeid(ti) is typeid(TypeInfo_Struct)) // avoid a complete dynamic type cast + { + auto sti = cast(TypeInfo_Struct)cast(void*)ti; + if (sti.xdtor) + return size_t.sizeof; + } + return 0; +} + +/** + Set the allocated length of the array block. This is called + any time an array is appended to or its length is set. + + The allocated block looks like this for blocks < PAGESIZE: + + |elem0|elem1|elem2|...|elemN-1|emptyspace|N*elemsize| + + + The size of the allocated length at the end depends on the block size: + + a block of 16 to 256 bytes has an 8-bit length. + + a block with 512 to pagesize/2 bytes has a 16-bit length. + + For blocks >= pagesize, the length is a size_t and is at the beginning of the + block. The reason we have to do this is because the block can extend into + more pages, so we cannot trust the block length if it sits at the end of the + block, because it might have just been extended. If we can prove in the + future that the block is unshared, we may be able to change this, but I'm not + sure it's important. + + In order to do put the length at the front, we have to provide 16 bytes + buffer space in case the block has to be aligned properly. In x86, certain + SSE instructions will only work if the data is 16-byte aligned. In addition, + we need the sentinel byte to prevent accidental pointers to the next block. + Because of the extra overhead, we only do this for page size and above, where + the overhead is minimal compared to the block size. + + So for those blocks, it looks like: + + |N*elemsize|padding|elem0|elem1|...|elemN-1|emptyspace|sentinelbyte| + + where elem0 starts 16 bytes after the first byte. + */ +bool __setArrayAllocLength(ref BlkInfo info, size_t newlength, bool isshared, const TypeInfo tinext, size_t oldlength = size_t.max) pure nothrow +{ + __setBlockFinalizerInfo(info, tinext); + + size_t typeInfoSize = (info.attr & BlkAttr.STRUCTFINAL) ? size_t.sizeof : 0; + return __setArrayAllocLengthImpl(info, newlength, isshared, oldlength, typeInfoSize); +} + +// the impl function, used both above and in core.internal.array.utils +bool __setArrayAllocLengthImpl(ref BlkInfo info, size_t newlength, bool isshared, size_t oldlength, size_t typeInfoSize) pure nothrow +{ + import core.atomic; + + if (info.size <= 256) + { + import core.checkedint; + + bool overflow; + auto newlength_padded = addu(newlength, + addu(SMALLPAD, typeInfoSize, overflow), + overflow); + + if (newlength_padded > info.size || overflow) + // new size does not fit inside block + return false; + + auto length = cast(ubyte *)(info.base + info.size - typeInfoSize - SMALLPAD); + if (oldlength != size_t.max) + { + if (isshared) + { + return cas(cast(shared)length, cast(ubyte)oldlength, cast(ubyte)newlength); + } + else + { + if (*length == cast(ubyte)oldlength) + *length = cast(ubyte)newlength; + else + return false; + } + } + else + { + // setting the initial length, no cas needed + *length = cast(ubyte)newlength; + } + } + else if (info.size <= PAGESIZE / 2) + { + if (newlength + MEDPAD + typeInfoSize > info.size) + // new size does not fit inside block + return false; + auto length = cast(ushort *)(info.base + info.size - typeInfoSize - MEDPAD); + if (oldlength != size_t.max) + { + if (isshared) + { + return cas(cast(shared)length, cast(ushort)oldlength, cast(ushort)newlength); + } + else + { + if (*length == oldlength) + *length = cast(ushort)newlength; + else + return false; + } + } + else + { + // setting the initial length, no cas needed + *length = cast(ushort)newlength; + } + } + else + { + if (newlength + LARGEPAD > info.size) + // new size does not fit inside block + return false; + auto length = cast(size_t *)(info.base); + if (oldlength != size_t.max) + { + if (isshared) + { + return cas(cast(shared)length, cast(size_t)oldlength, cast(size_t)newlength); + } + else + { + if (*length == oldlength) + *length = newlength; + else + return false; + } + } + else + { + // setting the initial length, no cas needed + *length = newlength; + } + } + return true; // resize succeeded +} + +/** + The block finalizer info is set separately from the array length, as that is + only needed on the initial setup of the block. No shared is needed, since + this should only happen when the block is new. + If the STRUCTFINAL bit is not set, no finalizer is stored (but if needed the + slot is zeroed) + */ +void __setBlockFinalizerInfo(ref BlkInfo info, const TypeInfo ti) pure nothrow +{ + if ((info.attr & BlkAttr.APPENDABLE) && info.size > PAGESIZE / 2) + { + // if the structfinal bit is not set, we don't have a finalizer. But we + // should still zero out the finalizer slot. + auto context = (info.attr & BlkAttr.STRUCTFINAL) ? cast(void*)ti : null; + + // array used size goes at the beginning. We can stuff the typeinfo + // right after it, as we need to use 16 bytes anyway. + // + auto typeInfo = cast(void**)info.base + 1; + *typeInfo = context; + version (D_LP64) {} else + { + // zero out the extra padding + (cast(size_t*)info.base)[2 .. 4] = 0; + } + } + else if(info.attr & BlkAttr.STRUCTFINAL) + { + // all other cases the typeinfo gets put at the end of the block + auto typeInfo = cast(void**)(info.base + info.size) - 1; + *typeInfo = cast(void*) ti; + } +} + +/** + Get the finalizer info from the block (typeinfo). + If called on a block, without STRUCTFINAL set, returns null. + */ +const(TypeInfo) __getBlockFinalizerInfo(ref BlkInfo info) pure nothrow +{ + return __getBlockFinalizerInfo(info.base, info.size, info.attr); +} + +/// ditto +const(TypeInfo) __getBlockFinalizerInfo(void* base, size_t size, uint attr) pure nothrow +{ + if (!(attr & BlkAttr.STRUCTFINAL)) + return null; + + bool isLargeArray = (attr & BlkAttr.APPENDABLE) && size > PAGESIZE / 2; + auto typeInfo = isLargeArray ? + base + size_t.sizeof : + base + size - size_t.sizeof; + assert(*cast(size_t*)typeInfo != 0); + return *cast(TypeInfo*)typeInfo; +} + +/** + get the used size of the array for the given block + */ +size_t __arrayAllocLength(ref BlkInfo info) pure nothrow + in(info.attr & BlkAttr.APPENDABLE) +{ + auto typeInfoSize = (info.attr & BlkAttr.STRUCTFINAL) ? size_t.sizeof : 0; + if (info.size <= 256) + return *cast(ubyte *)(info.base + info.size - typeInfoSize - SMALLPAD); + + if (info.size <= PAGESIZE / 2) + return *cast(ushort *)(info.base + info.size - typeInfoSize - MEDPAD); + + return *cast(size_t *)(info.base); +} + +/** + Atomically get the used size of the array for the given block + */ +size_t __arrayAllocLengthAtomic(ref BlkInfo info) pure nothrow + in(info.attr & BlkAttr.APPENDABLE) +{ + import core.atomic; + auto typeInfoSize = (info.attr & BlkAttr.STRUCTFINAL) ? size_t.sizeof : 0; + if (info.size <= 256) + return atomicLoad(*cast(shared(ubyte)*)(info.base + info.size - typeInfoSize - SMALLPAD)); + + if (info.size <= PAGESIZE / 2) + return atomicLoad(*cast(shared(ushort)*)(info.base + info.size - typeInfoSize - MEDPAD)); + + return atomicLoad(*cast(shared(size_t)*)(info.base)); +} + +/** + Get the maximum bytes that can be stored in the given block. + */ +size_t __arrayAllocCapacity(ref BlkInfo info) pure nothrow + in(info.attr & BlkAttr.APPENDABLE) +{ + // Capacity is a calculation based solely on the block info. + if (info.size > PAGESIZE / 2) + return info.size - LARGEPAD; + + auto typeInfoSize = (info.attr & BlkAttr.STRUCTFINAL) ? size_t.sizeof : 0; + auto padsize = info.size <= 256 ? SMALLPAD : MEDPAD; + return info.size - typeInfoSize - padsize; +} + +/** + get the padding required to allocate size bytes. Note that the padding is + NOT included in the passed in size. Therefore, do NOT call this function + with the size of an allocated block. + */ +size_t __arrayPad(size_t size, const TypeInfo tinext) nothrow pure @trusted +{ + return size > MAXMEDSIZE ? LARGEPAD : ((size > MAXSMALLSIZE ? MEDPAD : SMALLPAD) + structTypeInfoSize(tinext)); +} + +/** + get the padding required to allocate size bytes, use the bits to determine + which metadata must be stored. + */ +size_t __allocPad(size_t size, uint bits) nothrow pure @trusted +{ + auto finalizerSize = (bits & BlkAttr.STRUCTFINAL) ? (void*).sizeof : 0; + if (bits & BlkAttr.APPENDABLE) + { + if (size > MAXMEDSIZE - finalizerSize) + return LARGEPAD; + auto pad = (size > MAXSMALLSIZE - finalizerSize) ? MEDPAD : SMALLPAD; + return pad + finalizerSize; + } + + return finalizerSize; +} + +/** + * Get the start of the array for the given block. + * + * Params: + * info = array metadata + * base = pointer to base of memory block + * size = size of memory block. + * Returns: + * pointer to the start of the array + */ +void *__arrayStart()(return scope BlkInfo info) nothrow pure +{ + return __arrayStart(info.base, info.size); +} +/// Ditto +void *__arrayStart()(return scope void* base, size_t size) nothrow pure +{ + return base + ((size & BIGLENGTHMASK) ? LARGEPREFIX : 0); +} + +/** + * Trim a block's extents to the known valid data that is not metadata. This + * takes into account the finalizer and array metadata. + */ +void __trimExtents(ref scope void* base, ref size_t blockSize, uint attr) nothrow pure @nogc +{ + if (attr & BlkAttr.APPENDABLE) + { + if (blockSize > PAGESIZE / 2) + { + // large block, it's always LARGEPREFIX bytes at the front. + blockSize = *(cast(size_t*)base); + base += LARGEPREFIX; + return; + } + + void *pend = base + blockSize; + if (attr & BlkAttr.STRUCTFINAL) + // skip the finalizer + pend -= (void*).sizeof; + + // get the actual length + if (blockSize <= 256) + blockSize = *(cast(ubyte*)pend - 1); + else // medium array block + blockSize = *(cast(ushort*)pend - 1); + } + else if (attr & BlkAttr.STRUCTFINAL) + { + // not appendable, but has a finalizer in the block. This is always + // stored at the end. + blockSize -= (void*).sizeof; + } +} diff --git a/runtime/druntime/src/core/internal/gc/impl/conservative/gc.d b/runtime/druntime/src/core/internal/gc/impl/conservative/gc.d index 3030303d2c..c7323fd5f4 100644 --- a/runtime/druntime/src/core/internal/gc/impl/conservative/gc.d +++ b/runtime/druntime/src/core/internal/gc/impl/conservative/gc.d @@ -15,7 +15,7 @@ module core.internal.gc.impl.conservative.gc; //debug = PARALLEL_PRINTF; // turn on printf's //debug = COLLECT_PRINTF; // turn on printf's //debug = MARK_PRINTF; // turn on printf's -//debug = PRINTF_TO_FILE; // redirect printf's ouptut to file "gcx.log" +//debug = PRINTF_TO_FILE; // redirect printf's output to file "gcx.log" //debug = LOGGING; // log allocations / frees //debug = MEMSTOMP; // stomp on memory //debug = SENTINEL; // add underrun/overrrun protection @@ -41,8 +41,9 @@ import core.gc.gcinterface; import core.internal.container.treap; import core.internal.spinlock; import core.internal.gc.pooltable; +import core.internal.gc.blkcache; -import cstdlib = core.stdc.stdlib : calloc, free, malloc, realloc; +import cstdlib = core.stdc.stdlib; import core.stdc.string : memcpy, memset, memmove; import core.bitop; import core.thread; @@ -90,8 +91,8 @@ private { // to allow compilation of this module without access to the rt package, // make these functions available from rt.lifetime - void rt_finalizeFromGC(void* p, size_t size, uint attr) nothrow; - int rt_hasFinalizerInSegment(void* p, size_t size, uint attr, const scope void[] segment) nothrow; + void rt_finalizeFromGC(void* p, size_t size, uint attr, const(TypeInfo) typeInfo) nothrow; + int rt_hasFinalizerInSegment(void* p, size_t size, const(TypeInfo) typeInfo, const scope void[] segment) nothrow; // Declared as an extern instead of importing core.exception // to avoid inlining - see https://issues.dlang.org/show_bug.cgi?id=13725. @@ -405,7 +406,7 @@ class ConservativeGC : GC p = sentinel_sub(p); if (p != pool.findBase(p)) return 0; - auto biti = cast(size_t)(p - pool.baseAddr) >> pool.shiftBy; + const biti = cast(size_t)(p - pool.baseAddr) >> pool.shiftBy; oldb = pool.getBits(biti); pool.setBits(biti, mask); @@ -448,7 +449,7 @@ class ConservativeGC : GC p = sentinel_sub(p); if (p != pool.findBase(p)) return 0; - auto biti = cast(size_t)(p - pool.baseAddr) >> pool.shiftBy; + const biti = cast(size_t)(p - pool.baseAddr) >> pool.shiftBy; oldb = pool.getBits(biti); pool.clrBits(biti, mask); @@ -475,23 +476,39 @@ class ConservativeGC : GC */ void *malloc(size_t size, uint bits = 0, const TypeInfo ti = null) nothrow { - if (!size) + import core.internal.gc.blockmeta; + + if (size == 0) + { + return null; + } + + adjustAttrs(bits, ti); + + immutable padding = __allocPad(size, bits); + + bool overflow; + import core.checkedint : addu; + immutable needed = addu(size, padding, overflow); + if (overflow) { return null; } size_t localAllocSize = void; - auto p = runLocked!(mallocNoSync, mallocTime, numMallocs)(size, bits, localAllocSize, ti); + auto p = runLocked!(mallocNoSync, mallocTime, numMallocs)(needed, bits, localAllocSize, ti); invalidate(p[0 .. localAllocSize], 0xF0, true); + auto ret = setupMetadata(p[0 .. localAllocSize], bits, padding, size, ti); + if (!(bits & BlkAttr.NO_SCAN)) { - memset(p + size, 0, localAllocSize - size); + memset(ret.ptr + size, 0, ret.length - size); } - return p; + return ret.ptr; } @@ -503,7 +520,7 @@ class ConservativeGC : GC assert(size != 0); debug(PRINTF) - printf("GC::malloc(gcx = %p, size = %d bits = %x, ti = %s)\n", gcx, size, bits, debugTypeName(ti).ptr); + printf("GC::malloc(gcx = %p, size = %zd bits = %x, ti = %s)\n", gcx, size, bits, debugTypeName(ti).ptr); assert(gcx); //debug(PRINTF) printf("gcx.self = %x, pthread_self() = %x\n", gcx.self, pthread_self()); @@ -527,6 +544,8 @@ class ConservativeGC : GC BlkInfo qalloc( size_t size, uint bits, const scope TypeInfo ti) nothrow { + // qalloc should not be used for building metadata-containing blocks, + // so avoid all the checking for bits and array padding. if (!size) { @@ -564,25 +583,45 @@ class ConservativeGC : GC */ void *calloc(size_t size, uint bits = 0, const TypeInfo ti = null) nothrow { + import core.internal.gc.blockmeta; + if (!size) { return null; } + adjustAttrs(bits, ti); + + immutable padding = __allocPad(size, bits); + bool overflow; + import core.checkedint : addu; + immutable needed = addu(size, padding, overflow); + if (overflow) + { + return null; + } + + size_t localAllocSize = void; - auto p = runLocked!(mallocNoSync, mallocTime, numMallocs)(size, bits, localAllocSize, ti); + auto p = runLocked!(mallocNoSync, mallocTime, numMallocs)(needed, bits, localAllocSize, ti); debug (VALGRIND) makeMemUndefined(p[0..size]); - invalidate((p + size)[0 .. localAllocSize - size], 0xF0, true); - memset(p, 0, size); - if (!(bits & BlkAttr.NO_SCAN)) + auto ret = setupMetadata(p[0 .. localAllocSize], bits, padding, size, ti); + + invalidate((ret.ptr + size)[0 .. ret.length - size], 0xF0, true); + + if (bits & BlkAttr.NO_SCAN) { - memset(p + size, 0, localAllocSize - size); + memset(ret.ptr, 0, size); + } + else + { + memset(ret.ptr, 0, ret.length); } - return p; + return ret.ptr; } /** @@ -595,7 +634,8 @@ class ConservativeGC : GC * Params: * p = A pointer to the root of a valid memory block or to null. * size = The desired allocation size in bytes. - * bits = A bitmask of the attributes to set on this block. + * bits = A bitmask of the attributes to set on this block. APPENDABLE and + * FINALIZE are not allowed for realloc. * ti = TypeInfo to describe the memory. * * Returns: @@ -607,14 +647,28 @@ class ConservativeGC : GC */ void *realloc(void *p, size_t size, uint bits = 0, const TypeInfo ti = null) nothrow { + if (bits & (BlkAttr.APPENDABLE | BlkAttr.FINALIZE)) + // these bits are not allowed. We can't properly manage + // reallocation of such blocks. + onInvalidMemoryOperationError(); + size_t localAllocSize = void; auto oldp = p; p = runLocked!(reallocNoSync, mallocTime, numMallocs)(p, size, bits, localAllocSize, ti); - if (p && !(bits & BlkAttr.NO_SCAN)) + if (p) { - memset(p + size, 0, localAllocSize - size); + // invalidate any block info cache we have for the old allocation. + import core.internal.gc.blkcache; + if (auto bic = __getBlkInfo(oldp)) { + *bic = BlkInfo.init; + } + + if (!(bits & BlkAttr.NO_SCAN)) + { + memset(p + size, 0, localAllocSize - size); + } } return p; @@ -662,7 +716,8 @@ class ConservativeGC : GC void* doMalloc() { if (!bits) - bits = pool.getBits(biti); + bits = pool.getBits(biti) & + ~(BlkAttr.APPENDABLE | BlkAttr.FINALIZE | BlkAttr.STRUCTFINAL); void* p2 = mallocNoSync(size, bits, alloc_size, ti); debug (SENTINEL) @@ -759,7 +814,16 @@ class ConservativeGC : GC size_t extend(void* p, size_t minsize, size_t maxsize, const TypeInfo ti) nothrow { - return runLocked!(extendNoSync, extendTime, numExtends)(p, minsize, maxsize, ti); + auto result = runLocked!(extendNoSync, extendTime, numExtends)(p, minsize, maxsize, ti); + if (result != 0) { + // invalidate any block info cache we have for the old allocation. + import core.internal.gc.blkcache; + if (auto bic = __getBlkInfo(p)) { + *bic = BlkInfo.init; + } + } + + return result; } @@ -785,25 +849,25 @@ class ConservativeGC : GC return 0; auto lpool = cast(LargeObjectPool*) pool; - size_t pagenum = lpool.pagenumOf(p); + const pagenum = lpool.pagenumOf(p); if (lpool.pagetable[pagenum] != Bins.B_PAGE) return 0; - size_t psz = lpool.bPageOffsets[pagenum]; + uint psz = lpool.bPageOffsets[pagenum]; assert(psz > 0); - auto minsz = lpool.numPages(minsize); - auto maxsz = lpool.numPages(maxsize); + const minsz = lpool.numPages(minsize); + const maxsz = lpool.numPages(maxsize); if (pagenum + psz >= lpool.npages) return 0; if (lpool.pagetable[pagenum + psz] != Bins.B_FREE) return 0; - size_t freesz = lpool.bPageOffsets[pagenum + psz]; + const freesz = lpool.bPageOffsets[pagenum + psz]; if (freesz < minsz) return 0; - size_t sz = freesz > maxsz ? maxsz : freesz; + const sz = freesz > maxsz ? maxsz : freesz; invalidate((pool.baseAddr + (pagenum + psz) * PAGESIZE)[0 .. sz * PAGESIZE], 0xF0, true); memset(lpool.pagetable + pagenum + psz, Bins.B_PAGEPLUS, sz); lpool.bPageOffsets[pagenum] = cast(uint) (psz + sz); @@ -867,16 +931,24 @@ class ConservativeGC : GC return; } - return runLocked!(freeNoSync, freeTime, numFrees)(p); + auto didFree = runLocked!(freeNoSync, freeTime, numFrees)(p); + + if (didFree) { + // invalidate any block info cache we have for the old allocation. + import core.internal.gc.blkcache; + if (auto bic = __getBlkInfo(p)) { + *bic = BlkInfo.init; + } + } } // // Implementation of free. // - private void freeNoSync(void *p) nothrow @nogc + private bool freeNoSync(void *p) nothrow @nogc { - debug(PRINTF) printf("Freeing %p\n", cast(size_t) p); + debug(PRINTF) printf("Freeing %#zx\n", cast(size_t) p); assert (p); Pool* pool; @@ -887,11 +959,11 @@ class ConservativeGC : GC // Find which page it is in pool = gcx.findPool(p); if (!pool) // if not one of ours - return; // ignore + return false; // ignore pagenum = pool.pagenumOf(p); - debug(PRINTF) printf("pool base = %p, PAGENUM = %d of %d, bin = %d\n", pool.baseAddr, pagenum, pool.npages, pool.pagetable[pagenum]); + debug(PRINTF) printf("pool base = %p, PAGENUM = %zd of %zd, bin = %d\n", pool.baseAddr, pagenum, pool.npages, pool.pagetable[pagenum]); debug(PRINTF) if (pool.isLargeObject) printf("Block size = %d\n", pool.bPageOffsets[pagenum]); bin = pool.pagetable[pagenum]; @@ -899,11 +971,11 @@ class ConservativeGC : GC // Verify that the pointer is at the beginning of a block, // no action should be taken if p is an interior pointer if (bin > Bins.B_PAGE) // B_PAGEPLUS or B_FREE - return; + return false; size_t off = (sentinel_sub(p) - pool.baseAddr); size_t base = baseOffset(off, bin); if (off != base) - return; + return false; sentinel_Invariant(p); auto q = p; @@ -928,7 +1000,7 @@ class ConservativeGC : GC { biti = cast(size_t)(p - pool.baseAddr) >> pool.ShiftBy.Small; if (pool.freebits.test (biti)) - return; + return false; // Add to free list List *list = cast(List*)p; @@ -948,6 +1020,8 @@ class ConservativeGC : GC pool.clrBits(biti, ~BlkAttr.NONE); gcx.leakDetector.log_free(sentinel_add(p), ssize); + + return true; } @@ -1377,6 +1451,193 @@ class ConservativeGC : GC stats.freeSize += freeListSize; stats.allocatedInCurrentThread = bytesAllocated; } + + // ARRAY FUNCTIONS + void[] getArrayUsed(void *ptr, bool atomic = false) nothrow + { + import core.internal.gc.blockmeta; + import core.internal.gc.blkcache; + import core.internal.array.utils; + + // lookup the block info, using the cache if possible. + auto bic = atomic ? null : __getBlkInfo(ptr); + auto info = bic ? *bic : query(ptr); + + if (!(info.attr & BlkAttr.APPENDABLE)) + // not appendable + return null; + + assert(info.base); // sanity check. + if (!bic && !atomic) + // cache the lookup for next time + __insertBlkInfoCache(info, null); + + auto usedSize = atomic ? __arrayAllocLengthAtomic(info) : __arrayAllocLength(info); + return __arrayStart(info)[0 .. usedSize]; + } + + /* NOTE about @trusted in these functions: + * These functions do a lot of pointer manipulation, and has writeable + * access to BlkInfo which is used to interface with other parts of the GC, + * including the block metadata and block cache. Marking these functions as + * @safe would mean that any modification of BlkInfo fields should be + * considered @safe, which is not the case. For example, it would be + * perfectly legal to change the BlkInfo size to some huge number, and then + * store it in the block cache to blow up later. The utility functions + * count on the BlkInfo representing the correct information inside the GC. + * + * In order to mark these @safe, we would need a BlkInfo that has + * restrictive access (i.e. @system only) to the information inside the + * BlkInfo. Until then any use of these structures needs to be @trusted, + * and therefore the entire functions are @trusted. The API is still @safe + * because the information is stored and looked up by the GC, not the + * caller. + */ + bool expandArrayUsed(void[] slice, size_t newUsed, bool atomic = false) nothrow @trusted + { + import core.internal.gc.blockmeta; + import core.internal.gc.blkcache; + import core.internal.array.utils; + + if (newUsed < slice.length) + // cannot "expand" by shrinking. + return false; + + // lookup the block info, using the cache if possible + auto bic = atomic ? null : __getBlkInfo(slice.ptr); + auto info = bic ? *bic : query(slice.ptr); + + if (!(info.attr & BlkAttr.APPENDABLE)) + // not appendable + return false; + + assert(info.base); // sanity check. + + immutable offset = slice.ptr - __arrayStart(info); + newUsed += offset; + auto existingUsed = slice.length + offset; + + size_t typeInfoSize = (info.attr & BlkAttr.STRUCTFINAL) ? size_t.sizeof : 0; + if (__setArrayAllocLengthImpl(info, offset + newUsed, atomic, existingUsed, typeInfoSize)) + { + // could expand without extending + if (!bic && !atomic) + // cache the lookup for next time + __insertBlkInfoCache(info, null); + return true; + } + + // if we got here, just setting the used size did not work. + if (info.size < PAGESIZE) + // nothing else we can do + return false; + + // try extending the block into subsequent pages. + immutable requiredExtension = newUsed - info.size - LARGEPAD; + auto extendedSize = extend(info.base, requiredExtension, requiredExtension, null); + if (extendedSize == 0) + // could not extend, can't satisfy the request + return false; + + info.size = extendedSize; + if (bic) + *bic = info; + else if (!atomic) + __insertBlkInfoCache(info, null); + + // this should always work. + return __setArrayAllocLengthImpl(info, newUsed, atomic, existingUsed, typeInfoSize); + } + + bool shrinkArrayUsed(void[] slice, size_t existingUsed, bool atomic = false) nothrow + { + import core.internal.gc.blockmeta; + import core.internal.gc.blkcache; + import core.internal.array.utils; + + if (existingUsed < slice.length) + // cannot "shrink" by growing. + return false; + + // lookup the block info, using the cache if possible. + auto bic = atomic ? null : __getBlkInfo(slice.ptr); + auto info = bic ? *bic : query(slice.ptr); + + if (!(info.attr & BlkAttr.APPENDABLE)) + // not appendable + return false; + + assert(info.base); // sanity check + + immutable offset = slice.ptr - __arrayStart(info); + existingUsed += offset; + auto newUsed = slice.length + offset; + + size_t typeInfoSize = (info.attr & BlkAttr.STRUCTFINAL) ? size_t.sizeof : 0; + + if (__setArrayAllocLengthImpl(info, newUsed, atomic, existingUsed, typeInfoSize)) + { + if (!bic && !atomic) + __insertBlkInfoCache(info, null); + return true; + } + + return false; + } + + size_t reserveArrayCapacity(void[] slice, size_t request, bool atomic = false) nothrow @trusted + { + import core.internal.gc.blockmeta; + import core.internal.gc.blkcache; + import core.internal.array.utils; + + // lookup the block info, using the cache if possible. + auto bic = atomic ? null : __getBlkInfo(slice.ptr); + auto info = bic ? *bic : query(slice.ptr); + + if (!(info.attr & BlkAttr.APPENDABLE)) + // not appendable + return 0; + + assert(info.base); // sanity check + + immutable offset = slice.ptr - __arrayStart(info); + request += offset; + auto existingUsed = slice.length + offset; + + // make sure this slice ends at the used space + auto blockUsed = atomic ? __arrayAllocLengthAtomic(info) : __arrayAllocLength(info); + if (existingUsed != blockUsed) + // not an expandable slice. + return 0; + + // see if the capacity can contain the existing data + auto existingCapacity = __arrayAllocCapacity(info); + if (existingCapacity < request) + { + if (info.size < PAGESIZE) + // no possibility to extend + return 0; + + immutable requiredExtension = request - existingCapacity; + auto extendedSize = extend(info.base, requiredExtension, requiredExtension, null); + if (extendedSize == 0) + // could not extend, can't satisfy the request + return 0; + + info.size = extendedSize; + + // update the block info cache if it was used + if (bic) + *bic = info; + else if (!atomic) + __insertBlkInfoCache(info, null); + + existingCapacity = __arrayAllocCapacity(info); + } + + return existingCapacity - offset; + } } @@ -1427,7 +1688,7 @@ short[PAGESIZE / 16][Bins.B_NUMSMALL + 1] calcBinBase() foreach (i, size; binsize) { - short end = (PAGESIZE / size) * size; + short end = cast(short) ((PAGESIZE / size) * size); short bsz = size / 16; foreach (off; 0..PAGESIZE/16) { @@ -1576,7 +1837,7 @@ struct Gcx long apiTime = mallocTime + reallocTime + freeTime + extendTime + otherTime + lockTime; printf("\tGC API: %lld ms\n", toDuration(apiTime).total!"msecs"); - sprintf(apitxt.ptr, " API%5ld ms", toDuration(apiTime).total!"msecs"); + sprintf(apitxt.ptr, " API%5lld ms", toDuration(apiTime).total!"msecs"); } printf("GC summary:%5lld MB,%5lld GC%5lld ms, Pauses%5lld ms <%5lld ms%s\n", @@ -1610,7 +1871,7 @@ struct Gcx void Invariant() const { } debug(INVARIANT) - invariant() + invariant { if (initialized) { @@ -1982,7 +2243,7 @@ struct Gcx */ void* bigAlloc(size_t size, ref size_t alloc_size, uint bits, const TypeInfo ti = null) nothrow { - debug(PRINTF) printf("In bigAlloc. Size: %d\n", size); + debug(PRINTF) printf("In bigAlloc. Size: %zd\n", size); LargeObjectPool* pool; size_t pn; @@ -2046,7 +2307,7 @@ struct Gcx debug(PRINTF) printFreeInfo(&pool.base); auto p = pool.baseAddr + pn * PAGESIZE; - debug(PRINTF) printf("Got large alloc: %p, pt = %d, np = %d\n", p, pool.pagetable[pn], npages); + debug(PRINTF) printf("Got large alloc: %p, pt = %d, np = %zd\n", p, pool.pagetable[pn], npages); invalidate(p[0 .. size], 0xF1, true); alloc_size = npages * PAGESIZE; //debug(PRINTF) printf("\tp = %p\n", p); @@ -2623,9 +2884,13 @@ struct Gcx if (pool.finals.nbits && pool.finals.clear(biti)) { + import core.internal.gc.blockmeta; size_t size = npages * PAGESIZE - SENTINEL_EXTRA; + size = sentinel_size(q, size); uint attr = pool.getBits(biti); - rt_finalizeFromGC(q, sentinel_size(q, size), attr); + auto ti = __getBlockFinalizerInfo(q, size, attr); + __trimExtents(q, size, attr); + rt_finalizeFromGC(q, size, attr, ti); } pool.clrBits(biti, ~BlkAttr.NONE ^ BlkAttr.FINALIZE); @@ -2740,21 +3005,28 @@ struct Gcx { immutable biti = base + i; - if (!pool.mark.test(biti)) - { - void* q = sentinel_add(p); - sentinel_Invariant(q); + if (pool.mark.test(biti)) + continue; - if (pool.finals.nbits && pool.finals.test(biti)) - rt_finalizeFromGC(q, sentinel_size(q, size), pool.getBits(biti)); + void* q = sentinel_add(p); + sentinel_Invariant(q); + + if (pool.finals.nbits && pool.finals.test(biti)) + { + import core.internal.gc.blockmeta; + size_t ssize = sentinel_size(q, size); + uint attr = pool.getBits(biti); + auto ti = __getBlockFinalizerInfo(q, ssize, attr); + __trimExtents(q, ssize, attr); + rt_finalizeFromGC(q, ssize, attr, ti); + } - assert(core.bitop.bt(toFree.ptr, i)); + assert(core.bitop.bt(toFree.ptr, i)); - debug(COLLECT_PRINTF) printf("\tcollecting %p\n", p); - leakDetector.log_free(q, sentinel_size(q, size)); + debug(COLLECT_PRINTF) printf("\tcollecting %p\n", p); + leakDetector.log_free(q, sentinel_size(q, size)); - invalidate(p[0 .. size], 0xF3, false); - } + invalidate(p[0 .. size], 0xF3, false); } } @@ -2877,7 +3149,7 @@ struct Gcx markProcPid = 0; // process GC marks then sweep thread_suspendAll(); - thread_processGCMarks(&isMarked); + thread_processTLSGCData(&clearBlkCacheData); thread_resumeAll(); break; case ChildStatus.running: @@ -3112,7 +3384,7 @@ Lmark: markAll!(markConservative!false)(); } - thread_processGCMarks(&isMarked); + thread_processTLSGCData(&clearBlkCacheData); thread_resumeAll(); isFinal = false; } @@ -3165,18 +3437,31 @@ Lmark: return freedPages; } + /** + * Clear the block cache data if it exists, given the data which is the + * block info cache. + * + * Warning! This should only be called while the world is stopped inside + * the fullcollect function after all live objects have been marked, but + * before sweeping. + */ + void *clearBlkCacheData(void* data) scope nothrow + { + processGCMarks(data, &isMarked); + return data; + } + /** * Returns true if the addr lies within a marked block. * * Warning! This should only be called while the world is stopped inside * the fullcollect function after all live objects have been marked, but before sweeping. */ - int isMarked(void *addr) scope nothrow + IsMarked isMarked(void *addr) scope nothrow { // first, we find the Pool this block is in, then check to see if the // mark bit is clear. - auto pool = findPool(addr); - if (pool) + if (auto pool = findPool(addr)) { auto offset = cast(size_t)(addr - pool.baseAddr); auto pn = offset / PAGESIZE; @@ -3378,7 +3663,7 @@ Lmark: version (Posix) { - import core.sys.posix.signal; + import core.sys.posix.signal : pthread_sigmask, SIG_BLOCK, SIG_SETMASK, sigfillset, sigset_t; // block all signals, scanBackground inherits this mask. // see https://issues.dlang.org/show_bug.cgi?id=20256 sigset_t new_mask, old_mask; @@ -3462,9 +3747,12 @@ Lmark: if (atomicLoad(busyThreads) == 0) return; - debug(PARALLEL_PRINTF) + version (Posix) debug (PARALLEL_PRINTF) + { + import core.sys.posix.pthread : pthread_self, pthread_t; pthread_t threadId = pthread_self(); - debug(PARALLEL_PRINTF) printf("scanBackground thread %d start\n", threadId); + printf("scanBackground thread %d start\n", threadId); + } ScanRange!precise rng; alias toscan = scanStack!precise; @@ -3480,13 +3768,16 @@ Lmark: busyThreads.atomicOp!"+="(1); if (toscan.popLocked(rng)) { - debug(PARALLEL_PRINTF) printf("scanBackground thread %d scanning range [%p,%lld] from stack\n", threadId, - rng.pbot, cast(long) (rng.ptop - rng.pbot)); + version (Posix) debug (PARALLEL_PRINTF) + { + printf("scanBackground thread %d scanning range [%p,%lld] from stack\n", + threadId, rng.pbot, cast(long) (rng.ptop - rng.pbot)); + } mark!(precise, true, true)(rng); } busyThreads.atomicOp!"-="(1); } - debug(PARALLEL_PRINTF) printf("scanBackground thread %d done\n", threadId); + version (Posix) debug (PARALLEL_PRINTF) printf("scanBackground thread %d done\n", threadId); } } @@ -3928,7 +4219,7 @@ struct Pool void Invariant() const {} debug(INVARIANT) - invariant() + invariant { if (baseAddr) { @@ -4006,12 +4297,12 @@ struct Pool debugTypeName(ti).ptr, p, bitmap, cast(ulong)element_size); debug(PRINTF) for (size_t i = 0; i < element_size/((void*).sizeof); i++) - printf("%d", (bitmap[i/(8*size_t.sizeof)] >> (i%(8*size_t.sizeof))) & 1); + printf("%zd", (bitmap[i/(8*size_t.sizeof)] >> (i%(8*size_t.sizeof))) & 1); debug(PRINTF) printf("\n"); if (tocopy * (void*).sizeof < s) // better safe than sorry: if allocated more, assume pointers inside { - debug(PRINTF) printf(" Appending %d pointer bits\n", s/(void*).sizeof - tocopy); + debug(PRINTF) printf(" Appending %zd pointer bits\n", s/(void*).sizeof - tocopy); is_pointer.setRange(offset/(void*).sizeof + tocopy, s/(void*).sizeof - tocopy); } } @@ -4247,10 +4538,13 @@ struct LargeObjectPool size_t size = sentinel_size(p, getSize(pn)); uint attr = getBits(biti); - if (!rt_hasFinalizerInSegment(p, size, attr, segment)) + import core.internal.gc.blockmeta; + auto ti = __getBlockFinalizerInfo(p, size, attr); + if (!rt_hasFinalizerInSegment(p, size, ti, segment)) continue; - rt_finalizeFromGC(p, size, attr); + __trimExtents(p, size, attr); + rt_finalizeFromGC(p, size, attr, ti); clrBits(biti, ~BlkAttr.NONE); @@ -4371,11 +4665,14 @@ struct SmallObjectPool auto q = sentinel_add(p); uint attr = getBits(biti); - const ssize = sentinel_size(q, size); - if (!rt_hasFinalizerInSegment(q, ssize, attr, segment)) + auto ssize = sentinel_size(q, size); + import core.internal.gc.blockmeta; + auto ti = __getBlockFinalizerInfo(q, ssize, attr); + if (!rt_hasFinalizerInSegment(q, ssize, ti, segment)) continue; - rt_finalizeFromGC(q, ssize, attr); + __trimExtents(q, ssize, attr); + rt_finalizeFromGC(q, ssize, attr, ti); freeBits = true; toFree.set(i); @@ -4524,7 +4821,7 @@ debug(PRINTF_TO_FILE) } len += fprintf(gcx_fh, fmt, args); fflush(gcx_fh); - import core.stdc.string; + import core.stdc.string : strlen; hadNewline = fmt && fmt[0] && fmt[strlen(fmt) - 1] == '\n'; return len; } @@ -4537,7 +4834,7 @@ debug(PRINTF) void printFreeInfo(Pool* pool) nothrow if (pool.pagetable[i] >= Bins.B_FREE) nReallyFree++; } - printf("Pool %p: %d really free, %d supposedly free\n", pool, nReallyFree, pool.freepages); + printf("Pool %p: %d really free, %zd supposedly free\n", pool, nReallyFree, pool.freepages); } debug(PRINTF) @@ -4546,7 +4843,7 @@ void printGCBits(GCBits* bits) for (size_t i = 0; i < bits.nwords; i++) { if (i % 32 == 0) printf("\n\t"); - printf("%x ", bits.data[i]); + printf("%zx ", bits.data[i]); } printf("\n"); } @@ -4990,8 +5287,8 @@ version (D_LP64) unittest catch (OutOfMemoryError) { // ignore if the system still doesn't have enough virtual memory - import core.stdc.stdio; - printf("%s(%d): out-of-memory execption ignored, phys_mem = %zd", + import core.stdc.stdio : printf; + printf("%s(%d): out-of-memory exception ignored, phys_mem = %zd", __FILE__.ptr, __LINE__, phys_mem); } } @@ -5045,7 +5342,7 @@ unittest unittest { import core.memory; - import core.stdc.stdio; + import core.stdc.stdio : printf; // allocate from large pool auto o = GC.malloc(10); @@ -5122,3 +5419,42 @@ void undefinedWrite(T)(ref T var, T value) nothrow else var = value; } + +private void adjustAttrs(ref uint attrs, const TypeInfo ti) nothrow +{ + bool hasContext = ti !is null; + if((attrs & BlkAttr.FINALIZE) && hasContext && typeid(ti) is typeid(TypeInfo_Struct)) + attrs |= BlkAttr.STRUCTFINAL; + else + // STRUCTFINAL now just means "has a context pointer added to the block" + attrs &= ~BlkAttr.STRUCTFINAL; +} + +// sets up the array/context pointer metadata based on the block allocated. +// This is called on any block *creation*, and not on updating the array +// metadata. +// +// The return value is the true data that the user can use. +private void[] setupMetadata(void[] block, uint bits, size_t padding, size_t used, const TypeInfo ti) nothrow +{ + import core.internal.gc.blockmeta; + import core.internal.array.utils; + + BlkInfo info = BlkInfo( + base: block.ptr, + attr: bits, + size: block.length + ); + + + __setBlockFinalizerInfo(info, ti); + + if (bits & BlkAttr.APPENDABLE) { + auto typeInfoSize = (bits & BlkAttr.STRUCTFINAL) ? (void*).sizeof : 0; + auto success = __setArrayAllocLengthImpl(info, used, false, size_t.max, typeInfoSize); + assert(success); + return __arrayStart(info)[0 .. block.length - padding]; + } + + return block.ptr[0 .. block.length - padding]; +} diff --git a/runtime/druntime/src/core/internal/gc/impl/manual/gc.d b/runtime/druntime/src/core/internal/gc/impl/manual/gc.d index 36add7a2d6..3da92db14e 100644 --- a/runtime/druntime/src/core/internal/gc/impl/manual/gc.d +++ b/runtime/druntime/src/core/internal/gc/impl/manual/gc.d @@ -267,4 +267,24 @@ class ManualGC : GC { return typeof(return).init; } + + void[] getArrayUsed(void *ptr, bool atomic = false) nothrow + { + return null; + } + + bool expandArrayUsed(void[] slice, size_t newUsed, bool atomic = false) nothrow @safe + { + return false; + } + + size_t reserveArrayCapacity(void[] slice, size_t request, bool atomic = false) nothrow @safe + { + return 0; + } + + bool shrinkArrayUsed(void[] slice, size_t existingUsed, bool atomic = false) nothrow + { + return false; + } } diff --git a/runtime/druntime/src/core/internal/gc/impl/proto/gc.d b/runtime/druntime/src/core/internal/gc/impl/proto/gc.d index dbe8600711..cbdcdb8852 100644 --- a/runtime/druntime/src/core/internal/gc/impl/proto/gc.d +++ b/runtime/druntime/src/core/internal/gc/impl/proto/gc.d @@ -241,4 +241,24 @@ class ProtoGC : GC { return stats().allocatedInCurrentThread; } + + void[] getArrayUsed(void *ptr, bool atomic = false) nothrow + { + return null; + } + + bool expandArrayUsed(void[] slice, size_t newUsed, bool atomic = false) nothrow @safe + { + return false; + } + + size_t reserveArrayCapacity(void[] slice, size_t request, bool atomic = false) nothrow @safe + { + return 0; + } + + bool shrinkArrayUsed(void[] slice, size_t existingUsed, bool atomic = false) nothrow + { + return false; + } } diff --git a/runtime/druntime/src/core/internal/gc/os.d b/runtime/druntime/src/core/internal/gc/os.d index 0db1753575..d5431d4ed9 100644 --- a/runtime/druntime/src/core/internal/gc/os.d +++ b/runtime/druntime/src/core/internal/gc/os.d @@ -33,8 +33,10 @@ else version (Posix) else version (WatchOS) version = Darwin; - import core.sys.posix.sys.mman; - import core.stdc.stdlib; + 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; /// Possible results for the wait_pid() function. @@ -67,22 +69,18 @@ else version (Posix) while (waited_pid == -1 && errno == EINTR); if (waited_pid == 0) return ChildStatus.running; - else if (errno == ECHILD) + if (errno == ECHILD) return ChildStatus.done; // someone called posix.syswait - else if (waited_pid != pid || status != 0) + if (waited_pid != pid || status != 0) onForkError(); return ChildStatus.done; } - public import core.sys.posix.unistd: pid_t, fork; - import core.sys.posix.sys.wait: waitpid, WNOHANG; - import core.stdc.errno: errno, EINTR, ECHILD; - //version = GC_Use_Alloc_MMap; } else { - import core.stdc.stdlib; + import core.stdc.stdlib : free, malloc; //version = GC_Use_Alloc_Malloc; } @@ -292,12 +290,18 @@ else version (Posix) { ulong os_physical_mem(bool avail) nothrow @nogc { - import core.sys.posix.unistd; + static import core.sys.posix.unistd; + import core.sys.posix.unistd : _SC_PAGESIZE, _SC_PHYS_PAGES, sysconf; const pageSize = sysconf(_SC_PAGESIZE); - static if (__traits(compiles, _SC_AVPHYS_PAGES)) // not available on all platforms + static if (__traits(compiles, core.sys.posix.unistd._SC_AVPHYS_PAGES)) // not available on all platforms + { + import core.sys.posix.unistd : _SC_AVPHYS_PAGES; const sc = avail ? _SC_AVPHYS_PAGES : _SC_PHYS_PAGES; + } else + { const sc = _SC_PHYS_PAGES; + } const pages = sysconf(sc); return pageSize * pages; } diff --git a/runtime/druntime/src/core/internal/gc/pooltable.d b/runtime/druntime/src/core/internal/gc/pooltable.d index f9ec3d2a22..412ade4290 100644 --- a/runtime/druntime/src/core/internal/gc/pooltable.d +++ b/runtime/druntime/src/core/internal/gc/pooltable.d @@ -7,7 +7,7 @@ */ module core.internal.gc.pooltable; -static import cstdlib=core.stdc.stdlib; +static import cstdlib = core.stdc.stdlib; struct PoolTable(Pool) { diff --git a/runtime/druntime/src/core/internal/gc/proxy.d b/runtime/druntime/src/core/internal/gc/proxy.d index 7e3b55c84c..b29486c5d4 100644 --- a/runtime/druntime/src/core/internal/gc/proxy.d +++ b/runtime/druntime/src/core/internal/gc/proxy.d @@ -246,6 +246,26 @@ extern (C) return instance.allocatedInCurrentThread(); } + void[] gc_getArrayUsed(void *ptr, bool atomic) nothrow + { + return instance.getArrayUsed( ptr, atomic ); + } + + bool gc_expandArrayUsed(void[] slice, size_t newUsed, bool atomic) nothrow + { + return instance.expandArrayUsed( slice, newUsed, atomic ); + } + + size_t gc_reserveArrayCapacity(void[] slice, size_t request, bool atomic) nothrow + { + return instance.reserveArrayCapacity( slice, request, atomic ); + } + + bool gc_shrinkArrayUsed(void[] slice, size_t existingUsed, bool atomic) nothrow + { + return instance.shrinkArrayUsed( slice, existingUsed, atomic ); + } + GC gc_getProxy() nothrow { return instance; diff --git a/runtime/druntime/src/core/internal/newaa.d b/runtime/druntime/src/core/internal/newaa.d index 7c858f3352..47283f2803 100644 --- a/runtime/druntime/src/core/internal/newaa.d +++ b/runtime/druntime/src/core/internal/newaa.d @@ -44,7 +44,7 @@ struct Impl Bucket[] buckets; uint used; uint deleted; - TypeInfo_Struct entryTI; + const(TypeInfo) entryTI; uint firstUsed; immutable uint keysz; immutable uint valsz; @@ -75,15 +75,6 @@ private size_t mix(size_t h) @safe pure nothrow @nogc return h; } -struct Entry(K, V) -{ - // can make this const, because we aren't really going to use it aside from - // construction. - const K key; - V value; -} - - // create a binary-compatible AA structure that can be used directly as an // associative array. // NOTE: this must only be called during CTFE @@ -92,7 +83,7 @@ AAShell makeAA(K, V)(V[K] src) @trusted assert(__ctfe, "makeAA Must only be called at compile time"); immutable srclen = src.length; assert(srclen <= uint.max); - alias E = Entry!(K, V); + alias E = TypeInfo_AssociativeArray.Entry!(K, V); if (srclen == 0) return AAShell.init; // first, determine the size that would be used if we grew the bucket list diff --git a/runtime/druntime/src/core/internal/parseoptions.d b/runtime/druntime/src/core/internal/parseoptions.d index 2dd3ec8375..bc4755517e 100644 --- a/runtime/druntime/src/core/internal/parseoptions.d +++ b/runtime/druntime/src/core/internal/parseoptions.d @@ -9,12 +9,10 @@ module core.internal.parseoptions; -import core.stdc.stdlib; -import core.stdc.stdio; -import core.stdc.ctype; -import core.stdc.string; -import core.vararg; import core.internal.traits : externDFunc, hasUDA; +import core.stdc.ctype : isdigit, isspace; +import core.stdc.stdio : fprintf, snprintf, sscanf, stderr; +import core.vararg; @nogc nothrow: diff --git a/runtime/druntime/src/core/internal/postblit.d b/runtime/druntime/src/core/internal/postblit.d index ed4ec1b7ff..3377ba5c3d 100644 --- a/runtime/druntime/src/core/internal/postblit.d +++ b/runtime/druntime/src/core/internal/postblit.d @@ -5,7 +5,7 @@ License: Distributed under the $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). (See accompanying file LICENSE) - Source: $(DRUNTIMESRC core/_internal/_destruction.d) + Source: $(DRUNTIMESRC core/_internal/_postblit.d) */ module core.internal.postblit; diff --git a/runtime/druntime/src/core/internal/qsort.d b/runtime/druntime/src/core/internal/qsort.d index 0040f6b460..8938a445ba 100644 --- a/runtime/druntime/src/core/internal/qsort.d +++ b/runtime/druntime/src/core/internal/qsort.d @@ -10,7 +10,7 @@ module core.internal.qsort; //debug=qsort; -import core.stdc.stdlib; +debug (qsort) import core.stdc.stdio : printf; version (OSX) version = Darwin; @@ -148,6 +148,8 @@ else version (CRuntime_UClibc) } else { + import core.stdc.stdlib : qsort; + private TypeInfo tiglobal; extern (C) void[] _adSort(return scope void[] a, TypeInfo ti) @@ -164,7 +166,6 @@ else unittest { - debug(qsort) import core.stdc.stdio; debug(qsort) printf("array.sort.unittest()\n"); int[] a = new int[10]; diff --git a/runtime/druntime/src/core/internal/string.d b/runtime/druntime/src/core/internal/string.d index 7bb319ecfa..e5abce47db 100644 --- a/runtime/druntime/src/core/internal/string.d +++ b/runtime/druntime/src/core/internal/string.d @@ -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, Walter Bright - * Source: $(DRUNTIMESRC rt/util/_string.d) + * Source: $(DRUNTIMESRC core/internal/_string.d) */ module core.internal.string; diff --git a/runtime/druntime/src/core/internal/traits.d b/runtime/druntime/src/core/internal/traits.d index f0d9ebc9a8..2475559878 100644 --- a/runtime/druntime/src/core/internal/traits.d +++ b/runtime/druntime/src/core/internal/traits.d @@ -267,8 +267,12 @@ template hasElaborateDestructor(S) } else static if (is(S == struct)) { - enum hasElaborateDestructor = __traits(hasMember, S, "__dtor") - || anySatisfy!(.hasElaborateDestructor, Fields!S); + // Once https://issues.dlang.org/show_bug.cgi?id=24865 is fixed, then + // this should be the implementation, but until that's fixed, we need the + // uncommented code. + // enum hasElaborateDestructor = __traits(hasMember, S, "__xdtor"); + + enum hasElaborateDestructor = hasDtor([__traits(allMembers, S)]); } else { @@ -276,6 +280,64 @@ template hasElaborateDestructor(S) } } +private bool hasDtor(string[] members) +{ + foreach (name; members) + { + if (name == "__xdtor") + return true; + } + + return false; +} + +@safe unittest +{ + static struct NoDestructor {} + static assert(!hasElaborateDestructor!NoDestructor); + static assert(!hasElaborateDestructor!(NoDestructor[42])); + static assert(!hasElaborateDestructor!(NoDestructor[0])); + static assert(!hasElaborateDestructor!(NoDestructor[])); + + static struct HasDestructor { ~this() {} } + static assert( hasElaborateDestructor!HasDestructor); + static assert( hasElaborateDestructor!(HasDestructor[42])); + static assert(!hasElaborateDestructor!(HasDestructor[0])); + static assert(!hasElaborateDestructor!(HasDestructor[])); + + static struct HasDestructor2 { HasDestructor s; } + static assert( hasElaborateDestructor!HasDestructor2); + static assert( hasElaborateDestructor!(HasDestructor2[42])); + static assert(!hasElaborateDestructor!(HasDestructor2[0])); + static assert(!hasElaborateDestructor!(HasDestructor2[])); + + static class HasFinalizer { ~this() {} } + static assert(!hasElaborateDestructor!HasFinalizer); + + static struct HasUnion { union { HasDestructor s; } } + static assert(!hasElaborateDestructor!HasUnion); + static assert(!hasElaborateDestructor!(HasUnion[42])); + static assert(!hasElaborateDestructor!(HasUnion[0])); + static assert(!hasElaborateDestructor!(HasUnion[])); + + static assert(!hasElaborateDestructor!int); + static assert(!hasElaborateDestructor!(int[0])); + static assert(!hasElaborateDestructor!(int[42])); + static assert(!hasElaborateDestructor!(int[])); +} + +// https://issues.dlang.org/show_bug.cgi?id=24865 +@safe unittest +{ + static struct S2 { ~this() {} } + static struct S3 { S2 field; } + static struct S6 { S3[0] field; } + + static assert( hasElaborateDestructor!S2); + static assert( hasElaborateDestructor!S3); + static assert(!hasElaborateDestructor!S6); +} + // std.traits.hasElaborateCopyDestructor template hasElaborateCopyConstructor(S) { @@ -302,7 +364,7 @@ template hasElaborateCopyConstructor(S) this(int x, int y) {} } - static assert(hasElaborateCopyConstructor!S); + static assert( hasElaborateCopyConstructor!S); static assert(!hasElaborateCopyConstructor!(S[0][1])); static struct S2 @@ -320,7 +382,11 @@ template hasElaborateCopyConstructor(S) this(int x, int y) {} } - static assert(hasElaborateCopyConstructor!S3); + static assert( hasElaborateCopyConstructor!S3); + + static struct S4 { union { S s; } } + + static assert(!hasElaborateCopyConstructor!S4); } template hasElaborateAssign(S) @@ -332,8 +398,7 @@ template hasElaborateAssign(S) else static if (is(S == struct)) { enum hasElaborateAssign = is(typeof(S.init.opAssign(rvalueOf!S))) || - is(typeof(S.init.opAssign(lvalueOf!S))) || - anySatisfy!(.hasElaborateAssign, Fields!S); + is(typeof(S.init.opAssign(lvalueOf!S))); } else { @@ -341,17 +406,337 @@ template hasElaborateAssign(S) } } +unittest +{ + { + static struct S {} + static assert(!hasElaborateAssign!S); + static assert(!hasElaborateAssign!(S[10])); + static assert(!hasElaborateAssign!(S[0])); + static assert(!hasElaborateAssign!(S[])); + } + { + static struct S { int i; } + static assert(!hasElaborateAssign!S); + static assert(!hasElaborateAssign!(S[10])); + static assert(!hasElaborateAssign!(S[0])); + static assert(!hasElaborateAssign!(S[])); + } + { + static struct S { void opAssign(S) {} } + static assert( hasElaborateAssign!S); + static assert( hasElaborateAssign!(S[10])); + static assert(!hasElaborateAssign!(S[0])); + static assert(!hasElaborateAssign!(S[])); + } + { + static struct S { void opAssign(ref S) {} } + static assert( hasElaborateAssign!S); + static assert( hasElaborateAssign!(S[10])); + static assert(!hasElaborateAssign!(S[0])); + static assert(!hasElaborateAssign!(S[])); + } + { + static struct S { void opAssign(int) {} } + static assert(!hasElaborateAssign!S); + static assert(!hasElaborateAssign!(S[10])); + static assert(!hasElaborateAssign!(S[0])); + static assert(!hasElaborateAssign!(S[])); + } + { + static struct S { this(this) {} } + static assert( hasElaborateAssign!S); + static assert( hasElaborateAssign!(S[10])); + static assert(!hasElaborateAssign!(S[0])); + static assert(!hasElaborateAssign!(S[])); + } + // https://issues.dlang.org/show_bug.cgi?id=24834 + /+ + { + static struct S { this(ref S) {} } + static assert( hasElaborateAssign!S); + static assert( hasElaborateAssign!(S[10])); + static assert(!hasElaborateAssign!(S[0])); + static assert(!hasElaborateAssign!(S[])); + } + +/ + { + static struct S { ~this() {} } + static assert( hasElaborateAssign!S); + static assert( hasElaborateAssign!(S[10])); + static assert(!hasElaborateAssign!(S[0])); + static assert(!hasElaborateAssign!(S[])); + } + { + static struct S { @disable void opAssign(S); } + static assert(!hasElaborateAssign!S); + static assert(!hasElaborateAssign!(S[10])); + static assert(!hasElaborateAssign!(S[0])); + static assert(!hasElaborateAssign!(S[])); + } + { + static struct Member {} + static struct S { Member member; } + static assert(!hasElaborateAssign!S); + static assert(!hasElaborateAssign!(S[10])); + static assert(!hasElaborateAssign!(S[0])); + static assert(!hasElaborateAssign!(S[])); + } + { + static struct Member { void opAssign(Member) {} } + static struct S { Member member; } + static assert( hasElaborateAssign!S); + static assert( hasElaborateAssign!(S[10])); + static assert(!hasElaborateAssign!(S[0])); + static assert(!hasElaborateAssign!(S[])); + } + { + static struct Member {} + static struct S { Member member; void opAssign(S) {} } + static assert( hasElaborateAssign!S); + static assert( hasElaborateAssign!(S[10])); + static assert(!hasElaborateAssign!(S[0])); + static assert(!hasElaborateAssign!(S[])); + } + { + static struct Member { @disable void opAssign(Member); } + static struct S { Member member; } + static assert(!hasElaborateAssign!S); + static assert(!hasElaborateAssign!(S[10])); + static assert(!hasElaborateAssign!(S[0])); + static assert(!hasElaborateAssign!(S[])); + } + { + static struct Member { @disable void opAssign(Member); } + static struct S { Member member; void opAssign(S) {} } + static assert( hasElaborateAssign!S); + static assert( hasElaborateAssign!(S[10])); + static assert(!hasElaborateAssign!(S[0])); + static assert(!hasElaborateAssign!(S[])); + } + { + static struct Member { void opAssign(Member) {} } + static struct S { Member member; @disable void opAssign(S); } + static assert(!hasElaborateAssign!S); + static assert(!hasElaborateAssign!(S[10])); + static assert(!hasElaborateAssign!(S[0])); + static assert(!hasElaborateAssign!(S[])); + } + { + static struct Member { void opAssign(Member) {} } + static struct S { union { Member member; } } + static assert(!hasElaborateAssign!S); + static assert(!hasElaborateAssign!(S[10])); + static assert(!hasElaborateAssign!(S[0])); + static assert(!hasElaborateAssign!(S[])); + } + + static assert(!hasElaborateAssign!int); + static assert(!hasElaborateAssign!(string[])); + static assert(!hasElaborateAssign!Object); +} + template hasIndirections(T) { - static if (is(T == struct) || is(T == union)) + static if (is(T == enum)) + enum hasIndirections = hasIndirections!(OriginalType!T); + else static if (is(T == struct) || is(T == union)) enum hasIndirections = anySatisfy!(.hasIndirections, typeof(T.tupleof)); + else static if (__traits(isAssociativeArray, T) || is(T == class) || is(T == interface)) + enum hasIndirections = true; else static if (is(T == E[N], E, size_t N)) - enum hasIndirections = T.sizeof && is(E == void) ? true : hasIndirections!(BaseElemOf!E); + enum hasIndirections = T.sizeof && (is(immutable E == immutable void) || hasIndirections!(BaseElemOf!E)); else static if (isFunctionPointer!T) enum hasIndirections = false; else - enum hasIndirections = isPointer!T || isDelegate!T || isDynamicArray!T || - __traits(isAssociativeArray, T) || is (T == class) || is(T == interface); + enum hasIndirections = isPointer!T || isDelegate!T || isDynamicArray!T; +} + +@safe unittest +{ + static assert(!hasIndirections!int); + static assert(!hasIndirections!(const int)); + + static assert( hasIndirections!(int*)); + static assert( hasIndirections!(const int*)); + + static assert( hasIndirections!(int[])); + static assert(!hasIndirections!(int[42])); + static assert(!hasIndirections!(int[0])); + + static assert( hasIndirections!(int*)); + static assert( hasIndirections!(int*[])); + static assert( hasIndirections!(int*[42])); + static assert(!hasIndirections!(int*[0])); + + static assert( hasIndirections!string); + static assert( hasIndirections!(string[])); + static assert( hasIndirections!(string[42])); + static assert(!hasIndirections!(string[0])); + + static assert( hasIndirections!(void[])); + static assert( hasIndirections!(void[17])); + static assert(!hasIndirections!(void[0])); + + static assert( hasIndirections!(string[int])); + static assert( hasIndirections!(string[int]*)); + static assert( hasIndirections!(string[int][])); + static assert( hasIndirections!(string[int][12])); + static assert(!hasIndirections!(string[int][0])); + + static assert(!hasIndirections!(int function(string))); + static assert( hasIndirections!(int delegate(string))); + static assert(!hasIndirections!(const(int function(string)))); + static assert( hasIndirections!(const(int delegate(string)))); + static assert(!hasIndirections!(immutable(int function(string)))); + static assert( hasIndirections!(immutable(int delegate(string)))); + + static class C {} + static assert( hasIndirections!C); + + static interface I {} + static assert( hasIndirections!I); + + { + enum E : int { a } + static assert(!hasIndirections!E); + } + { + enum E : int* { a } + static assert( hasIndirections!E); + } + { + enum E : string { a = "" } + static assert( hasIndirections!E); + } + { + enum E : int[] { a = null } + static assert( hasIndirections!E); + } + { + enum E : int[3] { a = [1, 2, 3] } + static assert(!hasIndirections!E); + } + { + enum E : int*[3] { a = [null, null, null] } + static assert( hasIndirections!E); + } + { + enum E : int*[0] { a = int*[0].init } + static assert(!hasIndirections!E); + } + { + enum E : C { a = null } + static assert( hasIndirections!E); + } + { + enum E : I { a = null } + static assert( hasIndirections!E); + } + + { + static struct S {} + static assert(!hasIndirections!S); + + enum E : S { a = S.init } + static assert(!hasIndirections!S); + } + { + static struct S { int i; } + static assert(!hasIndirections!S); + + enum E : S { a = S.init } + static assert(!hasIndirections!S); + } + { + static struct S { C c; } + static assert( hasIndirections!S); + + enum E : S { a = S.init } + static assert( hasIndirections!S); + } + { + static struct S { int[] arr; } + static assert( hasIndirections!S); + + enum E : S { a = S.init } + static assert( hasIndirections!S); + } + { + int local; + struct S { void foo() { ++local; } } + static assert( hasIndirections!S); + + enum E : S { a = S.init } + static assert( hasIndirections!S); + } + + { + static union U {} + static assert(!hasIndirections!U); + } + { + static union U { int i; } + static assert(!hasIndirections!U); + } + { + static union U { C c; } + static assert( hasIndirections!U); + } + { + static union U { int[] arr; } + static assert( hasIndirections!U); + } +} + +// https://issues.dlang.org/show_bug.cgi?id=12000 +@safe unittest +{ + static struct S(T) + { + static assert(hasIndirections!T); + } + + static class A(T) + { + S!A a; + } + + A!int dummy; +} + +// https://github.com/dlang/dmd/issues/20812 +@safe unittest +{ + static assert(!hasIndirections!void); + static assert(!hasIndirections!(const void)); + static assert(!hasIndirections!(inout void)); + static assert(!hasIndirections!(immutable void)); + static assert(!hasIndirections!(shared void)); + + static assert( hasIndirections!(void*)); + static assert( hasIndirections!(const void*)); + static assert( hasIndirections!(inout void*)); + static assert( hasIndirections!(immutable void*)); + static assert( hasIndirections!(shared void*)); + + static assert( hasIndirections!(void[])); + static assert( hasIndirections!(const void[])); + static assert( hasIndirections!(inout void[])); + static assert( hasIndirections!(immutable void[])); + static assert( hasIndirections!(shared void[])); + + static assert( hasIndirections!(void[42])); + static assert( hasIndirections!(const void[42])); + static assert( hasIndirections!(inout void[42])); + static assert( hasIndirections!(immutable void[42])); + static assert( hasIndirections!(shared void[42])); + + static assert(!hasIndirections!(void[0])); + static assert(!hasIndirections!(const void[0])); + static assert(!hasIndirections!(inout void[0])); + static assert(!hasIndirections!(immutable void[0])); + static assert(!hasIndirections!(shared void[0])); } template hasUnsharedIndirections(T) diff --git a/runtime/druntime/src/core/internal/utf.d b/runtime/druntime/src/core/internal/utf.d index 9808b9947f..71c3211947 100644 --- a/runtime/druntime/src/core/internal/utf.d +++ b/runtime/druntime/src/core/internal/utf.d @@ -21,6 +21,8 @@ module core.internal.utf; +debug (utf) import core.stdc.stdio : printf; + extern (C) void onUnicodeError( string msg, size_t idx, string file = __FILE__, size_t line = __LINE__ ) @safe pure; /******************************* diff --git a/runtime/druntime/src/core/internal/util/array.d b/runtime/druntime/src/core/internal/util/array.d index 066ee7e72d..1a5c41bd8e 100644 --- a/runtime/druntime/src/core/internal/util/array.d +++ b/runtime/druntime/src/core/internal/util/array.d @@ -10,7 +10,7 @@ module core.internal.util.array; import core.internal.string; -import core.stdc.stdint; +import core.stdc.stdint : uintptr_t; // TLS storage shared for all error messages. diff --git a/runtime/druntime/src/core/lifetime.d b/runtime/druntime/src/core/lifetime.d index d65477fe00..49512ab14b 100644 --- a/runtime/druntime/src/core/lifetime.d +++ b/runtime/druntime/src/core/lifetime.d @@ -1699,7 +1699,7 @@ template forward(args...) { x_ = forward!x; } - this()(auto const ref X x) + this()(auto ref const X x) { x_ = forward!x; } @@ -2348,7 +2348,7 @@ pure nothrow @nogc @system unittest debug(PRINTF) { - import core.stdc.stdio; + import core.stdc.stdio : printf; } /// Implementation of `_d_delstruct` and `_d_delstructTrace` @@ -2788,7 +2788,7 @@ if (is(T == class)) /** * TraceGC wrapper around $(REF _d_newclassT, core,lifetime). */ -T _d_newclassTTrace(T)(string file, int line, string funcname) @trusted +T _d_newclassTTrace(T)(string file = __FILE__, int line = __LINE__, string funcname = __FUNCTION__) @trusted { version (D_TypeInfo) { @@ -2828,21 +2828,11 @@ T* _d_newitemT(T)() @trusted import core.memory : GC; auto flags = !hasIndirections!T ? GC.BlkAttr.NO_SCAN : GC.BlkAttr.NONE; - immutable tiSize = TypeInfoSize!T; immutable itemSize = T.sizeof; - immutable totalSize = itemSize + tiSize; - if (tiSize) - flags |= GC.BlkAttr.STRUCTFINAL | GC.BlkAttr.FINALIZE; + if (TypeInfoSize!T) + flags |= GC.BlkAttr.FINALIZE; - auto blkInfo = GC.qalloc(totalSize, flags, null); - auto p = blkInfo.base; - - if (tiSize) - { - // The GC might not have cleared the padding area in the block. - *cast(TypeInfo*) (p + (itemSize & ~(size_t.sizeof - 1))) = null; - *cast(TypeInfo*) (p + blkInfo.size - tiSize) = cast() typeid(T); - } + auto p = GC.malloc(itemSize, flags, typeid(T)); emplaceInitializer(*(cast(T*) p)); @@ -2995,10 +2985,19 @@ version (D_ProfileGC) /** * TraceGC wrapper around $(REF _d_newitemT, core,lifetime). */ - T* _d_newitemTTrace(T)(string file, int line, string funcname) @trusted + T* _d_newitemTTrace(T)(string file = __FILE__, int line = __LINE__, string funcname = __FUNCTION__) @trusted { version (D_TypeInfo) { + static if (is(T == struct)) + { + // prime the TypeInfo name, we don't want that affecting the allocated bytes + // Issue https://github.com/dlang/dmd/issues/20832 + static string typeName(TypeInfo_Struct ti) nothrow @trusted => ti.name; + auto tnPure = cast(string function(TypeInfo_Struct ti) nothrow pure @trusted)&typeName; + cast(void)tnPure(typeid(T)); + } + import core.internal.array.utils : TraceHook, gcStatsPure, accumulatePure; mixin(TraceHook!(T.stringof, "_d_newitemT")); diff --git a/runtime/druntime/src/core/memory.d b/runtime/druntime/src/core/memory.d index 09a7197798..e3a46a088c 100644 --- a/runtime/druntime/src/core/memory.d +++ b/runtime/druntime/src/core/memory.d @@ -100,6 +100,10 @@ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) * Authors: Sean Kelly, Alex Rønne Petersen * Source: $(DRUNTIMESRC core/_memory.d) + * Macros: + * WARN_UNINITIALIZED=$(RED Warning): + * $1 will be uninitialized, and may happen to hold pointers to GC memory. + * Consider zeroing out any uninitialized bytes which won't be immediately written to. */ module core.memory; @@ -400,7 +404,7 @@ extern(D): * a = A bit field containing any bits to set for this memory block. * * Returns: - * The result of a call to getAttr after the specified bits have been + * The result of a call to $(LREF getAttr) after the specified bits have been * set. */ static uint setAttr( const scope void* p, uint a ) nothrow @@ -427,7 +431,7 @@ extern(D): * a = A bit field containing any bits to clear for this memory block. * * Returns: - * The result of a call to getAttr after the specified bits have been + * The result of a call to $(LREF getAttr) after the specified bits have been * cleared. */ static uint clrAttr( const scope void* p, uint a ) nothrow @@ -461,6 +465,8 @@ extern(C): * A reference to the allocated memory or null if insufficient memory * is available. * + * $(WARN_UNINITIALIZED Allocated memory) + * * Throws: * OutOfMemoryError on allocation failure. */ @@ -472,7 +478,7 @@ extern(C): /** * Requests an aligned block of managed memory from the garbage collector. - * This memory may be deleted at will with a call to free, or it may be + * This memory may be deleted at will with a call to $(LREF free), or it may be * discarded and cleaned up automatically during a collection run. If * allocation fails, this function will call onOutOfMemory which is * expected to throw an OutOfMemoryError. @@ -487,6 +493,8 @@ extern(C): * Information regarding the allocated memory block or BlkInfo.init on * error. * + * $(WARN_UNINITIALIZED Allocated memory) + * * Throws: * OutOfMemoryError on allocation failure. */ @@ -564,6 +572,8 @@ extern(C): * zero or the pointer does not point to the base of an GC allocated * memory block. * + * $(WARN_UNINITIALIZED Any extra bytes past the initial size) + * * Throws: * `OutOfMemoryError` on allocation failure. */ @@ -608,6 +618,8 @@ extern(C): * The size in bytes of the extended memory block referenced by p or zero * if no extension occurred. * + * $(WARN_UNINITIALIZED Any extension bytes) + * * Note: * Extend may also be used to extend slices (or memory blocks with * $(LREF APPENDABLE) info). However, use the return value only @@ -669,7 +681,7 @@ extern(C): * If p references memory not originally allocated by this garbage * collector, if p points to the interior of a memory block, or if this * method is called from a finalizer, no action will be taken. The block - * will not be finalized regardless of whether the FINALIZE attribute is + * will not be finalized regardless of whether the $(LREF FINALIZE) attribute is * set. If finalization is desired, call $(REF1 destroy, object) prior to `GC.free`. * * Params: @@ -707,7 +719,7 @@ extern(D): /** * Returns the true size of the memory block referenced by p. This value - * represents the maximum number of bytes for which a call to realloc may + * represents the maximum number of bytes for which a call to $(LREF realloc) may * resize the existing block in place. If p references memory not * originally allocated by this garbage collector, points to the interior * of a memory block, or if p is null, zero will be returned. @@ -1172,7 +1184,7 @@ extern (C) private @system @nogc nothrow { ref int fakePureErrnoImpl() { - import core.stdc.errno; + import core.stdc.errno : errno; return errno(); } } diff --git a/runtime/druntime/src/core/runtime.d b/runtime/druntime/src/core/runtime.d index 2a5e980b08..f307a382ed 100644 --- a/runtime/druntime/src/core/runtime.d +++ b/runtime/druntime/src/core/runtime.d @@ -4,7 +4,7 @@ * Copyright: Copyright Sean Kelly 2005 - 2009. * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) * Authors: Sean Kelly - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/druntime/src/core/runtime.d, _runtime.d) + * Source: $(DRUNTIMESRC core/_runtime.d) * Documentation: https://dlang.org/phobos/core_runtime.html */ @@ -572,7 +572,9 @@ extern (C) UnitTestResult runModuleUnitTests() static if (hasExecinfo) { - import core.sys.posix.signal; // segv handler + // segv handler + import core.sys.posix.signal : SA_RESETHAND, SA_SIGINFO, sigaction, sigaction_t, SIGBUS, sigfillset, siginfo_t, + SIGSEGV; static extern (C) void unittestSegvHandler( int signum, siginfo_t* info, void* ptr ) nothrow { @@ -630,7 +632,7 @@ extern (C) UnitTestResult runModuleUnitTests() if (moduleName.length && e.file.length > moduleName.length && e.file[0 .. moduleName.length] == moduleName) { - import core.stdc.stdio; + import core.stdc.stdio : printf; printf("%.*s(%llu): [unittest] %.*s\n", cast(int) e.file.length, e.file.ptr, cast(ulong) e.line, cast(int) e.message.length, e.message.ptr); @@ -779,7 +781,7 @@ Throwable.TraceInfo defaultTraceHandler( void* ptr = null ) // @nogc unittest { import core.runtime; - import core.stdc.stdio; + import core.stdc.stdio : printf; void main() { diff --git a/runtime/druntime/src/core/stdc/config.d b/runtime/druntime/src/core/stdc/config.d index ea74c85386..34f78e03e4 100644 --- a/runtime/druntime/src/core/stdc/config.d +++ b/runtime/druntime/src/core/stdc/config.d @@ -118,7 +118,26 @@ else version (TVOS) else version (WatchOS) version = Darwin; -version (Windows) +version (GNU) +{ + import gcc.builtins; + + alias __builtin_clong c_long; + alias __builtin_culong c_ulong; + + enum __c_long : __builtin_clong; + enum __c_ulong : __builtin_culong; + + alias __c_long cpp_long; + alias __c_ulong cpp_ulong; + + enum __c_longlong : __builtin_clonglong; + enum __c_ulonglong : __builtin_culonglong; + + alias __c_longlong cpp_longlong; + alias __c_ulonglong cpp_ulonglong; +} +else version (Windows) { enum __c_long : int; enum __c_ulong : uint; diff --git a/runtime/druntime/src/core/stdc/errno.d b/runtime/druntime/src/core/stdc/errno.d index 0430e6b33e..db347e404b 100644 --- a/runtime/druntime/src/core/stdc/errno.d +++ b/runtime/druntime/src/core/stdc/errno.d @@ -8,7 +8,7 @@ * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). * (See accompanying file LICENSE) * Authors: Sean Kelly, Alex Rønne Petersen - * Source: https://github.com/dlang/dmd/blob/master/druntime/src/core/stdc/errno.d + * Source: $(DRUNTIMESRC core/stdc/_errno.d) * Standards: ISO/IEC 9899:1999 (E) */ diff --git a/runtime/druntime/src/core/stdc/fenv.d b/runtime/druntime/src/core/stdc/fenv.d index 6cd75f3a79..ce77d5c1e6 100644 --- a/runtime/druntime/src/core/stdc/fenv.d +++ b/runtime/druntime/src/core/stdc/fenv.d @@ -432,6 +432,14 @@ else version (CRuntime_Musl) } alias ushort fexcept_t; } + else version (LoongArch64) + { + struct fenv_t + { + uint __cw; + } + alias uint fexcept_t; + } else { static assert(false, "Architecture not supported."); @@ -959,7 +967,7 @@ version (CRuntime_Microsoft) // supported since MSVCRT 12 (VS 2013) only double num; double denom; } - static __gshared immutable(Entry[5]) table = + static immutable Entry[5] table = [ // Raise exception by evaluating num / denom: { FE_INVALID, 0.0, 0.0 }, { FE_DIVBYZERO, 1.0, 0.0 }, diff --git a/runtime/druntime/src/core/stdc/stdatomic.d b/runtime/druntime/src/core/stdc/stdatomic.d index 72e037f7e9..51968af8c5 100644 --- a/runtime/druntime/src/core/stdc/stdatomic.d +++ b/runtime/druntime/src/core/stdc/stdatomic.d @@ -18,6 +18,7 @@ module core.stdc.stdatomic; import core.atomic : MemoryOrder; import core.internal.atomic; import core.stdc.config; +import core.stdc.stddef; import core.stdc.stdint; @safe nothrow @nogc: @@ -54,7 +55,7 @@ enum /// ATOMIC_CHAR32_T_LOCK_FREE = IsAtomicLockFree!dchar ? 2 : 0, /// - ATOMIC_WCHAR_T_LOCK_FREE = ATOMIC_CHAR16_T_LOCK_FREE, + ATOMIC_WCHAR_T_LOCK_FREE = IsAtomicLockFree!wchar_t ? 2 : 0, /// ATOMIC_SHORT_LOCK_FREE = IsAtomicLockFree!short ? 2 : 0, /// @@ -365,7 +366,7 @@ alias atomic_char16_t = shared(wchar); /// alias atomic_char32_t = shared(dchar); /// -alias atomic_wchar_t = shared(wchar); +alias atomic_wchar_t = shared(wchar_t); /// alias atomic_int_least8_t = shared(int_least8_t); diff --git a/runtime/druntime/src/core/stdc/stdint.d b/runtime/druntime/src/core/stdc/stdint.d index 1776269378..4f9b98cd8c 100644 --- a/runtime/druntime/src/core/stdc/stdint.d +++ b/runtime/druntime/src/core/stdc/stdint.d @@ -480,13 +480,21 @@ enum int_fast64_t INT_FAST64_MIN = int_fast64_t.min; /// enum int_fast64_t INT_FAST64_MAX = int_fast64_t.max; +/// +enum uint_fast8_t UINT_FAST8_MIN = uint_fast8_t.min; /// enum uint_fast8_t UINT_FAST8_MAX = uint_fast8_t.max; /// +enum uint_fast16_t UINT_FAST16_MIN = uint_fast16_t.min; +/// enum uint_fast16_t UINT_FAST16_MAX = uint_fast16_t.max; /// +enum uint_fast32_t UINT_FAST32_MIN = uint_fast32_t.min; +/// enum uint_fast32_t UINT_FAST32_MAX = uint_fast32_t.max; /// +enum uint_fast64_t UINT_FAST64_MIN = uint_fast64_t.min; +/// enum uint_fast64_t UINT_FAST64_MAX = uint_fast64_t.max; /// diff --git a/runtime/druntime/src/core/stdc/stdio.d b/runtime/druntime/src/core/stdc/stdio.d index ba7015e8d8..c9b6d7b6b7 100644 --- a/runtime/druntime/src/core/stdc/stdio.d +++ b/runtime/druntime/src/core/stdc/stdio.d @@ -9,7 +9,7 @@ * (See accompanying file LICENSE) * Authors: Sean Kelly, * Alex Rønne Petersen - * Source: https://github.com/dlang/dmd/blob/master/druntime/src/core/stdc/stdio.d + * Source: $(DRUNTIMESRC core/stdc/_stdio.d) * Standards: ISO/IEC 9899:1999 (E) */ diff --git a/runtime/druntime/src/core/stdc/wctype.d b/runtime/druntime/src/core/stdc/wctype.d index b37e8322dc..88b6a01245 100644 --- a/runtime/druntime/src/core/stdc/wctype.d +++ b/runtime/druntime/src/core/stdc/wctype.d @@ -14,6 +14,7 @@ module core.stdc.wctype; +import core.stdc.config; public import core.stdc.wchar_; // for wint_t, WEOF extern (C): @@ -21,10 +22,41 @@ extern (C): nothrow: @nogc: -/// -alias wchar_t wctrans_t; -/// -alias wchar_t wctype_t; +version (CRuntime_Glibc) +{ + /// + alias wctype_t = c_ulong; + /// + alias wctrans_t = const(int)*; +} +else version (CRuntime_Musl) +{ + /// + alias wctype_t = c_ulong; + /// + alias wctrans_t = const(int)*; +} +else version (FreeBSD) +{ + /// + alias wctype_t = c_ulong; + /// + alias wctrans_t = int; +} +else version (CRuntime_Bionic) +{ + /// + alias wctype_t = c_long; + /// + alias wctrans_t = const(void)*; +} +else +{ + /// + alias wchar_t wctrans_t; + /// + alias wchar_t wctype_t; +} /// pure int iswalnum(wint_t wc); @@ -63,3 +95,17 @@ pure wint_t towupper(wint_t wc); wint_t towctrans(wint_t wc, wctrans_t desc); /// @system wctrans_t wctrans(const scope char* property); + +unittest +{ + assert(iswalpha('A')); + assert(!iswalpha('0')); + wctype_t alpha = wctype("alpha"); + assert(alpha); + wctrans_t tolower = wctrans("tolower"); + assert(tolower); + assert(iswctype('A', alpha)); + assert(!iswctype('0', alpha)); + assert(towctrans('A', tolower) == 'a'); + assert(towctrans('0', tolower) == '0'); +} diff --git a/runtime/druntime/src/core/stdcpp/allocator.d b/runtime/druntime/src/core/stdcpp/allocator.d index f9e84688a3..b9ca627703 100644 --- a/runtime/druntime/src/core/stdcpp/allocator.d +++ b/runtime/druntime/src/core/stdcpp/allocator.d @@ -305,9 +305,10 @@ version (CppRuntime_Microsoft) { enum size_t _Big_allocation_threshold = 4096; enum size_t _Big_allocation_alignment = 32; + enum isPowerOf2(size_t v) = v && !(v & (v - 1)); static assert(2 * (void*).sizeof <= _Big_allocation_alignment, "Big allocation alignment should at least match vector register alignment"); - static assert((v => v != 0 && (v & (v - 1)) == 0)(_Big_allocation_alignment), "Big allocation alignment must be a power of two"); + static assert(isPowerOf2!_Big_allocation_alignment, "Big allocation alignment must be a power of two"); static assert(size_t.sizeof == (void*).sizeof, "uintptr_t is not the same size as size_t"); // NOTE: this must track `_DEBUG` macro used in C++... diff --git a/runtime/druntime/src/core/stdcpp/string_view.d b/runtime/druntime/src/core/stdcpp/string_view.d index 7aa0ec19f3..9b947f5681 100644 --- a/runtime/druntime/src/core/stdcpp/string_view.d +++ b/runtime/druntime/src/core/stdcpp/string_view.d @@ -43,7 +43,7 @@ extern(C++, struct) struct char_traits(CharT) {} /** * D language counterpart to C++ std::basic_string_view. * -* C++ reference: $(LINK2 hhttps://en.cppreference.com/w/cpp/string/basic_string_view) +* C++ reference: $(LINK2 https://en.cppreference.com/w/cpp/string/basic_string_view, std::basic_string_view) */ extern(C++, class) struct basic_string_view(T, Traits = char_traits!T) { diff --git a/runtime/druntime/src/core/stdcpp/xutility.d b/runtime/druntime/src/core/stdcpp/xutility.d index 5e2e711ba6..f93df68deb 100644 --- a/runtime/druntime/src/core/stdcpp/xutility.d +++ b/runtime/druntime/src/core/stdcpp/xutility.d @@ -35,6 +35,7 @@ enum CppStdRevision : uint cpp14 = 201402, cpp17 = 201703, cpp20 = 202002, + cpp23 = 202302, } /** diff --git a/runtime/druntime/src/core/sync/condition.d b/runtime/druntime/src/core/sync/condition.d index afcfd744f0..3acee7a7f4 100644 --- a/runtime/druntime/src/core/sync/condition.d +++ b/runtime/druntime/src/core/sync/condition.d @@ -35,10 +35,11 @@ version (Windows) } else version (Posix) { + import core.stdc.errno : EAGAIN, ETIMEDOUT; import core.sync.config; - import core.stdc.errno; - import core.sys.posix.pthread; - import core.sys.posix.time; + import core.sys.posix.pthread : pthread_cond_broadcast, pthread_cond_destroy, pthread_cond_init, + pthread_cond_signal, pthread_cond_t, pthread_cond_timedwait, pthread_cond_wait; + import core.sys.posix.time : timespec; } else { @@ -127,8 +128,12 @@ class Condition { m_assocMutex = m; } - static if ( is( typeof( pthread_condattr_setclock ) ) ) + static if ( is( typeof( imported!"core.sys.posix.pthread".pthread_condattr_setclock ) ) ) { + import core.sys.posix.pthread : pthread_condattr_destroy, pthread_condattr_init, + pthread_condattr_setclock; + import core.sys.posix.sys.types : pthread_condattr_t; + import core.sys.posix.time : CLOCK_MONOTONIC; () @trusted { pthread_condattr_t attr = void; @@ -620,9 +625,9 @@ private: unittest { - import core.thread; import core.sync.mutex; import core.sync.semaphore; + import core.thread; void testNotify() @@ -786,9 +791,9 @@ unittest unittest { - import core.thread; import core.sync.mutex; import core.sync.semaphore; + import core.thread; void testNotify() diff --git a/runtime/druntime/src/core/sync/config.d b/runtime/druntime/src/core/sync/config.d index 39f7a8cd76..f68502464e 100644 --- a/runtime/druntime/src/core/sync/config.d +++ b/runtime/druntime/src/core/sync/config.d @@ -18,16 +18,16 @@ module core.sync.config; version (Posix) { - import core.sys.posix.pthread; - import core.sys.posix.time; - import core.sys.posix.sys.time; + import core.sys.posix.sys.time : gettimeofday, timeval; + import core.sys.posix.time : timespec; import core.time; void mktspec( ref timespec t ) nothrow @nogc { - static if ( is (typeof ( pthread_condattr_setclock ) ) ) + static if ( is (typeof ( imported!"core.sys.posix.pthread".pthread_condattr_setclock ) ) ) { + import core.sys.posix.time : clock_gettime, CLOCK_MONOTONIC; clock_gettime( CLOCK_MONOTONIC, &t ); } else diff --git a/runtime/druntime/src/core/sync/event.d b/runtime/druntime/src/core/sync/event.d index 048607f6ed..6278218f67 100644 --- a/runtime/druntime/src/core/sync/event.d +++ b/runtime/druntime/src/core/sync/event.d @@ -20,17 +20,19 @@ version (Windows) } else version (Posix) { - import core.sys.posix.pthread; - import core.sys.posix.sys.types; - import core.sys.posix.time; + import core.sys.posix.pthread : pthread_cond_broadcast, pthread_cond_destroy, pthread_cond_init, + pthread_cond_timedwait, pthread_cond_wait, pthread_mutex_destroy, pthread_mutex_init, pthread_mutex_lock, + pthread_mutex_unlock; + import core.sys.posix.sys.types : pthread_cond_t, pthread_mutex_t; + import core.sys.posix.time : timespec; } else { static assert(false, "Platform not supported"); } -import core.time; import core.internal.abort : abort; +import core.time; /** * represents an event. Clients of an event are suspended while waiting @@ -105,8 +107,13 @@ nothrow @nogc: return; pthread_mutex_init(cast(pthread_mutex_t*) &m_mutex, null) == 0 || abort("Error: pthread_mutex_init failed."); - static if ( is( typeof( pthread_condattr_setclock ) ) ) + + static if ( is( typeof( imported!"core.sys.posix.pthread".pthread_condattr_setclock ) ) ) { + import core.sys.posix.pthread : CLOCK_MONOTONIC, pthread_condattr_destroy, pthread_condattr_init, + pthread_condattr_setclock; + import core.sys.posix.sys.types : pthread_condattr_t; + pthread_condattr_t attr = void; pthread_condattr_init(&attr) == 0 || abort("Error: pthread_condattr_init failed."); @@ -320,7 +327,8 @@ private: unittest { - import core.thread, core.atomic; + import core.atomic; + import core.thread; scope event = new Event(true, false); int numThreads = 10; diff --git a/runtime/druntime/src/core/sync/mutex.d b/runtime/druntime/src/core/sync/mutex.d index 5f547cd04e..8993f85071 100644 --- a/runtime/druntime/src/core/sync/mutex.d +++ b/runtime/druntime/src/core/sync/mutex.d @@ -26,7 +26,10 @@ version (Windows) } else version (Posix) { - import core.sys.posix.pthread; + import core.sys.posix.pthread : pthread_mutex_destroy, pthread_mutex_init, pthread_mutex_lock, + PTHREAD_MUTEX_RECURSIVE, pthread_mutex_trylock, pthread_mutex_unlock, pthread_mutexattr_destroy, + pthread_mutexattr_init, pthread_mutexattr_settype; + import core.sys.posix.sys.types : pthread_mutex_t, pthread_mutexattr_t; } else { @@ -344,8 +347,8 @@ unittest // Test @nogc usage. @system @nogc nothrow unittest { - import core.stdc.stdlib : malloc, free; import core.lifetime : emplace; + import core.stdc.stdlib : free, malloc; auto mtx = cast(shared Mutex) malloc(__traits(classInstanceSize, Mutex)); emplace(mtx); diff --git a/runtime/druntime/src/core/sync/rwmutex.d b/runtime/druntime/src/core/sync/rwmutex.d index 07c5bdbe36..cb581621ec 100644 --- a/runtime/druntime/src/core/sync/rwmutex.d +++ b/runtime/druntime/src/core/sync/rwmutex.d @@ -21,11 +21,6 @@ import core.sync.condition; import core.sync.mutex; import core.memory; -version (Posix) -{ - import core.sys.posix.pthread; -} - //////////////////////////////////////////////////////////////////////////////// // ReadWriteMutex diff --git a/runtime/druntime/src/core/sync/semaphore.d b/runtime/druntime/src/core/sync/semaphore.d index cf2bddbf10..44465669b7 100644 --- a/runtime/druntime/src/core/sync/semaphore.d +++ b/runtime/druntime/src/core/sync/semaphore.d @@ -37,17 +37,18 @@ version (Windows) } else version (Darwin) { + import core.stdc.errno : EINTR, errno; import core.sync.config; - import core.stdc.errno; - import core.sys.posix.time; - import core.sys.darwin.mach.semaphore; + import core.sys.darwin.mach.kern_return : KERN_ABORTED, KERN_OPERATION_TIMED_OUT; + import core.sys.darwin.mach.semaphore : mach_task_self, mach_timespec_t, semaphore_create, semaphore_destroy, + semaphore_signal, semaphore_t, semaphore_timedwait, semaphore_wait, SYNC_POLICY_FIFO; } else version (Posix) { + import core.stdc.errno : EAGAIN, EINTR, errno, ETIMEDOUT; import core.sync.config; - import core.stdc.errno; - import core.sys.posix.pthread; - import core.sys.posix.semaphore; + import core.sys.posix.semaphore : sem_destroy, sem_init, sem_post, sem_t, sem_timedwait, sem_trywait, sem_wait; + import core.sys.posix.time : clock_gettime, CLOCK_REALTIME, timespec; } else { @@ -253,8 +254,6 @@ class Semaphore } else version (Posix) { - import core.sys.posix.time : clock_gettime, CLOCK_REALTIME; - timespec t = void; clock_gettime( CLOCK_REALTIME, &t ); mvtspec( t, period ); @@ -364,7 +363,8 @@ protected: unittest { - import core.thread, core.atomic; + import core.atomic; + import core.thread; void testWait() { diff --git a/runtime/druntime/src/core/sys/darwin/mach/stab.d b/runtime/druntime/src/core/sys/darwin/mach/stab.d index ecdb4560a6..7125cf2814 100644 --- a/runtime/druntime/src/core/sys/darwin/mach/stab.d +++ b/runtime/druntime/src/core/sys/darwin/mach/stab.d @@ -24,7 +24,7 @@ * * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). * Authors: Mathias 'Geod24' Lang - * Source: $(DRUNTIMESRC core/sys/darwin/mach/_nlist.d) + * Source: $(DRUNTIMESRC core/sys/darwin/mach/_stab.d) */ module core.sys.darwin.mach.stab; diff --git a/runtime/druntime/src/core/sys/freebsd/unistd.d b/runtime/druntime/src/core/sys/freebsd/unistd.d index 493cda1c8c..ebb7afa272 100644 --- a/runtime/druntime/src/core/sys/freebsd/unistd.d +++ b/runtime/druntime/src/core/sys/freebsd/unistd.d @@ -17,3 +17,5 @@ extern(C): nothrow: int getosreldate() pure @trusted; + +void closefrom(int); diff --git a/runtime/druntime/src/core/sys/linux/perf_event.d b/runtime/druntime/src/core/sys/linux/perf_event.d index e0a0c99514..0ad072974d 100644 --- a/runtime/druntime/src/core/sys/linux/perf_event.d +++ b/runtime/druntime/src/core/sys/linux/perf_event.d @@ -516,8 +516,8 @@ struct perf_event_attr (cast(typeof(perf_event_attr_bitmanip)) v << 0U) & 1U)); } - enum ulong disabled_min = cast(ulong) 0U; - enum ulong disabled_max = cast(ulong) 1U; + enum ulong disabled_min = 0UL; + enum ulong disabled_max = 1UL; /// @property ulong inherit() @safe pure nothrow @nogc const { @@ -536,8 +536,8 @@ struct perf_event_attr (cast(typeof(perf_event_attr_bitmanip)) v << 1U) & 2U)); } - enum ulong inherit_min = cast(ulong) 0U; - enum ulong inherit_max = cast(ulong) 1U; + enum ulong inherit_min = 0UL; + enum ulong inherit_max = 1UL; /// @property ulong pinned() @safe pure nothrow @nogc const { @@ -556,8 +556,8 @@ struct perf_event_attr (cast(typeof(perf_event_attr_bitmanip)) v << 2U) & 4U)); } - enum ulong pinned_min = cast(ulong) 0U; - enum ulong pinned_max = cast(ulong) 1U; + enum ulong pinned_min = 0UL; + enum ulong pinned_max = 1UL; /// @property ulong exclusive() @safe pure nothrow @nogc const { @@ -576,8 +576,8 @@ struct perf_event_attr (cast(typeof(perf_event_attr_bitmanip)) v << 3U) & 8U)); } - enum ulong exclusive_min = cast(ulong) 0U; - enum ulong exclusive_max = cast(ulong) 1U; + enum ulong exclusive_min = 0UL; + enum ulong exclusive_max = 1UL; /// @property ulong exclude_user() @safe pure nothrow @nogc const { @@ -596,8 +596,8 @@ struct perf_event_attr (cast(typeof(perf_event_attr_bitmanip)) v << 4U) & 16U)); } - enum ulong exclude_user_min = cast(ulong) 0U; - enum ulong exclude_user_max = cast(ulong) 1U; + enum ulong exclude_user_min = 0UL; + enum ulong exclude_user_max = 1UL; /// @property ulong exclude_kernel() @safe pure nothrow @nogc const { @@ -616,8 +616,8 @@ struct perf_event_attr (cast(typeof(perf_event_attr_bitmanip)) v << 5U) & 32U)); } - enum ulong exclude_kernel_min = cast(ulong) 0U; - enum ulong exclude_kernel_max = cast(ulong) 1U; + enum ulong exclude_kernel_min = 0UL; + enum ulong exclude_kernel_max = 1UL; /// @property ulong exclude_hv() @safe pure nothrow @nogc const { @@ -636,8 +636,8 @@ struct perf_event_attr (cast(typeof(perf_event_attr_bitmanip)) v << 6U) & 64U)); } - enum ulong exclude_hv_min = cast(ulong) 0U; - enum ulong exclude_hv_max = cast(ulong) 1U; + enum ulong exclude_hv_min = 0UL; + enum ulong exclude_hv_max = 1UL; /// @property ulong exclude_idle() @safe pure nothrow @nogc const { @@ -656,8 +656,8 @@ struct perf_event_attr (cast(typeof(perf_event_attr_bitmanip)) v << 7U) & 128U)); } - enum ulong exclude_idle_min = cast(ulong) 0U; - enum ulong exclude_idle_max = cast(ulong) 1U; + enum ulong exclude_idle_min = 0UL; + enum ulong exclude_idle_max = 1UL; /// @property ulong mmap() @safe pure nothrow @nogc const { @@ -674,8 +674,8 @@ struct perf_event_attr (cast(typeof(perf_event_attr_bitmanip)) v << 8U) & 256U)); } - enum ulong mmap_min = cast(ulong) 0U; - enum ulong mmap_max = cast(ulong) 1U; + enum ulong mmap_min = 0UL; + enum ulong mmap_max = 1UL; /// @property ulong comm() @safe pure nothrow @nogc const { @@ -692,8 +692,8 @@ struct perf_event_attr (cast(typeof(perf_event_attr_bitmanip)) v << 9U) & 512U)); } - enum ulong comm_min = cast(ulong) 0U; - enum ulong comm_max = cast(ulong) 1U; + enum ulong comm_min = 0UL; + enum ulong comm_max = 1UL; /// @property ulong freq() @safe pure nothrow @nogc const { @@ -710,8 +710,8 @@ struct perf_event_attr (cast(typeof(perf_event_attr_bitmanip)) v << 10U) & 1024U)); } - enum ulong freq_min = cast(ulong) 0U; - enum ulong freq_max = cast(ulong) 1U; + enum ulong freq_min = 0UL; + enum ulong freq_max = 1UL; /// @property ulong inherit_stat() @safe pure nothrow @nogc const { @@ -730,8 +730,8 @@ struct perf_event_attr (cast(typeof(perf_event_attr_bitmanip)) v << 11U) & 2048U)); } - enum ulong inherit_stat_min = cast(ulong) 0U; - enum ulong inherit_stat_max = cast(ulong) 1U; + enum ulong inherit_stat_min = 0UL; + enum ulong inherit_stat_max = 1UL; /// @property ulong enable_on_exec() @safe pure nothrow @nogc const { @@ -750,8 +750,8 @@ struct perf_event_attr (cast(typeof(perf_event_attr_bitmanip)) v << 12U) & 4096U)); } - enum ulong enable_on_exec_min = cast(ulong) 0U; - enum ulong enable_on_exec_max = cast(ulong) 1U; + enum ulong enable_on_exec_min = 0UL; + enum ulong enable_on_exec_max = 1UL; /// @property ulong task() @safe pure nothrow @nogc const { @@ -768,8 +768,8 @@ struct perf_event_attr (cast(typeof(perf_event_attr_bitmanip)) v << 13U) & 8192U)); } - enum ulong task_min = cast(ulong) 0U; - enum ulong task_max = cast(ulong) 1U; + enum ulong task_min = 0UL; + enum ulong task_max = 1UL; /// @property ulong watermark() @safe pure nothrow @nogc const { @@ -788,8 +788,8 @@ struct perf_event_attr (cast(typeof(perf_event_attr_bitmanip)) v << 14U) & 16384U)); } - enum ulong watermark_min = cast(ulong) 0U; - enum ulong watermark_max = cast(ulong) 1U; + enum ulong watermark_min = 0UL; + enum ulong watermark_max = 1UL; /// @property ulong precise_ip() @safe pure nothrow @nogc const { @@ -808,8 +808,8 @@ struct perf_event_attr (cast(typeof(perf_event_attr_bitmanip)) v << 15U) & 98304U)); } - enum ulong precise_ip_min = cast(ulong) 0U; - enum ulong precise_ip_max = cast(ulong) 3U; + enum ulong precise_ip_min = 0UL; + enum ulong precise_ip_max = 3UL; /// @property ulong mmap_data() @safe pure nothrow @nogc const { @@ -828,8 +828,8 @@ struct perf_event_attr (cast(typeof(perf_event_attr_bitmanip)) v << 17U) & 131072U)); } - enum ulong mmap_data_min = cast(ulong) 0U; - enum ulong mmap_data_max = cast(ulong) 1U; + enum ulong mmap_data_min = 0UL; + enum ulong mmap_data_max = 1UL; /// @property ulong sample_id_all() @safe pure nothrow @nogc const { @@ -848,8 +848,8 @@ struct perf_event_attr (cast(typeof(perf_event_attr_bitmanip)) v << 18U) & 262144U)); } - enum ulong sample_id_all_min = cast(ulong) 0U; - enum ulong sample_id_all_max = cast(ulong) 1U; + enum ulong sample_id_all_min = 0UL; + enum ulong sample_id_all_max = 1UL; /// @property ulong exclude_host() @safe pure nothrow @nogc const { @@ -868,8 +868,8 @@ struct perf_event_attr (cast(typeof(perf_event_attr_bitmanip)) v << 19U) & 524288U)); } - enum ulong exclude_host_min = cast(ulong) 0U; - enum ulong exclude_host_max = cast(ulong) 1U; + enum ulong exclude_host_min = 0UL; + enum ulong exclude_host_max = 1UL; /// @property ulong exclude_guest() @safe pure nothrow @nogc const { @@ -888,8 +888,8 @@ struct perf_event_attr (cast(typeof(perf_event_attr_bitmanip)) v << 20U) & 1048576U)); } - enum ulong exclude_guest_min = cast(ulong) 0U; - enum ulong exclude_guest_max = cast(ulong) 1U; + enum ulong exclude_guest_min = 0UL; + enum ulong exclude_guest_max = 1UL; /// @property ulong exclude_callchain_kernel() @safe pure nothrow @nogc const { @@ -908,8 +908,8 @@ struct perf_event_attr (cast(typeof(perf_event_attr_bitmanip)) v << 21U) & 2097152U)); } - enum ulong exclude_callchain_kernel_min = cast(ulong) 0U; - enum ulong exclude_callchain_kernel_max = cast(ulong) 1U; + enum ulong exclude_callchain_kernel_min = 0UL; + enum ulong exclude_callchain_kernel_max = 1UL; /// @property ulong exclude_callchain_user() @safe pure nothrow @nogc const { @@ -928,8 +928,8 @@ struct perf_event_attr (cast(typeof(perf_event_attr_bitmanip)) v << 22U) & 4194304U)); } - enum ulong exclude_callchain_user_min = cast(ulong) 0U; - enum ulong exclude_callchain_user_max = cast(ulong) 1U; + enum ulong exclude_callchain_user_min = 0UL; + enum ulong exclude_callchain_user_max = 1UL; /// @property ulong mmap2() @safe pure nothrow @nogc const { @@ -948,8 +948,8 @@ struct perf_event_attr (cast(typeof(perf_event_attr_bitmanip)) v << 23U) & 8388608U)); } - enum ulong mmap2_min = cast(ulong) 0U; - enum ulong mmap2_max = cast(ulong) 1U; + enum ulong mmap2_min = 0UL; + enum ulong mmap2_max = 1UL; /// @property ulong comm_exec() @safe pure nothrow @nogc const { @@ -968,8 +968,8 @@ struct perf_event_attr (cast(typeof(perf_event_attr_bitmanip)) v << 24U) & 16777216U)); } - enum ulong comm_exec_min = cast(ulong) 0U; - enum ulong comm_exec_max = cast(ulong) 1U; + enum ulong comm_exec_min = 0UL; + enum ulong comm_exec_max = 1UL; /// @property ulong use_clockid() @safe pure nothrow @nogc const { @@ -988,8 +988,8 @@ struct perf_event_attr (cast(typeof(perf_event_attr_bitmanip)) v << 25U) & 33554432U)); } - enum ulong use_clockid_min = cast(ulong) 0U; - enum ulong use_clockid_max = cast(ulong) 1U; + enum ulong use_clockid_min = 0UL; + enum ulong use_clockid_max = 1UL; /// @property ulong context_switch() @safe pure nothrow @nogc const { @@ -1008,8 +1008,8 @@ struct perf_event_attr (cast(typeof(perf_event_attr_bitmanip)) v << 26U) & 67108864U)); } - enum ulong context_switch_min = cast(ulong) 0U; - enum ulong context_switch_max = cast(ulong) 1U; + enum ulong context_switch_min = 0UL; + enum ulong context_switch_max = 1UL; /// @property ulong write_backward() @safe pure nothrow @nogc const { @@ -1028,8 +1028,8 @@ struct perf_event_attr (cast(typeof(perf_event_attr_bitmanip)) v << 27U) & 134217728U)); } - enum ulong write_backward_min = cast(ulong) 0U; - enum ulong write_backward_max = cast(ulong) 1U; + enum ulong write_backward_min = 0UL; + enum ulong write_backward_max = 1UL; /// @property ulong namespaces() @safe pure nothrow @nogc const { @@ -1048,8 +1048,8 @@ struct perf_event_attr (cast(typeof(perf_event_attr_bitmanip)) v << 28U) & 268435456U)); } - enum ulong namespaces_min = cast(ulong) 0U; - enum ulong namespaces_max = cast(ulong) 1U; + enum ulong namespaces_min = 0UL; + enum ulong namespaces_max = 1UL; /// @property ulong __reserved_1() @safe pure nothrow @nogc const { @@ -1069,8 +1069,8 @@ struct perf_event_attr (cast(typeof(perf_event_attr_bitmanip)) v << 29U) & 18446744073172680704UL)); } - enum ulong __reserved_1_min = cast(ulong) 0U; - enum ulong __reserved_1_max = cast(ulong) 34359738367UL; + enum ulong __reserved_1_min = 0UL; + enum ulong __reserved_1_max = 34359738367UL; /// union { @@ -1242,8 +1242,8 @@ struct perf_event_mmap_page (cast(typeof(mmap_page_bitmanip)) v << 0U) & 1U)); } - enum ulong cap_bit0_min = cast(ulong) 0U; - enum ulong cap_bit0_max = cast(ulong) 1U; + enum ulong cap_bit0_min = 0UL; + enum ulong cap_bit0_max = 1UL; /// @property ulong cap_bit0_is_deprecated() @safe pure nothrow @nogc const { @@ -1262,8 +1262,8 @@ struct perf_event_mmap_page (cast(typeof(mmap_page_bitmanip)) v << 1U) & 2U)); } - enum ulong cap_bit0_is_deprecated_min = cast(ulong) 0U; - enum ulong cap_bit0_is_deprecated_max = cast(ulong) 1U; + enum ulong cap_bit0_is_deprecated_min = 0UL; + enum ulong cap_bit0_is_deprecated_max = 1UL; /// @property ulong cap_user_rdpmc() @safe pure nothrow @nogc const { @@ -1282,8 +1282,8 @@ struct perf_event_mmap_page (cast(typeof(mmap_page_bitmanip)) v << 2U) & 4U)); } - enum ulong cap_user_rdpmc_min = cast(ulong) 0U; - enum ulong cap_user_rdpmc_max = cast(ulong) 1U; + enum ulong cap_user_rdpmc_min = 0UL; + enum ulong cap_user_rdpmc_max = 1UL; /// @property ulong cap_user_time() @safe pure nothrow @nogc const { @@ -1302,8 +1302,8 @@ struct perf_event_mmap_page (cast(typeof(mmap_page_bitmanip)) v << 3U) & 8U)); } - enum ulong cap_user_time_min = cast(ulong) 0U; - enum ulong cap_user_time_max = cast(ulong) 1U; + enum ulong cap_user_time_min = 0UL; + enum ulong cap_user_time_max = 1UL; /// @property ulong cap_user_time_zero() @safe pure nothrow @nogc const { @@ -1322,8 +1322,8 @@ struct perf_event_mmap_page (cast(typeof(mmap_page_bitmanip)) v << 4U) & 16U)); } - enum ulong cap_user_time_zero_min = cast(ulong) 0U; - enum ulong cap_user_time_zero_max = cast(ulong) 1U; + enum ulong cap_user_time_zero_min = 0UL; + enum ulong cap_user_time_zero_max = 1UL; /// @property ulong cap_____res() @safe pure nothrow @nogc const { @@ -1342,8 +1342,8 @@ struct perf_event_mmap_page (cast(typeof(mmap_page_bitmanip)) v << 5U) & 18446744073709551584UL)); } - enum ulong cap_____res_min = cast(ulong) 0U; - enum ulong cap_____res_max = cast(ulong) 576460752303423487UL; + enum ulong cap_____res_min = 0UL; + enum ulong cap_____res_max = 576460752303423487UL; } } @@ -1891,8 +1891,8 @@ version (LittleEndian) (cast(typeof(perf_mem_data_src_bitmanip)) v << 0U) & 31U)); } - enum ulong mem_op_min = cast(ulong) 0U; - enum ulong mem_op_max = cast(ulong) 31U; + enum ulong mem_op_min = 0UL; + enum ulong mem_op_max = 31UL; /// @property ulong mem_lvl() @safe pure nothrow @nogc const { @@ -1912,8 +1912,8 @@ version (LittleEndian) (cast(typeof(perf_mem_data_src_bitmanip)) v << 5U) & 524256U)); } - enum ulong mem_lvl_min = cast(ulong) 0U; - enum ulong mem_lvl_max = cast(ulong) 16383U; + enum ulong mem_lvl_min = 0UL; + enum ulong mem_lvl_max = 16383UL; /// @property ulong mem_snoop() @safe pure nothrow @nogc const { @@ -1933,8 +1933,8 @@ version (LittleEndian) (cast(typeof(perf_mem_data_src_bitmanip)) v << 19U) & 16252928U)); } - enum ulong mem_snoop_min = cast(ulong) 0U; - enum ulong mem_snoop_max = cast(ulong) 31U; + enum ulong mem_snoop_min = 0UL; + enum ulong mem_snoop_max = 31UL; /// @property ulong mem_lock() @safe pure nothrow @nogc const { @@ -1954,8 +1954,8 @@ version (LittleEndian) (cast(typeof(perf_mem_data_src_bitmanip)) v << 24U) & 50331648U)); } - enum ulong mem_lock_min = cast(ulong) 0U; - enum ulong mem_lock_max = cast(ulong) 3U; + enum ulong mem_lock_min = 0UL; + enum ulong mem_lock_max = 3UL; /// @property ulong mem_dtlb() @safe pure nothrow @nogc const { @@ -1975,8 +1975,8 @@ version (LittleEndian) (cast(typeof(perf_mem_data_src_bitmanip)) v << 26U) & 8522825728UL)); } - enum ulong mem_dtlb_min = cast(ulong) 0U; - enum ulong mem_dtlb_max = cast(ulong) 127U; + enum ulong mem_dtlb_min = 0UL; + enum ulong mem_dtlb_max = 127UL; /// @property ulong mem_lvl_num() @safe pure nothrow @nogc const { @@ -1996,8 +1996,8 @@ version (LittleEndian) (cast(typeof(perf_mem_data_src_bitmanip)) v << 33U) & 128849018880UL)); } - enum ulong mem_lvl_num_min = cast(ulong) 0U; - enum ulong mem_lvl_num_max = cast(ulong) 15U; + enum ulong mem_lvl_num_min = 0UL; + enum ulong mem_lvl_num_max = 15UL; /// @property ulong mem_remote() @safe pure nothrow @nogc const { @@ -2017,8 +2017,8 @@ version (LittleEndian) (cast(typeof(perf_mem_data_src_bitmanip)) v << 37U) & 137438953472UL)); } - enum ulong mem_remote_min = cast(ulong) 0U; - enum ulong mem_remote_max = cast(ulong) 1U; + enum ulong mem_remote_min = 0UL; + enum ulong mem_remote_max = 1UL; /// @property ulong mem_snoopx() @safe pure nothrow @nogc const { @@ -2038,8 +2038,8 @@ version (LittleEndian) (cast(typeof(perf_mem_data_src_bitmanip)) v << 38U) & 824633720832UL)); } - enum ulong mem_snoopx_min = cast(ulong) 0U; - enum ulong mem_snoopx_max = cast(ulong) 3U; + enum ulong mem_snoopx_min = 0UL; + enum ulong mem_snoopx_max = 3UL; /// @property ulong mem_rsvd() @safe pure nothrow @nogc const { @@ -2060,8 +2060,8 @@ version (LittleEndian) (cast(typeof(perf_mem_data_src_bitmanip)) v << 40U) & 18446742974197923840UL)); } - enum ulong mem_rsvd_min = cast(ulong) 0U; - enum ulong mem_rsvd_max = cast(ulong) 16777215U; + enum ulong mem_rsvd_min = 0UL; + enum ulong mem_rsvd_max = 16777215UL; } } @@ -2099,8 +2099,8 @@ else (cast(typeof(perf_mem_data_src)) v << 0U) & 16777215U)); } - enum ulong mem_rsvd_min = cast(ulong) 0U; - enum ulong mem_rsvd_max = cast(ulong) 16777215U; + enum ulong mem_rsvd_min = 0UL; + enum ulong mem_rsvd_max = 16777215UL; /// @property ulong mem_snoopx() @safe pure nothrow @nogc const { @@ -2119,8 +2119,8 @@ else (cast(typeof(perf_mem_data_src)) v << 24U) & 50331648U)); } - enum ulong mem_snoopx_min = cast(ulong) 0U; - enum ulong mem_snoopx_max = cast(ulong) 3U; + enum ulong mem_snoopx_min = 0UL; + enum ulong mem_snoopx_max = 3UL; /// @property ulong mem_remote() @safe pure nothrow @nogc const { @@ -2139,8 +2139,8 @@ else (cast(typeof(perf_mem_data_src)) v << 26U) & 67108864U)); } - enum ulong mem_remote_min = cast(ulong) 0U; - enum ulong mem_remote_max = cast(ulong) 1U; + enum ulong mem_remote_min = 0UL; + enum ulong mem_remote_max = 1UL; /// @property ulong mem_lvl_num() @safe pure nothrow @nogc const { @@ -2159,8 +2159,8 @@ else (cast(typeof(perf_mem_data_src)) v << 27U) & 2013265920U)); } - enum ulong mem_lvl_num_min = cast(ulong) 0U; - enum ulong mem_lvl_num_max = cast(ulong) 15U; + enum ulong mem_lvl_num_min = 0UL; + enum ulong mem_lvl_num_max = 15UL; /// @property ulong mem_dtlb() @safe pure nothrow @nogc const { @@ -2179,8 +2179,8 @@ else (cast(typeof(perf_mem_data_src)) v << 31U) & 272730423296UL)); } - enum ulong mem_dtlb_min = cast(ulong) 0U; - enum ulong mem_dtlb_max = cast(ulong) 127U; + enum ulong mem_dtlb_min = 0UL; + enum ulong mem_dtlb_max = 127UL; /// @property ulong mem_lock() @safe pure nothrow @nogc const { @@ -2199,8 +2199,8 @@ else (cast(typeof(perf_mem_data_src)) v << 38U) & 824633720832UL)); } - enum ulong mem_lock_min = cast(ulong) 0U; - enum ulong mem_lock_max = cast(ulong) 3U; + enum ulong mem_lock_min = 0UL; + enum ulong mem_lock_max = 3UL; /// @property ulong mem_snoop() @safe pure nothrow @nogc const { @@ -2219,8 +2219,8 @@ else (cast(typeof(perf_mem_data_src)) v << 40U) & 34084860461056UL)); } - enum ulong mem_snoop_min = cast(ulong) 0U; - enum ulong mem_snoop_max = cast(ulong) 31U; + enum ulong mem_snoop_min = 0UL; + enum ulong mem_snoop_max = 31UL; /// @property ulong mem_lvl() @safe pure nothrow @nogc const { @@ -2239,8 +2239,8 @@ else (cast(typeof(perf_mem_data_src)) v << 45U) & 576425567931334656UL)); } - enum ulong mem_lvl_min = cast(ulong) 0U; - enum ulong mem_lvl_max = cast(ulong) 16383U; + enum ulong mem_lvl_min = 0UL; + enum ulong mem_lvl_max = 16383UL; /// @property ulong mem_op() @safe pure nothrow @nogc const { @@ -2259,8 +2259,8 @@ else (cast(typeof(perf_mem_data_src)) v << 59U) & 17870283321406128128UL)); } - enum ulong mem_op_min = cast(ulong) 0U; - enum ulong mem_op_max = cast(ulong) 31U; + enum ulong mem_op_min = 0UL; + enum ulong mem_op_max = 31UL; } } } @@ -2392,8 +2392,8 @@ struct perf_branch_entry (cast(typeof(perf_branch_entry_bitmanip)) v << 0U) & 1U)); } - enum ulong mispred_min = cast(ulong) 0U; - enum ulong mispred_max = cast(ulong) 1U; + enum ulong mispred_min = 0UL; + enum ulong mispred_max = 1UL; /// @property ulong predicted() @safe pure nothrow @nogc const { @@ -2412,8 +2412,8 @@ struct perf_branch_entry (cast(typeof(perf_branch_entry_bitmanip)) v << 1U) & 2U)); } - enum ulong predicted_min = cast(ulong) 0U; - enum ulong predicted_max = cast(ulong) 1U; + enum ulong predicted_min = 0UL; + enum ulong predicted_max = 1UL; /// @property ulong in_tx() @safe pure nothrow @nogc const { @@ -2432,8 +2432,8 @@ struct perf_branch_entry (cast(typeof(perf_branch_entry_bitmanip)) v << 2U) & 4U)); } - enum ulong in_tx_min = cast(ulong) 0U; - enum ulong in_tx_max = cast(ulong) 1U; + enum ulong in_tx_min = 0UL; + enum ulong in_tx_max = 1UL; /// @property ulong abort() @safe pure nothrow @nogc const { @@ -2452,8 +2452,8 @@ struct perf_branch_entry (cast(typeof(perf_branch_entry_bitmanip)) v << 3U) & 8U)); } - enum ulong abort_min = cast(ulong) 0U; - enum ulong abort_max = cast(ulong) 1U; + enum ulong abort_min = 0UL; + enum ulong abort_max = 1UL; /// @property ulong cycles() @safe pure nothrow @nogc const { @@ -2472,8 +2472,8 @@ struct perf_branch_entry (cast(typeof(perf_branch_entry_bitmanip)) v << 4U) & 1048560U)); } - enum ulong cycles_min = cast(ulong) 0U; - enum ulong cycles_max = cast(ulong) 65535U; + enum ulong cycles_min = 0UL; + enum ulong cycles_max = 65535UL; /// @property ulong type() @safe pure nothrow @nogc const { @@ -2490,8 +2490,8 @@ struct perf_branch_entry (cast(typeof(perf_branch_entry_bitmanip)) v << 20U) & 15728640U)); } - enum ulong type_min = cast(ulong) 0U; - enum ulong type_max = cast(ulong) 15U; + enum ulong type_min = 0UL; + enum ulong type_max = 15UL; /// @property ulong reserved() @safe pure nothrow @nogc const { @@ -2511,6 +2511,6 @@ struct perf_branch_entry (cast(typeof(perf_branch_entry_bitmanip)) v << 24U) & 18446744073692774400UL)); } - enum ulong reserved_min = cast(ulong) 0U; - enum ulong reserved_max = cast(ulong) 1099511627775UL; + enum ulong reserved_min = 0UL; + enum ulong reserved_max = 1099511627775UL; } diff --git a/runtime/druntime/src/core/sys/linux/sys/procfs.d b/runtime/druntime/src/core/sys/linux/sys/procfs.d index 6a113e172d..ceea5223ed 100644 --- a/runtime/druntime/src/core/sys/linux/sys/procfs.d +++ b/runtime/druntime/src/core/sys/linux/sys/procfs.d @@ -7,9 +7,8 @@ module core.sys.linux.sys.procfs; +version (linux): + import core.sys.posix.sys.types : pid_t; -version (linux) -{ - alias lwpid_t = pid_t; -} +alias lwpid_t = pid_t; diff --git a/runtime/druntime/src/core/sys/linux/unistd.d b/runtime/druntime/src/core/sys/linux/unistd.d index faa226cf40..548870268d 100644 --- a/runtime/druntime/src/core/sys/linux/unistd.d +++ b/runtime/druntime/src/core/sys/linux/unistd.d @@ -5,6 +5,7 @@ public import core.sys.posix.unistd; version (linux): extern(C): nothrow: +@nogc: // Additional seek constants for sparse file handling // from Linux's unistd.h, stdio.h, and linux/fs.h @@ -21,3 +22,6 @@ char* getpass(const(char)* prompt); // Exit all threads in a process void exit_group(int status); + +/// Close all open file descriptors greater or equal to `lowfd` +void closefrom(int lowfd); diff --git a/runtime/druntime/src/core/sys/openbsd/unistd.d b/runtime/druntime/src/core/sys/openbsd/unistd.d index 4232c03604..9618543d8d 100644 --- a/runtime/druntime/src/core/sys/openbsd/unistd.d +++ b/runtime/druntime/src/core/sys/openbsd/unistd.d @@ -19,3 +19,5 @@ int getthrname(pid_t, char*, size_t); int pledge(const scope char*, const scope char*); int setthrname(pid_t, const scope char*); int unveil(const scope char*, const scope char*); + +int closefrom(int); diff --git a/runtime/druntime/src/core/sys/posix/fcntl.d b/runtime/druntime/src/core/sys/posix/fcntl.d index c0e9006a04..3da9fd72e6 100644 --- a/runtime/druntime/src/core/sys/posix/fcntl.d +++ b/runtime/druntime/src/core/sys/posix/fcntl.d @@ -475,6 +475,7 @@ else version (Darwin) enum F_UNLCK = 2; enum F_WRLCK = 3; + enum O_NOFOLLOW = 0x0100; enum O_CREAT = 0x0200; enum O_EXCL = 0x0800; enum O_NOCTTY = 0; diff --git a/runtime/druntime/src/core/sys/posix/signal.d b/runtime/druntime/src/core/sys/posix/signal.d index a8b7f7511a..e3d80fb021 100644 --- a/runtime/druntime/src/core/sys/posix/signal.d +++ b/runtime/druntime/src/core/sys/posix/signal.d @@ -136,7 +136,7 @@ version (Solaris) import core.sys.posix.unistd; @property int SIGRTMIN() nothrow @nogc { - __gshared static int sig = -1; + __gshared int sig = -1; if (sig == -1) { sig = cast(int)sysconf(_SC_SIGRT_MIN); } @@ -144,7 +144,7 @@ version (Solaris) } @property int SIGRTMAX() nothrow @nogc { - __gshared static int sig = -1; + __gshared int sig = -1; if (sig == -1) { sig = cast(int)sysconf(_SC_SIGRT_MAX); } @@ -180,7 +180,7 @@ else version (linux) } @property int SIGRTMIN() nothrow @nogc { - __gshared static int sig = -1; + __gshared int sig = -1; if (sig == -1) { sig = __libc_current_sigrtmin(); } @@ -188,7 +188,7 @@ else version (linux) } @property int SIGRTMAX() nothrow @nogc { - __gshared static int sig = -1; + __gshared int sig = -1; if (sig == -1) { sig = __libc_current_sigrtmax(); } @@ -2758,6 +2758,11 @@ else version (CRuntime_Musl) enum MINSIGSTKSZ = 2048; enum SIGSTKSZ = 8192; } + else version (LoongArch64) + { + enum MINSIGSTKSZ = 4096; + enum SIGSTKSZ = 16384; + } else static assert(0, "unimplemented"); diff --git a/runtime/druntime/src/core/sys/posix/spawn.d b/runtime/druntime/src/core/sys/posix/spawn.d index 789053396f..081270b42c 100644 --- a/runtime/druntime/src/core/sys/posix/spawn.d +++ b/runtime/druntime/src/core/sys/posix/spawn.d @@ -4,7 +4,7 @@ * Copyright: Copyright (C) 2018 by The D Language Foundation, All Rights Reserved * Authors: Petar Kirov * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/druntime/src/core/sys/posix/spawn.d, _spawn.d) + * Source: $(DRUNTIMESRC core/sys/posix/_spawn.d) * Standards: The Open Group Base Specifications Issue 6, IEEE Std 1003.1, 2004 Edition */ module core.sys.posix.spawn; diff --git a/runtime/druntime/src/core/sys/posix/stdc/time.d b/runtime/druntime/src/core/sys/posix/stdc/time.d index d48a0ea3ed..c5a2e1b718 100644 --- a/runtime/druntime/src/core/sys/posix/stdc/time.d +++ b/runtime/druntime/src/core/sys/posix/stdc/time.d @@ -9,7 +9,7 @@ * (See accompanying file LICENSE) * Authors: Sean Kelly, * Alex Rønne Petersen - * Source: $(DRUNTIMESRC core/stdc/_time.d) + * Source: $(DRUNTIMESRC core/sys/posix/stdc/_time.d) * Standards: ISO/IEC 9899:1999 (E) */ diff --git a/runtime/druntime/src/core/sys/posix/sys/resource.d b/runtime/druntime/src/core/sys/posix/sys/resource.d index 3a9a187639..b75c794e92 100644 --- a/runtime/druntime/src/core/sys/posix/sys/resource.d +++ b/runtime/druntime/src/core/sys/posix/sys/resource.d @@ -82,14 +82,14 @@ version (linux) } static if (__USE_FILE_OFFSET64) - alias ulong rlim_t; + alias ulong rlim_t; else - alias c_ulong rlim_t; + alias c_ulong rlim_t; static if (__USE_FILE_OFFSET64) enum RLIM_INFINITY = 0xffffffffffffffffUL; else - enum RLIM_INFINITY = cast(c_ulong)(~0UL); + enum RLIM_INFINITY = cast(c_ulong)~0UL; enum RLIM_SAVED_MAX = RLIM_INFINITY; enum RLIM_SAVED_CUR = RLIM_INFINITY; @@ -163,7 +163,7 @@ else version (Darwin) enum { - RLIM_INFINITY = ((cast(ulong) 1 << 63) - 1), + RLIM_INFINITY = ((1UL << 63) - 1), RLIM_SAVED_MAX = RLIM_INFINITY, RLIM_SAVED_CUR = RLIM_INFINITY, } @@ -205,7 +205,7 @@ else version (FreeBSD) enum { - RLIM_INFINITY = (cast(rlim_t)((cast(ulong) 1 << 63) - 1)), + RLIM_INFINITY = (cast(rlim_t)((1UL << 63) - 1)), RLIM_SAVED_MAX = RLIM_INFINITY, RLIM_SAVED_CUR = RLIM_INFINITY, } @@ -262,7 +262,7 @@ else version (NetBSD) enum { - RLIM_INFINITY = (cast(rlim_t)((cast(ulong) 1 << 63) - 1)), + RLIM_INFINITY = (cast(rlim_t)((1UL << 63) - 1)), RLIM_SAVED_MAX = RLIM_INFINITY, RLIM_SAVED_CUR = RLIM_INFINITY, } @@ -319,7 +319,7 @@ else version (OpenBSD) enum { - RLIM_INFINITY = (cast(rlim_t)((cast(ulong) 1 << 63) - 1)), + RLIM_INFINITY = (cast(rlim_t)((1UL << 63) - 1)), RLIM_SAVED_MAX = RLIM_INFINITY, RLIM_SAVED_CUR = RLIM_INFINITY, } @@ -378,7 +378,7 @@ else version (DragonFlyBSD) enum { - RLIM_INFINITY = (cast(rlim_t)((cast(ulong) 1 << 63) - 1)), + RLIM_INFINITY = (cast(rlim_t)((1UL << 63) - 1)), RLIM_SAVED_MAX = RLIM_INFINITY, RLIM_SAVED_CUR = RLIM_INFINITY, } diff --git a/runtime/druntime/src/core/sys/posix/sys/socket.d b/runtime/druntime/src/core/sys/posix/sys/socket.d index 7c6fab6315..12824373d5 100644 --- a/runtime/druntime/src/core/sys/posix/sys/socket.d +++ b/runtime/druntime/src/core/sys/posix/sys/socket.d @@ -30,6 +30,7 @@ else version (WatchOS) version (ARM) version = ARM_Any; version (AArch64) version = ARM_Any; version (HPPA) version = HPPA_Any; +version (HPPA64) version = HPPA_Any; version (MIPS32) version = MIPS_Any; version (MIPS64) version = MIPS_Any; version (PPC) version = PPC_Any; @@ -317,6 +318,7 @@ version (linux) SO_RCVLOWAT = 0x1004, SO_RCVTIMEO = 0x1006, SO_REUSEADDR = 0x0004, + SO_REUSEPORT = 0x0200, SO_SNDBUF = 0x1001, SO_SNDLOWAT = 0x1003, SO_SNDTIMEO = 0x1005, @@ -351,6 +353,7 @@ version (linux) SO_RCVLOWAT = 0x1004, SO_RCVTIMEO = 0x1006, SO_REUSEADDR = 0x0004, + SO_REUSEPORT = 0x0200, SO_SNDBUF = 0x1001, SO_SNDLOWAT = 0x1003, SO_SNDTIMEO = 0x1005, @@ -385,6 +388,7 @@ version (linux) SO_RCVLOWAT = 16, SO_RCVTIMEO = 18, SO_REUSEADDR = 2, + SO_REUSEPORT = 15, SO_SNDBUF = 7, SO_SNDLOWAT = 17, SO_SNDTIMEO = 19, @@ -454,6 +458,7 @@ version (linux) SO_RCVLOWAT = 18, SO_RCVTIMEO = 20, SO_REUSEADDR = 2, + SO_REUSEPORT = 15, SO_SNDBUF = 7, SO_SNDLOWAT = 19, SO_SNDTIMEO = 21, @@ -488,6 +493,7 @@ version (linux) SO_RCVLOWAT = 18, SO_RCVTIMEO = 20, SO_REUSEADDR = 2, + SO_REUSEPORT = 0x0200, //FIXME: the rest appear to be wrong SO_SNDBUF = 7, SO_SNDLOWAT = 19, SO_SNDTIMEO = 21, @@ -522,6 +528,7 @@ version (linux) SO_RCVLOWAT = 18, SO_RCVTIMEO = 20, SO_REUSEADDR = 2, + SO_REUSEPORT = 15, SO_SNDBUF = 7, SO_SNDLOWAT = 19, SO_SNDTIMEO = 21, @@ -556,6 +563,7 @@ version (linux) SO_RCVLOWAT = 18, SO_RCVTIMEO = 20, SO_REUSEADDR = 2, + SO_REUSEPORT = 15, SO_SNDBUF = 7, SO_SNDLOWAT = 19, SO_SNDTIMEO = 21, @@ -1547,6 +1555,7 @@ else version (Solaris) SO_RCVLOWAT = 0x1004, SO_RCVTIMEO = 0x1006, SO_REUSEADDR = 0x0004, + SO_REUSEPORT = 0x100e, SO_SNDBUF = 0x1001, SO_SNDLOWAT = 0x1003, SO_SNDTIMEO = 0x1005, diff --git a/runtime/druntime/src/core/sys/windows/aclapi.d b/runtime/druntime/src/core/sys/windows/aclapi.d index 4905351b56..0b9f755ed6 100644 --- a/runtime/druntime/src/core/sys/windows/aclapi.d +++ b/runtime/druntime/src/core/sys/windows/aclapi.d @@ -15,7 +15,7 @@ pragma(lib, "advapi32"); import core.sys.windows.accctrl, core.sys.windows.basetyps, core.sys.windows.w32api, core.sys.windows.winnt; -extern (Windows) { +extern (Windows) nothrow @nogc { VOID BuildExplicitAccessWithNameA(PEXPLICIT_ACCESS_A, LPSTR, DWORD, ACCESS_MODE, DWORD); VOID BuildExplicitAccessWithNameW(PEXPLICIT_ACCESS_W, LPWSTR, DWORD, diff --git a/runtime/druntime/src/core/sys/windows/aclui.d b/runtime/druntime/src/core/sys/windows/aclui.d index 08be626ad2..d7db31738f 100644 --- a/runtime/druntime/src/core/sys/windows/aclui.d +++ b/runtime/druntime/src/core/sys/windows/aclui.d @@ -114,7 +114,7 @@ alias ISecurityInformation LPSECURITYINFO; // FIXME: linkage attribute? extern (C) /+DECLSPEC_IMPORT+/ extern const IID IID_ISecurityInformation; -extern (Windows) { +extern (Windows) nothrow @nogc { HPROPSHEETPAGE CreateSecurityPage(LPSECURITYINFO psi); BOOL EditSecurity(HWND hwndOwner, LPSECURITYINFO psi); } diff --git a/runtime/druntime/src/core/sys/windows/bcrypt.d b/runtime/druntime/src/core/sys/windows/bcrypt.d new file mode 100644 index 0000000000..6edf0df7a5 --- /dev/null +++ b/runtime/druntime/src/core/sys/windows/bcrypt.d @@ -0,0 +1,900 @@ +/** + * Cryptographic primitives + * + * Translated from MinGW-w64 Windows headers + * + * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) + * Authors: Aya Partridge + * Source: $(DRUNTIMESRC core/sys/windows/_bcrypt.d) + */ +module core.sys.windows.bcrypt; +version (Windows): + +pragma(lib, "bcrypt"); + +import core.sys.windows.ntdef, core.sys.windows.sdkddkver, core.sys.windows.winbase, core.sys.windows.windef, core.sys.windows.winnt; + +pragma(inline, true) +bool BCRYPT_SUCCESS(NTSTATUS Status) @nogc nothrow pure @safe => Status >= 0; + +enum BCRYPT_OBJECT_ALIGNMENT = 16; + +enum { + BCRYPT_KDF_HASH = "HASH"w, + BCRYPT_KDF_HMAC = "HMAC"w, + BCRYPT_KDF_TLS_PRF = "TLS_PRF"w, + BCRYPT_KDF_SP80056A_CONCAT = "SP800_56A_CONCAT"w, +} +static if (NTDDI_VERSION >= NTDDI_WINBLUE) +enum { + BCRYPT_KDF_RAW_SECRET = "TRUNCATE"w, +} +static if (NTDDI_VERSION >= NTDDI_WIN10_RS4) +enum { + BCRYPT_KDF_HKDF = "HKDF"w, +} + +enum { + KDF_HASH_ALGORITHM = 0x0, + KDF_SECRET_PREPEND = 0x1, + KDF_SECRET_APPEND = 0x2, + KDF_HMAC_KEY = 0x3, + KDF_TLS_PRF_LABEL = 0x4, + KDF_TLS_PRF_SEED = 0x5, + KDF_SECRET_HANDLE = 0x6, +} +static if (NTDDI_VERSION >= NTDDI_WIN7) +enum { + KDF_TLS_PRF_PROTOCOL = 0x7, + KDF_ALGORITHMID = 0x8, + KDF_PARTYUINFO = 0x9, + KDF_PARTYVINFO = 0xA, + KDF_SUPPPUBINFO = 0xB, + KDF_SUPPPRIVINFO = 0xC, +} +static if (NTDDI_VERSION >= NTDDI_WIN8) +enum { + KDF_LABEL = 0xD, + KDF_CONTEXT = 0xE, + KDF_SALT = 0xF, + KDF_ITERATION_COUNT = 0x10, + KDF_GENERIC_PARAMETER = 0x11, + KDF_KEYBITLENGTH = 0x12, +} +static if (NTDDI_VERSION >= NTDDI_WIN10_RS4) +enum { + KDF_HKDF_SALT = 0x13, + KDF_HKDF_INFO = 0x14, +} + +enum KDF_USE_SECRET_AS_HMAC_KEY_FLAG = 1; + +enum BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO_VERSION = 1; + +enum { + BCRYPT_AUTH_MODE_CHAIN_CALLS_FLAG = 0x00000001, + BCRYPT_AUTH_MODE_IN_PROGRESS_FLAG = 0x00000002, +} + +pragma(inline, true) +void BCRYPT_INIT_AUTH_MODE_INFO(ref BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO _AUTH_INFO_STRUCT_) @nogc nothrow pure @safe { + (() @trusted => RtlZeroMemory(&_AUTH_INFO_STRUCT_, _AUTH_INFO_STRUCT_.sizeof))(); + _AUTH_INFO_STRUCT_.cbSize = _AUTH_INFO_STRUCT_.sizeof; + _AUTH_INFO_STRUCT_.dwInfoVersion = BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO_VERSION; +} + +enum { + BCRYPT_OPAQUE_KEY_BLOB = "OpaqueKeyBlob"w, + BCRYPT_KEY_DATA_BLOB = "KeyDataBlob"w, +} +static if (NTDDI_VERSION >= NTDDI_WIN7) +enum { + BCRYPT_AES_WRAP_KEY_BLOB = "Rfc3565KeyWrapBlob"w, +} + +enum { + BCRYPT_ALGORITHM_NAME = "AlgorithmName"w, + BCRYPT_AUTH_TAG_LENGTH = "AuthTagLength"w, + BCRYPT_BLOCK_LENGTH = "BlockLength"w, + BCRYPT_BLOCK_SIZE_LIST = "BlockSizeList"w, + BCRYPT_CHAINING_MODE = "ChainingMode"w, + BCRYPT_CHAIN_MODE_CBC = "ChainingModeCBC"w, + BCRYPT_CHAIN_MODE_CCM = "ChainingModeCCM"w, + BCRYPT_CHAIN_MODE_CFB = "ChainingModeCFB"w, + BCRYPT_CHAIN_MODE_ECB = "ChainingModeECB"w, + BCRYPT_CHAIN_MODE_GCM = "ChainingModeGCM"w, + BCRYPT_CHAIN_MODE_NA = "ChainingModeN/A"w, + BCRYPT_EFFECTIVE_KEY_LENGTH = "EffectiveKeyLength"w, + BCRYPT_HASH_BLOCK_LENGTH = "HashBlockLength"w, + BCRYPT_HASH_LENGTH = "HashDigestLength"w, + BCRYPT_HASH_OID_LIST = "HashOIDList"w, + BCRYPT_INITIALIZATION_VECTOR = "IV"w, + BCRYPT_IS_KEYED_HASH = "IsKeyedHash"w, + BCRYPT_IS_REUSABLE_HASH = "IsReusableHash"w, + BCRYPT_KEY_LENGTH = "KeyLength"w, + BCRYPT_KEY_LENGTHS = "KeyLengths"w, + BCRYPT_KEY_OBJECT_LENGTH = "KeyObjectLength"w, + BCRYPT_KEY_STRENGTH = "KeyStrength"w, + BCRYPT_MESSAGE_BLOCK_LENGTH = "MessageBlockLength"w, + BCRYPT_OBJECT_LENGTH = "ObjectLength"w, + BCRYPT_PADDING_SCHEMES = "PaddingSchemes"w, + BCRYPT_PCP_PLATFORM_TYPE_PROPERTY = "PCP_PLATFORM_TYPE"w, + BCRYPT_PCP_PROVIDER_VERSION_PROPERTY = "PCP_PROVIDER_VERSION"w, + BCRYPT_PRIMITIVE_TYPE = "PrimitiveType"w, + BCRYPT_PROVIDER_HANDLE = "ProviderHandle"w, + BCRYPT_PUBLIC_KEY_LENGTH = "PublicKeyLength"w, + BCRYPT_SIGNATURE_LENGTH = "SignatureLength"w, +} +static if (NTDDI_VERSION >= NTDDI_WINBLUE) +enum { + BCRYPT_MULTI_OBJECT_LENGTH = "MultiObjectLength"w, +} +static if (NTDDI_VERSION >= NTDDI_WIN10_RS4) +enum { + BCRYPT_IS_IFX_TPM_WEAK_KEY = "IsIfxTpmWeakKey"w, + BCRYPT_HKDF_HASH_ALGORITHM = "HkdfHashAlgorithm"w, + BCRYPT_HKDF_SALT_AND_FINALIZE = "HkdfSaltAndFinalize"w, + BCRYPT_HKDF_PRK_AND_FINALIZE = "HkdfPrkAndFinalize"w, +} +static if (NTDDI_VERSION >= NTDDI_WIN11_ZN) +enum { + BCRYPT_FUNCTION_NAME_STRING = "FunctionNameString"w, + BCRYPT_CUSTOMIZATION_STRING = "CustomizationString"w, +} +static if (NTDDI_VERSION >= NTDDI_WIN11_GA) +enum { + BCRYPT_CHAIN_MODE_KWP = "ChainingModeKWP"w, +} + +enum { + BCRYPT_SUPPORTED_PAD_ROUTER = 0x00000001, + BCRYPT_SUPPORTED_PAD_PKCS1_ENC = 0x00000002, + BCRYPT_SUPPORTED_PAD_PKCS1_SIG = 0x00000004, + BCRYPT_SUPPORTED_PAD_OAEP = 0x00000008, + BCRYPT_SUPPORTED_PAD_PSS = 0x00000010, +} + +enum BCRYPT_PROV_DISPATCH = 0x00000001; + +enum BCRYPT_BLOCK_PADDING = 0x00000001; + +static if (NTDDI_VERSION >= NTDDI_WIN10_CO) +enum BCRYPT_GENERATE_IV = 0x00000020; + +enum { + BCRYPT_PAD_NONE = 0x00000001, + BCRYPT_PAD_PKCS1 = 0x00000002, + BCRYPT_PAD_OAEP = 0x00000004, + BCRYPT_PAD_PSS = 0x00000008, +} +static if (NTDDI_VERSION >= NTDDI_WINBLUE) +enum { + BCRYPT_PAD_PKCS1_OPTIONAL_HASH_OID = 0x00000010, +} + +enum BCRYPTBUFFER_VERSION = 0; + +struct BCRYPT_KEY_LENGTHS_STRUCT { + ULONG dwMinLength; + ULONG dwMaxLength; + ULONG dwIncrement; +} + +alias BCRYPT_AUTH_TAG_LENGTHS_STRUCT = BCRYPT_KEY_LENGTHS_STRUCT; + +struct BCRYPT_OID { + ULONG cbOID; + PUCHAR pbOID; +} + +struct BCRYPT_OID_LIST { + ULONG dwOIDCount; + BCRYPT_OID* pOIDs; +} + +struct BCRYPT_PKCS1_PADDING_INFO { + LPCWSTR pszAlgId; +} + +struct BCRYPT_PSS_PADDING_INFO { + LPCWSTR pszAlgId; + ULONG cbSalt; +} + +struct BCRYPT_OAEP_PADDING_INFO { + LPCWSTR pszAlgId; + PUCHAR pbLabel; + ULONG cbLabel; +} + +struct BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO { + ULONG cbSize; + ULONG dwInfoVersion; + PUCHAR pbNonce; + ULONG cbNonce; + PUCHAR pbAuthData; + ULONG cbAuthData; + PUCHAR pbTag; + ULONG cbTag; + PUCHAR pbMacContext; + ULONG cbMacContext; + ULONG cbAAD; + ULONGLONG cbData; + ULONG dwFlags; +} +alias PBCRYPT_AUTHENTICATED_CIPHER_MODE_INFO = BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO*; + +struct BCryptBuffer { + ULONG cbBuffer; + ULONG BufferType; + PVOID pvBuffer; +} +alias PBCryptBuffer = BCryptBuffer*; + +struct BCryptBufferDesc { + ULONG ulVersion; + ULONG cBuffers; + PBCryptBuffer pBuffers; +} +alias PBCryptBufferDesc = BCryptBufferDesc*; + +enum { + BCRYPT_PUBLIC_KEY_BLOB = "PUBLICBLOB"w, + BCRYPT_PRIVATE_KEY_BLOB = "PRIVATEBLOB"w, +} + +enum { + BCRYPT_RSAPUBLIC_BLOB = "RSAPUBLICBLOB"w, + BCRYPT_RSAPRIVATE_BLOB = "RSAPRIVATEBLOB"w, + LEGACY_RSAPUBLIC_BLOB = "CAPIPUBLICBLOB"w, + LEGACY_RSAPRIVATE_BLOB = "CAPIPRIVATEBLOB"w, +} + +enum { + BCRYPT_RSAPUBLIC_MAGIC = 0x31415352, + BCRYPT_RSAPRIVATE_MAGIC = 0x32415352, +} + +enum BCRYPT_RSAFULLPRIVATE_BLOB = "RSAFULLPRIVATEBLOB"w; + +enum BCRYPT_RSAFULLPRIVATE_MAGIC = 0x33415352; + +static if (NTDDI_VERSION >= NTDDI_WIN8) { + enum BCRYPT_GLOBAL_PARAMETERS = "SecretAgreementParam"w; + enum BCRYPT_PRIVATE_KEY = "PrivKeyVal"w; +} + +enum { + BCRYPT_ECCPUBLIC_BLOB = "ECCPUBLICBLOB"w, + BCRYPT_ECCPRIVATE_BLOB = "ECCPRIVATEBLOB"w, +} +static if (NTDDI_VERSION >= NTDDI_WINTHRESHOLD) +enum { + BCRYPT_ECCFULLPUBLIC_BLOB = "ECCFULLPUBLICBLOB"w, + BCRYPT_ECCFULLPRIVATE_BLOB = "ECCFULLPRIVATEBLOB"w, +} + +static if (NTDDI_VERSION >= NTDDI_WINTHRESHOLD) +enum SSL_ECCPUBLIC_BLOB = "SSLECCPUBLICBLOB"w; + +enum TLS_13_PRE_SHARED_KEY = "TLS13PRESHAREDKEY"w; + +enum { + BCRYPT_ECDH_PUBLIC_P256_MAGIC = 0x314B4345, + BCRYPT_ECDH_PRIVATE_P256_MAGIC = 0x324B4345, + BCRYPT_ECDH_PUBLIC_P384_MAGIC = 0x334B4345, + BCRYPT_ECDH_PRIVATE_P384_MAGIC = 0x344B4345, + BCRYPT_ECDH_PUBLIC_P521_MAGIC = 0x354B4345, + BCRYPT_ECDH_PRIVATE_P521_MAGIC = 0x364B4345, +} +static if (NTDDI_VERSION >= NTDDI_WINTHRESHOLD) +enum { + BCRYPT_ECDH_PUBLIC_GENERIC_MAGIC = 0x504B4345, + BCRYPT_ECDH_PRIVATE_GENERIC_MAGIC = 0x564B4345, +} + +enum { + BCRYPT_ECDSA_PUBLIC_P256_MAGIC = 0x31534345, + BCRYPT_ECDSA_PRIVATE_P256_MAGIC = 0x32534345, + BCRYPT_ECDSA_PUBLIC_P384_MAGIC = 0x33534345, + BCRYPT_ECDSA_PRIVATE_P384_MAGIC = 0x34534345, + BCRYPT_ECDSA_PUBLIC_P521_MAGIC = 0x35534345, + BCRYPT_ECDSA_PRIVATE_P521_MAGIC = 0x36534345, +} +static if (NTDDI_VERSION >= NTDDI_WINTHRESHOLD) +enum { + BCRYPT_ECDSA_PUBLIC_GENERIC_MAGIC = 0x50444345, + BCRYPT_ECDSA_PRIVATE_GENERIC_MAGIC = 0x56444345, +} + +enum { + BCRYPT_DH_PUBLIC_BLOB = "DHPUBLICBLOB"w, + BCRYPT_DH_PRIVATE_BLOB = "DHPRIVATEBLOB"w, + LEGACY_DH_PUBLIC_BLOB = "CAPIDHPUBLICBLOB"w, + LEGACY_DH_PRIVATE_BLOB = "CAPIDHPRIVATEBLOB"w, +} + +enum { + BCRYPT_DH_PUBLIC_MAGIC = 0x42504844, + BCRYPT_DH_PRIVATE_MAGIC = 0x56504844, +} + +enum { + BCRYPT_DH_PARAMETERS = "DHParameters"w, + BCRYPT_DH_PARAMETERS_MAGIC = 0x4D504844, +} + +enum { + BCRYPT_DSA_PUBLIC_BLOB = "DSAPUBLICBLOB"w, + BCRYPT_DSA_PRIVATE_BLOB = "DSAPRIVATEBLOB"w, + LEGACY_DSA_PUBLIC_BLOB = "CAPIDSAPUBLICBLOB"w, + LEGACY_DSA_PRIVATE_BLOB = "CAPIDSAPRIVATEBLOB"w, + LEGACY_DSA_V2_PUBLIC_BLOB = "V2CAPIDSAPUBLICBLOB"w, + LEGACY_DSA_V2_PRIVATE_BLOB = "V2CAPIDSAPRIVATEBLOB"w, +} + +enum { + BCRYPT_DSA_PUBLIC_MAGIC = 0x42505344, + BCRYPT_DSA_PRIVATE_MAGIC = 0x56505344, + BCRYPT_DSA_PUBLIC_MAGIC_V2 = 0x32425044, + BCRYPT_DSA_PRIVATE_MAGIC_V2 = 0x32565044, +} + +enum { + BCRYPT_KEY_DATA_BLOB_MAGIC = 0x4D42444B, + BCRYPT_KEY_DATA_BLOB_VERSION1 = 0x1, +} + +enum { + BCRYPT_DSA_PARAMETERS = "DSAParameters"w, + BCRYPT_DSA_PARAMETERS_MAGIC = 0x4D505344, + BCRYPT_DSA_PARAMETERS_MAGIC_V2 = 0x324D5044, +} + +enum { + MS_PRIMITIVE_PROVIDER = "Microsoft Primitive Provider"w, + MS_PLATFORM_CRYPTO_PROVIDER = "Microsoft Platform Crypto Provider"w, +} + +enum { + BCRYPT_RSA_ALGORITHM = "RSA"w, + BCRYPT_RSA_SIGN_ALGORITHM = "RSA_SIGN"w, + BCRYPT_DH_ALGORITHM = "DH"w, + BCRYPT_DSA_ALGORITHM = "DSA"w, + BCRYPT_RC2_ALGORITHM = "RC2"w, + BCRYPT_RC4_ALGORITHM = "RC4"w, + BCRYPT_AES_ALGORITHM = "AES"w, + BCRYPT_DES_ALGORITHM = "DES"w, + BCRYPT_DESX_ALGORITHM = "DESX"w, + BCRYPT_3DES_ALGORITHM = "3DES"w, + BCRYPT_3DES_112_ALGORITHM = "3DES_112"w, + BCRYPT_MD2_ALGORITHM = "MD2"w, + BCRYPT_MD4_ALGORITHM = "MD4"w, + BCRYPT_MD5_ALGORITHM = "MD5"w, + BCRYPT_SHA1_ALGORITHM = "SHA1"w, + BCRYPT_SHA256_ALGORITHM = "SHA256"w, + BCRYPT_SHA384_ALGORITHM = "SHA384"w, + BCRYPT_SHA512_ALGORITHM = "SHA512"w, + BCRYPT_AES_GMAC_ALGORITHM = "AES-GMAC"w, + BCRYPT_AES_CMAC_ALGORITHM = "AES-CMAC"w, + BCRYPT_ECDSA_P256_ALGORITHM = "ECDSA_P256"w, + BCRYPT_ECDSA_P384_ALGORITHM = "ECDSA_P384"w, + BCRYPT_ECDSA_P521_ALGORITHM = "ECDSA_P521"w, + BCRYPT_ECDH_P256_ALGORITHM = "ECDH_P256"w, + BCRYPT_ECDH_P384_ALGORITHM = "ECDH_P384"w, + BCRYPT_ECDH_P521_ALGORITHM = "ECDH_P521"w, + BCRYPT_RNG_ALGORITHM = "RNG"w, + BCRYPT_RNG_FIPS186_DSA_ALGORITHM = "FIPS186DSARNG"w, + BCRYPT_RNG_DUAL_EC_ALGORITHM = "DUALECRNG"w, + BCRYPT_SP800108_CTR_HMAC_ALGORITHM = "SP800_108_CTR_HMAC"w, + BCRYPT_SP80056A_CONCAT_ALGORITHM = "SP800_56A_CONCAT"w, + BCRYPT_PBKDF2_ALGORITHM = "PBKDF2"w, + BCRYPT_CAPI_KDF_ALGORITHM = "CAPI_KDF"w, +} + +enum { + BCRYPT_CIPHER_INTERFACE = 0x00000001, + BCRYPT_HASH_INTERFACE = 0x00000002, + BCRYPT_ASYMMETRIC_ENCRYPTION_INTERFACE = 0x00000003, + BCRYPT_SECRET_AGREEMENT_INTERFACE = 0x00000004, + BCRYPT_SIGNATURE_INTERFACE = 0x00000005, + BCRYPT_RNG_INTERFACE = 0x00000006, + BCRYPT_KEY_DERIVATION_INTERFACE = 0x00000007, +} + +static if (NTDDI_VERSION >= NTDDI_WINTHRESHOLD) +enum : BCRYPT_ALG_HANDLE { + BCRYPT_MD2_ALG_HANDLE = cast(BCRYPT_ALG_HANDLE) 0x00000001, + BCRYPT_MD4_ALG_HANDLE = cast(BCRYPT_ALG_HANDLE) 0x00000011, + BCRYPT_MD5_ALG_HANDLE = cast(BCRYPT_ALG_HANDLE) 0x00000021, + BCRYPT_SHA1_ALG_HANDLE = cast(BCRYPT_ALG_HANDLE) 0x00000031, + BCRYPT_SHA256_ALG_HANDLE = cast(BCRYPT_ALG_HANDLE) 0x00000041, + BCRYPT_SHA384_ALG_HANDLE = cast(BCRYPT_ALG_HANDLE) 0x00000051, + BCRYPT_SHA512_ALG_HANDLE = cast(BCRYPT_ALG_HANDLE) 0x00000061, + BCRYPT_RC4_ALG_HANDLE = cast(BCRYPT_ALG_HANDLE) 0x00000071, + BCRYPT_RNG_ALG_HANDLE = cast(BCRYPT_ALG_HANDLE) 0x00000081, + BCRYPT_HMAC_MD5_ALG_HANDLE = cast(BCRYPT_ALG_HANDLE) 0x00000091, + BCRYPT_HMAC_SHA1_ALG_HANDLE = cast(BCRYPT_ALG_HANDLE) 0x000000A1, + BCRYPT_HMAC_SHA256_ALG_HANDLE = cast(BCRYPT_ALG_HANDLE) 0x000000B1, + BCRYPT_HMAC_SHA384_ALG_HANDLE = cast(BCRYPT_ALG_HANDLE) 0x000000C1, + BCRYPT_HMAC_SHA512_ALG_HANDLE = cast(BCRYPT_ALG_HANDLE) 0x000000D1, + BCRYPT_RSA_ALG_HANDLE = cast(BCRYPT_ALG_HANDLE) 0x000000E1, + BCRYPT_ECDSA_ALG_HANDLE = cast(BCRYPT_ALG_HANDLE) 0x000000F1, + + BCRYPT_AES_CMAC_ALG_HANDLE = cast(BCRYPT_ALG_HANDLE) 0x00000101, + BCRYPT_AES_GMAC_ALG_HANDLE = cast(BCRYPT_ALG_HANDLE) 0x00000111, + BCRYPT_HMAC_MD2_ALG_HANDLE = cast(BCRYPT_ALG_HANDLE) 0x00000121, + BCRYPT_HMAC_MD4_ALG_HANDLE = cast(BCRYPT_ALG_HANDLE) 0x00000131, + + BCRYPT_3DES_CBC_ALG_HANDLE = cast(BCRYPT_ALG_HANDLE) 0x00000141, + BCRYPT_3DES_ECB_ALG_HANDLE = cast(BCRYPT_ALG_HANDLE) 0x00000151, + BCRYPT_3DES_CFB_ALG_HANDLE = cast(BCRYPT_ALG_HANDLE) 0x00000161, + BCRYPT_3DES_112_CBC_ALG_HANDLE = cast(BCRYPT_ALG_HANDLE) 0x00000171, + BCRYPT_3DES_112_ECB_ALG_HANDLE = cast(BCRYPT_ALG_HANDLE) 0x00000181, + BCRYPT_3DES_112_CFB_ALG_HANDLE = cast(BCRYPT_ALG_HANDLE) 0x00000191, + BCRYPT_AES_CBC_ALG_HANDLE = cast(BCRYPT_ALG_HANDLE) 0x000001A1, + BCRYPT_AES_ECB_ALG_HANDLE = cast(BCRYPT_ALG_HANDLE) 0x000001B1, + BCRYPT_AES_CFB_ALG_HANDLE = cast(BCRYPT_ALG_HANDLE) 0x000001C1, + BCRYPT_AES_CCM_ALG_HANDLE = cast(BCRYPT_ALG_HANDLE) 0x000001D1, + BCRYPT_AES_GCM_ALG_HANDLE = cast(BCRYPT_ALG_HANDLE) 0x000001E1, + BCRYPT_DES_CBC_ALG_HANDLE = cast(BCRYPT_ALG_HANDLE) 0x000001F1, + BCRYPT_DES_ECB_ALG_HANDLE = cast(BCRYPT_ALG_HANDLE) 0x00000201, + BCRYPT_DES_CFB_ALG_HANDLE = cast(BCRYPT_ALG_HANDLE) 0x00000211, + BCRYPT_DESX_CBC_ALG_HANDLE = cast(BCRYPT_ALG_HANDLE) 0x00000221, + BCRYPT_DESX_ECB_ALG_HANDLE = cast(BCRYPT_ALG_HANDLE) 0x00000231, + BCRYPT_DESX_CFB_ALG_HANDLE = cast(BCRYPT_ALG_HANDLE) 0x00000241, + BCRYPT_RC2_CBC_ALG_HANDLE = cast(BCRYPT_ALG_HANDLE) 0x00000251, + BCRYPT_RC2_ECB_ALG_HANDLE = cast(BCRYPT_ALG_HANDLE) 0x00000261, + BCRYPT_RC2_CFB_ALG_HANDLE = cast(BCRYPT_ALG_HANDLE) 0x00000271, + + BCRYPT_DH_ALG_HANDLE = cast(BCRYPT_ALG_HANDLE) 0x00000281, + BCRYPT_ECDH_ALG_HANDLE = cast(BCRYPT_ALG_HANDLE) 0x00000291, + BCRYPT_ECDH_P256_ALG_HANDLE = cast(BCRYPT_ALG_HANDLE) 0x000002A1, + BCRYPT_ECDH_P384_ALG_HANDLE = cast(BCRYPT_ALG_HANDLE) 0x000002B1, + BCRYPT_ECDH_P521_ALG_HANDLE = cast(BCRYPT_ALG_HANDLE) 0x000002C1, + BCRYPT_DSA_ALG_HANDLE = cast(BCRYPT_ALG_HANDLE) 0x000002D1, + BCRYPT_ECDSA_P256_ALG_HANDLE = cast(BCRYPT_ALG_HANDLE) 0x000002E1, + BCRYPT_ECDSA_P384_ALG_HANDLE = cast(BCRYPT_ALG_HANDLE) 0x000002F1, + BCRYPT_ECDSA_P521_ALG_HANDLE = cast(BCRYPT_ALG_HANDLE) 0x00000301, + BCRYPT_RSA_SIGN_ALG_HANDLE = cast(BCRYPT_ALG_HANDLE) 0x00000311, + + BCRYPT_CAPI_KDF_ALG_HANDLE = cast(BCRYPT_ALG_HANDLE) 0x00000321, + BCRYPT_PBKDF2_ALG_HANDLE = cast(BCRYPT_ALG_HANDLE) 0x00000331, + + BCRYPT_SP800108_CTR_HMAC_ALG_HANDLE = cast(BCRYPT_ALG_HANDLE) 0x00000341, + BCRYPT_SP80056A_CONCAT_ALG_HANDLE = cast(BCRYPT_ALG_HANDLE) 0x00000351, + + BCRYPT_TLS1_1_KDF_ALG_HANDLE = cast(BCRYPT_ALG_HANDLE) 0x00000361, + BCRYPT_TLS1_2_KDF_ALG_HANDLE = cast(BCRYPT_ALG_HANDLE) 0x00000371, + + BCRYPT_XTS_AES_ALG_HANDLE = cast(BCRYPT_ALG_HANDLE) 0x00000381, + + BCRYPT_HKDF_ALG_HANDLE = cast(BCRYPT_ALG_HANDLE) 0x00000391, +} +static if (NTDDI_VERSION >= NTDDI_WIN10_FE) +enum { + BCRYPT_CHACHA20_POLY1305_ALG_HANDLE = cast(BCRYPT_ALG_HANDLE) 0x000003A1, +} +static if (NTDDI_VERSION >= NTDDI_WIN11_ZN) +enum { + BCRYPT_SHA3_256_ALG_HANDLE = cast(BCRYPT_ALG_HANDLE) 0x000003B1, + BCRYPT_SHA3_384_ALG_HANDLE = cast(BCRYPT_ALG_HANDLE) 0x000003C1, + BCRYPT_SHA3_512_ALG_HANDLE = cast(BCRYPT_ALG_HANDLE) 0x000003D1, + BCRYPT_HMAC_SHA3_256_ALG_HANDLE = cast(BCRYPT_ALG_HANDLE) 0x000003E1, + BCRYPT_HMAC_SHA3_384_ALG_HANDLE = cast(BCRYPT_ALG_HANDLE) 0x000003F1, + BCRYPT_HMAC_SHA3_512_ALG_HANDLE = cast(BCRYPT_ALG_HANDLE) 0x00000401, + BCRYPT_CSHAKE128_ALG_HANDLE = cast(BCRYPT_ALG_HANDLE) 0x00000411, + BCRYPT_CSHAKE256_ALG_HANDLE = cast(BCRYPT_ALG_HANDLE) 0x00000421, + BCRYPT_KMAC128_ALG_HANDLE = cast(BCRYPT_ALG_HANDLE) 0x00000431, + BCRYPT_KMAC256_ALG_HANDLE = cast(BCRYPT_ALG_HANDLE) 0x00000441, +} +static if (NTDDI_VERSION >= NTDDI_WIN11_GA) +enum { + BCRYPT_AES_KWP_ALG_HANDLE = cast(BCRYPT_ALG_HANDLE) 0x00000451, +} + +enum { + BCRYPT_ALG_HANDLE_HMAC_FLAG = 0x00000008, + BCRYPT_CAPI_AES_FLAG = 0x00000010, + BCRYPT_HASH_REUSABLE_FLAG = 0x00000020, + + BCRYPT_BUFFERS_LOCKED_FLAG = 0x00000040, +} + +enum { + BCRYPT_CIPHER_OPERATION = 0x00000001, + BCRYPT_HASH_OPERATION = 0x00000002, + BCRYPT_ASYMMETRIC_ENCRYPTION_OPERATION = 0x00000004, + BCRYPT_SECRET_AGREEMENT_OPERATION = 0x00000008, + BCRYPT_SIGNATURE_OPERATION = 0x00000010, + BCRYPT_RNG_OPERATION = 0x00000020, + BCRYPT_KEY_DERIVATION_OPERATION = 0x00000040, +} + +enum { + BCRYPT_PUBLIC_KEY_FLAG = 0x00000001, + BCRYPT_PRIVATE_KEY_FLAG = 0x00000002, +} + +enum BCRYPT_NO_KEY_VALIDATION = 0x00000008; + +enum { + BCRYPT_RNG_USE_ENTROPY_IN_BUFFER = 0x00000001, + BCRYPT_USE_SYSTEM_PREFERRED_RNG = 0x00000002, +} + +pragma(inline, true) @nogc nothrow pure @safe { + BCRYPT_INTERFACE_VERSION BCRYPT_MAKE_INTERFACE_VERSION(int major, int minor) => + BCRYPT_INTERFACE_VERSION(MajorVersion: cast(USHORT)major, MinorVersion: cast(USHORT)minor); + bool BCRYPT_IS_INTERFACE_VERSION_COMPATIBLE(BCRYPT_INTERFACE_VERSION loader, BCRYPT_INTERFACE_VERSION provider) => + loader.MajorVersion <= provider.MajorVersion; +} + +enum { + BCRYPT_CIPHER_INTERFACE_VERSION_1 = BCRYPT_MAKE_INTERFACE_VERSION(1, 0), + BCRYPT_HASH_INTERFACE_VERSION_1 = BCRYPT_MAKE_INTERFACE_VERSION(1, 0), + BCRYPT_ASYMMETRIC_ENCRYPTION_INTERFACE_VERSION_1 = BCRYPT_MAKE_INTERFACE_VERSION(1, 0), + BCRYPT_SECRET_AGREEMENT_INTERFACE_VERSION_1 = BCRYPT_MAKE_INTERFACE_VERSION(1, 0), + BCRYPT_SIGNATURE_INTERFACE_VERSION_1 = BCRYPT_MAKE_INTERFACE_VERSION(1, 0), + BCRYPT_RNG_INTERFACE_VERSION_1 = BCRYPT_MAKE_INTERFACE_VERSION(1, 0), +} + +enum { + CRYPT_MIN_DEPENDENCIES = 0x00000001, + CRYPT_PROCESS_ISOLATE = 0x00010000, +} + +enum { + CRYPT_UM = 0x00000001, + CRYPT_KM = 0x00000002, + CRYPT_MM = 0x00000003, + CRYPT_ANY = 0x00000004, +} + +enum CRYPT_OVERWRITE = 0x00000001; + +enum { + CRYPT_LOCAL = 0x00000001, + CRYPT_DOMAIN = 0x00000002, +} + +enum { + CRYPT_EXCLUSIVE = 0x00000001, + CRYPT_OVERRIDE = 0x00010000, +} + +enum { + CRYPT_ALL_FUNCTIONS = 0x00000001, + CRYPT_ALL_PROVIDERS = 0x00000002, +} + +enum { + CRYPT_PRIORITY_TOP = 0x00000000, + CRYPT_PRIORITY_BOTTOM = 0xFFFFFFFF, +} + +enum CRYPT_DEFAULT_CONTEXT = "Default"w; + +alias BCRYPT_HANDLE = PVOID; +alias BCRYPT_ALG_HANDLE = PVOID; +alias BCRYPT_KEY_HANDLE = PVOID; +alias BCRYPT_HASH_HANDLE = PVOID; +alias BCRYPT_SECRET_HANDLE = PVOID; + +struct BCRYPT_KEY_BLOB { + ULONG Magic; +} + +struct BCRYPT_RSAKEY_BLOB { + ULONG Magic; + ULONG BitLength; + ULONG cbPublicExp; + ULONG cbModulus; + ULONG cbPrime1; + ULONG cbPrime2; +} + +struct BCRYPT_ECCKEY_BLOB { + ULONG dwMagic; + ULONG cbKey; +} +alias PBCRYPT_ECCKEY_BLOB = BCRYPT_ECCKEY_BLOB*; + +static if (NTDDI_VERSION >= NTDDI_WINTHRESHOLD) { + struct SSL_ECCKEY_BLOB { + ULONG dwCurveType; + ULONG cbKey; + } + alias PSSL_ECCKEY_BLOB = SSL_ECCKEY_BLOB*; + + enum BCRYPT_ECC_FULLKEY_BLOB_V1 = 0x1; + + alias ECC_CURVE_TYPE_ENUM = int; + enum : ECC_CURVE_TYPE_ENUM { + BCRYPT_ECC_PRIME_SHORT_WEIERSTRASS_CURVE = 0x1, + BCRYPT_ECC_PRIME_TWISTED_EDWARDS_CURVE = 0x2, + BCRYPT_ECC_PRIME_MONTGOMERY_CURVE = 0x3 + } + + alias ECC_CURVE_ALG_ID_ENUM = int; + enum : ECC_CURVE_ALG_ID_ENUM { + BCRYPT_NO_CURVE_GENERATION_ALG_ID = 0x0, + } + + struct BCRYPT_ECCFULLKEY_BLOB { + ULONG dwMagic; + ULONG dwVersion; + ECC_CURVE_TYPE_ENUM dwCurveType; + ECC_CURVE_ALG_ID_ENUM dwCurveGenerationAlgId; + ULONG cbFieldLength; + ULONG cbSubgroupOrder; + ULONG cbCofactor; + ULONG cbSeed; + } + alias PBCRYPT_ECCFULLKEY_BLOB = BCRYPT_ECCFULLKEY_BLOB*; +} + +struct BCRYPT_DH_KEY_BLOB { + ULONG dwMagic; + ULONG cbKey; +} +alias PBCRYPT_DH_KEY_BLOB = BCRYPT_DH_KEY_BLOB*; + +struct BCRYPT_DH_PARAMETER_HEADER { + ULONG cbLength; + ULONG dwMagic; + ULONG cbKeyLength; +} + +struct BCRYPT_DSA_KEY_BLOB { + ULONG dwMagic; + ULONG cbKey; + UCHAR[4] Count; + UCHAR[20] Seed; + UCHAR[20] q; +} +alias PBCRYPT_DSA_KEY_BLOB = BCRYPT_DSA_KEY_BLOB*; + +alias HASHALGORITHM_ENUM = int; +enum : HASHALGORITHM_ENUM { + DSA_HASH_ALGORITHM_SHA1, + DSA_HASH_ALGORITHM_SHA256, + DSA_HASH_ALGORITHM_SHA512, +} + +alias DSAFIPSVERSION_ENUM = int; +enum : DSAFIPSVERSION_ENUM { + DSA_FIPS186_2, + DSA_FIPS186_3, +} + +struct BCRYPT_DSA_KEY_BLOB_V2 { + ULONG dwMagic; + ULONG cbKey; + HASHALGORITHM_ENUM hashAlgorithm; + DSAFIPSVERSION_ENUM standardVersion; + ULONG cbSeedLength; + ULONG cbGroupSize; + UCHAR[4] Count; +} +alias PBCRYPT_DSA_KEY_BLOB_V2 = BCRYPT_DSA_KEY_BLOB_V2*; + +struct BCRYPT_KEY_DATA_BLOB_HEADER { + ULONG dwMagic; + ULONG dwVersion; + ULONG cbKeyData; +} +alias PBCRYPT_KEY_DATA_BLOB_HEADER = BCRYPT_KEY_DATA_BLOB_HEADER*; + +struct BCRYPT_DSA_PARAMETER_HEADER { + ULONG cbLength; + ULONG dwMagic; + ULONG cbKeyLength; + UCHAR[4] Count; + UCHAR[20] Seed; + UCHAR[20] q; +} + +struct BCRYPT_DSA_PARAMETER_HEADER_V2 { + ULONG cbLength; + ULONG dwMagic; + ULONG cbKeyLength; + HASHALGORITHM_ENUM hashAlgorithm; + DSAFIPSVERSION_ENUM standardVersion; + ULONG cbSeedLength; + ULONG cbGroupSize; + UCHAR[4] Count; +} + +struct BCRYPT_ECC_CURVE_NAMES { + ULONG dwEccCurveNames; + LPWSTR* pEccCurveNames; +} + +alias BCRYPT_HASH_OPERATION_TYPE = int; +enum : BCRYPT_HASH_OPERATION_TYPE { + BCRYPT_HASH_OPERATION_HASH_DATA = 1, + BCRYPT_HASH_OPERATION_FINISH_HASH = 2, +} + +struct BCRYPT_MULTI_HASH_OPERATION { + ULONG iHash; + BCRYPT_HASH_OPERATION_TYPE hashOperation; + PUCHAR pbBuffer; + ULONG cbBuffer; +} + +alias BCRYPT_MULTI_OPERATION_TYPE = int; +enum : BCRYPT_MULTI_OPERATION_TYPE { + BCRYPT_OPERATION_TYPE_HASH = 1, +} + +struct BCRYPT_MULTI_OBJECT_LENGTH_STRUCT { + ULONG cbPerObject; + ULONG cbPerElement; +} + +struct BCRYPT_ALGORITHM_IDENTIFIER { + LPWSTR pszName; + ULONG dwClass; + ULONG dwFlags; +} + +struct BCRYPT_PROVIDER_NAME { + LPWSTR pszProviderName; +} + +struct BCRYPT_INTERFACE_VERSION { + USHORT MajorVersion; + USHORT MinorVersion; +} +alias PBCRYPT_INTERFACE_VERSION = BCRYPT_INTERFACE_VERSION*; + +struct CRYPT_INTERFACE_REG { + ULONG dwInterface; + ULONG dwFlags; + ULONG cFunctions; + PWSTR* rgpszFunctions; +} +alias PCRYPT_INTERFACE_REG = CRYPT_INTERFACE_REG*; + +struct CRYPT_IMAGE_REG { + PWSTR pszImage; + ULONG cInterfaces; + PCRYPT_INTERFACE_REG* rgpInterfaces; +} +alias PCRYPT_IMAGE_REG = CRYPT_IMAGE_REG*; + +struct CRYPT_PROVIDER_REG { + ULONG cAliases; + PWSTR* rgpszAliases; + PCRYPT_IMAGE_REG pUM; + PCRYPT_IMAGE_REG pKM; +} +alias PCRYPT_PROVIDER_REG = CRYPT_PROVIDER_REG*; + +struct CRYPT_PROVIDERS { + ULONG cProviders; + PWSTR* rgpszProviders; +} +alias PCRYPT_PROVIDERS = CRYPT_PROVIDERS*; + +struct CRYPT_CONTEXT_CONFIG { + ULONG dwFlags; + ULONG dwReserved; +} +alias PCRYPT_CONTEXT_CONFIG = CRYPT_CONTEXT_CONFIG*; + +struct CRYPT_CONTEXT_FUNCTION_CONFIG { + ULONG dwFlags; + ULONG dwReserved; +} +alias PCRYPT_CONTEXT_FUNCTION_CONFIG = CRYPT_CONTEXT_FUNCTION_CONFIG*; + +struct CRYPT_CONTEXTS { + ULONG cContexts; + PWSTR* rgpszContexts; +} +alias PCRYPT_CONTEXTS = CRYPT_CONTEXTS*; + +struct CRYPT_CONTEXT_FUNCTIONS { + ULONG cFunctions; + PWSTR* rgpszFunctions; +} +alias PCRYPT_CONTEXT_FUNCTIONS = CRYPT_CONTEXT_FUNCTIONS*; + +struct CRYPT_CONTEXT_FUNCTION_PROVIDERS { + ULONG cProviders; + PWSTR* rgpszProviders; +} +alias PCRYPT_CONTEXT_FUNCTION_PROVIDERS = CRYPT_CONTEXT_FUNCTION_PROVIDERS*; + +struct CRYPT_PROPERTY_REF { + PWSTR pszProperty; + ULONG cbValue; + PUCHAR pbValue; +} +alias PCRYPT_PROPERTY_REF = CRYPT_PROPERTY_REF*; + +struct CRYPT_IMAGE_REF { + PWSTR pszImage; + ULONG dwFlags; +} +alias PCRYPT_IMAGE_REF = CRYPT_IMAGE_REF*; + +struct CRYPT_PROVIDER_REF { + ULONG dwInterface; + PWSTR pszFunction; + PWSTR pszProvider; + ULONG cProperties; + PCRYPT_PROPERTY_REF* rgpProperties; + PCRYPT_IMAGE_REF pUM; + PCRYPT_IMAGE_REF pKM; +} +alias PCRYPT_PROVIDER_REF = CRYPT_PROVIDER_REF*; + +struct CRYPT_PROVIDER_REFS { + ULONG cProviders; + PCRYPT_PROVIDER_REF* rgpProviders; +} +alias PCRYPT_PROVIDER_REFS = CRYPT_PROVIDER_REFS*; + +extern(Windows) @nogc nothrow { + NTSTATUS BCryptOpenAlgorithmProvider(scope BCRYPT_ALG_HANDLE* phAlgorithm, scope LPCWSTR pszAlgId, scope LPCWSTR pszImplementation, ULONG dwFlags); + NTSTATUS BCryptEnumAlgorithms(ULONG dwAlgOperations, scope ULONG* pAlgCount, scope BCRYPT_ALGORITHM_IDENTIFIER** ppAlgList, ULONG dwFlags); + NTSTATUS BCryptEnumProviders(scope LPCWSTR pszAlgId, scope ULONG* pImplCount, scope BCRYPT_PROVIDER_NAME** ppImplList, ULONG dwFlags); + NTSTATUS BCryptGetProperty(BCRYPT_HANDLE hObject, scope LPCWSTR pszProperty, scope PUCHAR pbOutput, ULONG cbOutput, scope ULONG* pcbResult, ULONG dwFlags); + NTSTATUS BCryptSetProperty(BCRYPT_HANDLE hObject, scope LPCWSTR pszProperty, scope PUCHAR pbInput, ULONG cbInput, ULONG dwFlags); + NTSTATUS BCryptCloseAlgorithmProvider(BCRYPT_ALG_HANDLE hAlgorithm, ULONG dwFlags); + VOID BCryptFreeBuffer(PVOID pvBuffer); + NTSTATUS BCryptGenerateSymmetricKey(BCRYPT_ALG_HANDLE hAlgorithm, scope BCRYPT_KEY_HANDLE* phKey, scope PUCHAR pbKeyObject, ULONG cbKeyObject, scope PUCHAR pbSecret, ULONG cbSecret, ULONG dwFlags); + NTSTATUS BCryptGenerateKeyPair(BCRYPT_ALG_HANDLE hAlgorithm, scope BCRYPT_KEY_HANDLE* phKey, ULONG dwLength, ULONG dwFlags); + NTSTATUS BCryptEncrypt(BCRYPT_KEY_HANDLE hKey, scope PUCHAR pbInput, ULONG cbInput, scope VOID* pPaddingInfo, scope PUCHAR pbIV, ULONG cbIV, scope PUCHAR pbOutput, ULONG cbOutput, scope ULONG* pcbResult, ULONG dwFlags); + NTSTATUS BCryptDecrypt(BCRYPT_KEY_HANDLE hKey, scope PUCHAR pbInput, ULONG cbInput, scope VOID* pPaddingInfo, scope PUCHAR pbIV, ULONG cbIV, scope PUCHAR pbOutput, ULONG cbOutput, scope ULONG* pcbResult, ULONG dwFlags); + NTSTATUS BCryptExportKey(BCRYPT_KEY_HANDLE hKey, BCRYPT_KEY_HANDLE hExportKey, scope LPCWSTR pszBlobType, scope PUCHAR pbOutput, ULONG cbOutput, scope ULONG* pcbResult, ULONG dwFlags); + NTSTATUS BCryptImportKey(BCRYPT_ALG_HANDLE hAlgorithm, BCRYPT_KEY_HANDLE hImportKey, scope LPCWSTR pszBlobType, scope BCRYPT_KEY_HANDLE* phKey, scope PUCHAR pbKeyObject, ULONG cbKeyObject, scope PUCHAR pbInput, ULONG cbInput, ULONG dwFlags); + NTSTATUS BCryptImportKeyPair(BCRYPT_ALG_HANDLE hAlgorithm, BCRYPT_KEY_HANDLE hImportKey, scope LPCWSTR pszBlobType, scope BCRYPT_KEY_HANDLE* phKey, scope PUCHAR pbInput, ULONG cbInput, ULONG dwFlags); + NTSTATUS BCryptDuplicateKey(BCRYPT_KEY_HANDLE hKey, scope BCRYPT_KEY_HANDLE* phNewKey, scope PUCHAR pbKeyObject, ULONG cbKeyObject, ULONG dwFlags); + NTSTATUS BCryptFinalizeKeyPair(BCRYPT_KEY_HANDLE hKey, ULONG dwFlags); + NTSTATUS BCryptDestroyKey(BCRYPT_KEY_HANDLE hKey); + NTSTATUS BCryptDestroySecret(BCRYPT_SECRET_HANDLE hSecret); + NTSTATUS BCryptSignHash(BCRYPT_KEY_HANDLE hKey, scope VOID* pPaddingInfo, scope PUCHAR pbInput, ULONG cbInput, scope PUCHAR pbOutput, ULONG cbOutput, scope ULONG* pcbResult, ULONG dwFlags); + NTSTATUS BCryptVerifySignature(BCRYPT_KEY_HANDLE hKey, scope VOID* pPaddingInfo, scope PUCHAR pbHash, ULONG cbHash, scope PUCHAR pbSignature, ULONG cbSignature, ULONG dwFlags); + NTSTATUS BCryptSecretAgreement(BCRYPT_KEY_HANDLE hPrivKey, BCRYPT_KEY_HANDLE hPubKey, scope BCRYPT_SECRET_HANDLE* phAgreedSecret, ULONG dwFlags); + NTSTATUS BCryptDeriveKey(BCRYPT_SECRET_HANDLE hSharedSecret, LPCWSTR pwszKDF, scope BCryptBufferDesc* pParameterList, scope PUCHAR pbDerivedKey, ULONG cbDerivedKey, scope ULONG* pcbResult, ULONG dwFlags); + NTSTATUS BCryptKeyDerivation(BCRYPT_KEY_HANDLE hKey, scope BCryptBufferDesc* pParameterList, scope PUCHAR pbDerivedKey, ULONG cbDerivedKey, scope ULONG* pcbResult, ULONG dwFlags); + NTSTATUS BCryptCreateHash(BCRYPT_ALG_HANDLE hAlgorithm, scope BCRYPT_HASH_HANDLE* phHash, scope PUCHAR pbHashObject, ULONG cbHashObject, scope PUCHAR pbSecret, ULONG cbSecret, ULONG dwFlags); + NTSTATUS BCryptHashData(BCRYPT_HASH_HANDLE hHash, scope PUCHAR pbInput, ULONG cbInput, ULONG dwFlags); + NTSTATUS BCryptFinishHash(BCRYPT_HASH_HANDLE hHash, scope PUCHAR pbOutput, ULONG cbOutput, ULONG dwFlags); + static if (NTDDI_VERSION >= NTDDI_WINBLUE) { + NTSTATUS BCryptCreateMultiHash(BCRYPT_ALG_HANDLE hAlgorithm, scope BCRYPT_HASH_HANDLE* phHash, ULONG nHashes, scope PUCHAR pbHashObject, ULONG cbHashObject, scope PUCHAR pbSecret, ULONG cbSecret, ULONG dwFlags); + NTSTATUS BCryptProcessMultiOperations(BCRYPT_HANDLE hObject, BCRYPT_MULTI_OPERATION_TYPE operationType, scope PVOID pOperations, ULONG cbOperations, ULONG dwFlags); + } + NTSTATUS BCryptDuplicateHash(BCRYPT_HASH_HANDLE hHash, scope BCRYPT_HASH_HANDLE* phNewHash, scope PUCHAR pbHashObject, ULONG cbHashObject, ULONG dwFlags); + NTSTATUS BCryptDestroyHash(BCRYPT_HASH_HANDLE hHash); + static if (NTDDI_VERSION >= NTDDI_WINTHRESHOLD) { + NTSTATUS BCryptHash(BCRYPT_ALG_HANDLE hAlgorithm, scope PUCHAR pbSecret, ULONG cbSecret, scope PUCHAR pbInput, ULONG cbInput, scope PUCHAR pbOutput, ULONG cbOutput); + } + NTSTATUS BCryptGenRandom(BCRYPT_ALG_HANDLE hAlgorithm, scope PUCHAR pbBuffer, ULONG cbBuffer, ULONG dwFlags); + NTSTATUS BCryptDeriveKeyCapi(BCRYPT_HASH_HANDLE hHash, BCRYPT_ALG_HANDLE hTargetAlg, scope PUCHAR pbDerivedKey, ULONG cbDerivedKey, ULONG dwFlags); + NTSTATUS BCryptDeriveKeyPBKDF2(BCRYPT_ALG_HANDLE hPrf, scope PUCHAR pbPassword, ULONG cbPassword, scope PUCHAR pbSalt, ULONG cbSalt, ULONGLONG cIterations, scope PUCHAR pbDerivedKey, ULONG cbDerivedKey, ULONG dwFlags); + NTSTATUS BCryptResolveProviders(scope LPCWSTR pszContext, ULONG dwInterface, scope LPCWSTR pszFunction, scope LPCWSTR pszProvider, ULONG dwMode, ULONG dwFlags, scope ULONG* pcbBuffer, scope PCRYPT_PROVIDER_REFS* ppBuffer); + NTSTATUS BCryptGetFipsAlgorithmMode(scope BOOLEAN* pfEnabled); + NTSTATUS BCryptQueryProviderRegistration(scope LPCWSTR pszProvider, ULONG dwMode, ULONG dwInterface, scope ULONG* pcbBuffer, scope PCRYPT_PROVIDER_REG* ppBuffer); + NTSTATUS BCryptEnumRegisteredProviders(scope ULONG* pcbBuffer, scope PCRYPT_PROVIDERS* ppBuffer); + NTSTATUS BCryptCreateContext(ULONG dwTable, scope LPCWSTR pszContext, PCRYPT_CONTEXT_CONFIG pConfig); + NTSTATUS BCryptDeleteContext(ULONG dwTable, scope LPCWSTR pszContext); + NTSTATUS BCryptEnumContexts(ULONG dwTable, scope ULONG* pcbBuffer, scope PCRYPT_CONTEXTS* ppBuffer); + NTSTATUS BCryptConfigureContext(ULONG dwTable, scope LPCWSTR pszContext, PCRYPT_CONTEXT_CONFIG pConfig); + NTSTATUS BCryptQueryContextConfiguration(ULONG dwTable, scope LPCWSTR pszContext, scope ULONG* pcbBuffer, scope PCRYPT_CONTEXT_CONFIG* ppBuffer); + NTSTATUS BCryptAddContextFunction(ULONG dwTable, scope LPCWSTR pszContext, ULONG dwInterface, scope LPCWSTR pszFunction, ULONG dwPosition); + NTSTATUS BCryptRemoveContextFunction(ULONG dwTable, scope LPCWSTR pszContext, ULONG dwInterface, scope LPCWSTR pszFunction); + NTSTATUS BCryptEnumContextFunctions(ULONG dwTable, scope LPCWSTR pszContext, ULONG dwInterface, scope ULONG* pcbBuffer, scope PCRYPT_CONTEXT_FUNCTIONS* ppBuffer); + NTSTATUS BCryptConfigureContextFunction(ULONG dwTable, scope LPCWSTR pszContext, ULONG dwInterface, scope LPCWSTR pszFunction, PCRYPT_CONTEXT_FUNCTION_CONFIG pConfig); + NTSTATUS BCryptQueryContextFunctionConfiguration(ULONG dwTable, scope LPCWSTR pszContext, ULONG dwInterface, scope LPCWSTR pszFunction, scope ULONG* pcbBuffer, scope PCRYPT_CONTEXT_FUNCTION_CONFIG* ppBuffer); + NTSTATUS BCryptEnumContextFunctionProviders(ULONG dwTable, scope LPCWSTR pszContext, ULONG dwInterface, scope LPCWSTR pszFunction, scope ULONG* pcbBuffer, scope PCRYPT_CONTEXT_FUNCTION_PROVIDERS* ppBuffer); + NTSTATUS BCryptSetContextFunctionProperty(ULONG dwTable, scope LPCWSTR pszContext, ULONG dwInterface, scope LPCWSTR pszFunction, LPCWSTR pszProperty, ULONG cbValue, scope PUCHAR pbValue); + NTSTATUS BCryptQueryContextFunctionProperty(ULONG dwTable, scope LPCWSTR pszContext, ULONG dwInterface, scope LPCWSTR pszFunction, LPCWSTR pszProperty, ULONG* pcbValue, scope PUCHAR* ppbValue); + deprecated NTSTATUS BCryptRegisterConfigChangeNotify(scope HANDLE* phEvent); + NTSTATUS BCryptUnregisterConfigChangeNotify(HANDLE hEvent); +} diff --git a/runtime/druntime/src/core/sys/windows/commctrl.d b/runtime/druntime/src/core/sys/windows/commctrl.d index fe4ebbc75d..073587f01c 100644 --- a/runtime/druntime/src/core/sys/windows/commctrl.d +++ b/runtime/druntime/src/core/sys/windows/commctrl.d @@ -5014,7 +5014,7 @@ BOOL Animate_Seek(HWND hwnd, int frame) { return Animate_Play(hwnd, frame, frame, 1); } -extern (Windows) { +extern (Windows) nothrow @nogc { HBITMAP CreateMappedBitmap(HINSTANCE, INT_PTR, UINT, LPCOLORMAP, int); HWND CreateStatusWindowA(LONG, LPCSTR, HWND, UINT); HWND CreateStatusWindowW(LONG, LPCWSTR, HWND, UINT); @@ -5068,7 +5068,7 @@ BOOL DateTime_SetSystemtime(HWND hwnd, WPARAM flag, LPSYSTEMTIME lpSysTime) { cast(LPARAM) lpSysTime); } -extern (Windows) { +extern (Windows) nothrow @nogc { void DrawInsert(HWND, HWND, int); void DrawStatusTextA(HDC, LPRECT, LPCSTR, UINT); void DrawStatusTextW(HDC, LPRECT, LPCWSTR, UINT); @@ -5142,7 +5142,7 @@ static if (_WIN32_IE >= 0x400) { } } -extern (Windows) { +extern (Windows) nothrow @nogc { HDSA DSA_Create(INT, INT); BOOL DSA_Destroy(HDSA); VOID DSA_DestroyCallback(HDSA, PFNDSAENUMCALLBACK, PVOID); @@ -5758,7 +5758,7 @@ BOOL MonthCal_SetRange(HWND w, DWORD f, LPSYSTEMTIME st) { cast(LPARAM) st); } -extern (Windows) BOOL ShowHideMenuCtl(HWND, UINT_PTR, PINT); +extern (Windows) nothrow @nogc BOOL ShowHideMenuCtl(HWND, UINT_PTR, PINT); BOOL TabCtrl_GetItem(HWND w, int i, LPTCITEM p) { return cast(BOOL) SendMessage(w, TCM_GETITEM, i, cast(LPARAM) p); @@ -6069,7 +6069,7 @@ static if (_WIN32_IE >= 0x300) { return cast(BOOL) SendMessage(w, LVM_SETITEMCOUNT, i, cast(LPARAM) f); } - extern (Windows) { + extern (Windows) nothrow @nogc { WINBOOL ImageList_SetImageCount(HIMAGELIST, UINT); WINBOOL ImageList_Copy(HIMAGELIST, int, HIMAGELIST, int, UINT); WINBOOL ImageList_DrawIndirect(IMAGELISTDRAWPARAMS*); diff --git a/runtime/druntime/src/core/sys/windows/custcntl.d b/runtime/druntime/src/core/sys/windows/custcntl.d index f9234ac4f8..ddac960738 100644 --- a/runtime/druntime/src/core/sys/windows/custcntl.d +++ b/runtime/druntime/src/core/sys/windows/custcntl.d @@ -99,6 +99,7 @@ extern (Windows) { alias INT function(DWORD, DWORD, HFONT, LPWSTR) LPFNCCSIZETOTEXTW; alias UINT function(LPCCINFOA) LPFNCCINFOA; alias UINT function(LPCCINFOW) LPFNCCINFOW; +nothrow @nogc: UINT CustomControlInfoA(LPCCINFOA acci); UINT CustomControlInfoW(LPCCINFOW acci); } diff --git a/runtime/druntime/src/core/sys/windows/dbghelp.d b/runtime/druntime/src/core/sys/windows/dbghelp.d index 55fbc56a75..fb6fb66867 100644 --- a/runtime/druntime/src/core/sys/windows/dbghelp.d +++ b/runtime/druntime/src/core/sys/windows/dbghelp.d @@ -6,7 +6,7 @@ * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). * (See accompanying file LICENSE) * Authors: Benjamin Thaut, Sean Kelly - * Source: $(DRUNTIMESRC core/sys/windows/_stacktrace.d) + * Source: $(DRUNTIMESRC core/sys/windows/_dbghelp.d) */ module core.sys.windows.dbghelp; diff --git a/runtime/druntime/src/core/sys/windows/dde.d b/runtime/druntime/src/core/sys/windows/dde.d index d9b8bec515..975c62e42f 100644 --- a/runtime/druntime/src/core/sys/windows/dde.d +++ b/runtime/druntime/src/core/sys/windows/dde.d @@ -148,7 +148,7 @@ deprecated struct DDEUP { @property bool fAckReq(bool f) { _bf = cast(ushort) ((_bf & ~0x8000) | (f << 15)); return f; } } -extern (Windows) { +extern (Windows) nothrow @nogc { BOOL DdeSetQualityOfService(HWND, const(SECURITY_QUALITY_OF_SERVICE)*, PSECURITY_QUALITY_OF_SERVICE); BOOL ImpersonateDdeClientWindow(HWND, HWND); diff --git a/runtime/druntime/src/core/sys/windows/ddeml.d b/runtime/druntime/src/core/sys/windows/ddeml.d index 2e8e3ee25d..0ff1a3f4f1 100644 --- a/runtime/druntime/src/core/sys/windows/ddeml.d +++ b/runtime/druntime/src/core/sys/windows/ddeml.d @@ -332,7 +332,7 @@ struct MONMSGSTRUCT { } alias MONMSGSTRUCT* PMONMSGSTRUCT; -extern (Windows) { +extern (Windows) nothrow @nogc { BOOL DdeAbandonTransaction(DWORD, HCONV, DWORD); PBYTE DdeAccessData(HDDEDATA, PDWORD); HDDEDATA DdeAddData(HDDEDATA, PBYTE, DWORD, DWORD); diff --git a/runtime/druntime/src/core/sys/windows/dhcpcsdk.d b/runtime/druntime/src/core/sys/windows/dhcpcsdk.d index d21169a734..6171469c8c 100644 --- a/runtime/druntime/src/core/sys/windows/dhcpcsdk.d +++ b/runtime/druntime/src/core/sys/windows/dhcpcsdk.d @@ -45,7 +45,7 @@ struct DHCPCAPI_PARAMS_ARRAY { } alias DHCPCAPI_PARAMS_ARRAY* PDHCPCAPI_PARAMS_ARRAY, LPDHCPCAPI_PARAMS_ARRAY; -extern (Windows) { +extern (Windows) nothrow @nogc { void DhcpCApiCleanup(); DWORD DhcpCApiInitialize(LPDWORD); DWORD DhcpDeRegisterParamChange(DWORD, LPVOID, LPVOID); diff --git a/runtime/druntime/src/core/sys/windows/dll.d b/runtime/druntime/src/core/sys/windows/dll.d index 69c038d78e..07eadc7e28 100644 --- a/runtime/druntime/src/core/sys/windows/dll.d +++ b/runtime/druntime/src/core/sys/windows/dll.d @@ -114,19 +114,19 @@ version (Win32) // _NtdllBaseTag - tag used for RtlAllocateHeap // _LdrpTlsList - root of the double linked list with TlsList entries - static __gshared int* pNtdllBaseTag; // remembered for reusage in addTlsData - - static __gshared ubyte[] jmp_LdrpInitialize = [ 0x33, 0xED, 0xE9 ]; // xor ebp,ebp; jmp _LdrpInitialize - static __gshared ubyte[] jmp__LdrpInitialize = [ 0x5D, 0xE9 ]; // pop ebp; jmp __LdrpInitialize - static __gshared ubyte[] jmp__LdrpInitialize_xp64 = [ 0x5D, 0x90, 0x90, 0x90, 0x90, 0x90 ]; // pop ebp; nop; nop; nop; nop; nop; - static __gshared ubyte[] call_LdrpInitializeThread = [ 0xFF, 0x75, 0x08, 0xE8 ]; // push [ebp+8]; call _LdrpInitializeThread - static __gshared ubyte[] call_LdrpAllocateTls = [ 0x00, 0x00, 0xE8 ]; // jne 0xc3; call _LdrpAllocateTls - static __gshared ubyte[] call_LdrpAllocateTls_svr03 = [ 0x65, 0xfc, 0x00, 0xE8 ]; // and [ebp+fc], 0; call _LdrpAllocateTls - static __gshared ubyte[] jne_LdrpAllocateTls = [ 0x0f, 0x85 ]; // jne body_LdrpAllocateTls - static __gshared ubyte[] mov_LdrpNumberOfTlsEntries = [ 0x8B, 0x0D ]; // mov ecx, _LdrpNumberOfTlsEntries - static __gshared ubyte[] mov_NtdllBaseTag = [ 0x51, 0x8B, 0x0D ]; // push ecx; mov ecx, _NtdllBaseTag - static __gshared ubyte[] mov_NtdllBaseTag_srv03 = [ 0x50, 0xA1 ]; // push eax; mov eax, _NtdllBaseTag - static __gshared ubyte[] mov_LdrpTlsList = [ 0x8B, 0x3D ]; // mov edi, _LdrpTlsList + __gshared int* pNtdllBaseTag; // remembered for reusage in addTlsData + + __gshared ubyte[] jmp_LdrpInitialize = [ 0x33, 0xED, 0xE9 ]; // xor ebp,ebp; jmp _LdrpInitialize + __gshared ubyte[] jmp__LdrpInitialize = [ 0x5D, 0xE9 ]; // pop ebp; jmp __LdrpInitialize + __gshared ubyte[] jmp__LdrpInitialize_xp64 = [ 0x5D, 0x90, 0x90, 0x90, 0x90, 0x90 ]; // pop ebp; nop; nop; nop; nop; nop; + __gshared ubyte[] call_LdrpInitializeThread = [ 0xFF, 0x75, 0x08, 0xE8 ]; // push [ebp+8]; call _LdrpInitializeThread + __gshared ubyte[] call_LdrpAllocateTls = [ 0x00, 0x00, 0xE8 ]; // jne 0xc3; call _LdrpAllocateTls + __gshared ubyte[] call_LdrpAllocateTls_svr03 = [ 0x65, 0xfc, 0x00, 0xE8 ]; // and [ebp+fc], 0; call _LdrpAllocateTls + __gshared ubyte[] jne_LdrpAllocateTls = [ 0x0f, 0x85 ]; // jne body_LdrpAllocateTls + __gshared ubyte[] mov_LdrpNumberOfTlsEntries = [ 0x8B, 0x0D ]; // mov ecx, _LdrpNumberOfTlsEntries + __gshared ubyte[] mov_NtdllBaseTag = [ 0x51, 0x8B, 0x0D ]; // push ecx; mov ecx, _NtdllBaseTag + __gshared ubyte[] mov_NtdllBaseTag_srv03 = [ 0x50, 0xA1 ]; // push eax; mov eax, _NtdllBaseTag + __gshared ubyte[] mov_LdrpTlsList = [ 0x8B, 0x3D ]; // mov edi, _LdrpTlsList static LdrpTlsListEntry* addTlsListEntry( void** peb, void* tlsstart, void* tlsend, void* tls_callbacks_a, int* tlsindex ) nothrow { @@ -402,7 +402,7 @@ private bool isWindows8OrLater() nothrow @nogc int dll_getRefCount( HINSTANCE hInstance ) nothrow @nogc { void** peb; - version (X86_64) + version (D_InlineAsm_X86_64) { asm pure nothrow @nogc { @@ -411,7 +411,7 @@ int dll_getRefCount( HINSTANCE hInstance ) nothrow @nogc mov peb, RAX; } } - else version (X86) + else version (D_InlineAsm_X86) { asm pure nothrow @nogc { @@ -421,7 +421,14 @@ int dll_getRefCount( HINSTANCE hInstance ) nothrow @nogc } else version (AArch64) { - asm nothrow @nogc { "ldr %0, [x18,%1]" : "=r" (peb) : "r" (0x30); } + asm pure nothrow @nogc { "ldr %0, [x18,%1]" : "=r" (peb) : "r" (0x30); } + } + else version (GNU_InlineAsm) + { + version (X86_64) + asm pure nothrow @nogc { "movq %%gs:0x60, %0;" : "=r" (peb); } + else version (X86) + asm pure nothrow @nogc { "movl %%fs:0x30, %0;" : "=r" (peb); } } dll_aux.LDR_MODULE *ldrMod = dll_aux.findLdrModule( hInstance, peb ); if ( !ldrMod ) diff --git a/runtime/druntime/src/core/sys/windows/errorrep.d b/runtime/druntime/src/core/sys/windows/errorrep.d index 42fad9a39e..840ea61f3c 100644 --- a/runtime/druntime/src/core/sys/windows/errorrep.d +++ b/runtime/druntime/src/core/sys/windows/errorrep.d @@ -29,7 +29,7 @@ enum EFaultRepRetVal { frrvOkHeadless // = 7 } -extern (Windows) { +extern (Windows) nothrow @nogc { BOOL AddERExcludedApplicationA(LPCSTR); BOOL AddERExcludedApplicationW(LPCWSTR); EFaultRepRetVal ReportFault(LPEXCEPTION_POINTERS, DWORD); diff --git a/runtime/druntime/src/core/sys/windows/httpext.d b/runtime/druntime/src/core/sys/windows/httpext.d index 200b2843c4..b7f6aedcbd 100644 --- a/runtime/druntime/src/core/sys/windows/httpext.d +++ b/runtime/druntime/src/core/sys/windows/httpext.d @@ -109,7 +109,7 @@ struct HSE_SEND_HEADER_EX_INFO { } alias HSE_SEND_HEADER_EX_INFO* LPHSE_SEND_HEADER_EX_INF; -extern (Windows) { +extern (Windows) nothrow @nogc { BOOL GetExtensionVersion(HSE_VERSION_INFO*); DWORD HttpExtensionProc(EXTENSION_CONTROL_BLOCK*); BOOL TerminateExtension(DWORD); diff --git a/runtime/druntime/src/core/sys/windows/imagehlp.d b/runtime/druntime/src/core/sys/windows/imagehlp.d index 68d88d7878..c6d28e68fd 100644 --- a/runtime/druntime/src/core/sys/windows/imagehlp.d +++ b/runtime/druntime/src/core/sys/windows/imagehlp.d @@ -295,6 +295,7 @@ extern (Windows) { alias BOOL function(DIGEST_HANDLE refdata, PBYTE pData, DWORD dwLength) DIGEST_FUNCTION; +nothrow @nogc: PIMAGE_NT_HEADERS CheckSumMappedFile(LPVOID, DWORD, LPDWORD, LPDWORD); DWORD MapFileAndCheckSumA(LPSTR, LPDWORD, LPDWORD); DWORD MapFileAndCheckSumW(PWSTR, LPDWORD, LPDWORD); diff --git a/runtime/druntime/src/core/sys/windows/intshcut.d b/runtime/druntime/src/core/sys/windows/intshcut.d index ab662e418e..cc4bf62354 100644 --- a/runtime/druntime/src/core/sys/windows/intshcut.d +++ b/runtime/druntime/src/core/sys/windows/intshcut.d @@ -70,7 +70,7 @@ interface IUniformResourceLocator : IUnknown { alias IUniformResourceLocator PIUniformResourceLocator, PCIUniformResourceLocator; -extern (Windows) { +extern (Windows) nothrow @nogc { BOOL InetIsOffline(DWORD); HRESULT MIMEAssociationDialogA(HWND, DWORD, PCSTR, PCSTR, PSTR, UINT); HRESULT MIMEAssociationDialogW(HWND, DWORD, PCWSTR, PCWSTR, PWSTR, UINT); diff --git a/runtime/druntime/src/core/sys/windows/iphlpapi.d b/runtime/druntime/src/core/sys/windows/iphlpapi.d index 4a8e64cbef..d8f0948ca3 100644 --- a/runtime/druntime/src/core/sys/windows/iphlpapi.d +++ b/runtime/druntime/src/core/sys/windows/iphlpapi.d @@ -13,7 +13,7 @@ version (Windows): import core.sys.windows.ipexport, core.sys.windows.iprtrmib, core.sys.windows.iptypes; import core.sys.windows.winbase, core.sys.windows.windef; -extern (Windows) { +extern (Windows) nothrow @nogc { DWORD AddIPAddress(IPAddr, IPMask, DWORD, PULONG, PULONG); DWORD CreateIpForwardEntry(PMIB_IPFORWARDROW); DWORD CreateIpNetEntry(PMIB_IPNETROW); diff --git a/runtime/druntime/src/core/sys/windows/lmaccess.d b/runtime/druntime/src/core/sys/windows/lmaccess.d index 9791ff6e39..cf6f9d4c4d 100644 --- a/runtime/druntime/src/core/sys/windows/lmaccess.d +++ b/runtime/druntime/src/core/sys/windows/lmaccess.d @@ -708,7 +708,7 @@ struct NETLOGON_INFO_3{ } alias NETLOGON_INFO_3* PNETLOGON_INFO_3; -extern (Windows) { +extern (Windows) nothrow @nogc { deprecated { /* These are obsolete */ NET_API_STATUS NetAccessAdd(LPCWSTR,DWORD,PBYTE,PDWORD); diff --git a/runtime/druntime/src/core/sys/windows/lmalert.d b/runtime/druntime/src/core/sys/windows/lmalert.d index ad0c3ca7b1..acb96cfbc9 100644 --- a/runtime/druntime/src/core/sys/windows/lmalert.d +++ b/runtime/druntime/src/core/sys/windows/lmalert.d @@ -71,7 +71,7 @@ struct USER_OTHER_INFO{ } alias USER_OTHER_INFO* PUSER_OTHER_INFO, LPUSER_OTHER_INFO; -extern (Windows) { +extern (Windows) nothrow @nogc { NET_API_STATUS NetAlertRaise(LPCWSTR,PVOID,DWORD); NET_API_STATUS NetAlertRaiseEx(LPCWSTR,PVOID,DWORD,LPCWSTR); } diff --git a/runtime/druntime/src/core/sys/windows/lmapibuf.d b/runtime/druntime/src/core/sys/windows/lmapibuf.d index e8559543f3..85ccd1f49f 100644 --- a/runtime/druntime/src/core/sys/windows/lmapibuf.d +++ b/runtime/druntime/src/core/sys/windows/lmapibuf.d @@ -12,7 +12,7 @@ pragma(lib, "netapi32"); import core.sys.windows.lmcons, core.sys.windows.windef; -extern (Windows) { +extern (Windows) nothrow @nogc { NET_API_STATUS NetApiBufferAllocate(DWORD, PVOID*); NET_API_STATUS NetApiBufferFree(PVOID); NET_API_STATUS NetApiBufferReallocate(PVOID, DWORD, PVOID*); diff --git a/runtime/druntime/src/core/sys/windows/lmmsg.d b/runtime/druntime/src/core/sys/windows/lmmsg.d index a3abd60542..eab8788274 100644 --- a/runtime/druntime/src/core/sys/windows/lmmsg.d +++ b/runtime/druntime/src/core/sys/windows/lmmsg.d @@ -32,7 +32,7 @@ struct MSG_INFO_1 { } alias MSG_INFO_1* PMSG_INFO_1, LPMSG_INFO_1; -extern (Windows) { +extern (Windows) nothrow @nogc { NET_API_STATUS NetMessageBufferSend(LPCWSTR, LPCWSTR, LPCWSTR, PBYTE, DWORD); NET_API_STATUS NetMessageNameAdd(LPCWSTR, LPCWSTR); diff --git a/runtime/druntime/src/core/sys/windows/lmremutl.d b/runtime/druntime/src/core/sys/windows/lmremutl.d index 8c90df73f2..196be667fe 100644 --- a/runtime/druntime/src/core/sys/windows/lmremutl.d +++ b/runtime/druntime/src/core/sys/windows/lmremutl.d @@ -52,7 +52,7 @@ struct TIME_OF_DAY_INFO { } alias TIME_OF_DAY_INFO* PTIME_OF_DAY_INFO, LPTIME_OF_DAY_INFO; -extern (Windows) { +extern (Windows) nothrow @nogc { NET_API_STATUS NetRemoteTOD(LPCWSTR, PBYTE*); NET_API_STATUS NetRemoteComputerSupports(LPCWSTR, DWORD, PDWORD); NET_API_STATUS RxRemoteApi(DWORD, LPCWSTR, LPDESC, LPDESC, LPDESC, diff --git a/runtime/druntime/src/core/sys/windows/mgmtapi.d b/runtime/druntime/src/core/sys/windows/mgmtapi.d index 8f84eea54e..336d24e750 100644 --- a/runtime/druntime/src/core/sys/windows/mgmtapi.d +++ b/runtime/druntime/src/core/sys/windows/mgmtapi.d @@ -29,7 +29,7 @@ enum MGMCTL_SETAGENTPORT = 1; alias PVOID LPSNMP_MGR_SESSION; -extern (Windows) { +extern (Windows) nothrow @nogc { BOOL SnmpMgrClose(LPSNMP_MGR_SESSION); BOOL SnmpMgrCtl(LPSNMP_MGR_SESSION, DWORD, LPVOID, DWORD, LPVOID, DWORD, LPDWORD); diff --git a/runtime/druntime/src/core/sys/windows/ntdef.d b/runtime/druntime/src/core/sys/windows/ntdef.d index a0213dc56f..160443312b 100644 --- a/runtime/druntime/src/core/sys/windows/ntdef.d +++ b/runtime/druntime/src/core/sys/windows/ntdef.d @@ -33,7 +33,12 @@ void InitializeObjectAttributes(OBJECT_ATTRIBUTES* p, UNICODE_STRING* n, } } -bool NT_SUCCESS(int x) { return x >= 0; } +pragma(inline, true) @safe pure nothrow @nogc { + bool NT_SUCCESS(NTSTATUS Status) { return Status >= 0; } + bool NT_INFORMATION(NTSTATUS Status) { return ((cast(ULONG) Status) >> 30) == 1; } + bool NT_WARNING(NTSTATUS Status) { return ((cast(ULONG) Status) >> 30) == 2; } + bool NT_ERROR(NTSTATUS Status) { return ((cast(ULONG) Status) >> 30) == 3; } +} /* In MinGW, NTSTATUS, UNICODE_STRING, STRING and their associated pointer * type aliases are defined in ntdef.h, ntsecapi.h and subauth.h, each of diff --git a/runtime/druntime/src/core/sys/windows/ntdll.d b/runtime/druntime/src/core/sys/windows/ntdll.d index 28d560c537..7f0c47a15f 100644 --- a/runtime/druntime/src/core/sys/windows/ntdll.d +++ b/runtime/druntime/src/core/sys/windows/ntdll.d @@ -19,4 +19,4 @@ enum SHUTDOWN_ACTION { ShutdownPowerOff } -extern (Windows) uint NtShutdownSystem(SHUTDOWN_ACTION Action); +extern (Windows) nothrow @nogc uint NtShutdownSystem(SHUTDOWN_ACTION Action); diff --git a/runtime/druntime/src/core/sys/windows/ntsecapi.d b/runtime/druntime/src/core/sys/windows/ntsecapi.d index fa08a74ee5..77101f669b 100644 --- a/runtime/druntime/src/core/sys/windows/ntsecapi.d +++ b/runtime/druntime/src/core/sys/windows/ntsecapi.d @@ -726,7 +726,7 @@ struct TRUSTED_DOMAIN_FULL_INFORMATION { } alias TRUSTED_DOMAIN_FULL_INFORMATION* PTRUSTED_DOMAIN_FULL_INFORMATION; -extern (Windows) { +extern (Windows) nothrow @nogc { NTSTATUS LsaAddAccountRights(LSA_HANDLE, PSID, PLSA_UNICODE_STRING, ULONG); NTSTATUS LsaCallAuthenticationPackage(HANDLE, ULONG, PVOID, ULONG, diff --git a/runtime/druntime/src/core/sys/windows/ole.d b/runtime/druntime/src/core/sys/windows/ole.d index b9e55a3b12..7c51d7a1be 100644 --- a/runtime/druntime/src/core/sys/windows/ole.d +++ b/runtime/druntime/src/core/sys/windows/ole.d @@ -283,7 +283,7 @@ struct OLESERVERDOC { } alias OLESERVERDOC* LPOLESERVERDOC; -extern (Windows) { +extern (Windows) nothrow @nogc { OLESTATUS OleDelete(LPOLEOBJECT); OLESTATUS OleRelease(LPOLEOBJECT); OLESTATUS OleSaveToStream(LPOLEOBJECT, LPOLESTREAM); diff --git a/runtime/druntime/src/core/sys/windows/ole2.d b/runtime/druntime/src/core/sys/windows/ole2.d index 3fef058411..fcbbc8a3d2 100644 --- a/runtime/druntime/src/core/sys/windows/ole2.d +++ b/runtime/druntime/src/core/sys/windows/ole2.d @@ -48,7 +48,7 @@ extern (Windows) { } alias OLESTREAMVTBL* LPOLESTREAMVTBL; -extern (Windows) { +extern (Windows) nothrow @nogc { HRESULT CreateDataAdviseHolder(LPDATAADVISEHOLDER*); DWORD OleBuildVersion(); HRESULT ReadClassStg(LPSTORAGE, CLSID*); diff --git a/runtime/druntime/src/core/sys/windows/oleacc.d b/runtime/druntime/src/core/sys/windows/oleacc.d index b19855d9af..8a97a93e34 100644 --- a/runtime/druntime/src/core/sys/windows/oleacc.d +++ b/runtime/druntime/src/core/sys/windows/oleacc.d @@ -186,7 +186,7 @@ interface IAccessible : IDispatch { alias IAccessible LPACCESSIBLE; -extern (Windows) { +extern (Windows) nothrow @nogc { HRESULT AccessibleChildren(IAccessible, LONG, LONG, VARIANT*, LONG*); HRESULT AccessibleObjectFromEvent(HWND, DWORD, DWORD, IAccessible, VARIANT*); HRESULT AccessibleObjectFromPoint(POINT, IAccessible*, VARIANT*); diff --git a/runtime/druntime/src/core/sys/windows/oleauto.d b/runtime/druntime/src/core/sys/windows/oleauto.d index 188813609f..f4bb40259a 100644 --- a/runtime/druntime/src/core/sys/windows/oleauto.d +++ b/runtime/druntime/src/core/sys/windows/oleauto.d @@ -226,7 +226,7 @@ deprecated { // not actually deprecated, but they aren't converted yet. alias ICreateTypeLib2 LPCREATETYPELIB2; } -extern (Windows) { +extern (Windows) nothrow @nogc { BSTR SysAllocString(const(OLECHAR)*); int SysReAllocString(BSTR*, const(OLECHAR)*); BSTR SysAllocStringLen(const(OLECHAR)*, uint); diff --git a/runtime/druntime/src/core/sys/windows/oledlg.d b/runtime/druntime/src/core/sys/windows/oledlg.d index f810f6c13d..26e0a79160 100644 --- a/runtime/druntime/src/core/sys/windows/oledlg.d +++ b/runtime/druntime/src/core/sys/windows/oledlg.d @@ -387,7 +387,7 @@ struct OLEUIINSERTOBJECTA { } alias OLEUIINSERTOBJECTA* POLEUIINSERTOBJECTA, LPOLEUIINSERTOBJECTA; -extern (Windows) { +extern (Windows) nothrow @nogc { UINT OleUIInsertObjectW(LPOLEUIINSERTOBJECTW); UINT OleUIInsertObjectA(LPOLEUIINSERTOBJECTA); } @@ -849,7 +849,7 @@ struct OLEUIOBJECTPROPSA { } alias OLEUIOBJECTPROPSA* POLEUIOBJECTPROPSA, LPOLEUIOBJECTPROPSA; -extern (Windows) { +extern (Windows) nothrow @nogc { BOOL OleUIAddVerbMenuW(LPOLEOBJECT, LPCWSTR, HMENU, UINT, UINT, UINT, BOOL, UINT, HMENU*); BOOL OleUIAddVerbMenuA(LPOLEOBJECT, LPCSTR, HMENU, UINT, UINT, UINT, BOOL, UINT, HMENU*); UINT OleUIBusyW(LPOLEUIBUSYW); diff --git a/runtime/druntime/src/core/sys/windows/prsht.d b/runtime/druntime/src/core/sys/windows/prsht.d index 053b1fa2ac..570d1689b3 100644 --- a/runtime/druntime/src/core/sys/windows/prsht.d +++ b/runtime/druntime/src/core/sys/windows/prsht.d @@ -331,7 +331,7 @@ struct PSHNOTIFY { } alias PSHNOTIFY* LPPSHNOTIFY; -extern (Windows) { +extern (Windows) nothrow @nogc { HPROPSHEETPAGE CreatePropertySheetPageA(LPCPROPSHEETPAGEA); HPROPSHEETPAGE CreatePropertySheetPageW(LPCPROPSHEETPAGEW); BOOL DestroyPropertySheetPage(HPROPSHEETPAGE); diff --git a/runtime/druntime/src/core/sys/windows/psapi.d b/runtime/druntime/src/core/sys/windows/psapi.d index 968ce6c453..42fc5fdd6b 100644 --- a/runtime/druntime/src/core/sys/windows/psapi.d +++ b/runtime/druntime/src/core/sys/windows/psapi.d @@ -90,7 +90,7 @@ alias BOOL function(LPVOID, PENUM_PAGE_FILE_INFORMATION, LPCSTR) // Grouped by application, not in alphabetical order. -extern (Windows) { +extern (Windows) nothrow @nogc { /* Process Information * http://windowssdk.msdn.microsoft.com/library/ms684870.aspx */ BOOL EnumProcesses(DWORD*, DWORD, DWORD*); /* NT/2000/XP/Server2003/Vista/Longhorn */ diff --git a/runtime/druntime/src/core/sys/windows/rpc.d b/runtime/druntime/src/core/sys/windows/rpc.d index b432bc7c68..3b7e28d213 100644 --- a/runtime/druntime/src/core/sys/windows/rpc.d +++ b/runtime/druntime/src/core/sys/windows/rpc.d @@ -26,6 +26,6 @@ public import core.sys.windows.winerror; alias MIDL_user_allocate midl_user_allocate; alias MIDL_user_free midl_user_free; -extern (Windows) { +extern (Windows) nothrow @nogc { int I_RpcMapWin32Status(RPC_STATUS); } diff --git a/runtime/druntime/src/core/sys/windows/sdkddkver.d b/runtime/druntime/src/core/sys/windows/sdkddkver.d index 3f5c01be14..bdaaa81d6d 100644 --- a/runtime/druntime/src/core/sys/windows/sdkddkver.d +++ b/runtime/druntime/src/core/sys/windows/sdkddkver.d @@ -7,9 +7,10 @@ * Source: $(DRUNTIMESRC core/sys/windows/sdkddkver.d) */ module core.sys.windows.sdkddkver; - version (Windows): +import core.sys.windows.w32api; + enum _WIN32_WINNT_NT4 = 0x0400; enum _WIN32_WINNT_WIN2K = 0x0500; enum _WIN32_WINNT_WINXP = 0x0501; @@ -21,6 +22,7 @@ enum _WIN32_WINNT_LONGHORN = 0x0600; enum _WIN32_WINNT_WIN7 = 0x0601; enum _WIN32_WINNT_WIN8 = 0x0602; enum _WIN32_WINNT_WINBLUE = 0x0603; +enum _WIN32_WINNT_WINTHRESHOLD = 0x0A00; enum _WIN32_WINNT_WIN10 = 0x0A00; enum _WIN32_IE_IE20 = 0x0200; @@ -38,6 +40,7 @@ enum _WIN32_IE_IE70 = 0x0700; enum _WIN32_IE_IE80 = 0x0800; enum _WIN32_IE_IE90 = 0x0900; enum _WIN32_IE_IE100 = 0x0A00; +enum _WIN32_IE_IE110 = 0x0A00; enum _WIN32_IE_NT4 = _WIN32_IE_IE20; enum _WIN32_IE_NT4SP1 = _WIN32_IE_IE20; @@ -64,6 +67,8 @@ enum _WIN32_IE_LONGHORN = _WIN32_IE_IE70; enum _WIN32_IE_WIN7 = _WIN32_IE_IE80; enum _WIN32_IE_WIN8 = _WIN32_IE_IE100; enum _WIN32_IE_WINBLUE = _WIN32_IE_IE100; +enum _WIN32_IE_WINTHRESHOLD = _WIN32_IE_IE110; +enum _WIN32_IE_WIN10 = _WIN32_IE_IE110; enum NTDDI_WIN2K = 0x05000000; @@ -106,12 +111,45 @@ enum NTDDI_WS08SP4 = NTDDI_WIN6SP4; enum NTDDI_WIN7 = 0x06010000; enum NTDDI_WIN8 = 0x06020000; enum NTDDI_WINBLUE = 0x06030000; - -enum OSVERSION_MASK = 0xFFFF0000; +enum NTDDI_WINTHRESHOLD = 0x0A000000; +enum NTDDI_WIN10 = 0x0A000000; +enum NTDDI_WIN10_TH2 = 0x0A000001; +enum NTDDI_WIN10_RS1 = 0x0A000002; +enum NTDDI_WIN10_RS2 = 0x0A000003; +enum NTDDI_WIN10_RS3 = 0x0A000004; +enum NTDDI_WIN10_RS4 = 0x0A000005; +enum NTDDI_WIN10_RS5 = 0x0A000006; +enum NTDDI_WIN10_19H1 = 0x0A000007; +enum NTDDI_WIN10_VB = 0x0A000008; +enum NTDDI_WIN10_MN = 0x0A000009; +enum NTDDI_WIN10_FE = 0x0A00000A; +enum NTDDI_WIN10_CO = 0x0A00000B; +enum NTDDI_WIN10_NI = 0x0A00000C; +enum NTDDI_WIN10_CU = 0x0A00000D; +enum NTDDI_WIN11_ZN = 0x0A00000E; +enum NTDDI_WIN11_GA = 0x0A00000F; +enum NTDDI_WIN11_GE = 0x0A000010; + +enum WDK_NTDDI_VERSION = NTDDI_WIN11_GE; + +enum OSVERSION_MASK = 0xFFFF0000U; enum SPVERSION_MASK = 0x0000FF00; enum SUBVERSION_MASK = 0x000000FF; -enum _WIN32_WINNT = 0x0603; +pragma(inline, true) @nogc nothrow pure @safe { + uint OSVER(uint Version) => Version & OSVERSION_MASK; + uint SPVER(uint Version) => (Version & SPVERSION_MASK) >> 8; + uint SUBVER(uint Version) => Version & SUBVERSION_MASK; + + uint NTDDI_VERSION_FROM_WIN32_WINNT2(uint Version) => Version * 0x10000; + alias NTDDI_VERSION_FROM_WIN32_WINNT = NTDDI_VERSION_FROM_WIN32_WINNT2; +} + + +static if (_WIN32_WINNT < _WIN32_WINNT_WIN10) { + enum NTDDI_VERSION = NTDDI_VERSION_FROM_WIN32_WINNT(_WIN32_WINNT); +} else { + enum NTDDI_VERSION = WDK_NTDDI_VERSION; +} -enum NTDDI_VERSION = 0x06030000; -enum WINVER = _WIN32_WINNT; +enum WINVER = _WIN32_WINNT; diff --git a/runtime/druntime/src/core/sys/windows/sql.d b/runtime/druntime/src/core/sys/windows/sql.d index 9054ce589e..013d9f06e6 100644 --- a/runtime/druntime/src/core/sys/windows/sql.d +++ b/runtime/druntime/src/core/sys/windows/sql.d @@ -11,13 +11,15 @@ $(RED Warning: */ module core.sys.windows.sql; + +enum ODBCVER = 0x0400; + +deprecated ("The ODBC 3.5 modules are deprecated. Please use the ODBC4 modules in the `etc.c.odbc` package."): version (Windows): public import core.sys.windows.sqltypes; import core.sys.windows.windef; -enum ODBCVER = 0x0351; - enum SQL_ACCESSIBLE_PROCEDURES=20; enum SQL_ACCESSIBLE_TABLES=19; enum SQL_ALL_TYPES=0; diff --git a/runtime/druntime/src/core/sys/windows/sqlext.d b/runtime/druntime/src/core/sys/windows/sqlext.d index b871fbbbb8..a53918394d 100644 --- a/runtime/druntime/src/core/sys/windows/sqlext.d +++ b/runtime/druntime/src/core/sys/windows/sqlext.d @@ -11,6 +11,7 @@ $(RED Warning: */ module core.sys.windows.sqlext; +deprecated ("The ODBC 3.5 modules are deprecated. Please use the ODBC4 modules in the `etc.c.odbc` package."): version (Windows): /* Conversion notes: diff --git a/runtime/druntime/src/core/sys/windows/sqltypes.d b/runtime/druntime/src/core/sys/windows/sqltypes.d index 04fac6bac6..ca91f01585 100644 --- a/runtime/druntime/src/core/sys/windows/sqltypes.d +++ b/runtime/druntime/src/core/sys/windows/sqltypes.d @@ -70,6 +70,10 @@ alias long ODBCINT64, SQLBIGINT; alias ulong SQLUBIGINT; //} +//Everything above this line may by used by odbcinst.d +//Everything below this line is deprecated +deprecated ("The ODBC 3.5 modules are deprecated. Please use the ODBC4 modules in the `etc.c.odbc` package."): + struct DATE_STRUCT { SQLSMALLINT year; SQLUSMALLINT month; diff --git a/runtime/druntime/src/core/sys/windows/sqlucode.d b/runtime/druntime/src/core/sys/windows/sqlucode.d index e7ae1d276b..d20952d5b4 100644 --- a/runtime/druntime/src/core/sys/windows/sqlucode.d +++ b/runtime/druntime/src/core/sys/windows/sqlucode.d @@ -11,6 +11,7 @@ $(RED Warning: */ module core.sys.windows.sqlucode; +deprecated ("The ODBC 3.5 modules are deprecated. Please use the ODBC4 modules in the `etc.c.odbc` package."): version (Windows): version (ANSI) {} else version = Unicode; diff --git a/runtime/druntime/src/core/sys/windows/stdc/time.d b/runtime/druntime/src/core/sys/windows/stdc/time.d index 97eb4bf1e1..407b92c20a 100644 --- a/runtime/druntime/src/core/sys/windows/stdc/time.d +++ b/runtime/druntime/src/core/sys/windows/stdc/time.d @@ -9,7 +9,7 @@ * (See accompanying file LICENSE) * Authors: Sean Kelly, * Alex Rønne Petersen - * Source: $(DRUNTIMESRC core/stdc/_time.d) + * Source: $(DRUNTIMESRC core/sys/windows/stdc/_time.d) * Standards: ISO/IEC 9899:1999 (E) */ diff --git a/runtime/druntime/src/core/sys/windows/threadaux.d b/runtime/druntime/src/core/sys/windows/threadaux.d index bf537478c2..5467f6a850 100644 --- a/runtime/druntime/src/core/sys/windows/threadaux.d +++ b/runtime/druntime/src/core/sys/windows/threadaux.d @@ -177,7 +177,7 @@ struct thread_aux { static void** getTEB() nothrow @nogc { - version (Win32) + version (D_InlineAsm_X86) { asm pure nothrow @nogc { @@ -186,7 +186,7 @@ struct thread_aux ret; } } - else version (Win64) + else version (D_InlineAsm_X86_64) { asm pure nothrow @nogc { @@ -196,6 +196,17 @@ struct thread_aux ret; } } + else version (GNU_InlineAsm) + { + void** teb; + version (X86) + asm pure nothrow @nogc { "movl %%fs:0x18, %0;" : "=r" (teb); } + else version (X86_64) + asm pure nothrow @nogc { "movq %%gs:0x30, %0;" : "=r" (teb); } + else + static assert(false); + return teb; + } else { static assert(false); diff --git a/runtime/druntime/src/core/sys/windows/w32api.d b/runtime/druntime/src/core/sys/windows/w32api.d index 5a8a59bad4..bf84195380 100644 --- a/runtime/druntime/src/core/sys/windows/w32api.d +++ b/runtime/druntime/src/core/sys/windows/w32api.d @@ -10,12 +10,45 @@ module core.sys.windows.w32api; version (Windows): +import core.sys.windows.sdkddkver; + version (ANSI) {} else version = Unicode; enum __W32API_VERSION = 3.17; enum __W32API_MAJOR_VERSION = 3; enum __W32API_MINOR_VERSION = 17; +enum Windows95 = 0x0400; +enum Windows98 = 0x0410; +enum WindowsME = 0x0500; + +enum WindowsNT4 = 0x0400; +enum Windows2000 = 0x0500; +enum WindowsXP = 0x0501; +enum Windows2003 = 0x0502; +enum WindowsVista = 0x0600; +enum Windows7 = 0x0601; +enum Windows8 = 0x0602; + +enum IE3 = 0x0300; +enum IE301 = 0x0300; +enum IE302 = 0x0300; +enum IE4 = 0x0400; +enum IE401 = 0x0401; +enum IE5 = 0x0500; +enum IE5a = 0x0500; +enum IE5b = 0x0500; +enum IE501 = 0x0501; +enum IE55 = 0x0501; +enum IE56 = 0x0560; +enum IE6 = 0x0600; +enum IE601 = 0x0601; +enum IE602 = 0x0603; +enum IE7 = 0x0700; +enum IE8 = 0x0800; +enum IE9 = 0x0900; +enum IE10 = 0x0A00; + /* These version identifiers are used to specify the minimum version of Windows that an * application will support. * @@ -23,62 +56,64 @@ enum __W32API_MINOR_VERSION = 17; * Windows 9x is no longer supported, either by Microsoft or by DMD, this distinction has been * removed in order to simplify the bindings. */ - version (Windows10) { - enum uint _WIN32_WINNT = 0xA00; +version (Windows11) { + enum uint _WIN32_WINNT = _WIN32_WINNT_WIN10; +} else version (Windows10) { + enum uint _WIN32_WINNT = _WIN32_WINNT_WIN10; } else version (Windows8_1) { // also Windows2012R2 - enum uint _WIN32_WINNT = 0x603; + enum uint _WIN32_WINNT = _WIN32_WINNT_WINBLUE; } else version (Windows8) { // also Windows2012 - enum uint _WIN32_WINNT = 0x602; + enum uint _WIN32_WINNT = _WIN32_WINNT_WIN8; } else version (Windows7) { // also Windows2008R2 - enum uint _WIN32_WINNT = 0x601; + enum uint _WIN32_WINNT = _WIN32_WINNT_WIN7; } else version (WindowsVista) { // also Windows2008 - enum uint _WIN32_WINNT = 0x600; + enum uint _WIN32_WINNT = _WIN32_WINNT_VISTA; } else version (Windows2003) { // also WindowsHomeServer, WindowsXP64 - enum uint _WIN32_WINNT = 0x502; + enum uint _WIN32_WINNT = _WIN32_WINNT_WS03; } else version (WindowsXP) { - enum uint _WIN32_WINNT = 0x501; + enum uint _WIN32_WINNT = _WIN32_WINNT_WINXP; } else version (Windows2000) { // Current DMD doesn't support any version of Windows older than XP, // but third-party compilers could use this - enum uint _WIN32_WINNT = 0x500; + enum uint _WIN32_WINNT = _WIN32_WINNT_WIN2K; } else { - enum uint _WIN32_WINNT = 0x501; + enum uint _WIN32_WINNT = _WIN32_WINNT_WIN7; } version (IE11) { - enum uint _WIN32_IE = 0xA00; + enum uint _WIN32_IE = _WIN32_IE_IE110; } else version (IE10) { - enum uint _WIN32_IE = 0xA00; + enum uint _WIN32_IE = _WIN32_IE_IE100; } else version (IE9) { - enum uint _WIN32_IE = 0x900; + enum uint _WIN32_IE = _WIN32_IE_IE90; } else version (IE8) { - enum uint _WIN32_IE = 0x800; + enum uint _WIN32_IE = _WIN32_IE_IE80; } else version (IE7) { - enum uint _WIN32_IE = 0x700; + enum uint _WIN32_IE = _WIN32_IE_IE70; } else version (IE602) { - enum uint _WIN32_IE = 0x603; + enum uint _WIN32_IE = _WIN32_IE_IE60SP2; } else version (IE601) { - enum uint _WIN32_IE = 0x601; + enum uint _WIN32_IE = _WIN32_IE_IE60SP1; } else version (IE6) { - enum uint _WIN32_IE = 0x600; + enum uint _WIN32_IE = _WIN32_IE_IE60; } else version (IE56) { - enum uint _WIN32_IE = 0x560; + enum uint _WIN32_IE = _WIN32_IE_IE60; } else version (IE55) { - enum uint _WIN32_IE = 0x550; + enum uint _WIN32_IE = _WIN32_IE_IE55; } else version (IE501) { - enum uint _WIN32_IE = 0x501; + enum uint _WIN32_IE = _WIN32_IE_IE501; } else version (IE5) { - enum uint _WIN32_IE = 0x500; + enum uint _WIN32_IE = _WIN32_IE_IE50; } else version (IE401) { - enum uint _WIN32_IE = 0x401; + enum uint _WIN32_IE = _WIN32_IE_IE401; } else version (IE4) { - enum uint _WIN32_IE = 0x400; + enum uint _WIN32_IE = _WIN32_IE_IE40; } else version (IE3) { - enum uint _WIN32_IE = 0x300; -} else static if (_WIN32_WINNT >= 0x500) { - enum uint _WIN32_IE = 0x600; -} else static if (_WIN32_WINNT >= 0x410) { - enum uint _WIN32_IE = 0x400; + enum uint _WIN32_IE = _WIN32_IE_IE30; +} else static if (_WIN32_WINNT >= _WIN32_WINNT_WIN2K) { + enum uint _WIN32_IE = _WIN32_IE_IE60; +} else static if (_WIN32_WINNT >= Windows98) { //NOTE: _WIN32_WINNT will never be set this low + enum uint _WIN32_IE = _WIN32_IE_IE40; } else { enum uint _WIN32_IE = 0; } diff --git a/runtime/druntime/src/core/sys/windows/winbase.d b/runtime/druntime/src/core/sys/windows/winbase.d index 2e32ba25ab..935c7c432c 100644 --- a/runtime/druntime/src/core/sys/windows/winbase.d +++ b/runtime/druntime/src/core/sys/windows/winbase.d @@ -345,6 +345,14 @@ enum DWORD SECURITY_SQOS_PRESENT = 0x00100000, SECURITY_VALID_SQOS_FLAGS = 0x001F0000; +// for GetFinalPathNameByHandle() +enum DWORD + VOLUME_NAME_DOS = 0x0, + VOLUME_NAME_GUID = 0x1, + VOLUME_NAME_NT = 0x2, + VOLUME_NAME_NONE = 0x4, + FILE_NAME_NORMALIZED = 0x0, + FILE_NAME_OPENED = 0x8; // Thread exit code enum DWORD STILL_ACTIVE = 0x103; @@ -1333,6 +1341,51 @@ enum GET_FILEEX_INFO_LEVELS { GetFileExMaxInfoLevel } +import core.sys.windows.sdkddkver : NTDDI_VERSION, NTDDI_LONGHORN; + +static if (NTDDI_VERSION >= NTDDI_LONGHORN) +{ + enum FILE_INFO_BY_HANDLE_CLASS + { + FileBasicInfo, + FileStandardInfo, + FileNameInfo, + FileRenameInfo, + FileDispositionInfo, + FileAllocationInfo, + FileEndOfFileInfo, + FileStreamInfo, + FileCompressionInfo, + FileAttributeTagInfo, + FileIdBothDirectoryInfo, + FileIdBothDirectoryRestartInfo, + FileIoPriorityHintInfo, + FileRemoteProtocolInfo, + FileFullDirectoryInfo, + FileFullDirectoryRestartInfo, +// static if (NTDDI_VERSION >= NTDDI_WIN8) +// { + FileStorageInfo, + FileAlignmentInfo, + FileIdInfo, + FileIdExtdDirectoryInfo, + FileIdExtdDirectoryRestartInfo, +// } +// static if (NTDDI_VERSION >= NTDDI_WIN10_RS1) +// { + FileDispositionInfoEx, + FileRenameInfoEx, +// } +// static if (NTDDI_VERSION >= NTDDI_WIN10_19H1) +// { + FileCaseSensitiveInfo, + FileNormalizedNameInfo, +// } + MaximumFileInfoByHandleClass + } + alias PFILE_INFO_BY_HANDLE_CLASS = FILE_INFO_BY_HANDLE_CLASS*; +} + struct SYSTEM_INFO { union { DWORD dwOemId; @@ -1590,12 +1643,32 @@ static if (_WIN32_WINNT >= 0x410) { alias DWORD EXECUTION_STATE; } -// CreateSymbolicLink +// CreateSymbolicLink, GetFileInformationByHandleEx static if (_WIN32_WINNT >= 0x600) { enum { SYMBOLIC_LINK_FLAG_DIRECTORY = 0x1, SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE = 0x2 } + + struct FILE_BASIC_INFO + { + LARGE_INTEGER CreationTime; + LARGE_INTEGER LastAccessTime; + LARGE_INTEGER LastWriteTime; + LARGE_INTEGER ChangeTime; + DWORD FileAttributes; + } + alias PFILE_BASIC_INFO = FILE_BASIC_INFO*; + + struct FILE_STANDARD_INFO + { + LARGE_INTEGER AllocationSize; + LARGE_INTEGER EndOfFile; + DWORD NumberOfLinks; + BOOLEAN DeletePending; + BOOLEAN Directory; + } + alias PFILE_STANDARD_INFO = FILE_STANDARD_INFO*; } // Callbacks @@ -1716,8 +1789,8 @@ extern (Windows) nothrow @nogc { alias RtlMoveMemory = memmove; alias RtlCopyMemory = memcpy; - pragma(inline, true) void RtlFillMemory(PVOID Destination, SIZE_T Length, BYTE Fill) { memset(Destination, Fill, Length); } - pragma(inline, true) void RtlZeroMemory(PVOID Destination, SIZE_T Length) { memset(Destination, 0, Length); } + pragma(inline, true) void RtlFillMemory(PVOID Destination, SIZE_T Length, BYTE Fill) pure { memset(Destination, Fill, Length); } + pragma(inline, true) void RtlZeroMemory(PVOID Destination, SIZE_T Length) pure { memset(Destination, 0, Length); } alias MoveMemory = RtlMoveMemory; alias CopyMemory = RtlCopyMemory; alias FillMemory = RtlFillMemory; @@ -1844,6 +1917,8 @@ WINBASEAPI DWORD WINAPI GetCurrentThreadId(void); DWORD GetFileSize(HANDLE, PDWORD); BOOL GetFileTime(HANDLE, LPFILETIME, LPFILETIME, LPFILETIME); DWORD GetFileType(HANDLE); + DWORD GetFinalPathNameByHandleA(HANDLE, LPSTR, DWORD, DWORD); + DWORD GetFinalPathNameByHandleW(HANDLE, LPWSTR, DWORD, DWORD); DWORD GetFullPathNameA(LPCSTR, DWORD, LPSTR, LPSTR*); DWORD GetFullPathNameW(LPCWSTR, DWORD, LPWSTR, LPWSTR*); DWORD GetLastError() @trusted; @@ -2485,6 +2560,7 @@ WINBASEAPI BOOL WINAPI SetEvent(HANDLE); static if (_WIN32_WINNT >= 0x600) { BOOL CreateSymbolicLinkA(LPCSTR, LPCSTR, DWORD); BOOL CreateSymbolicLinkW(LPCWSTR, LPCWSTR, DWORD); + BOOL GetFileInformationByHandleEx(HANDLE, FILE_INFO_BY_HANDLE_CLASS, LPVOID, DWORD); } } diff --git a/runtime/druntime/src/core/sys/windows/winhttp.d b/runtime/druntime/src/core/sys/windows/winhttp.d index 63d59cb157..1de3b9366a 100644 --- a/runtime/druntime/src/core/sys/windows/winhttp.d +++ b/runtime/druntime/src/core/sys/windows/winhttp.d @@ -771,7 +771,7 @@ static if (_WIN32_WINNT >= 0x602) } -extern (Windows) { +extern (Windows) nothrow @nogc { BOOL WinHttpAddRequestHeaders(HINTERNET hRequest, LPCWSTR pwszHeaders, DWORD dwHeadersLength, DWORD dwModifiers); BOOL WinHttpCheckPlatform(); diff --git a/runtime/druntime/src/core/sys/windows/winnetwk.d b/runtime/druntime/src/core/sys/windows/winnetwk.d index 9bf2e64614..568c2e89e4 100644 --- a/runtime/druntime/src/core/sys/windows/winnetwk.d +++ b/runtime/druntime/src/core/sys/windows/winnetwk.d @@ -315,7 +315,7 @@ struct NETCONNECTINFOSTRUCT { } alias NETCONNECTINFOSTRUCT* LPNETCONNECTINFOSTRUCT; -extern (Windows) { +extern (Windows) nothrow @nogc { DWORD WNetAddConnection2A(LPNETRESOURCEA, LPCSTR, LPCSTR, DWORD); DWORD WNetAddConnection2W(LPNETRESOURCEW, LPCWSTR, LPCWSTR, DWORD); DWORD WNetAddConnection3A(HWND, LPNETRESOURCEA, LPCSTR, LPCSTR, DWORD); diff --git a/runtime/druntime/src/core/sys/windows/winsock2.d b/runtime/druntime/src/core/sys/windows/winsock2.d index b036df39df..626f79e89c 100644 --- a/runtime/druntime/src/core/sys/windows/winsock2.d +++ b/runtime/druntime/src/core/sys/windows/winsock2.d @@ -405,7 +405,7 @@ const(SOCKET)* stop = start + set.fd_count; // Adds. -void FD_SET(SOCKET fd, fd_set* set) pure @nogc +void FD_SET(SOCKET fd, fd_set* set) pure @nogc { uint c = set.fd_count; set.fd_array.ptr[c] = fd; @@ -547,13 +547,29 @@ enum: uint enum: int { - AI_PASSIVE = 0x1, - AI_CANONNAME = 0x2, - AI_NUMERICHOST = 0x4, - AI_ADDRCONFIG = 0x0400, - AI_NON_AUTHORITATIVE = 0x04000, - AI_SECURE = 0x08000, - AI_RETURN_PREFERRED_NAMES = 0x010000, + AI_PASSIVE = 0x1, // Socket address will be used in bind() call + AI_CANONNAME = 0x2, // Return canonical name in first ai_canonname + AI_NUMERICHOST = 0x4, // Nodename must be a numeric address string + AI_NUMERICSERV = 0x8, // Servicename must be a numeric port number + AI_DNS_ONLY = 0x10, // Restrict queries to unicast DNS only (no LLMNR, netbios, etc.) + AI_FORCE_CLEAR_TEXT = 0x20, // Force clear text DNS query + AI_BYPASS_DNS_CACHE = 0x40, // Bypass DNS cache + AI_RETURN_TTL = 0x80, // Return record TTL + AI_ALL = 0x0100, // Query both IP6 and IP4 with AI_V4MAPPED + AI_ADDRCONFIG = 0x0400, // Resolution only if global address configured + AI_V4MAPPED = 0x0800, // On v6 failure, query v4 and convert to V4MAPPED format + AI_NON_AUTHORITATIVE = 0x04000, // LUP_NON_AUTHORITATIVE + AI_SECURE = 0x08000, // LUP_SECURE + AI_RETURN_PREFERRED_NAMES = 0x010000, // LUP_RETURN_PREFERRED_NAMES + AI_FQDN = 0x00020000, // Return the FQDN in ai_canonname + AI_FILESERVER = 0x00040000, // Resolving fileserver name resolution + AI_DISABLE_IDN_ENCODING = 0x00080000, // Disable Internationalized Domain Names handling + AI_SECURE_WITH_FALLBACK = 0x00100000, // Forces clear text fallback if the secure DNS query fails + AI_EXCLUSIVE_CUSTOM_SERVERS = 0x00200000, // Use exclusively the custom DNS servers + AI_RETURN_RESPONSE_FLAGS = 0x10000000, // Requests extra information about the DNS results + AI_REQUIRE_SECURE = 0x20000000, // Forces the DNS query to be done over seucre protocols + AI_RESOLUTION_HANDLE = 0x40000000, // Request resolution handle + AI_EXTENDED = 0x80000000, // Indicates this is extended ADDRINFOEX(2/..) struct } @@ -702,12 +718,12 @@ struct hostent // Note: These are Winsock2!! struct WSAOVERLAPPED; alias LPWSAOVERLAPPED = WSAOVERLAPPED*; -alias LPWSAOVERLAPPED_COMPLETION_ROUTINE = void function(uint, uint, LPWSAOVERLAPPED, uint); +alias LPWSAOVERLAPPED_COMPLETION_ROUTINE = void function(uint, uint, LPWSAOVERLAPPED, uint) nothrow @nogc; int WSAIoctl(SOCKET s, uint dwIoControlCode, void* lpvInBuffer, uint cbInBuffer, void* lpvOutBuffer, uint cbOutBuffer, uint* lpcbBytesReturned, - LPWSAOVERLAPPED lpOverlapped, LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine); + LPWSAOVERLAPPED lpOverlapped, LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine) @nogc; enum IOC_VENDOR = 0x18000000; @@ -720,3 +736,31 @@ struct tcp_keepalive uint keepalivetime; uint keepaliveinterval; } + + +struct pollfd +{ + SOCKET fd; // Socket handle + short events; // Requested events to monitor + short revents; // Returned events indicating status +} +alias WSAPOLLFD = pollfd; +alias PWSAPOLLFD = pollfd*; +alias LPWSAPOLLFD = pollfd*; + +enum: short { + POLLRDNORM = 0x0100, + POLLRDBAND = 0x0200, + POLLIN = (POLLRDNORM | POLLRDBAND), + POLLPRI = 0x0400, + + POLLWRNORM = 0x0010, + POLLOUT = (POLLWRNORM), + POLLWRBAND = 0x0020, + + POLLERR = 0x0001, + POLLHUP = 0x0002, + POLLNVAL = 0x0004 +} + +int WSAPoll(LPWSAPOLLFD fdArray, uint fds, int timeout) @nogc; diff --git a/runtime/druntime/src/core/sys/windows/winsvc.d b/runtime/druntime/src/core/sys/windows/winsvc.d index e6418ac8ac..a34cd25820 100644 --- a/runtime/druntime/src/core/sys/windows/winsvc.d +++ b/runtime/druntime/src/core/sys/windows/winsvc.d @@ -272,7 +272,7 @@ static if (_WIN32_WINNT >= 0x500) { alias SERVICE_FAILURE_ACTIONSW* LPSERVICE_FAILURE_ACTIONSW; } -extern (Windows) { +extern (Windows) nothrow @nogc { BOOL ChangeServiceConfigA(SC_HANDLE, DWORD, DWORD, DWORD, LPCSTR, LPCSTR, LPDWORD, LPCSTR, LPCSTR, LPCSTR, LPCSTR); BOOL ChangeServiceConfigW(SC_HANDLE, DWORD, DWORD, DWORD, LPCWSTR, diff --git a/runtime/druntime/src/core/sys/windows/winuser.d b/runtime/druntime/src/core/sys/windows/winuser.d index 469e68e666..f641444078 100644 --- a/runtime/druntime/src/core/sys/windows/winuser.d +++ b/runtime/druntime/src/core/sys/windows/winuser.d @@ -3848,7 +3848,7 @@ int GetMenuStringA(HMENU, UINT, LPSTR, int, UINT); int GetMenuStringW(HMENU, UINT, LPWSTR, int, UINT); BOOL GetMessageA(LPMSG, HWND, UINT, UINT); BOOL GetMessageW(LPMSG, HWND, UINT, UINT); -LONG GetMessageExtraInfo(); +LPARAM GetMessageExtraInfo(); DWORD GetMessagePos(); LONG GetMessageTime(); diff --git a/runtime/druntime/src/core/sys/windows/winver.d b/runtime/druntime/src/core/sys/windows/winver.d index dc3d881d2c..646891b1be 100644 --- a/runtime/druntime/src/core/sys/windows/winver.d +++ b/runtime/druntime/src/core/sys/windows/winver.d @@ -139,7 +139,7 @@ struct VS_FIXEDFILEINFO { DWORD dwFileDateLS; } -extern (Windows) { +extern (Windows) nothrow @nogc { DWORD VerFindFileA(DWORD, LPCSTR, LPCSTR, LPCSTR, LPSTR, PUINT, LPSTR, PUINT); DWORD VerFindFileW(DWORD, LPCWSTR, LPCWSTR, LPCWSTR, LPWSTR, PUINT, LPWSTR, diff --git a/runtime/druntime/src/core/thread/fiber/base.d b/runtime/druntime/src/core/thread/fiber/base.d new file mode 100644 index 0000000000..115d876aff --- /dev/null +++ b/runtime/druntime/src/core/thread/fiber/base.d @@ -0,0 +1,1405 @@ +/** + * Base fiber module provides OS-indepedent part of lightweight threads aka fibers. + * + * Copyright: Copyright Sean Kelly 2005 - 2012. + * License: Distributed under the + * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). + * (See accompanying file LICENSE) + * Authors: Sean Kelly, Walter Bright, Alex Rønne Petersen, Martin Nowak + * Source: $(DRUNTIMESRC core/thread/fiber/base.d) + */ + +module core.thread.fiber.base; + +package: + +import core.thread.fiber; +import core.thread.threadbase; +import core.thread.threadgroup; +import core.thread.types; +import core.thread.context; + +import core.memory : pageSize; + + +version (LDC) +{ + import ldc.attributes : assumeUsed; + + // Unconditionally change ABI to support sanitizers (adds fields to data structures): + version = SupportSanitizers_ABI; + // But runtime code is conditionally added by `SupportSanitizers`: + version (SupportSanitizers) + { + import ldc.sanitizers_optionally_linked; + } + + // Detect when a Fiber migrates between Threads for systems in which it may be + // unsafe to do so. A unittest below helps decide if CheckFiberMigration + // should be set for your system. + + version (OSX) version = Darwin; + else version (iOS) version = Darwin; + else version (TVOS) version = Darwin; + else version (WatchOS) version = Darwin; + + version (Darwin) version = CheckFiberMigration; + else version (Android) version = CheckFiberMigration; + else version (AArch64) version = CheckFiberMigration; + else version (PPC64) version = CheckFiberMigration; + // Fiber migration across threads is (probably) not possible with ASan fakestack enabled (different parts of the stack + // will contain fakestack pointers that were created on different threads...) + else version (SupportSanitizers) version = CheckFiberMigration; + + + // Fiber support for SjLj style exceptions + // + // Exception handling based on setjmp/longjmp tracks the unwind points with a + // linked list stack managed by _Unwind_SjLj_Register and + // _Unwind_SjLj_Unregister. In the context of Fibers, the stack needs to be + // Fiber local, otherwise unwinding could weave through functions on other + // Fibers as opposed to just the current Fiber. The solution is to give each + // Fiber a m_sjljExStackTop. + // + // Two implementations known to have this SjLj stack design are GCC's libgcc + // and darwin libunwind for ARM (iOS). Functions to get/set the current SjLj + // stack are named differently in each implmentation: + // + // https://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-sjlj.c + // + // libgcc + // struct SjLj_Function_Context* _Unwind_SjLj_GetContext(void) + // void _Unwind_SjLj_SetContext(struct SjLj_Function_Context *fc) + // + // http://www.opensource.apple.com/source/libunwind/libunwind-30/src/Unwind-sjlj.c + // + // darwin (OS X) + // _Unwind_FunctionContext* __Unwind_SjLj_GetTopOfFunctionStack(); + // void __Unwind_SjLj_SetTopOfFunctionStack(_Unwind_FunctionContext* fc); + // + // These functions are not extern but if we peek at the implementations it + // turns out that _Unwind_SjLj_Register and _Unwind_SjLj_Unregister in both + // libraries will manipulate the stack as we need. + + version (GNU_SjLj_Exceptions) version = SjLj_Exceptions; + version (iOS) version(ARM) version = SjLj_Exceptions; + + version (SjLj_Exceptions) + private + { + // libgcc struct SjLj_Function_Context and darwin struct + // _Unwind_FunctionContext have same initial layout so can get away with + // one type to mimic header of both here. + struct SjLjFuncContext + { + SjLjFuncContext* prev; + // rest of this struc we don't care about in swapSjLjStackTop below. + } + + extern(C) @nogc nothrow + { + void _Unwind_SjLj_Register(SjLjFuncContext* fc); + void _Unwind_SjLj_Unregister(SjLjFuncContext* fc); + } + + // Swap in a new stack top, returning the previous one + SjLjFuncContext* swapSjLjStackTop(SjLjFuncContext* newtop) @nogc nothrow + { + // register a dummy context to retrieve stack top, then plop our new + // stack top in its place before unregistering, making it the new top. + SjLjFuncContext fc; + _Unwind_SjLj_Register(&fc); + + SjLjFuncContext* prevtop = fc.prev; + fc.prev = newtop; + _Unwind_SjLj_Unregister(&fc); + + return prevtop; + } + } +} + + +package +{ + import core.atomic : atomicStore, cas, MemoryOrder; + import core.exception : onOutOfMemoryError; + import core.stdc.stdlib : abort; + + extern (C) void fiber_entryPoint() nothrow /* LDC */ @assumeUsed + { + FiberBase obj = FiberBase.getThis(); + assert( obj ); + + version (SupportSanitizers) + { + informSanitizerOfFinishSwitchFiber(obj.__fake_stack, &obj.__from_stack_bottom, &obj.__from_stack_size); + } + + assert( ThreadBase.getThis().m_curr is obj.m_ctxt ); + atomicStore!(MemoryOrder.raw)(*cast(shared)&ThreadBase.getThis().m_lock, false); + obj.m_ctxt.tstack = obj.m_ctxt.bstack; + obj.m_state = FiberBase.State.EXEC; + + try + { + obj.run(); + } + catch ( Throwable t ) + { + obj.m_unhandled = t; + } + + static if ( __traits( compiles, ucontext_t ) ) + obj.m_ucur = &obj.m_utxt; + + obj.m_state = Fiber.State.TERM; + obj.switchOut(); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// Fiber +/////////////////////////////////////////////////////////////////////////////// +/* + * Documentation of Fiber internals: + * + * The main routines to implement when porting Fibers to new architectures are + * fiber_switchContext and initStack. Some version constants have to be defined + * for the new platform as well, search for "Fiber Platform Detection and Memory Allocation". + * + * Fibers are based on a concept called 'Context'. A Context describes the execution + * state of a Fiber or main thread which is fully described by the stack, some + * registers and a return address at which the Fiber/Thread should continue executing. + * Please note that not only each Fiber has a Context, but each thread also has got a + * Context which describes the threads stack and state. If you call Fiber fib; fib.call + * the first time in a thread you switch from Threads Context into the Fibers Context. + * If you call fib.yield in that Fiber you switch out of the Fibers context and back + * into the Thread Context. (However, this is not always the case. You can call a Fiber + * from within another Fiber, then you switch Contexts between the Fibers and the Thread + * Context is not involved) + * + * In all current implementations the registers and the return address are actually + * saved on a Contexts stack. + * + * The fiber_switchContext routine has got two parameters: + * void** a: This is the _location_ where we have to store the current stack pointer, + * the stack pointer of the currently executing Context (Fiber or Thread). + * void* b: This is the pointer to the stack of the Context which we want to switch into. + * Note that we get the same pointer here as the one we stored into the void** a + * in a previous call to fiber_switchContext. + * + * In the simplest case, a fiber_switchContext rountine looks like this: + * fiber_switchContext: + * push {return Address} + * push {registers} + * copy {stack pointer} into {location pointed to by a} + * //We have now switch to the stack of a different Context! + * copy {b} into {stack pointer} + * pop {registers} + * pop {return Address} + * jump to {return Address} + * + * The GC uses the value returned in parameter a to scan the Fibers stack. It scans from + * the stack base to that value. As the GC dislikes false pointers we can actually optimize + * this a little: By storing registers which can not contain references to memory managed + * by the GC outside of the region marked by the stack base pointer and the stack pointer + * saved in fiber_switchContext we can prevent the GC from scanning them. + * Such registers are usually floating point registers and the return address. In order to + * implement this, we return a modified stack pointer from fiber_switchContext. However, + * we have to remember that when we restore the registers from the stack! + * + * --------------------------- <= Stack Base + * | Frame | <= Many other stack frames + * | Frame | + * |-------------------------| <= The last stack frame. This one is created by fiber_switchContext + * | registers with pointers | + * | | <= Stack pointer. GC stops scanning here + * | return address | + * |floating point registers | + * --------------------------- <= Real Stack End + * + * fiber_switchContext: + * push {registers with pointers} + * copy {stack pointer} into {location pointed to by a} + * push {return Address} + * push {Floating point registers} + * //We have now switch to the stack of a different Context! + * copy {b} into {stack pointer} + * //We now have to adjust the stack pointer to point to 'Real Stack End' so we can pop + * //the FP registers + * //+ or - depends on if your stack grows downwards or upwards + * {stack pointer} = {stack pointer} +- ({FPRegisters}.sizeof + {return address}.sizeof} + * pop {Floating point registers} + * pop {return Address} + * pop {registers with pointers} + * jump to {return Address} + * + * So the question now is which registers need to be saved? This depends on the specific + * architecture ABI of course, but here are some general guidelines: + * - If a register is callee-save (if the callee modifies the register it must saved and + * restored by the callee) it needs to be saved/restored in switchContext + * - If a register is caller-save it needn't be saved/restored. (Calling fiber_switchContext + * is a function call and the compiler therefore already must save these registers before + * calling fiber_switchContext) + * - Argument registers used for passing parameters to functions needn't be saved/restored + * - The return register needn't be saved/restored (fiber_switchContext hasn't got a return type) + * - All scratch registers needn't be saved/restored + * - The link register usually needn't be saved/restored (but sometimes it must be cleared - + * see below for details) + * - The frame pointer register - if it exists - is usually callee-save + * - All current implementations do not save control registers + * + * What happens on the first switch into a Fiber? We never saved a state for this fiber before, + * but the initial state is prepared in the initStack routine. (This routine will also be called + * when a Fiber is being resetted). initStack must produce exactly the same stack layout as the + * part of fiber_switchContext which saves the registers. Pay special attention to set the stack + * pointer correctly if you use the GC optimization mentioned before. the return Address saved in + * initStack must be the address of fiber_entrypoint. + * + * There's now a small but important difference between the first context switch into a fiber and + * further context switches. On the first switch, Fiber.call is used and the returnAddress in + * fiber_switchContext will point to fiber_entrypoint. The important thing here is that this jump + * is a _function call_, we call fiber_entrypoint by jumping before it's function prologue. On later + * calls, the user used yield() in a function, and therefore the return address points into a user + * function, after the yield call. So here the jump in fiber_switchContext is a _function return_, + * not a function call! + * + * The most important result of this is that on entering a function, i.e. fiber_entrypoint, we + * would have to provide a return address / set the link register once fiber_entrypoint + * returns. Now fiber_entrypoint does never return and therefore the actual value of the return + * address / link register is never read/used and therefore doesn't matter. When fiber_switchContext + * performs a _function return_ the value in the link register doesn't matter either. + * However, the link register will still be saved to the stack in fiber_entrypoint and some + * exception handling / stack unwinding code might read it from this stack location and crash. + * The exact solution depends on your architecture, but see the ARM implementation for a way + * to deal with this issue. + * + * The ARM implementation is meant to be used as a kind of documented example implementation. + * Look there for a concrete example. + * + * FIXME: fiber_entrypoint might benefit from a @noreturn attribute, but D doesn't have one. + */ + +/** + * This class provides a cooperative concurrency mechanism integrated with the + * threading and garbage collection functionality. Calling a fiber may be + * considered a blocking operation that returns when the fiber yields (via + * Fiber.yield()). Execution occurs within the context of the calling thread + * so synchronization is not necessary to guarantee memory visibility so long + * as the same thread calls the fiber each time. Please note that there is no + * requirement that a fiber be bound to one specific thread. Rather, fibers + * may be freely passed between threads so long as they are not currently + * executing. Like threads, a new fiber thread may be created using either + * derivation or composition, as in the following example. + * + * Warning: + * Status registers are not saved by the current implementations. This means + * floating point exception status bits (overflow, divide by 0), rounding mode + * and similar stuff is set per-thread, not per Fiber! + * + * Warning: + * On ARM FPU registers are not saved if druntime was compiled as ARM_SoftFloat. + * If such a build is used on a ARM_SoftFP system which actually has got a FPU + * and other libraries are using the FPU registers (other code is compiled + * as ARM_SoftFP) this can cause problems. Druntime must be compiled as + * ARM_SoftFP in this case. + * + * Authors: Based on a design by Mikola Lysenko. + */ +class FiberBase +{ + /** + * Initializes a fiber object which is associated with a static + * D function. + * + * Params: + * fn = The fiber function. + * sz = The stack size for this fiber. + * guardPageSize = size of the guard page to trap fiber's stack + * overflows. Beware that using this will increase + * the number of mmaped regions on platforms using mmap + * so an OS-imposed limit may be hit. + * + * In: + * fn must not be null. + */ + this( void function() fn, size_t sz, size_t guardPageSize ) nothrow + in + { + assert( fn ); + } + do + { + allocStack( sz, guardPageSize ); + reset( fn ); + } + + + /** + * Initializes a fiber object which is associated with a dynamic + * D function. + * + * Params: + * dg = The fiber function. + * sz = The stack size for this fiber. + * guardPageSize = size of the guard page to trap fiber's stack + * overflows. Beware that using this will increase + * the number of mmaped regions on platforms using mmap + * so an OS-imposed limit may be hit. + * + * In: + * dg must not be null. + */ + this( void delegate() dg, size_t sz, size_t guardPageSize ) nothrow + { + allocStack( sz, guardPageSize ); + reset( cast(void delegate() const) dg ); + } + + + /** + * Cleans up any remaining resources used by this object. + */ + ~this() nothrow @nogc + { + // NOTE: A live reference to this object will exist on its associated + // stack from the first time its call() method has been called + // until its execution completes with State.TERM. Thus, the only + // times this dtor should be called are either if the fiber has + // terminated (and therefore has no active stack) or if the user + // explicitly deletes this object. The latter case is an error + // but is not easily tested for, since State.HOLD may imply that + // the fiber was just created but has never been run. There is + // not a compelling case to create a State.INIT just to offer a + // means of ensuring the user isn't violating this object's + // contract, so for now this requirement will be enforced by + // documentation only. + freeStack(); + } + + + /////////////////////////////////////////////////////////////////////////// + // General Actions + /////////////////////////////////////////////////////////////////////////// + + + /** + * Transfers execution to this fiber object. The calling context will be + * suspended until the fiber calls Fiber.yield() or until it terminates + * via an unhandled exception. + * + * Params: + * rethrow = Rethrow any unhandled exception which may have caused this + * fiber to terminate. + * + * In: + * This fiber must be in state HOLD. + * + * Throws: + * Any exception not handled by the joined thread. + * + * Returns: + * Any exception not handled by this fiber if rethrow = false, null + * otherwise. + */ + // Not marked with any attributes, even though `nothrow @nogc` works + // because it calls arbitrary user code. Most of the implementation + // is already `@nogc nothrow`, but in order for `Fiber.call` to + // propagate the attributes of the user's function, the Fiber + // class needs to be templated. + final Throwable call( Rethrow rethrow = Rethrow.yes ) + { + return rethrow ? call!(Rethrow.yes)() : call!(Rethrow.no); + } + + /// ditto + final Throwable call( Rethrow rethrow )() + { + callImpl(); + if ( m_unhandled ) + { + Throwable t = m_unhandled; + m_unhandled = null; + static if ( rethrow ) + throw t; + else + return t; + } + return null; + } + + private void callImpl() nothrow @nogc + in + { + assert( m_state == State.HOLD ); + } + do + { + FiberBase cur = getThis(); + + static if ( __traits( compiles, ucontext_t ) ) + m_ucur = cur ? &cur.m_utxt : &Fiber.sm_utxt; + + setThis( this ); + this.switchIn(); + setThis( cur ); + + static if ( __traits( compiles, ucontext_t ) ) + m_ucur = null; + + // NOTE: If the fiber has terminated then the stack pointers must be + // reset. This ensures that the stack for this fiber is not + // scanned if the fiber has terminated. This is necessary to + // prevent any references lingering on the stack from delaying + // the collection of otherwise dead objects. The most notable + // being the current object, which is referenced at the top of + // fiber_entryPoint. + if ( m_state == State.TERM ) + { + m_ctxt.tstack = m_ctxt.bstack; + } + } + + /// Flag to control rethrow behavior of $(D $(LREF call)) + enum Rethrow : bool { no, yes } + + /** + * Resets this fiber so that it may be re-used, optionally with a + * new function/delegate. This routine should only be called for + * fibers that have terminated, as doing otherwise could result in + * scope-dependent functionality that is not executed. + * Stack-based classes, for example, may not be cleaned up + * properly if a fiber is reset before it has terminated. + * + * In: + * This fiber must be in state TERM or HOLD. + */ + final void reset() nothrow @nogc + in + { + assert( m_state == State.TERM || m_state == State.HOLD ); + } + do + { + m_ctxt.tstack = m_ctxt.bstack; + m_state = State.HOLD; + initStack(); + m_unhandled = null; + } + + /// ditto + final void reset( void function() fn ) nothrow @nogc + { + reset(); + m_call = fn; + } + + /// ditto + final void reset( void delegate() dg ) nothrow @nogc + { + reset(); + m_call = dg; + } + + /////////////////////////////////////////////////////////////////////////// + // General Properties + /////////////////////////////////////////////////////////////////////////// + + + /// A fiber may occupy one of three states: HOLD, EXEC, and TERM. + enum State + { + /** The HOLD state applies to any fiber that is suspended and ready to + be called. */ + HOLD, + /** The EXEC state will be set for any fiber that is currently + executing. */ + EXEC, + /** The TERM state is set when a fiber terminates. Once a fiber + terminates, it must be reset before it may be called again. */ + TERM + } + + + /** + * Gets the current state of this fiber. + * + * Returns: + * The state of this fiber as an enumerated value. + */ + final @property State state() const @safe pure nothrow @nogc + { + return m_state; + } + + +version (LDC) +{ + /** + * Return true if migrating a Fiber between Threads is unsafe on this + * system. This is due to compiler optimizations that cache thread local + * variable addresses. When Fiber.yield() returns on a different + * Thread, the addresses refer to the previous Thread's variables. + */ + static @property bool migrationUnsafe() nothrow + { + version (CheckFiberMigration) + return true; + else + return false; + } + + /** + * Allow this Fiber to be resumed on a different thread for systems where + * Fiber migration is unsafe (migrationUnsafe() is true). Otherwise the + * first time a Fiber is resumed on a different Thread, a ThreadException + * is thrown. This provides the programmer a reminder to be careful and + * helps detect such usage in libraries being ported from other systems. + * + * Fiber migration on such systems can be done safely if you control all + * the code and know that thread locals are not involved. + * + * For systems without this issue, allowMigration does nothing, as you are + * always free to migrate. + */ + final void allowMigration() nothrow + { + // Does nothing if checking is disabled + version (CheckFiberMigration) + m_allowMigration = true; + } +} + + + /////////////////////////////////////////////////////////////////////////// + // Actions on Calling Fiber + /////////////////////////////////////////////////////////////////////////// + + + /** + * Forces a context switch to occur away from the calling fiber. + */ + static void yield() nothrow @nogc + { + FiberBase cur = getThis(); + assert( cur, "Fiber.yield() called with no active fiber" ); + assert( cur.m_state == State.EXEC ); + + static if ( __traits( compiles, ucontext_t ) ) + cur.m_ucur = &cur.m_utxt; + + cur.m_state = State.HOLD; + cur.switchOut(); + cur.m_state = State.EXEC; + } + + + /** + * Forces a context switch to occur away from the calling fiber and then + * throws obj in the calling fiber. + * + * Params: + * t = The object to throw. + * + * In: + * t must not be null. + */ + static void yieldAndThrow( Throwable t ) nothrow @nogc + in + { + assert( t ); + } + do + { + FiberBase cur = getThis(); + assert( cur, "Fiber.yield() called with no active fiber" ); + assert( cur.m_state == State.EXEC ); + + static if ( __traits( compiles, ucontext_t ) ) + cur.m_ucur = &cur.m_utxt; + + cur.m_unhandled = t; + cur.m_state = State.HOLD; + cur.switchOut(); + cur.m_state = State.EXEC; + } + + + /////////////////////////////////////////////////////////////////////////// + // Fiber Accessors + /////////////////////////////////////////////////////////////////////////// + + + /** + * Provides a reference to the calling fiber or null if no fiber is + * currently active. + * + * Returns: + * The fiber object representing the calling fiber or null if no fiber + * is currently active within this thread. The result of deleting this object is undefined. + */ + static FiberBase getThis() @safe nothrow @nogc + { + // LDC NOTE: + // Currently, it is not safe to migrate fibers across threads when they + // use TLS at all, as LLVM might cache the TLS address lookup across a + // context switch (see https://github.com/ldc-developers/ldc/issues/666). + // Preventing inlining of this function, as well as switch{In,Out} + // below, enables users to do this at least as long as they are very + // careful about accessing TLS data themselves (such as in the shared + // fiber unittest below, which tends to sporadically crash with enabled + // optimizations if this prevent-inlining workaround is removed). + version (LDC) pragma(inline, false); + + return sm_this; + } + + +private: + + // + // Fiber entry point. Invokes the function or delegate passed on + // construction (if any). + // + final void run() + { + m_call(); + } + + // + // Standard fiber data + // + Callable m_call; + bool m_isRunning; + version (LDC) + { + // Unconditionally add this field, that is only used with version(CheckFiberMigration), + // such that version(SupportSanitizers) does not change the ABI. + // (what is needed is version(SupportSanitizers_ABI || CheckFiberMigration)) + // The field is positioned after another bool, using up alignment padding space. + bool m_allowMigration; + + // Set first time switchIn called to indicate this Fiber's Thread + ThreadBase m_curThread; + + version (SjLj_Exceptions) + { + SjLjFuncContext* m_sjljExStackTop; + } + } + Throwable m_unhandled; + State m_state; + + +protected: + /////////////////////////////////////////////////////////////////////////// + // Stack Management + /////////////////////////////////////////////////////////////////////////// + + + // + // Allocate a new stack for this fiber. + // + abstract void allocStack( size_t sz, size_t guardPageSize ) nothrow; + + + // + // Free this fiber's stack. + // + abstract void freeStack() nothrow @nogc; + + + // + // Initialize the allocated stack. + // Look above the definition of 'class Fiber' for some information about the implementation of this routine + // + abstract void initStack() nothrow @nogc; + + + StackContext* m_ctxt; + size_t m_size; + void* m_pmem; + + static if ( __traits( compiles, ucontext_t ) ) + { + // NOTE: The static ucontext instance is used to represent the context + // of the executing thread. + static ucontext_t sm_utxt = void; + ucontext_t m_utxt = void; + package ucontext_t* m_ucur = null; + } + + +private: + /////////////////////////////////////////////////////////////////////////// + // Storage of Active Fiber + /////////////////////////////////////////////////////////////////////////// + + + // + // Sets a thread-local reference to the current fiber object. + // + static void setThis( FiberBase f ) nothrow @nogc + { + sm_this = f; + } + + static FiberBase sm_this; + + +private: + /////////////////////////////////////////////////////////////////////////// + // Context Switching + /////////////////////////////////////////////////////////////////////////// + + + // + // Switches into the stack held by this fiber. + // + final void switchIn() nothrow @nogc + { + // see note in getThis() + version (LDC) pragma(inline, false); + + ThreadBase tobj = ThreadBase.getThis(); + void** oldp = &tobj.m_curr.tstack; + void* newp = m_ctxt.tstack; + + version (CheckFiberMigration) + { + if (m_curThread is null || m_allowMigration) + m_curThread = tobj; + else if (tobj !is m_curThread) + { + // allocate and construct a ThreadException manually for @nogc + import core.stdc.stdlib : malloc; + import core.thread.threadbase : ThreadException; + enum threadExceptionSize = __traits(classInstanceSize, ThreadException); + if (auto e = cast(ThreadException) malloc(threadExceptionSize)) + { + import core.lifetime : emplace; + emplace(e, + "Migrating Fibers between Threads on this platform may lead " ~ + "to incorrect thread local variable access. To allow " ~ + "migration anyway, call Fiber.allowMigration()"); + m_unhandled = e; + // the exception will leak... + } + return; + } + } + else + { + m_curThread = tobj; + } + + version (SupportSanitizers) + { + // Fiber migration across threads is (probably) not possible with fakestack enabled (different parts of the stack + // will contain fakestack pointers that were created on different threads...) + m_ctxt.asan_fakestack = m_curThread.asan_fakestack; + } + + version (SjLj_Exceptions) + SjLjFuncContext* oldsjlj = swapSjLjStackTop(m_sjljExStackTop); + + // NOTE: The order of operations here is very important. The current + // stack top must be stored before m_lock is set, and pushContext + // must not be called until after m_lock is set. This process + // is intended to prevent a race condition with the suspend + // mechanism used for garbage collection. If it is not followed, + // a badly timed collection could cause the GC to scan from the + // bottom of one stack to the top of another, or to miss scanning + // a stack that still contains valid data. The old stack pointer + // oldp will be set again before the context switch to guarantee + // that it points to exactly the correct stack location so the + // successive pop operations will succeed. + *oldp = getStackTop(); + atomicStore!(MemoryOrder.raw)(*cast(shared)&tobj.m_lock, true); + tobj.pushContext( m_ctxt ); + + version (SupportSanitizers) + { + version (StackGrowsDown) + auto new_bottom = m_ctxt.bstack - m_size; + else + auto new_bottom = m_ctxt.bstack; + + informSanitizerOfStartSwitchFiber(&__fake_stack, new_bottom, m_size); + } + + fiber_switchContext( oldp, newp ); + + version (SupportSanitizers) + { + informSanitizerOfFinishSwitchFiber((m_state == State.TERM) ? null : __fake_stack, + &__from_stack_bottom, &__from_stack_size); + } + + // NOTE: As above, these operations must be performed in a strict order + // to prevent Bad Things from happening. + tobj.popContext(); + atomicStore!(MemoryOrder.raw)(*cast(shared)&tobj.m_lock, false); + tobj.m_curr.tstack = tobj.m_curr.bstack; + + version (SjLj_Exceptions) + m_sjljExStackTop = swapSjLjStackTop(oldsjlj); + } + + + // + // Switches out of the current stack and into the enclosing stack. + // + final void switchOut() nothrow @nogc + { + // see note in getThis() + version (LDC) pragma(inline, false); + + version (LDC) + ThreadBase tobj = m_curThread; + else + ThreadBase tobj = ThreadBase.getThis(); + void** oldp = &m_ctxt.tstack; + void* newp = tobj.m_curr.within.tstack; + + // NOTE: The order of operations here is very important. The current + // stack top must be stored before m_lock is set, and pushContext + // must not be called until after m_lock is set. This process + // is intended to prevent a race condition with the suspend + // mechanism used for garbage collection. If it is not followed, + // a badly timed collection could cause the GC to scan from the + // bottom of one stack to the top of another, or to miss scanning + // a stack that still contains valid data. The old stack pointer + // oldp will be set again before the context switch to guarantee + // that it points to exactly the correct stack location so the + // successive pop operations will succeed. + *oldp = getStackTop(); + atomicStore!(MemoryOrder.raw)(*cast(shared)&tobj.m_lock, true); + + version (SupportSanitizers) + { + informSanitizerOfStartSwitchFiber(&__fake_stack, __from_stack_bottom, __from_stack_size); + } + + fiber_switchContext( oldp, newp ); + + version (SupportSanitizers) + { + informSanitizerOfFinishSwitchFiber(__fake_stack, &__from_stack_bottom, &__from_stack_size); + } + + // NOTE: As above, these operations must be performed in a strict order + // to prevent Bad Things from happening. + // NOTE: If use of this fiber is multiplexed across threads, the thread + // executing here may be different from the one above, so get the + // current thread handle before unlocking, etc. + version (LDC) + tobj = m_curThread; + else + tobj = ThreadBase.getThis(); + atomicStore!(MemoryOrder.raw)(*cast(shared)&tobj.m_lock, false); + tobj.m_curr.tstack = tobj.m_curr.bstack; + } + + /////////////////////////////////////////////////////////////////////////// + // Address Sanitizer support + /////////////////////////////////////////////////////////////////////////// + version (SupportSanitizers_ABI) + { + private: + void* __fake_stack; + const(void)* __from_stack_bottom; + size_t __from_stack_size; + } +} + +/// +unittest { + int counter; + + class DerivedFiber : Fiber + { + this() + { + super( &run ); + } + + private : + void run() + { + counter += 2; + } + } + + void fiberFunc() + { + counter += 4; + Fiber.yield(); + counter += 8; + } + + // create instances of each type + Fiber derived = new DerivedFiber(); + Fiber composed = new Fiber( &fiberFunc ); + + assert( counter == 0 ); + + derived.call(); + assert( counter == 2, "Derived fiber increment." ); + + composed.call(); + assert( counter == 6, "First composed fiber increment." ); + + counter += 16; + assert( counter == 22, "Calling context increment." ); + + composed.call(); + assert( counter == 30, "Second composed fiber increment." ); + + // since each fiber has run to completion, each should have state TERM + assert( derived.state == Fiber.State.TERM ); + assert( composed.state == Fiber.State.TERM ); +} + +version (unittest) +{ + import core.thread.fiber: Fiber; +} + +version (CoreUnittest) +{ + class TestFiber : Fiber + { + this() + { + super(&run); + } + + void run() + { + foreach (i; 0 .. 1000) + { + sum += i; + Fiber.yield(); + } + } + + enum expSum = 1000 * 999 / 2; + size_t sum; + } + + void runTen() + { + TestFiber[10] fibs; + foreach (ref fib; fibs) + fib = new TestFiber(); + + bool cont; + do { + cont = false; + foreach (fib; fibs) { + if (fib.state == Fiber.State.HOLD) + { + fib.call(); + cont |= fib.state != Fiber.State.TERM; + } + } + } while (cont); + + foreach (fib; fibs) + { + assert(fib.sum == TestFiber.expSum); + } + } +} + + +// Single thread running separate fibers +unittest +{ + runTen(); +} + + +// Multiple threads running separate fibers +unittest +{ + auto group = new ThreadGroup(); + foreach (_; 0 .. 4) + { + group.create(&runTen); + } + group.joinAll(); +} + + +// Try to detect if version CheckFiberMigration should be set, or if it is +// set, make sure it behaves properly. The issue is that thread local addr +// may be cached by the compiler and that doesn't play well when Fibers +// migrate between Threads. This may only happen when optimization +// enabled. For that reason, should run this unittest with various +// optimization levels. +// +// https://github.com/ldc-developers/ldc/issues/666 +version (LDC) unittest +{ + import core.thread.osthread : Thread; + + static int tls; + + static yield_noinline() + { + import ldc.intrinsics; + pragma(LDC_never_inline); + Fiber.yield(); + } + + auto f = new Fiber( + { + ++tls; // happens on main thread + yield_noinline(); + ++tls; // happens on other thread, + // 1 if tls addr uncached, 2 if addr was cached + }); + + auto t = new Thread( + { + assert(tls == 0); + + version (CheckFiberMigration) + { + import core.thread.threadbase : ThreadException; + try + { + f.call(); + assert(false, "Should get ThreadException when Fiber migrated"); + } + catch (ThreadException ex) + { + } + + f.allowMigration(); + } + + f.call(); + // tls may be 0 (wrong) or 1 (good) depending on thread local handling + // by compiler + }); + + assert(tls == 0); + f.call(); + assert(tls == 1); + + t.start(); + t.join(); + + version (CheckFiberMigration) + { + assert(Fiber.migrationUnsafe); + } + else version (FiberMigrationTest) + { + assert(!Fiber.migrationUnsafe); + // If thread local addr not cached (correct behavior), then tls should + // still be 1. + assert(tls != 2, + "Not safe to migrate Fibers between Threads on your system. " ~ + "Consider setting version CheckFiberMigration for this system " ~ + "in thread.d"); + // verify un-cached correct case + assert(tls == 1); + } + else + { + if (tls == 2) + { + import core.stdc.stdio : puts; + puts("Not safe to migrate Fibers between Threads on your system. " ~ + "Consider setting version CheckFiberMigration for this system " ~ + "in thread.d"); + } + } +} + +// Multiple threads running shared fibers +unittest +{ + shared bool[10] locks; + TestFiber[10] fibs; + + void runShared() + { + bool cont; + do { + cont = false; + foreach (idx; 0 .. 10) + { + if (cas(&locks[idx], false, true)) + { + if (fibs[idx].state == Fiber.State.HOLD) + { + fibs[idx].call(); + cont |= fibs[idx].state != Fiber.State.TERM; + } + version (LDC) + locks[idx].atomicStore(false); + else + locks[idx] = false; + } + else + { + cont = true; + } + } + } while (cont); + } + + foreach (ref fib; fibs) + { + fib = new TestFiber(); + version (LDC) + fib.allowMigration(); + } + + auto group = new ThreadGroup(); + foreach (_; 0 .. 4) + { + group.create(&runShared); + } + group.joinAll(); + + foreach (fib; fibs) + { + assert(fib.sum == TestFiber.expSum); + } +} + + +// Test exception handling inside fibers. +unittest +{ + enum MSG = "Test message."; + string caughtMsg; + (new Fiber({ + try + { + throw new Exception(MSG); + } + catch (Exception e) + { + caughtMsg = e.msg; + } + })).call(); + assert(caughtMsg == MSG); +} + + +unittest +{ + int x = 0; + + (new Fiber({ + x++; + })).call(); + assert( x == 1 ); +} + +nothrow unittest +{ + new Fiber({}).call!(Fiber.Rethrow.no)(); +} + +unittest +{ + new Fiber({}).call(Fiber.Rethrow.yes); + new Fiber({}).call(Fiber.Rethrow.no); +} + +unittest +{ + enum MSG = "Test message."; + + try + { + (new Fiber(function() { + throw new Exception( MSG ); + })).call(); + assert( false, "Expected rethrown exception." ); + } + catch ( Throwable t ) + { + assert( t.msg == MSG ); + } +} + +// Test exception chaining when switching contexts in finally blocks. +unittest +{ + static void throwAndYield(string msg) { + try { + throw new Exception(msg); + } finally { + Fiber.yield(); + } + } + + static void fiber(string name) { + try { + try { + throwAndYield(name ~ ".1"); + } finally { + throwAndYield(name ~ ".2"); + } + } catch (Exception e) { + assert(e.msg == name ~ ".1"); + assert(e.next); + assert(e.next.msg == name ~ ".2"); + assert(!e.next.next); + } + } + + auto first = new Fiber(() => fiber("first")); + auto second = new Fiber(() => fiber("second")); + first.call(); + second.call(); + first.call(); + second.call(); + first.call(); + second.call(); + assert(first.state == Fiber.State.TERM); + assert(second.state == Fiber.State.TERM); +} + +// Test Fiber resetting +unittest +{ + static string method; + + static void foo() + { + method = "foo"; + } + + void bar() + { + method = "bar"; + } + + static void expect(Fiber fib, string s) + { + assert(fib.state == Fiber.State.HOLD); + fib.call(); + assert(fib.state == Fiber.State.TERM); + assert(method == s); method = null; + } + auto fib = new Fiber(&foo); + expect(fib, "foo"); + + fib.reset(); + expect(fib, "foo"); + + fib.reset(&foo); + expect(fib, "foo"); + + fib.reset(&bar); + expect(fib, "bar"); + + fib.reset(function void(){method = "function";}); + expect(fib, "function"); + + fib.reset(delegate void(){method = "delegate";}); + expect(fib, "delegate"); +} + +// Test unsafe reset in hold state +unittest +{ + auto fib = new Fiber(function {ubyte[2048] buf = void; Fiber.yield();}, 4096); + foreach (_; 0 .. 10) + { + fib.call(); + assert(fib.state == Fiber.State.HOLD); + fib.reset(); + } +} + +// stress testing GC stack scanning +unittest +{ + import core.memory; + import core.thread.osthread : Thread; + import core.time : dur; + + static void unreferencedThreadObject() + { + static void sleep() { Thread.sleep(dur!"msecs"(100)); } + auto thread = new Thread(&sleep).start(); + } + unreferencedThreadObject(); + GC.collect(); + + static class Foo + { + this(int value) + { + _value = value; + } + + int bar() + { + return _value; + } + + int _value; + } + + static void collect() + { + auto foo = new Foo(2); + assert(foo.bar() == 2); + GC.collect(); + Fiber.yield(); + GC.collect(); + assert(foo.bar() == 2); + } + + auto fiber = new Fiber(&collect); + + fiber.call(); + GC.collect(); + fiber.call(); + + // thread reference + auto foo = new Foo(2); + + void collect2() + { + assert(foo.bar() == 2); + GC.collect(); + Fiber.yield(); + GC.collect(); + assert(foo.bar() == 2); + } + + fiber = new Fiber(&collect2); + + fiber.call(); + GC.collect(); + fiber.call(); + + static void recurse(size_t cnt) + { + --cnt; + Fiber.yield(); + if (cnt) + { + auto fib = new Fiber(() { recurse(cnt); }); + fib.call(); + GC.collect(); + fib.call(); + } + } + fiber = new Fiber(() { recurse(20); }); + fiber.call(); +} diff --git a/runtime/druntime/src/core/thread/fiber.d b/runtime/druntime/src/core/thread/fiber/package.d similarity index 60% rename from runtime/druntime/src/core/thread/fiber.d rename to runtime/druntime/src/core/thread/fiber/package.d index dd4eb333b8..a3bb2a34eb 100644 --- a/runtime/druntime/src/core/thread/fiber.d +++ b/runtime/druntime/src/core/thread/fiber/package.d @@ -1,47 +1,29 @@ /** - * The fiber module provides OS-indepedent lightweight threads aka fibers. + * The fiber module provides lightweight threads aka fibers. * * Copyright: Copyright Sean Kelly 2005 - 2012. * License: Distributed under the * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). * (See accompanying file LICENSE) * Authors: Sean Kelly, Walter Bright, Alex Rønne Petersen, Martin Nowak - * Source: $(DRUNTIMESRC core/thread/fiber.d) + * Source: $(DRUNTIMESRC core/thread/fiber/package.d) */ module core.thread.fiber; +import core.thread.context; +import core.thread.fiber.base : fiber_entryPoint, FiberBase; import core.thread.threadbase; import core.thread.threadgroup; import core.thread.types; -import core.thread.context; import core.memory : pageSize; -version (OSX) - version = Darwin; -else version (iOS) - version = Darwin; -else version (TVOS) - version = Darwin; -else version (WatchOS) - version = Darwin; - version (LDC) { import ldc.attributes; import ldc.llvmasm; - - // Unconditionally change ABI to support sanitizers (adds fields to data structures): - version = SupportSanitizers_ABI; - // But runtime code is conditionally added by `SupportSanitizers`: - version (SupportSanitizers) - { - import ldc.sanitizers_optionally_linked; - } } -else - private enum assumeUsed = null; /////////////////////////////////////////////////////////////////////////////// // Fiber Platform Detection @@ -61,7 +43,7 @@ else version (Windows) { - import core.stdc.stdlib : malloc, free; + import core.stdc.stdlib : free, malloc; import core.sys.windows.winbase; import core.sys.windows.winnt; @@ -69,7 +51,7 @@ version (Windows) version = LDC_Windows; } -private +package { version (D_InlineAsm_X86) { @@ -193,7 +175,7 @@ private // a version identifier. Please note that this is considered // an obsolescent feature according to the POSIX spec, so a // custom solution is still preferred. - import core.sys.posix.ucontext; + import core.sys.posix.ucontext : getcontext, makecontext, MINSIGSTKSZ, swapcontext, ucontext_t; } } } @@ -202,226 +184,196 @@ private // Fiber Entry Point and Context Switch /////////////////////////////////////////////////////////////////////////////// -private +package { import core.atomic : atomicStore, cas, MemoryOrder; import core.exception : onOutOfMemoryError; import core.stdc.stdlib : abort; - extern (C) void fiber_entryPoint() nothrow /* LDC */ @assumeUsed + // Look above the definition of 'class Fiber' for some information about the implementation of this routine + version (AsmExternal) { - Fiber obj = Fiber.getThis(); - assert( obj ); - - version (SupportSanitizers) - { - informSanitizerOfFinishSwitchFiber(obj.__fake_stack, &obj.__from_stack_bottom, &obj.__from_stack_size); - } - - assert( ThreadBase.getThis().m_curr is obj.m_ctxt ); - atomicStore!(MemoryOrder.raw)(*cast(shared)&ThreadBase.getThis().m_lock, false); - obj.m_ctxt.tstack = obj.m_ctxt.bstack; - obj.m_state = Fiber.State.EXEC; - - try - { - obj.run(); - } - catch ( Throwable t ) - { - obj.m_unhandled = t; - } - - static if ( __traits( compiles, ucontext_t ) ) - obj.m_ucur = &obj.m_utxt; - - obj.m_state = Fiber.State.TERM; - obj.switchOut(); + extern (C) void fiber_switchContext( void** oldp, void* newp ) nothrow @nogc; + version (AArch64) + extern (C) void fiber_trampoline() nothrow; + version (LDC) // TODO: required? + version (LoongArch64) + extern (C) void fiber_trampoline() nothrow; } - - // Look above the definition of 'class Fiber' for some information about the implementation of this routine - version (AsmExternal) - { - extern (C) void fiber_switchContext( void** oldp, void* newp ) nothrow @nogc; - version (AArch64) - extern (C) void fiber_trampoline() nothrow; - version (LoongArch64) - extern (C) void fiber_trampoline() nothrow; - } - else version (LDC_Windows) - { - extern (C) void fiber_switchContext( void** oldp, void* newp ) nothrow @nogc @naked + else version (LDC_Windows) { - version (X86) - { - pragma(LDC_never_inline); - - __asm( - `// save current stack state - push %ebp - mov %esp, %ebp - push %edi - push %esi - push %ebx - push %fs:(0) - push %fs:(4) - push %fs:(8) - push %eax - - // store oldp again with more accurate address - mov 8(%ebp), %eax - mov %esp, (%eax) - // load newp to begin context switch - mov 12(%ebp), %esp - - // load saved state from new stack - pop %eax - pop %fs:(8) - pop %fs:(4) - pop %fs:(0) - pop %ebx - pop %esi - pop %edi - pop %ebp - - // 'return' to complete switch - pop %ecx - jmp *%ecx`, - "~{memory},~{ebp},~{esp},~{eax},~{ebx},~{ecx},~{esi},~{edi}" - ); - } - else version (X86_64) + extern (C) void fiber_switchContext( void** oldp, void* newp ) nothrow @nogc @naked { - // This inline asm assumes a return address has been pushed onto the stack - // (and so a stack not aligned to 16 bytes). - pragma(LDC_never_inline); - - __asm( - `// save current stack state - push %rbp - mov %rsp, %rbp - push %r12 - push %r13 - push %r14 - push %r15 - push %rdi - push %rsi - // 7 registers = 56 bytes; stack is now aligned to 16 bytes - sub $$0xA0, %rsp - movdqa %xmm6, 0x90(%rsp) - movdqa %xmm7, 0x80(%rsp) - movdqa %xmm8, 0x70(%rsp) - movdqa %xmm9, 0x60(%rsp) - movdqa %xmm10, 0x50(%rsp) - movdqa %xmm11, 0x40(%rsp) - movdqa %xmm12, 0x30(%rsp) - movdqa %xmm13, 0x20(%rsp) - movdqa %xmm14, 0x10(%rsp) - movdqa %xmm15, (%rsp) - push %rbx - xor %rax, %rax - push %gs:(%rax) - push %gs:8(%rax) - push %gs:16(%rax) - - // store oldp - mov %rsp, (%rcx) - // load newp to begin context switch - mov %rdx, %rsp - - // load saved state from new stack - pop %gs:16(%rax) - pop %gs:8(%rax) - pop %gs:(%rax) - pop %rbx; - movdqa (%rsp), %xmm15 - movdqa 0x10(%rsp), %xmm14 - movdqa 0x20(%rsp), %xmm13 - movdqa 0x30(%rsp), %xmm12 - movdqa 0x40(%rsp), %xmm11 - movdqa 0x50(%rsp), %xmm10 - movdqa 0x60(%rsp), %xmm9 - movdqa 0x70(%rsp), %xmm8 - movdqa 0x80(%rsp), %xmm7 - movdqa 0x90(%rsp), %xmm6 - add $$0xA0, %rsp - pop %rsi - pop %rdi - pop %r15 - pop %r14 - pop %r13 - pop %r12 - pop %rbp - - // 'return' to complete switch - pop %rcx - jmp *%rcx`, - "~{memory},~{rbp},~{rsp},~{rax},~{rbx},~{rcx},~{rsi},~{rdi},~{r12},~{r13},~{r14},~{r15}," ~ - "~{xmm6},~{xmm7},~{xmm8},~{xmm9},~{xmm10},~{xmm11},~{xmm12},~{xmm13},~{xmm14},~{xmm15}" - ); - } - else version (AsmAArch64_Windows) - { - pragma(LDC_never_inline); - asm pure nothrow @nogc + version (X86) { - `// save current stack state (similar to posix version in threadasm.S) - stp x19, x20, [sp, #-16]! - stp x21, x22, [sp, #-16]! - stp x23, x24, [sp, #-16]! - stp x25, x26, [sp, #-16]! - stp x27, x28, [sp, #-16]! - stp fp, lr, [sp, #-16]! - mov x19, sp // no need to scan FP registers, so snapshot sp here - - stp d8, d9, [sp, #-16]! - stp d10, d11, [sp, #-16]! - stp d12, d13, [sp, #-16]! - stp d14, d15, [sp, #-16]! - - ldr x20, [x18, #8] // read stack range from TEB - ldr x21, [x18, #16] - stp x20, x21, [sp, #-16]! - - ldr x20, [x18, #0x1478] // read Deallocation Stack - ldr w21, [x18, #0x1748] // read GuaranteedStackBytes - stp x20, x21, [sp, #-16]! - - // store oldp - str x19, [x0] - // load newp to begin context switch - sub x1, x1, #6*16 - mov sp, x1 - - ldp x20, x21, [sp], #16 // restore Deallocation/GuaranteedStackBytes - str x20, [x18, #0x1478] - str w21, [x18, #0x1748] // word only - - ldp x20, x21, [sp], #16 // restore stack range - str x20, [x18, #8] - str x21, [x18, #16] - - // load saved state from new stack - ldp d14, d15, [sp], #16 - ldp d12, d13, [sp], #16 - ldp d10, d11, [sp], #16 - ldp d8, d9, [sp], #16 - - ldp fp, lr, [sp], #16 - ldp x27, x28, [sp], #16 - ldp x25, x26, [sp], #16 - ldp x23, x24, [sp], #16 - ldp x21, x22, [sp], #16 - ldp x19, x20, [sp], #16 - - ret`; + pragma(LDC_never_inline); + + __asm( + `// save current stack state + push %ebp + mov %esp, %ebp + push %edi + push %esi + push %ebx + push %fs:(0) + push %fs:(4) + push %fs:(8) + push %eax + + // store oldp again with more accurate address + mov 8(%ebp), %eax + mov %esp, (%eax) + // load newp to begin context switch + mov 12(%ebp), %esp + + // load saved state from new stack + pop %eax + pop %fs:(8) + pop %fs:(4) + pop %fs:(0) + pop %ebx + pop %esi + pop %edi + pop %ebp + + // 'return' to complete switch + pop %ecx + jmp *%ecx`, + "~{memory},~{ebp},~{esp},~{eax},~{ebx},~{ecx},~{esi},~{edi}" + ); + } + else version (X86_64) + { + // This inline asm assumes a return address has been pushed onto the stack + // (and so a stack not aligned to 16 bytes). + pragma(LDC_never_inline); + + __asm( + `// save current stack state + push %rbp + mov %rsp, %rbp + push %r12 + push %r13 + push %r14 + push %r15 + push %rdi + push %rsi + // 7 registers = 56 bytes; stack is now aligned to 16 bytes + sub $$0xA0, %rsp + movdqa %xmm6, 0x90(%rsp) + movdqa %xmm7, 0x80(%rsp) + movdqa %xmm8, 0x70(%rsp) + movdqa %xmm9, 0x60(%rsp) + movdqa %xmm10, 0x50(%rsp) + movdqa %xmm11, 0x40(%rsp) + movdqa %xmm12, 0x30(%rsp) + movdqa %xmm13, 0x20(%rsp) + movdqa %xmm14, 0x10(%rsp) + movdqa %xmm15, (%rsp) + push %rbx + xor %rax, %rax + push %gs:(%rax) + push %gs:8(%rax) + push %gs:16(%rax) + + // store oldp + mov %rsp, (%rcx) + // load newp to begin context switch + mov %rdx, %rsp + + // load saved state from new stack + pop %gs:16(%rax) + pop %gs:8(%rax) + pop %gs:(%rax) + pop %rbx; + movdqa (%rsp), %xmm15 + movdqa 0x10(%rsp), %xmm14 + movdqa 0x20(%rsp), %xmm13 + movdqa 0x30(%rsp), %xmm12 + movdqa 0x40(%rsp), %xmm11 + movdqa 0x50(%rsp), %xmm10 + movdqa 0x60(%rsp), %xmm9 + movdqa 0x70(%rsp), %xmm8 + movdqa 0x80(%rsp), %xmm7 + movdqa 0x90(%rsp), %xmm6 + add $$0xA0, %rsp + pop %rsi + pop %rdi + pop %r15 + pop %r14 + pop %r13 + pop %r12 + pop %rbp + + // 'return' to complete switch + pop %rcx + jmp *%rcx`, + "~{memory},~{rbp},~{rsp},~{rax},~{rbx},~{rcx},~{rsi},~{rdi},~{r12},~{r13},~{r14},~{r15}," ~ + "~{xmm6},~{xmm7},~{xmm8},~{xmm9},~{xmm10},~{xmm11},~{xmm12},~{xmm13},~{xmm14},~{xmm15}" + ); + } + else version (AsmAArch64_Windows) + { + pragma(LDC_never_inline); + asm pure nothrow @nogc + { + `// save current stack state (similar to posix version in threadasm.S) + stp x19, x20, [sp, #-16]! + stp x21, x22, [sp, #-16]! + stp x23, x24, [sp, #-16]! + stp x25, x26, [sp, #-16]! + stp x27, x28, [sp, #-16]! + stp fp, lr, [sp, #-16]! + mov x19, sp // no need to scan FP registers, so snapshot sp here + + stp d8, d9, [sp, #-16]! + stp d10, d11, [sp, #-16]! + stp d12, d13, [sp, #-16]! + stp d14, d15, [sp, #-16]! + + ldr x20, [x18, #8] // read stack range from TEB + ldr x21, [x18, #16] + stp x20, x21, [sp, #-16]! + + ldr x20, [x18, #0x1478] // read Deallocation Stack + ldr w21, [x18, #0x1748] // read GuaranteedStackBytes + stp x20, x21, [sp, #-16]! + + // store oldp + str x19, [x0] + // load newp to begin context switch + sub x1, x1, #6*16 + mov sp, x1 + + ldp x20, x21, [sp], #16 // restore Deallocation/GuaranteedStackBytes + str x20, [x18, #0x1478] + str w21, [x18, #0x1748] // word only + + ldp x20, x21, [sp], #16 // restore stack range + str x20, [x18, #8] + str x21, [x18, #16] + + // load saved state from new stack + ldp d14, d15, [sp], #16 + ldp d12, d13, [sp], #16 + ldp d10, d11, [sp], #16 + ldp d8, d9, [sp], #16 + + ldp fp, lr, [sp], #16 + ldp x27, x28, [sp], #16 + ldp x25, x26, [sp], #16 + ldp x23, x24, [sp], #16 + ldp x21, x22, [sp], #16 + ldp x19, x20, [sp], #16 + + ret`; + } } + else + static assert(false); } - else - static assert(false); } - } - else + else extern (C) void fiber_switchContext( void** oldp, void* newp ) nothrow @nogc { // NOTE: The data pushed and popped in this routine must match the @@ -617,97 +569,6 @@ private } } -// Detect when a Fiber migrates between Threads for systems in which it may be -// unsafe to do so. A unittest below helps decide if CheckFiberMigration -// should be set for your system. - -version (LDC) -{ - version (Darwin) - { - version (ARM) version = CheckFiberMigration; - version (AArch64) version = CheckFiberMigration; - version (X86) version = CheckFiberMigration; - version (X86_64) version = CheckFiberMigration; - } - - version (Android) version = CheckFiberMigration; - - version (AArch64) version = CheckFiberMigration; - - version (PPC64) version = CheckFiberMigration; - - // Fiber migration across threads is (probably) not possible with ASan fakestack enabled (different parts of the stack - // will contain fakestack pointers that were created on different threads...) - version (SupportSanitizers) version = CheckFiberMigration; -} - -// Fiber support for SjLj style exceptions -// -// Exception handling based on setjmp/longjmp tracks the unwind points with a -// linked list stack managed by _Unwind_SjLj_Register and -// _Unwind_SjLj_Unregister. In the context of Fibers, the stack needs to be -// Fiber local, otherwise unwinding could weave through functions on other -// Fibers as opposed to just the current Fiber. The solution is to give each -// Fiber a m_sjljExStackTop. -// -// Two implementations known to have this SjLj stack design are GCC's libgcc -// and darwin libunwind for ARM (iOS). Functions to get/set the current SjLj -// stack are named differently in each implmentation: -// -// https://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-sjlj.c -// -// libgcc -// struct SjLj_Function_Context* _Unwind_SjLj_GetContext(void) -// void _Unwind_SjLj_SetContext(struct SjLj_Function_Context *fc) -// -// http://www.opensource.apple.com/source/libunwind/libunwind-30/src/Unwind-sjlj.c -// -// darwin (OS X) -// _Unwind_FunctionContext* __Unwind_SjLj_GetTopOfFunctionStack(); -// void __Unwind_SjLj_SetTopOfFunctionStack(_Unwind_FunctionContext* fc); -// -// These functions are not extern but if we peek at the implementations it -// turns out that _Unwind_SjLj_Register and _Unwind_SjLj_Unregister in both -// libraries will manipulate the stack as we need. - -version (GNU_SjLj_Exceptions) version = SjLj_Exceptions; -version (iOS) version(ARM) version = SjLj_Exceptions; - -version (SjLj_Exceptions) -private -{ - // libgcc struct SjLj_Function_Context and darwin struct - // _Unwind_FunctionContext have same initial layout so can get away with - // one type to mimic header of both here. - struct SjLjFuncContext - { - SjLjFuncContext* prev; - // rest of this struc we don't care about in swapSjLjStackTop below. - } - - extern(C) @nogc nothrow - { - void _Unwind_SjLj_Register(SjLjFuncContext* fc); - void _Unwind_SjLj_Unregister(SjLjFuncContext* fc); - } - - // Swap in a new stack top, returning the previous one - SjLjFuncContext* swapSjLjStackTop(SjLjFuncContext* newtop) @nogc nothrow - { - // register a dummy context to retrieve stack top, then plop our new - // stack top in its place before unregistering, making it the new top. - SjLjFuncContext fc; - _Unwind_SjLj_Register(&fc); - - SjLjFuncContext* prevtop = fc.prev; - fc.prev = newtop; - _Unwind_SjLj_Unregister(&fc); - - return prevtop; - } -} - /////////////////////////////////////////////////////////////////////////////// // Fiber /////////////////////////////////////////////////////////////////////////////// @@ -857,7 +718,7 @@ private * * Authors: Based on a design by Mikola Lysenko. */ -class Fiber +class Fiber : FiberBase { /////////////////////////////////////////////////////////////////////////// // Initialization @@ -899,14 +760,8 @@ class Fiber */ this( void function() fn, size_t sz = pageSize * defaultStackPages, size_t guardPageSize = pageSize ) nothrow - in { - assert( fn ); - } - do - { - allocStack( sz, guardPageSize ); - reset( fn ); + super( fn, sz, guardPageSize ); } @@ -928,271 +783,7 @@ class Fiber this( void delegate() dg, size_t sz = pageSize * defaultStackPages, size_t guardPageSize = pageSize ) nothrow { - allocStack( sz, guardPageSize ); - reset( cast(void delegate() const) dg ); - } - - - /** - * Cleans up any remaining resources used by this object. - */ - ~this() nothrow @nogc - { - // NOTE: A live reference to this object will exist on its associated - // stack from the first time its call() method has been called - // until its execution completes with State.TERM. Thus, the only - // times this dtor should be called are either if the fiber has - // terminated (and therefore has no active stack) or if the user - // explicitly deletes this object. The latter case is an error - // but is not easily tested for, since State.HOLD may imply that - // the fiber was just created but has never been run. There is - // not a compelling case to create a State.INIT just to offer a - // means of ensuring the user isn't violating this object's - // contract, so for now this requirement will be enforced by - // documentation only. - freeStack(); - } - - - /////////////////////////////////////////////////////////////////////////// - // General Actions - /////////////////////////////////////////////////////////////////////////// - - - /** - * Transfers execution to this fiber object. The calling context will be - * suspended until the fiber calls Fiber.yield() or until it terminates - * via an unhandled exception. - * - * Params: - * rethrow = Rethrow any unhandled exception which may have caused this - * fiber to terminate. - * - * In: - * This fiber must be in state HOLD. - * - * Throws: - * Any exception not handled by the joined thread. - * - * Returns: - * Any exception not handled by this fiber if rethrow = false, null - * otherwise. - */ - // Not marked with any attributes, even though `nothrow @nogc` works - // because it calls arbitrary user code. Most of the implementation - // is already `@nogc nothrow`, but in order for `Fiber.call` to - // propagate the attributes of the user's function, the Fiber - // class needs to be templated. - final Throwable call( Rethrow rethrow = Rethrow.yes ) - { - return rethrow ? call!(Rethrow.yes)() : call!(Rethrow.no); - } - - /// ditto - final Throwable call( Rethrow rethrow )() - { - callImpl(); - if ( m_unhandled ) - { - Throwable t = m_unhandled; - m_unhandled = null; - static if ( rethrow ) - throw t; - else - return t; - } - return null; - } - - private void callImpl() nothrow @nogc - in - { - assert( m_state == State.HOLD ); - } - do - { - Fiber cur = getThis(); - - static if ( __traits( compiles, ucontext_t ) ) - m_ucur = cur ? &cur.m_utxt : &Fiber.sm_utxt; - - setThis( this ); - this.switchIn(); - setThis( cur ); - - static if ( __traits( compiles, ucontext_t ) ) - m_ucur = null; - - // NOTE: If the fiber has terminated then the stack pointers must be - // reset. This ensures that the stack for this fiber is not - // scanned if the fiber has terminated. This is necessary to - // prevent any references lingering on the stack from delaying - // the collection of otherwise dead objects. The most notable - // being the current object, which is referenced at the top of - // fiber_entryPoint. - if ( m_state == State.TERM ) - { - m_ctxt.tstack = m_ctxt.bstack; - } - } - - /// Flag to control rethrow behavior of $(D $(LREF call)) - enum Rethrow : bool { no, yes } - - /** - * Resets this fiber so that it may be re-used, optionally with a - * new function/delegate. This routine should only be called for - * fibers that have terminated, as doing otherwise could result in - * scope-dependent functionality that is not executed. - * Stack-based classes, for example, may not be cleaned up - * properly if a fiber is reset before it has terminated. - * - * In: - * This fiber must be in state TERM or HOLD. - */ - final void reset() nothrow @nogc - in - { - assert( m_state == State.TERM || m_state == State.HOLD ); - } - do - { - m_ctxt.tstack = m_ctxt.bstack; - m_state = State.HOLD; - initStack(); - m_unhandled = null; - } - - /// ditto - final void reset( void function() fn ) nothrow @nogc - { - reset(); - m_call = fn; - } - - /// ditto - final void reset( void delegate() dg ) nothrow @nogc - { - reset(); - m_call = dg; - } - - /////////////////////////////////////////////////////////////////////////// - // General Properties - /////////////////////////////////////////////////////////////////////////// - - - /// A fiber may occupy one of three states: HOLD, EXEC, and TERM. - enum State - { - /** The HOLD state applies to any fiber that is suspended and ready to - be called. */ - HOLD, - /** The EXEC state will be set for any fiber that is currently - executing. */ - EXEC, - /** The TERM state is set when a fiber terminates. Once a fiber - terminates, it must be reset before it may be called again. */ - TERM - } - - - /** - * Gets the current state of this fiber. - * - * Returns: - * The state of this fiber as an enumerated value. - */ - final @property State state() const @safe pure nothrow @nogc - { - return m_state; - } - - /** - * Return true if migrating a Fiber between Threads is unsafe on this - * system. This is due to compiler optimizations that cache thread local - * variable addresses. When Fiber.yield() returns on a different - * Thread, the addresses refer to the previous Thread's variables. - */ - static @property bool migrationUnsafe() nothrow - { - version (CheckFiberMigration) - return true; - else - return false; - } - - /** - * Allow this Fiber to be resumed on a different thread for systems where - * Fiber migration is unsafe (migrationUnsafe() is true). Otherwise the - * first time a Fiber is resumed on a different Thread, a ThreadException - * is thrown. This provides the programmer a reminder to be careful and - * helps detect such usage in libraries being ported from other systems. - * - * Fiber migration on such systems can be done safely if you control all - * the code and know that thread locals are not involved. - * - * For systems without this issue, allowMigration does nothing, as you are - * always free to migrate. - */ - final void allowMigration() nothrow - { - // Does nothing if checking is disabled - version (CheckFiberMigration) - m_allowMigration = true; - } - - /////////////////////////////////////////////////////////////////////////// - // Actions on Calling Fiber - /////////////////////////////////////////////////////////////////////////// - - - /** - * Forces a context switch to occur away from the calling fiber. - */ - static void yield() nothrow @nogc - { - Fiber cur = getThis(); - assert( cur, "Fiber.yield() called with no active fiber" ); - assert( cur.m_state == State.EXEC ); - - static if ( __traits( compiles, ucontext_t ) ) - cur.m_ucur = &cur.m_utxt; - - cur.m_state = State.HOLD; - cur.switchOut(); - cur.m_state = State.EXEC; - } - - - /** - * Forces a context switch to occur away from the calling fiber and then - * throws obj in the calling fiber. - * - * Params: - * t = The object to throw. - * - * In: - * t must not be null. - */ - static void yieldAndThrow( Throwable t ) nothrow @nogc - in - { - assert( t ); - } - do - { - Fiber cur = getThis(); - assert( cur, "Fiber.yield() called with no active fiber" ); - assert( cur.m_state == State.EXEC ); - - static if ( __traits( compiles, ucontext_t ) ) - cur.m_ucur = &cur.m_utxt; - - cur.m_unhandled = t; - cur.m_state = State.HOLD; - cur.switchOut(); - cur.m_state = State.EXEC; + super( dg, sz, guardPageSize ); } @@ -1211,17 +802,7 @@ class Fiber */ static Fiber getThis() @safe nothrow @nogc { - // LDC NOTE: - // Currently, it is not safe to migrate fibers across threads when they - // use TLS at all, as LLVM might cache the TLS address lookup across a - // context switch (see https://github.com/ldc-developers/ldc/issues/666). - // Preventing inlining of this function, as well as switch{In,Out} - // below, enables users to do this at least as long as they are very - // careful about accessing TLS data themselves (such as in the shared - // fiber unittest below, which tends to sporadically crash with enabled - // optimizations if this prevent-inlining workaround is removed). - version (LDC) pragma(inline, false); - return sm_this; + return cast(Fiber) FiberBase.getThis(); } @@ -1242,42 +823,7 @@ class Fiber } } -private: - - // - // Fiber entry point. Invokes the function or delegate passed on - // construction (if any). - // - final void run() - { - m_call(); - } - - // - // Standard fiber data - // - Callable m_call; - bool m_isRunning; - version (LDC) - { - // Unconditionally add this field, that is only used with version(CheckFiberMigration), - // such that version(SupportSanitizers) does not change the ABI. - // (what is needed is version(SupportSanitizers_ABI || CheckFiberMigration)) - // The field is positioned after another bool, using up alignment padding space. - bool m_allowMigration; - } - Throwable m_unhandled; - State m_state; - - // Set first time switchIn called to indicate this Fiber's Thread - ThreadBase m_curThread; - - version (SjLj_Exceptions) - { - SjLjFuncContext* m_sjljExStackTop; - } - -private: +protected: /////////////////////////////////////////////////////////////////////////// // Stack Management /////////////////////////////////////////////////////////////////////////// @@ -1286,7 +832,7 @@ private: // // Allocate a new stack for this fiber. // - final void allocStack( size_t sz, size_t guardPageSize ) nothrow + final override void allocStack( size_t sz, size_t guardPageSize ) nothrow in { assert( !m_pmem && !m_ctxt ); @@ -1360,7 +906,9 @@ private: } else { - version (Posix) import core.sys.posix.sys.mman; // mmap, MAP_ANON + version (Posix) import core.sys.posix.sys.mman : MAP_ANON, MAP_FAILED, MAP_PRIVATE, mmap, + mprotect, PROT_NONE, PROT_READ, PROT_WRITE; + version (OpenBSD) import core.sys.posix.sys.mman : MAP_STACK; static if ( __traits( compiles, ucontext_t ) ) { @@ -1438,7 +986,7 @@ private: // // Free this fiber's stack. // - final void freeStack() nothrow @nogc + final override void freeStack() nothrow @nogc in(m_pmem) in(m_ctxt) { @@ -1454,7 +1002,7 @@ private: } else { - import core.sys.posix.sys.mman; // munmap + import core.sys.posix.sys.mman : mmap, munmap; static if ( __traits( compiles, mmap ) ) { @@ -1474,7 +1022,7 @@ private: // Initialize the allocated stack. // Look above the definition of 'class Fiber' for some information about the implementation of this routine // - final void initStack() nothrow @nogc + final override void initStack() nothrow @nogc in { assert( m_ctxt.tstack && m_ctxt.tstack == m_ctxt.bstack ); @@ -1549,7 +1097,7 @@ private: } enum sehChainEnd = cast(EXCEPTION_REGISTRATION*) 0xFFFFFFFF; - __gshared static fp_t finalHandler = null; + __gshared fp_t finalHandler = null; if ( finalHandler is null ) { version (LDC) @@ -1996,8 +1544,16 @@ private: // Only need to set return address ($r1). Everything else is fine // zero initialized. - pstack -= size_t.sizeof * 10; // skip past space reserved for $r22-$r31 - push(cast(size_t) &fiber_trampoline); // see threadasm.S for docs + version (LDC) // TODO: required? + { + pstack -= size_t.sizeof * 10; // skip past space reserved for $r22-$r31 + push(cast(size_t) &fiber_trampoline); // see threadasm.S for docs + } + else + { + pstack -= size_t.sizeof * 11; // skip past space reserved for $r21-$r31 + push(cast(size_t) &fiber_entryPoint); + } pstack += size_t.sizeof; // adjust sp (newp) above lr } else version (AsmAArch64_Posix) @@ -2068,677 +1624,6 @@ private: else static assert(0, "Not implemented"); } - - - StackContext* m_ctxt; - size_t m_size; - void* m_pmem; - - static if ( __traits( compiles, ucontext_t ) ) - { - // NOTE: The static ucontext instance is used to represent the context - // of the executing thread. - static ucontext_t sm_utxt = void; - ucontext_t m_utxt = void; - ucontext_t* m_ucur = null; - } - - -private: - /////////////////////////////////////////////////////////////////////////// - // Storage of Active Fiber - /////////////////////////////////////////////////////////////////////////// - - - // - // Sets a thread-local reference to the current fiber object. - // - static void setThis( Fiber f ) nothrow @nogc - { - sm_this = f; - } - - static Fiber sm_this; - - -private: - /////////////////////////////////////////////////////////////////////////// - // Context Switching - /////////////////////////////////////////////////////////////////////////// - - - // - // Switches into the stack held by this fiber. - // - final void switchIn() nothrow @nogc - { - // see note in getThis() - version (LDC) pragma(inline, false); - - ThreadBase tobj = ThreadBase.getThis(); - void** oldp = &tobj.m_curr.tstack; - void* newp = m_ctxt.tstack; - - version (CheckFiberMigration) - { - if (m_curThread is null || m_allowMigration) - m_curThread = tobj; - else if (tobj !is m_curThread) - { - // allocate and construct a ThreadException manually for @nogc - import core.stdc.stdlib : malloc; - import core.thread.threadbase : ThreadException; - enum threadExceptionSize = __traits(classInstanceSize, ThreadException); - if (auto e = cast(ThreadException) malloc(threadExceptionSize)) - { - import core.lifetime : emplace; - emplace(e, - "Migrating Fibers between Threads on this platform may lead " ~ - "to incorrect thread local variable access. To allow " ~ - "migration anyway, call Fiber.allowMigration()"); - m_unhandled = e; - // the exception will leak... - } - return; - } - } - else - { - m_curThread = tobj; - } - - version (SupportSanitizers) - { - // Fiber migration across threads is (probably) not possible with fakestack enabled (different parts of the stack - // will contain fakestack pointers that were created on different threads...) - m_ctxt.asan_fakestack = m_curThread.asan_fakestack; - } - - version (SjLj_Exceptions) - SjLjFuncContext* oldsjlj = swapSjLjStackTop(m_sjljExStackTop); - - // NOTE: The order of operations here is very important. The current - // stack top must be stored before m_lock is set, and pushContext - // must not be called until after m_lock is set. This process - // is intended to prevent a race condition with the suspend - // mechanism used for garbage collection. If it is not followed, - // a badly timed collection could cause the GC to scan from the - // bottom of one stack to the top of another, or to miss scanning - // a stack that still contains valid data. The old stack pointer - // oldp will be set again before the context switch to guarantee - // that it points to exactly the correct stack location so the - // successive pop operations will succeed. - *oldp = getStackTop(); - atomicStore!(MemoryOrder.raw)(*cast(shared)&tobj.m_lock, true); - tobj.pushContext( m_ctxt ); - - version (SupportSanitizers) - { - version (StackGrowsDown) - auto new_bottom = m_ctxt.bstack - m_size; - else - auto new_bottom = m_ctxt.bstack; - - informSanitizerOfStartSwitchFiber(&__fake_stack, new_bottom, m_size); - } - - fiber_switchContext( oldp, newp ); - - version (SupportSanitizers) - { - informSanitizerOfFinishSwitchFiber((m_state == State.TERM) ? null : __fake_stack, - &__from_stack_bottom, &__from_stack_size); - } - - // NOTE: As above, these operations must be performed in a strict order - // to prevent Bad Things from happening. - tobj.popContext(); - atomicStore!(MemoryOrder.raw)(*cast(shared)&tobj.m_lock, false); - tobj.m_curr.tstack = tobj.m_curr.bstack; - - version (SjLj_Exceptions) - m_sjljExStackTop = swapSjLjStackTop(oldsjlj); - } - - - // - // Switches out of the current stack and into the enclosing stack. - // - final void switchOut() nothrow @nogc - { - // see note in getThis() - version (LDC) pragma(inline, false); - - ThreadBase tobj = m_curThread; - void** oldp = &m_ctxt.tstack; - void* newp = tobj.m_curr.within.tstack; - - // NOTE: The order of operations here is very important. The current - // stack top must be stored before m_lock is set, and pushContext - // must not be called until after m_lock is set. This process - // is intended to prevent a race condition with the suspend - // mechanism used for garbage collection. If it is not followed, - // a badly timed collection could cause the GC to scan from the - // bottom of one stack to the top of another, or to miss scanning - // a stack that still contains valid data. The old stack pointer - // oldp will be set again before the context switch to guarantee - // that it points to exactly the correct stack location so the - // successive pop operations will succeed. - *oldp = getStackTop(); - atomicStore!(MemoryOrder.raw)(*cast(shared)&tobj.m_lock, true); - - version (SupportSanitizers) - { - informSanitizerOfStartSwitchFiber(&__fake_stack, __from_stack_bottom, __from_stack_size); - } - - fiber_switchContext( oldp, newp ); - - version (SupportSanitizers) - { - informSanitizerOfFinishSwitchFiber(__fake_stack, &__from_stack_bottom, &__from_stack_size); - } - - // NOTE: As above, these operations must be performed in a strict order - // to prevent Bad Things from happening. - // NOTE: If use of this fiber is multiplexed across threads, the thread - // executing here may be different from the one above, so get the - // current thread handle before unlocking, etc. - tobj = m_curThread; - atomicStore!(MemoryOrder.raw)(*cast(shared)&tobj.m_lock, false); - tobj.m_curr.tstack = tobj.m_curr.bstack; - } - - /////////////////////////////////////////////////////////////////////////// - // Address Sanitizer support - /////////////////////////////////////////////////////////////////////////// - version (SupportSanitizers_ABI) - { - private: - void* __fake_stack; - const(void)* __from_stack_bottom; - size_t __from_stack_size; - } -} - -/// -unittest { - int counter; - - class DerivedFiber : Fiber - { - this() - { - super( &run ); - } - - private : - void run() - { - counter += 2; - } - } - - void fiberFunc() - { - counter += 4; - Fiber.yield(); - counter += 8; - } - - // create instances of each type - Fiber derived = new DerivedFiber(); - Fiber composed = new Fiber( &fiberFunc ); - - assert( counter == 0 ); - - derived.call(); - assert( counter == 2, "Derived fiber increment." ); - - composed.call(); - assert( counter == 6, "First composed fiber increment." ); - - counter += 16; - assert( counter == 22, "Calling context increment." ); - - composed.call(); - assert( counter == 30, "Second composed fiber increment." ); - - // since each fiber has run to completion, each should have state TERM - assert( derived.state == Fiber.State.TERM ); - assert( composed.state == Fiber.State.TERM ); -} - -version (CoreUnittest) -{ - class TestFiber : Fiber - { - this() - { - super(&run); - } - - void run() - { - foreach (i; 0 .. 1000) - { - sum += i; - Fiber.yield(); - } - } - - enum expSum = 1000 * 999 / 2; - size_t sum; - } - - void runTen() - { - TestFiber[10] fibs; - foreach (ref fib; fibs) - fib = new TestFiber(); - - bool cont; - do { - cont = false; - foreach (fib; fibs) { - if (fib.state == Fiber.State.HOLD) - { - fib.call(); - cont |= fib.state != Fiber.State.TERM; - } - } - } while (cont); - - foreach (fib; fibs) - { - assert(fib.sum == TestFiber.expSum); - } - } -} - - -// Single thread running separate fibers -unittest -{ - runTen(); -} - - -// Multiple threads running separate fibers -unittest -{ - auto group = new ThreadGroup(); - foreach (_; 0 .. 4) - { - group.create(&runTen); - } - group.joinAll(); -} - -// Try to detect if version CheckFiberMigration should be set, or if it is -// set, make sure it behaves properly. The issue is that thread local addr -// may be cached by the compiler and that doesn't play well when Fibers -// migrate between Threads. This may only happen when optimization -// enabled. For that reason, should run this unittest with various -// optimization levels. -// -// https://github.com/ldc-developers/ldc/issues/666 -unittest -{ - import core.thread.osthread : Thread; - - static int tls; - - static yield_noinline() - { - import ldc.intrinsics; - pragma(LDC_never_inline); - Fiber.yield(); - } - - auto f = new Fiber( - { - ++tls; // happens on main thread - yield_noinline(); - ++tls; // happens on other thread, - // 1 if tls addr uncached, 2 if addr was cached - }); - - auto t = new Thread( - { - assert(tls == 0); - - version (CheckFiberMigration) - { - import core.thread.threadbase : ThreadException; - try - { - f.call(); - assert(false, "Should get ThreadException when Fiber migrated"); - } - catch (ThreadException ex) - { - } - - f.allowMigration(); - } - - f.call(); - // tls may be 0 (wrong) or 1 (good) depending on thread local handling - // by compiler - }); - - assert(tls == 0); - f.call(); - assert(tls == 1); - - t.start(); - t.join(); - - version (CheckFiberMigration) - { - assert(Fiber.migrationUnsafe); - } - else version (FiberMigrationTest) - { - assert(!Fiber.migrationUnsafe); - // If thread local addr not cached (correct behavior), then tls should - // still be 1. - assert(tls != 2, - "Not safe to migrate Fibers between Threads on your system. " ~ - "Consider setting version CheckFiberMigration for this system " ~ - "in thread.d"); - // verify un-cached correct case - assert(tls == 1); - } - else - { - if (tls == 2) - { - import core.stdc.stdio : puts; - puts("Not safe to migrate Fibers between Threads on your system. " ~ - "Consider setting version CheckFiberMigration for this system " ~ - "in thread.d"); - } - } -} - -// Multiple threads running shared fibers -unittest -{ - shared bool[10] locks; - TestFiber[10] fibs; - - void runShared() - { - bool cont; - do { - cont = false; - foreach (idx; 0 .. 10) - { - if (cas(&locks[idx], false, true)) - { - if (fibs[idx].state == Fiber.State.HOLD) - { - fibs[idx].call(); - cont |= fibs[idx].state != Fiber.State.TERM; - } - locks[idx].atomicStore(false); - } - else - { - cont = true; - } - } - } while (cont); - } - - foreach (ref fib; fibs) - { - fib = new TestFiber(); - fib.allowMigration(); - } - - auto group = new ThreadGroup(); - foreach (_; 0 .. 4) - { - group.create(&runShared); - } - group.joinAll(); - - foreach (fib; fibs) - { - assert(fib.sum == TestFiber.expSum); - } -} - - -// Test exception handling inside fibers. -unittest -{ - enum MSG = "Test message."; - string caughtMsg; - (new Fiber({ - try - { - throw new Exception(MSG); - } - catch (Exception e) - { - caughtMsg = e.msg; - } - })).call(); - assert(caughtMsg == MSG); -} - - -unittest -{ - int x = 0; - - (new Fiber({ - x++; - })).call(); - assert( x == 1 ); -} - -nothrow unittest -{ - new Fiber({}).call!(Fiber.Rethrow.no)(); -} - -unittest -{ - new Fiber({}).call(Fiber.Rethrow.yes); - new Fiber({}).call(Fiber.Rethrow.no); -} - -unittest -{ - enum MSG = "Test message."; - - try - { - (new Fiber(function() { - throw new Exception( MSG ); - })).call(); - assert( false, "Expected rethrown exception." ); - } - catch ( Throwable t ) - { - assert( t.msg == MSG ); - } -} - -// Test exception chaining when switching contexts in finally blocks. -unittest -{ - static void throwAndYield(string msg) { - try { - throw new Exception(msg); - } finally { - Fiber.yield(); - } - } - - static void fiber(string name) { - try { - try { - throwAndYield(name ~ ".1"); - } finally { - throwAndYield(name ~ ".2"); - } - } catch (Exception e) { - assert(e.msg == name ~ ".1"); - assert(e.next); - assert(e.next.msg == name ~ ".2"); - assert(!e.next.next); - } - } - - auto first = new Fiber(() => fiber("first")); - auto second = new Fiber(() => fiber("second")); - first.call(); - second.call(); - first.call(); - second.call(); - first.call(); - second.call(); - assert(first.state == Fiber.State.TERM); - assert(second.state == Fiber.State.TERM); -} - -// Test Fiber resetting -unittest -{ - static string method; - - static void foo() - { - method = "foo"; - } - - void bar() - { - method = "bar"; - } - - static void expect(Fiber fib, string s) - { - assert(fib.state == Fiber.State.HOLD); - fib.call(); - assert(fib.state == Fiber.State.TERM); - assert(method == s); method = null; - } - auto fib = new Fiber(&foo); - expect(fib, "foo"); - - fib.reset(); - expect(fib, "foo"); - - fib.reset(&foo); - expect(fib, "foo"); - - fib.reset(&bar); - expect(fib, "bar"); - - fib.reset(function void(){method = "function";}); - expect(fib, "function"); - - fib.reset(delegate void(){method = "delegate";}); - expect(fib, "delegate"); -} - -// Test unsafe reset in hold state -unittest -{ - auto fib = new Fiber(function {ubyte[2048] buf = void; Fiber.yield();}, 4096); - foreach (_; 0 .. 10) - { - fib.call(); - assert(fib.state == Fiber.State.HOLD); - fib.reset(); - } -} - -// stress testing GC stack scanning -unittest -{ - import core.memory; - import core.thread.osthread : Thread; - import core.time : dur; - - static void unreferencedThreadObject() - { - static void sleep() { Thread.sleep(dur!"msecs"(100)); } - auto thread = new Thread(&sleep).start(); - } - unreferencedThreadObject(); - GC.collect(); - - static class Foo - { - this(int value) - { - _value = value; - } - - int bar() - { - return _value; - } - - int _value; - } - - static void collect() - { - auto foo = new Foo(2); - assert(foo.bar() == 2); - GC.collect(); - Fiber.yield(); - GC.collect(); - assert(foo.bar() == 2); - } - - auto fiber = new Fiber(&collect); - - fiber.call(); - GC.collect(); - fiber.call(); - - // thread reference - auto foo = new Foo(2); - - void collect2() - { - assert(foo.bar() == 2); - GC.collect(); - Fiber.yield(); - GC.collect(); - assert(foo.bar() == 2); - } - - fiber = new Fiber(&collect2); - - fiber.call(); - GC.collect(); - fiber.call(); - - static void recurse(size_t cnt) - { - --cnt; - Fiber.yield(); - if (cnt) - { - auto fib = new Fiber(() { recurse(cnt); }); - fib.call(); - GC.collect(); - fib.call(); - } - } - fiber = new Fiber(() { recurse(20); }); - fiber.call(); } diff --git a/runtime/druntime/src/core/threadasm.S b/runtime/druntime/src/core/thread/fiber/switch_context_asm.S similarity index 100% rename from runtime/druntime/src/core/threadasm.S rename to runtime/druntime/src/core/thread/fiber/switch_context_asm.S diff --git a/runtime/druntime/src/core/thread/osthread.d b/runtime/druntime/src/core/thread/osthread.d index d78562525a..498961560d 100644 --- a/runtime/druntime/src/core/thread/osthread.d +++ b/runtime/druntime/src/core/thread/osthread.d @@ -12,14 +12,14 @@ module core.thread.osthread; -import core.thread.threadbase; -import core.thread.context; -import core.thread.types; import core.atomic; -import core.memory : GC, pageSize; -import core.time; import core.exception : onOutOfMemoryError; import core.internal.traits : externDFunc; +import core.memory : GC, pageSize; +import core.thread.context; +import core.thread.threadbase; +import core.thread.types; +import core.time; version (LDC) { @@ -46,7 +46,6 @@ version (LDC) } } - /////////////////////////////////////////////////////////////////////////////// // Platform Detection and Memory Allocation /////////////////////////////////////////////////////////////////////////////// @@ -79,28 +78,10 @@ else version (D_InlineAsm_X86_64) } } -version (Posix) -{ - version (AsmX86_Windows) {} else - version (AsmX86_Posix) {} else - version (AsmX86_64_Windows) {} else - version (AsmX86_64_Posix) {} else - version (AsmExternal) {} else - { - // NOTE: The ucontext implementation requires architecture specific - // data definitions to operate so testing for it must be done - // by checking for the existence of ucontext_t rather than by - // a version identifier. Please note that this is considered - // an obsolescent feature according to the POSIX spec, so a - // custom solution is still preferred. - import core.sys.posix.ucontext; - } -} - version (Windows) { import core.stdc.stdint : uintptr_t; // for _beginthreadex decl below - import core.stdc.stdlib; // for malloc, atexit + import core.stdc.stdlib : free, malloc, realloc; import core.sys.windows.basetsd /+: HANDLE+/; import core.sys.windows.threadaux /+: getThreadStackBottom, impersonate_thread, OpenThreadHandle+/; import core.sys.windows.winbase /+: CloseHandle, CREATE_SUSPENDED, DuplicateHandle, GetCurrentThread, @@ -116,25 +97,59 @@ version (Windows) } else version (Posix) { - import core.stdc.errno; - import core.sys.posix.semaphore; - import core.sys.posix.stdlib; // for malloc, valloc, free, atexit - import core.sys.posix.pthread; - import core.sys.posix.signal; - import core.sys.posix.time; + static import core.sys.posix.pthread; + static import core.sys.posix.signal; + import core.stdc.errno : EINTR, errno; + import core.sys.posix.pthread : pthread_atfork, pthread_attr_destroy, pthread_attr_getstack, pthread_attr_init, + pthread_attr_setstacksize, pthread_create, pthread_detach, pthread_getschedparam, pthread_join, pthread_self, + pthread_setschedparam, sched_get_priority_max, sched_get_priority_min, sched_param, sched_yield; + import core.sys.posix.semaphore : sem_init, sem_post, sem_t, sem_wait; + import core.sys.posix.signal : pthread_kill, sigaction, sigaction_t, sigdelset, sigfillset, sigset_t, sigsuspend, + SIGUSR1, stack_t; + import core.sys.posix.stdlib : free, malloc, realloc; + import core.sys.posix.sys.types : pthread_attr_t, pthread_key_t, pthread_t; + import core.sys.posix.time : nanosleep, timespec; version (Darwin) { - import core.sys.darwin.mach.thread_act; + 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, + thread_get_state, thread_resume, thread_suspend; import core.sys.darwin.pthread : pthread_mach_thread_np; + version (X86) + { + import core.sys.darwin.mach.thread_act : + x86_THREAD_STATE32, x86_THREAD_STATE32_COUNT, x86_thread_state32_t; + } + else version (X86_64) + { + import core.sys.darwin.mach.thread_act : + x86_THREAD_STATE64, x86_THREAD_STATE64_COUNT, x86_thread_state64_t; + } + else version (AArch64) + { + import core.sys.darwin.mach.thread_act : + ARM_THREAD_STATE64, ARM_THREAD_STATE64_COUNT, arm_thread_state64_t; + } + else version (PPC) + { + import core.sys.darwin.mach.thread_act : + PPC_THREAD_STATE32, PPC_THREAD_STATE32_COUNT, ppc_thread_state32_t; + } + else version (PPC64) + { + import core.sys.darwin.mach.thread_act : + PPC_THREAD_STATE64, PPC_THREAD_STATE64_COUNT, ppc_thread_state64_t; + } } } version (Solaris) { - import core.sys.solaris.sys.priocntl; - import core.sys.solaris.sys.types; 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; } version (GNU) @@ -876,8 +891,10 @@ class Thread : ThreadBase } else version (Posix) { - static if (__traits(compiles, pthread_setschedprio)) + static if (__traits(compiles, core.sys.posix.pthread.pthread_setschedprio)) { + import core.sys.posix.pthread : pthread_setschedprio; + if (auto err = pthread_setschedprio(m_addr, val)) { // ignore error if thread is not running => Bugzilla 8960 @@ -1161,52 +1178,6 @@ unittest t.join(); } -unittest -{ - // NOTE: This entire test is based on the assumption that no - // memory is allocated after the child thread is - // started. If an allocation happens, a collection could - // trigger, which would cause the synchronization below - // to cause a deadlock. - // NOTE: DO NOT USE LOCKS IN CRITICAL REGIONS IN NORMAL CODE. - - import core.sync.semaphore; - - auto sema = new Semaphore(), - semb = new Semaphore(); - - auto thr = new Thread( - { - thread_enterCriticalRegion(); - assert(thread_inCriticalRegion()); - sema.notify(); - - semb.wait(); - assert(thread_inCriticalRegion()); - - thread_exitCriticalRegion(); - assert(!thread_inCriticalRegion()); - sema.notify(); - - semb.wait(); - assert(!thread_inCriticalRegion()); - }); - - thr.start(); - - sema.wait(); - synchronized (ThreadBase.criticalRegionLock) - assert(thr.m_isInCriticalRegion); - semb.notify(); - - sema.wait(); - synchronized (ThreadBase.criticalRegionLock) - assert(!thr.m_isInCriticalRegion); - semb.notify(); - - thr.join(); -} - // https://issues.dlang.org/show_bug.cgi?id=22124 unittest { @@ -1219,36 +1190,6 @@ unittest static assert(!__traits(compiles, () @nogc => fun(thread, 3) )); } -unittest -{ - import core.sync.semaphore; - - shared bool inCriticalRegion; - auto sema = new Semaphore(), - semb = new Semaphore(); - - auto thr = new Thread( - { - thread_enterCriticalRegion(); - inCriticalRegion = true; - sema.notify(); - semb.wait(); - - Thread.sleep(dur!"msecs"(1)); - inCriticalRegion = false; - thread_exitCriticalRegion(); - }); - thr.start(); - - sema.wait(); - assert(inCriticalRegion); - semb.notify(); - - thread_suspendAll(); - assert(!inCriticalRegion); - thread_resumeAll(); -} - @nogc @safe nothrow unittest { @@ -1327,7 +1268,7 @@ private extern (D) ThreadBase attachThread(ThreadBase _thisThread) @nogc nothrow atomicStore!(MemoryOrder.raw)(thisThread.toThread.m_isRunning, true); } thisThread.m_isDaemon = true; - thisThread.tlsGCdataInit(); + thisThread.tlsRTdataInit(); Thread.setThis( thisThread ); version (Darwin) @@ -1402,7 +1343,7 @@ version (Windows) if ( addr == GetCurrentThreadId() ) { thisThread.m_hndl = GetCurrentThreadHandle(); - thisThread.tlsGCdataInit(); + thisThread.tlsRTdataInit(); Thread.setThis( thisThread ); version (SupportSanitizers) @@ -1416,7 +1357,7 @@ version (Windows) thisThread.m_hndl = OpenThreadHandle( addr ); impersonate_thread(addr, { - thisThread.tlsGCdataInit(); + thisThread.tlsRTdataInit(); Thread.setThis( thisThread ); version (SupportSanitizers) @@ -1451,8 +1392,107 @@ in (fn) void *sp = void; version (GNU) { - __builtin_unwind_init(); - sp = &sp; + // The generic solution below using a call to __builtin_unwind_init () + // followed by an assignment to sp has two issues: + // 1) On some archs it stores a huge amount of FP and Vector state which + // is not the subject of the scan - and, indeed might produce false + // hits. + // 2) Even on archs like X86, where there are no callee-saved FPRs/VRs there + // tend to be 'holes' in the frame allocations (to deal with alignment) which + // also will contain random data which could produce false positives. + // This solution stores only the integer callee-saved registers. + version (X86) + { + void*[3] regs = void; + asm pure nothrow @nogc + { + "movl %%ebx, %0" : "=m" (regs[0]); + "movl %%esi, %0" : "=m" (regs[1]); + "movl %%edi, %0" : "=m" (regs[2]); + } + sp = cast(void*)®s[0]; + } + else version (X86_64) + { + void*[5] regs = void; + asm pure nothrow @nogc + { + "movq %%rbx, %0" : "=m" (regs[0]); + "movq %%r12, %0" : "=m" (regs[1]); + "movq %%r13, %0" : "=m" (regs[2]); + "movq %%r14, %0" : "=m" (regs[3]); + "movq %%r15, %0" : "=m" (regs[4]); + } + sp = cast(void*)®s[0]; + } + else version (PPC) + { + void*[19] regs = void; + version (Darwin) + enum regname = "r"; + else + enum regname = ""; + static foreach (i; 0 .. regs.length) + {{ + enum int j = 13 + i; // source register + asm pure nothrow @nogc + { + "stw "~regname~j.stringof~", %0" : "=m" (regs[i]); + } + }} + sp = cast(void*)®s[0]; + } + else version (PPC64) + { + void*[19] regs = void; + version (Darwin) + enum regname = "r"; + else + enum regname = ""; + static foreach (i; 0 .. regs.length) + {{ + enum int j = 13 + i; // source register + asm pure nothrow @nogc + { + "std "~regname~j.stringof~", %0" : "=m" (regs[i]); + } + }} + sp = cast(void*)®s[0]; + } + else version (AArch64) + { + // Callee-save registers, x19-x28 according to AAPCS64, section + // 5.1.1. Include x29 fp because it optionally can be a callee + // saved reg + size_t[11] regs = void; + // store the registers in pairs + asm pure nothrow @nogc + { + "stp x19, x20, %0" : "=m" (regs[ 0]), "=m" (regs[1]); + "stp x21, x22, %0" : "=m" (regs[ 2]), "=m" (regs[3]); + "stp x23, x24, %0" : "=m" (regs[ 4]), "=m" (regs[5]); + "stp x25, x26, %0" : "=m" (regs[ 6]), "=m" (regs[7]); + "stp x27, x28, %0" : "=m" (regs[ 8]), "=m" (regs[9]); + "str x29, %0" : "=m" (regs[10]); + "mov %0, sp" : "=r" (sp); + } + } + else version (ARM) + { + // Callee-save registers, according to AAPCS, section 5.1.1. + // arm and thumb2 instructions + size_t[8] regs = void; + asm pure nothrow @nogc + { + "stm %0, {r4-r11}" : : "r" (regs.ptr) : "memory"; + "mov %0, sp" : "=r" (sp); + } + } + else + { + __builtin_unwind_init(); + sp = &sp; + } } else version (AsmX86_Posix) { @@ -1670,13 +1710,11 @@ private extern (D) void scanWindowsOnly(scope ScanAllThreadsTypeFn scan, ThreadB */ version (Posix) { - import core.sys.posix.unistd; - - alias getpid = core.sys.posix.unistd.getpid; + alias getpid = imported!"core.sys.posix.unistd".getpid; } else version (Windows) { - alias getpid = core.sys.windows.winbase.GetCurrentProcessId; + alias getpid = imported!"core.sys.windows.winbase".GetCurrentProcessId; } extern (C) @nogc nothrow @@ -1784,12 +1822,25 @@ private extern(D) void* getStackBottom() nothrow @nogc mov RAX, GS:[RAX]; ret; } + else version (GNU_InlineAsm) + { + void *bottom; + + version (X86) + asm pure nothrow @nogc { "movl %%fs:4, %0;" : "=r" (bottom); } + else version (X86_64) + asm pure nothrow @nogc { "movq %%gs:8, %0;" : "=r" (bottom); } + else + static assert(false, "Architecture not supported."); + + return bottom; + } else static assert(false, "Architecture not supported."); } else version (Darwin) { - import core.sys.darwin.pthread; + import core.sys.darwin.pthread : pthread_get_stackaddr_np; return pthread_get_stackaddr_np(pthread_self()); } else version (PThread_Getattr_NP) @@ -1854,21 +1905,11 @@ private extern(D) void* getStackBottom() nothrow @nogc */ private extern (D) bool suspend( Thread t ) nothrow @nogc { - Duration waittime = dur!"usecs"(10); - Lagain: if (!t.isRunning) { Thread.remove(t); return false; } - else if (t.m_isInCriticalRegion) - { - Thread.criticalRegionLock.unlock_nothrow(); - Thread.sleep(waittime); - if (waittime < dur!"msecs"(10)) waittime *= 2; - Thread.criticalRegionLock.lock_nothrow(); - goto Lagain; - } version (Windows) { @@ -2086,6 +2127,13 @@ private extern (D) bool suspend( Thread t ) nothrow @nogc return true; } +/** + * Runs the necessary operations required before stopping the world. + */ +extern (C) void thread_preStopTheWorld() nothrow { + Thread.slock.lock_nothrow(); +} + /** * Suspend all threads but the calling thread for "stop the world" garbage * collection runs. This function may be called multiple times, and must @@ -2118,13 +2166,11 @@ extern (C) void thread_suspendAll() nothrow return; } - Thread.slock.lock_nothrow(); + thread_preStopTheWorld(); { if ( ++suspendDepth > 1 ) return; - Thread.criticalRegionLock.lock_nothrow(); - scope (exit) Thread.criticalRegionLock.unlock_nothrow(); size_t cnt; bool suspendedSelf; Thread t = ThreadBase.sm_tbeg.toThread; @@ -2284,6 +2330,10 @@ extern (C) void thread_init() @nogc nothrow enum SIGRTMIN = SIGUSR1; enum SIGRTMAX = 32; } + else + { + import core.sys.posix.signal : SIGRTMAX, SIGRTMIN; + } if ( suspendSignalNumber == 0 ) { @@ -2307,8 +2357,12 @@ extern (C) void thread_init() @nogc nothrow // NOTE: SA_RESTART indicates that system calls should restart if they // are interrupted by a signal, but this is not available on all // Posix systems, even those that support multithreading. - static if ( __traits( compiles, SA_RESTART ) ) + static if (__traits(compiles, core.sys.posix.signal.SA_RESTART)) + { + import core.sys.posix.signal : SA_RESTART; + suspend.sa_flags = SA_RESTART; + } suspend.sa_handler = &thread_suspendHandler; // NOTE: We want to ignore all signals while in this handler, so fill @@ -2388,8 +2442,8 @@ version (Windows) obj.initDataStorage(); - Thread.setThis(obj); - Thread.add(obj); + Thread.registerThis(obj); + scope (exit) { Thread.remove(obj); @@ -2460,19 +2514,6 @@ else version (Posix) { private { - import core.stdc.errno; - import core.sys.posix.semaphore; - import core.sys.posix.stdlib; // for malloc, valloc, free, atexit - import core.sys.posix.pthread; - import core.sys.posix.signal; - import core.sys.posix.time; - - version (Darwin) - { - import core.sys.darwin.mach.thread_act; - import core.sys.darwin.pthread : pthread_mach_thread_np; - } - // // Entry point for POSIX threads // @@ -2501,8 +2542,9 @@ else version (Posix) obj.initDataStorage(); atomicStore!(MemoryOrder.raw)(obj.m_isRunning, true); - Thread.setThis(obj); // allocates lazy TLS (see Issue 11981) - Thread.add(obj); // can only receive signals from here on + + Thread.registerThis(obj); // can only receive signals from here on + scope (exit) { Thread.remove(obj); @@ -2530,14 +2572,18 @@ else version (Posix) // implementation actually requires default initialization // then pthread_cleanup should be restructured to maintain // the current lack of a link dependency. - static if ( __traits( compiles, pthread_cleanup ) ) + static if (__traits(compiles, core.sys.posix.pthread.pthread_cleanup)) { + import core.sys.posix.pthread : pthread_cleanup; + pthread_cleanup cleanup = void; cleanup.push( &thread_cleanupHandler, cast(void*) obj ); } - else static if ( __traits( compiles, pthread_cleanup_push ) ) + else static if (__traits(compiles, core.sys.posix.pthread.pthread_cleanup_push)) { - pthread_cleanup_push( &thread_cleanupHandler, cast(void*) obj ); + import core.sys.posix.pthread : pthread_cleanup_push; + + pthread_cleanup_push(&thread_cleanupHandler, cast(void*) obj); } else { @@ -2583,12 +2629,14 @@ else version (Posix) // NOTE: Normal cleanup is handled by scope(exit). - static if ( __traits( compiles, pthread_cleanup ) ) + static if (__traits(compiles, core.sys.posix.pthread.pthread_cleanup)) { cleanup.pop( 0 ); } - else static if ( __traits( compiles, pthread_cleanup_push ) ) + else static if (__traits(compiles, core.sys.posix.pthread.pthread_cleanup_push)) { + import core.sys.posix.pthread : pthread_cleanup_pop; + pthread_cleanup_pop( 0 ); } @@ -2602,6 +2650,39 @@ else version (Posix) __gshared sem_t suspendCount; + extern (C) bool thread_preSuspend( void* sp ) nothrow { + // NOTE: Since registers are being pushed and popped from the + // stack, any other stack data used by this function should + // be gone before the stack cleanup code is called below. + Thread obj = Thread.getThis(); + if (obj is null) + { + return false; + } + + if ( !obj.m_lock ) + { + obj.m_curr.tstack = sp; + } + + return true; + } + + extern (C) bool thread_postSuspend() nothrow { + Thread obj = Thread.getThis(); + if (obj is null) + { + return false; + } + + if ( !obj.m_lock ) + { + obj.m_curr.tstack = obj.m_curr.bstack; + } + + return true; + } + extern (C) void thread_suspendHandler( int sig ) nothrow in { @@ -2611,15 +2692,13 @@ else version (Posix) { void op(void* sp) nothrow { - // NOTE: Since registers are being pushed and popped from the - // stack, any other stack data used by this function should - // be gone before the stack cleanup code is called below. - Thread obj = Thread.getThis(); - assert(obj !is null); + bool supported = thread_preSuspend(getStackTop()); + assert(supported, "Tried to suspend a detached thread!"); - if ( !obj.m_lock ) + scope(exit) { - obj.m_curr.tstack = getStackTop(); + supported = thread_postSuspend(); + assert(supported, "Tried to suspend a detached thread!"); } sigset_t sigres = void; @@ -2635,11 +2714,6 @@ else version (Posix) assert( status == 0 ); sigsuspend( &sigres ); - - if ( !obj.m_lock ) - { - obj.m_curr.tstack = obj.m_curr.bstack; - } } callWithStackShell(&op); } @@ -2749,10 +2823,9 @@ private // Note: if the DLL is never unloaded, process termination kills all threads // and signals their handles before unconditionally calling DllMain(DLL_PROCESS_DETACH). - import core.sys.windows.winbase : FreeLibraryAndExitThread, GetModuleHandleExW, - GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT; - import core.sys.windows.windef : HMODULE; import core.sys.windows.dll : dll_getRefCount; + import core.sys.windows.winbase : FreeLibraryAndExitThread, GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, GetModuleHandleExW; + import core.sys.windows.windef : HMODULE; version (CRuntime_Microsoft) extern(C) extern __gshared ubyte msvcUsesUCRT; // from rt/msvc.d diff --git a/runtime/druntime/src/core/thread/threadbase.d b/runtime/druntime/src/core/thread/threadbase.d index 143eca7f5b..fb2e4e6579 100644 --- a/runtime/druntime/src/core/thread/threadbase.d +++ b/runtime/druntime/src/core/thread/threadbase.d @@ -7,7 +7,7 @@ * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). * (See accompanying file LICENSE) * Authors: Sean Kelly, Walter Bright, Alex Rønne Petersen, Martin Nowak - * Source: $(DRUNTIMESRC core/thread/osthread.d) + * Source: $(DRUNTIMESRC core/thread/threadbase.d) */ module core.thread.threadbase; @@ -32,9 +32,6 @@ private alias ScanDg = void delegate(void* pstart, void* pend) nothrow; alias rt_tlsgc_scan = externDFunc!("rt.tlsgc.scan", void function(void*, scope ScanDg) nothrow); - - alias rt_tlsgc_processGCMarks = - externDFunc!("rt.tlsgc.processGCMarks", void function(void*, scope IsMarkedDg) nothrow); } @@ -131,9 +128,14 @@ class ThreadBase return (no_context || not_registered); } - package void tlsGCdataInit() nothrow @nogc + ref void* tlsGCData() nothrow @nogc + { + return m_tlsgcdata; + } + + package void tlsRTdataInit() nothrow @nogc { - m_tlsgcdata = rt_tlsgc_init(); + m_tlsrtdata = rt_tlsgc_init(); } package void initDataStorage() nothrow @@ -150,18 +152,18 @@ class ThreadBase m_main.bstack = getStackBottom(); m_main.tstack = m_main.bstack; - tlsGCdataInit(); + tlsRTdataInit(); } package void destroyDataStorage() nothrow @nogc { - rt_tlsgc_destroy(m_tlsgcdata); - m_tlsgcdata = null; + rt_tlsgc_destroy(m_tlsrtdata); + m_tlsrtdata = null; } package void destroyDataStorageIfAvail() nothrow @nogc { - if (m_tlsgcdata) + if (m_tlsrtdata) destroyDataStorage(); } @@ -463,7 +465,6 @@ package: string m_name; size_t m_sz; bool m_isDaemon; - bool m_isInCriticalRegion; Throwable m_unhandled; /////////////////////////////////////////////////////////////////////////// @@ -485,6 +486,7 @@ package(core.thread): StackContext* m_curr; bool m_lock; private void* m_tlsgcdata; + private void* m_tlsrtdata; version (SupportSanitizers_ABI) { // Stores this thread's fake stack handler. This is to be stored into all StackContext's belonging to this thread (used for GC scanning). @@ -576,25 +578,17 @@ package(core.thread): return cast(Mutex)_slock.ptr; } - @property static Mutex criticalRegionLock() nothrow @nogc - { - return cast(Mutex)_criticalRegionLock.ptr; - } - __gshared align(mutexAlign) void[mutexClassInstanceSize] _slock; - __gshared align(mutexAlign) void[mutexClassInstanceSize] _criticalRegionLock; static void initLocks() @nogc nothrow { import core.lifetime : emplace; emplace!Mutex(_slock[]); - emplace!Mutex(_criticalRegionLock[]); } static void termLocks() @nogc nothrow { (cast(Mutex)_slock.ptr).__dtor(); - (cast(Mutex)_criticalRegionLock.ptr).__dtor(); } __gshared StackContext* sm_cbeg; @@ -768,6 +762,25 @@ package(core.thread): // to ensure that. slock.unlock_nothrow(); } + + // + // Add a thread to the global thread list, and also register This thread + // for use in `getThis`. This does both operations while protected by the + // static lock. This helps alternative GCs that use `thread_preSuspend` to + // determine whether druntime will provide scanning details during + // `thread_scanAll`. Without holding the lock for both operations, it's + // possible a `thread_preSuspend` would return true, but the scanning + // details would not be handled. + // + static void registerThis(ThreadBase t, bool rmAboutToStart = true) nothrow @nogc + { + slock.lock_nothrow(); + scope(exit) slock.unlock_nothrow(); + + setThis(t); + add(t, rmAboutToStart); + } + } @@ -990,6 +1003,13 @@ package __gshared uint suspendDepth = 0; private alias resume = externDFunc!("core.thread.osthread.resume", void function(ThreadBase) nothrow @nogc); +/** + * Run the necessary operation required after the world was resumed. + */ +extern (C) void thread_postRestartTheWorld() nothrow { + ThreadBase.slock.unlock_nothrow(); +} + /** * Resume all threads but the calling thread for "stop the world" garbage * collection runs. This function must be called once for each preceding @@ -1016,7 +1036,7 @@ do return; } - scope(exit) ThreadBase.slock.unlock_nothrow(); + scope(exit) thread_postRestartTheWorld(); { if (--suspendDepth > 0) return; @@ -1132,8 +1152,8 @@ private void scanAllTypeImpl(scope ScanAllThreadsTypeFn scan, void* curStackTop) scanWindowsOnly(scan, t); } - if (t.m_tlsgcdata !is null) - rt_tlsgc_scan(t.m_tlsgcdata, (p1, p2) => scan(ScanType.tls, p1, p2)); + if (t.m_tlsrtdata !is null) + rt_tlsgc_scan(t.m_tlsrtdata, (p1, p2) => scan(ScanType.tls, p1, p2)); } } @@ -1213,75 +1233,6 @@ extern (C) void thread_scanAll(scope ScanAllThreadsFn scan) nothrow private alias thread_yield = externDFunc!("core.thread.osthread.thread_yield", void function() @nogc nothrow); -/** - * Signals that the code following this call is a critical region. Any code in - * this region must finish running before the calling thread can be suspended - * by a call to thread_suspendAll. - * - * This function is, in particular, meant to help maintain garbage collector - * invariants when a lock is not used. - * - * A critical region is exited with thread_exitCriticalRegion. - * - * $(RED Warning): - * Using critical regions is extremely error-prone. For instance, using locks - * inside a critical region can easily result in a deadlock when another thread - * holding the lock already got suspended. - * - * The term and concept of a 'critical region' comes from - * $(LINK2 https://github.com/mono/mono/blob/521f4a198e442573c400835ef19bbb36b60b0ebb/mono/metadata/sgen-gc.h#L925, Mono's SGen garbage collector). - * - * In: - * The calling thread must be attached to the runtime. - */ -extern (C) void thread_enterCriticalRegion() @nogc -in -{ - assert(ThreadBase.getThis()); -} -do -{ - synchronized (ThreadBase.criticalRegionLock) - ThreadBase.getThis().m_isInCriticalRegion = true; -} - - -/** - * Signals that the calling thread is no longer in a critical region. Following - * a call to this function, the thread can once again be suspended. - * - * In: - * The calling thread must be attached to the runtime. - */ -extern (C) void thread_exitCriticalRegion() @nogc -in -{ - assert(ThreadBase.getThis()); -} -do -{ - synchronized (ThreadBase.criticalRegionLock) - ThreadBase.getThis().m_isInCriticalRegion = false; -} - - -/** - * Returns true if the current thread is in a critical region; otherwise, false. - * - * In: - * The calling thread must be attached to the runtime. - */ -extern (C) bool thread_inCriticalRegion() @nogc -in -{ - assert(ThreadBase.getThis()); -} -do -{ - synchronized (ThreadBase.criticalRegionLock) - return ThreadBase.getThis().m_isInCriticalRegion; -} - /** * A callback for thread errors in D during collections. Since an allocation is not possible @@ -1302,60 +1253,16 @@ package void onThreadError(string msg) nothrow @nogc throw error; } -unittest -{ - assert(!thread_inCriticalRegion()); - { - thread_enterCriticalRegion(); - - scope (exit) - thread_exitCriticalRegion(); - - assert(thread_inCriticalRegion()); - } - - assert(!thread_inCriticalRegion()); -} - - -/** - * Indicates whether an address has been marked by the GC. - */ -enum IsMarked : int -{ - no, /// Address is not marked. - yes, /// Address is marked. - unknown, /// Address is not managed by the GC. -} - -alias IsMarkedDg = int delegate(void* addr) nothrow; /// The isMarked callback function. +// GC-specific processing of TLSGC data. +alias ProcessTLSGCDataDg = void* delegate(void* data) nothrow; -/** - * This routine allows the runtime to process any special per-thread handling - * for the GC. This is needed for taking into account any memory that is - * referenced by non-scanned pointers but is about to be freed. That currently - * means the array append cache. - * - * Params: - * isMarked = The function used to check if $(D addr) is marked. - * - * In: - * This routine must be called just prior to resuming all threads. - */ -extern(C) void thread_processGCMarks(scope IsMarkedDg isMarked) nothrow +void thread_processTLSGCData(ProcessTLSGCDataDg dg) nothrow { for (ThreadBase t = ThreadBase.sm_tbeg; t; t = t.next) - { - /* Can be null if collection was triggered between adding a - * thread and calling rt_tlsgc_init. - */ - if (t.m_tlsgcdata !is null) - rt_tlsgc_processGCMarks(t.m_tlsgcdata, isMarked); - } + t.m_tlsgcdata = dg(t.m_tlsgcdata); } - /** * Returns the stack top of the currently active stack within the calling * thread. diff --git a/runtime/druntime/src/core/thread/threadgroup.d b/runtime/druntime/src/core/thread/threadgroup.d index d00ce05854..b2ab931390 100644 --- a/runtime/druntime/src/core/thread/threadgroup.d +++ b/runtime/druntime/src/core/thread/threadgroup.d @@ -6,7 +6,7 @@ * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). * (See accompanying file LICENSE) * Authors: Sean Kelly, Walter Bright, Alex Rønne Petersen, Martin Nowak - * Source: $(DRUNTIMESRC core/thread/osthread.d) + * Source: $(DRUNTIMESRC core/thread/threadgroup.d) */ module core.thread.threadgroup; diff --git a/runtime/druntime/src/core/thread/types.d b/runtime/druntime/src/core/thread/types.d index 991299b808..7d1a5fe7b4 100644 --- a/runtime/druntime/src/core/thread/types.d +++ b/runtime/druntime/src/core/thread/types.d @@ -6,7 +6,7 @@ * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). * (See accompanying file LICENSE) * Authors: Sean Kelly, Walter Bright, Alex Rønne Petersen, Martin Nowak - * Source: $(DRUNTIMESRC core/thread/osthread.d) + * Source: $(DRUNTIMESRC core/thread/types.d) */ module core.thread.types; @@ -20,7 +20,7 @@ version (Windows) else version (Posix) { - import core.sys.posix.pthread; + import core.sys.posix.sys.types : pthread_t; alias ThreadID = pthread_t; } @@ -63,7 +63,7 @@ shared static this() { version (Posix) { - import core.sys.posix.unistd; + import core.sys.posix.unistd : _SC_THREAD_STACK_MIN, sysconf; PTHREAD_STACK_MIN = cast(size_t)sysconf(_SC_THREAD_STACK_MIN); } diff --git a/runtime/druntime/src/core/time.d b/runtime/druntime/src/core/time.d index c3192fc39f..8189e67f8a 100644 --- a/runtime/druntime/src/core/time.d +++ b/runtime/druntime/src/core/time.d @@ -66,19 +66,8 @@ module core.time; import core.exception; -import core.stdc.time; -import core.stdc.stdio; import core.internal.string; - -version (Windows) -{ -import core.sys.windows.winbase /+: QueryPerformanceCounter, QueryPerformanceFrequency+/; -} -else version (Posix) -{ -import core.sys.posix.time; -import core.sys.posix.sys.time; -} +import core.stdc.time : time; version (OSX) version = Darwin; @@ -89,12 +78,30 @@ else version (TVOS) else version (WatchOS) version = Darwin; +version (Windows) +{ + import core.sys.windows.winbase /+: QueryPerformanceCounter, QueryPerformanceFrequency+/; +} +else version (Darwin) +{ + import core.sys.posix.sys.time : gettimeofday, timeval; + import core.sys.posix.time : timespec; +} +else version (Posix) +{ + import core.sys.posix.sys.time : gettimeofday, timeval; + import core.sys.posix.time : clock_getres, clock_gettime, CLOCK_MONOTONIC, timespec; +} + +version (unittest) import core.stdc.stdio : printf; + + //This probably should be moved somewhere else in druntime which //is Darwin-specific. version (Darwin) { -public import core.sys.darwin.mach.kern_return; +import core.sys.darwin.mach.kern_return : kern_return_t; extern(C) nothrow @nogc { @@ -701,9 +708,10 @@ public: version (CoreUnittest) unittest { - foreach (D; AliasSeq!(Duration, const Duration, immutable Duration)) + alias Types = AliasSeq!(Duration, const Duration, immutable Duration); + foreach (D; Types) { - foreach (E; AliasSeq!(Duration, const Duration, immutable Duration)) + foreach (E; Types) { assert((cast(D)Duration(5)) + (cast(E)Duration(7)) == Duration(12)); assert((cast(D)Duration(5)) - (cast(E)Duration(7)) == Duration(-2)); @@ -2693,7 +2701,7 @@ unittest // It would be too expensive to cover a large range of possible values for // ticks, so we use random values in an attempt to get reasonable coverage. - import core.stdc.stdlib; + import core.stdc.stdlib : rand, srand; immutable seed = cast(int)time(null); srand(seed); scope(failure) printf("seed %d\n", seed); @@ -2707,7 +2715,7 @@ unittest // than or equal to freq5, which at one point was considered for MonoTime's // ticksPerSecond rather than using the system's actual clock frequency, so // it seemed like a good test case to have. - import core.stdc.math; + import core.stdc.math : floor, log10, pow; immutable numDigitsMinus1 = cast(int)floor(log10(freq5)); auto freq6 = cast(long)pow(10, numDigitsMinus1); if (freq5 > freq6) @@ -3924,7 +3932,14 @@ version (CoreUnittest) const(char)* numToStringz()(long value) @trusted pure not } -import core.internal.traits : AliasSeq; +/+ + dmd @@@BUG18223@@@ + A selective import of `AliasSeq` happens to bleed through and causes symbol clashes downstream. + +/ +version (none) + import core.internal.traits : AliasSeq; +else + import core.internal.traits; /+ An adjusted copy of std.exception.assertThrown. +/ diff --git a/runtime/druntime/src/etc/linux/memoryerror.d b/runtime/druntime/src/etc/linux/memoryerror.d index e1363e9e10..b44a7f6d68 100644 --- a/runtime/druntime/src/etc/linux/memoryerror.d +++ b/runtime/druntime/src/etc/linux/memoryerror.d @@ -1,326 +1,453 @@ /** - * Handle page protection errors using D errors (exceptions). $(D NullPointerError) is - * thrown when dereferencing null pointers. A system-dependent error is thrown in other - * cases. - * - * Note: Only x86 and x86_64 are supported for now. + * Handle page protection errors using D errors (exceptions) or asserts. * * License: Distributed under the * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). * (See accompanying file LICENSE_1_0.txt) * Authors: Amaury SECHET, FeepingCreature, Vladimir Panteleev - * Source: $(DRUNTIMESRC etc/linux/memory.d) + * Source: $(DRUNTIMESRC etc/linux/memoryerror.d) */ module etc.linux.memoryerror; -version (CRuntime_Glibc) +version (linux) +{ + version (DigitalMars) + { + version (CRuntime_Glibc) + { + version (X86) + version = MemoryErrorSupported; + else version (X86_64) + version = MemoryErrorSupported; + } + } +} + +version (linux) { version (X86) - version = MemoryErrorSupported; - version (X86_64) - version = MemoryErrorSupported; + version = MemoryAssertSupported; + else version (X86_64) + version = MemoryAssertSupported; + else version (ARM) + version = MemoryAssertSupported; + else version (AArch64) + version = MemoryAssertSupported; + else version (PPC64) + version = MemoryAssertSupported; } -version (MemoryErrorSupported): -@system: +version (MemoryErrorSupported) + version = AnySupported; +else version (MemoryAssertSupported) + version = AnySupported; -import core.sys.posix.signal; -import core.sys.posix.ucontext; +version (AnySupported): -// Register and unregister memory error handler. +import core.sys.posix.signal : SA_SIGINFO, sigaction, sigaction_t, siginfo_t, SIGSEGV; +import ucontext = core.sys.posix.ucontext; -bool registerMemoryErrorHandler() nothrow +version (MemoryAssertSupported) { - sigaction_t action; - action.sa_sigaction = &handleSignal; - action.sa_flags = SA_SIGINFO; - - auto oldptr = &old_sigaction; - - return !sigaction(SIGSEGV, &action, oldptr); + import core.sys.posix.signal : SA_ONSTACK, sigaltstack, SIGSTKSZ, stack_t; } -bool deregisterMemoryErrorHandler() nothrow -{ - auto oldptr = &old_sigaction; +@system: - return !sigaction(SIGSEGV, oldptr, null); -} +// The first 64Kb are reserved for detecting null pointer dereferences. +// TODO: this is a platform-specific assumption, can be made more robust +private enum size_t MEMORY_RESERVED_FOR_NULL_DEREFERENCE = 4096 * 16; -/** - * Thrown on POSIX systems when a SIGSEGV signal is received. - */ -class InvalidPointerError : Error +version (MemoryErrorSupported) { - this(string file = __FILE__, size_t line = __LINE__, Throwable next = null) nothrow + /** + * Register memory error handler, store the old handler. + * + * `NullPointerError` is thrown when dereferencing null pointers. + * A generic `InvalidPointerError` error is thrown in other cases. + * + * Returns: whether the registration was successful + * + * Limitations: Only x86 and x86_64 are supported for now. + */ + bool registerMemoryErrorHandler() nothrow { - super("", file, line, next); + sigaction_t action; + action.sa_sigaction = &handleSignal; + action.sa_flags = SA_SIGINFO; + + auto oldptr = &oldSigactionMemoryError; + + return !sigaction(SIGSEGV, &action, oldptr); } - this(Throwable next, string file = __FILE__, size_t line = __LINE__) nothrow + /** + * Revert the memory error handler back to the one from before calling `registerMemoryErrorHandler()`. + * + * Returns: whether the registration of the old handler was successful + */ + bool deregisterMemoryErrorHandler() nothrow { - super("", file, line, next); + auto oldptr = &oldSigactionMemoryError; + + return !sigaction(SIGSEGV, oldptr, null); } -} -/** - * Thrown on null pointer dereferences. - */ -class NullPointerError : InvalidPointerError -{ - this(string file = __FILE__, size_t line = __LINE__, Throwable next = null) nothrow + /** + * Thrown on POSIX systems when a SIGSEGV signal is received. + */ + class InvalidPointerError : Error { - super(file, line, next); + this(string file = __FILE__, size_t line = __LINE__, Throwable next = null) nothrow + { + super("", file, line, next); + } + + this(Throwable next, string file = __FILE__, size_t line = __LINE__) nothrow + { + super("", file, line, next); + } } - this(Throwable next, string file = __FILE__, size_t line = __LINE__) nothrow + /** + * Thrown on null pointer dereferences. + */ + class NullPointerError : InvalidPointerError { - super(file, line, next); - } -} + this(string file = __FILE__, size_t line = __LINE__, Throwable next = null) nothrow + { + super(file, line, next); + } -unittest -{ - int* getNull() { return null; } + this(Throwable next, string file = __FILE__, size_t line = __LINE__) nothrow + { + super(file, line, next); + } + } - assert(registerMemoryErrorHandler()); + unittest + { + int* getNull() { return null; } - bool b; + assert(registerMemoryErrorHandler()); - try - { - *getNull() = 42; - } - catch (NullPointerError) - { - b = true; - } + bool b; - assert(b); + try + { + *getNull() = 42; + } + catch (NullPointerError) + { + b = true; + } - b = false; + assert(b); - try - { - *getNull() = 42; - } - catch (InvalidPointerError) - { - b = true; - } + b = false; - assert(b); + try + { + *getNull() = 42; + } + catch (InvalidPointerError) + { + b = true; + } - assert(deregisterMemoryErrorHandler()); -} + assert(b); -// Signal handler space. + assert(deregisterMemoryErrorHandler()); + } -private: + // Signal handler space. -__gshared sigaction_t old_sigaction; + private: -alias typeof(ucontext_t.init.uc_mcontext.gregs[0]) RegType; + __gshared sigaction_t oldSigactionMemoryError; -version (X86_64) -{ - static RegType savedRDI, savedRSI; + alias RegType = typeof(ucontext.ucontext_t.init.uc_mcontext.gregs[0]); - extern(C) - void handleSignal(int signum, siginfo_t* info, void* contextPtr) nothrow + version (X86_64) { - auto context = cast(ucontext_t*)contextPtr; - - // Save registers into global thread local, to allow recovery. - savedRDI = context.uc_mcontext.gregs[REG_RDI]; - savedRSI = context.uc_mcontext.gregs[REG_RSI]; - - // Hijack current context so we call our handler. - auto rip = context.uc_mcontext.gregs[REG_RIP]; - auto addr = cast(RegType) info.si_addr; - context.uc_mcontext.gregs[REG_RDI] = addr; - context.uc_mcontext.gregs[REG_RSI] = rip; - context.uc_mcontext.gregs[REG_RIP] = cast(RegType) ((rip != addr)?&sigsegvDataHandler:&sigsegvCodeHandler); - } + static RegType savedRDI, savedRSI; + + extern(C) + void handleSignal(int signum, siginfo_t* info, void* contextPtr) nothrow + { + auto context = cast(ucontext.ucontext_t*)contextPtr; + + // Save registers into global thread local, to allow recovery. + savedRDI = context.uc_mcontext.gregs[ucontext.REG_RDI]; + savedRSI = context.uc_mcontext.gregs[ucontext.REG_RSI]; + + // Hijack current context so we call our handler. + auto rip = context.uc_mcontext.gregs[ucontext.REG_RIP]; + auto addr = cast(RegType) info.si_addr; + context.uc_mcontext.gregs[ucontext.REG_RDI] = addr; + context.uc_mcontext.gregs[ucontext.REG_RSI] = rip; + context.uc_mcontext.gregs[ucontext.REG_RIP] = cast(RegType) ((rip != addr)?&sigsegvDataHandler:&sigsegvCodeHandler); + } - // All handler functions must be called with faulting address in RDI and original RIP in RSI. + // All handler functions must be called with faulting address in RDI and original RIP in RSI. - // This function is called when the segfault's cause is to call an invalid function pointer. - void sigsegvCodeHandler() - { - asm + // This function is called when the segfault's cause is to call an invalid function pointer. + void sigsegvCodeHandler() { - naked; + asm + { + naked; - // Handle the stack for an invalid function call (segfault at RIP). - // With the return pointer, the stack is now alligned. - push RBP; - mov RBP, RSP; + // Handle the stack for an invalid function call (segfault at RIP). + // With the return pointer, the stack is now alligned. + push RBP; + mov RBP, RSP; - jmp sigsegvDataHandler; + jmp sigsegvDataHandler; + } } - } - void sigsegvDataHandler() - { - asm + void sigsegvDataHandler() { - naked; - - push RSI; // return address (original RIP). - push RBP; // old RBP - mov RBP, RSP; - - pushfq; // Save flags. - push RAX; // RAX, RCX, RDX, and R8 to R11 are trash registers and must be preserved as local variables. - push RCX; - push RDX; - push R8; - push R9; - push R10; - push R11; // With 10 pushes, the stack is still aligned. - - // Parameter address is already set as RAX. - call sigsegvUserspaceProcess; - - // Restore RDI and RSI values. - call restoreRDI; - push RAX; // RDI is in RAX. It is pushed and will be poped back to RDI. - - call restoreRSI; - mov RSI, RAX; - - pop RDI; - - // Restore trash registers value. - pop R11; - pop R10; - pop R9; - pop R8; - pop RDX; - pop RCX; - pop RAX; - popfq; // Restore flags. - - // Return - pop RBP; - ret; + asm + { + naked; + + push RSI; // return address (original RIP). + push RBP; // old RBP + mov RBP, RSP; + + pushfq; // Save flags. + push RAX; // RAX, RCX, RDX, and R8 to R11 are trash registers and must be preserved as local variables. + push RCX; + push RDX; + push R8; + push R9; + push R10; + push R11; // With 10 pushes, the stack is still aligned. + + // Parameter address is already set as RAX. + call sigsegvUserspaceProcess; + + // Restore RDI and RSI values. + call restoreRDI; + push RAX; // RDI is in RAX. It is pushed and will be poped back to RDI. + + call restoreRSI; + mov RSI, RAX; + + pop RDI; + + // Restore trash registers value. + pop R11; + pop R10; + pop R9; + pop R8; + pop RDX; + pop RCX; + pop RAX; + popfq; // Restore flags. + + // Return + pop RBP; + ret; + } } - } - // The return value is stored in EAX and EDX, so this function restore the correct value for theses registers. - RegType restoreRDI() - { - return savedRDI; - } + // The return value is stored in EAX and EDX, so this function restore the correct value for theses registers. + RegType restoreRDI() + { + return savedRDI; + } - RegType restoreRSI() - { - return savedRSI; + RegType restoreRSI() + { + return savedRSI; + } } -} -else version (X86) -{ - static RegType savedEAX, savedEDX; - - extern(C) - void handleSignal(int signum, siginfo_t* info, void* contextPtr) nothrow + else version (X86) { - auto context = cast(ucontext_t*)contextPtr; - - // Save registers into global thread local, to allow recovery. - savedEAX = context.uc_mcontext.gregs[REG_EAX]; - savedEDX = context.uc_mcontext.gregs[REG_EDX]; - - // Hijack current context so we call our handler. - auto eip = context.uc_mcontext.gregs[REG_EIP]; - auto addr = cast(RegType) info.si_addr; - context.uc_mcontext.gregs[REG_EAX] = addr; - context.uc_mcontext.gregs[REG_EDX] = eip; - context.uc_mcontext.gregs[REG_EIP] = cast(RegType) ((eip != addr)?&sigsegvDataHandler:&sigsegvCodeHandler); - } + static RegType savedEAX, savedEDX; - // All handler functions must be called with faulting address in EAX and original EIP in EDX. - - // This function is called when the segfault's cause is to call an invalid function pointer. - void sigsegvCodeHandler() - { - asm + extern(C) + void handleSignal(int signum, siginfo_t* info, void* contextPtr) nothrow { - naked; + auto context = cast(ucontext.ucontext_t*)contextPtr; + + // Save registers into global thread local, to allow recovery. + savedEAX = context.uc_mcontext.gregs[ucontext.REG_EAX]; + savedEDX = context.uc_mcontext.gregs[ucontext.REG_EDX]; + + // Hijack current context so we call our handler. + auto eip = context.uc_mcontext.gregs[ucontext.REG_EIP]; + auto addr = cast(RegType) info.si_addr; + context.uc_mcontext.gregs[ucontext.REG_EAX] = addr; + context.uc_mcontext.gregs[ucontext.REG_EDX] = eip; + context.uc_mcontext.gregs[ucontext.REG_EIP] = cast(RegType) ((eip != addr)?&sigsegvDataHandler:&sigsegvCodeHandler); + } - // Handle the stack for an invalid function call (segfault at EIP). - // 4 bytes are used for function pointer; We need 12 byte to keep stack aligned. - sub ESP, 12; - mov 8[ESP], EBP; - mov EBP, ESP; + // All handler functions must be called with faulting address in EAX and original EIP in EDX. - jmp sigsegvDataHandler; + // This function is called when the segfault's cause is to call an invalid function pointer. + void sigsegvCodeHandler() + { + asm + { + naked; + + // Handle the stack for an invalid function call (segfault at EIP). + // 4 bytes are used for function pointer; We need 12 byte to keep stack aligned. + sub ESP, 12; + mov [ESP + 8], EBP; + mov EBP, ESP; + + jmp sigsegvDataHandler; + } } - } - void sigsegvDataHandler() - { - asm + void sigsegvDataHandler() { - naked; + asm + { + naked; - // We jump directly here if we are in a valid function call case. - push EDX; // return address (original EIP). - push EBP; // old EBP - mov EBP, ESP; + // We jump directly here if we are in a valid function call case. + push EDX; // return address (original EIP). + push EBP; // old EBP + mov EBP, ESP; - pushfd; // Save flags. - push ECX; // ECX is a trash register and must be preserved as local variable. - // 4 pushes have been done. The stack is aligned. + pushfd; // Save flags. + push ECX; // ECX is a trash register and must be preserved as local variable. + // 4 pushes have been done. The stack is aligned. - // Parameter address is already set as EAX. - call sigsegvUserspaceProcess; + // Parameter address is already set as EAX. + call sigsegvUserspaceProcess; - // Restore register values and return. - call restoreRegisters; + // Restore register values and return. + call restoreRegisters; - pop ECX; - popfd; // Restore flags. + pop ECX; + popfd; // Restore flags. - // Return - pop EBP; - ret; + // Return + pop EBP; + ret; + } + } + + // The return value is stored in EAX and EDX, so this function restore the correct value for theses registers. + RegType[2] restoreRegisters() + { + RegType[2] restore; + restore[0] = savedEAX; + restore[1] = savedEDX; + + return restore; } } + else + { + static assert(false, "Unsupported architecture."); + } - // The return value is stored in EAX and EDX, so this function restore the correct value for theses registers. - RegType[2] restoreRegisters() + // User space handler + void sigsegvUserspaceProcess(void* address) { - RegType[2] restore; - restore[0] = savedEAX; - restore[1] = savedEDX; + // SEGV_MAPERR, SEGV_ACCERR. + // The first page is protected to detect null dereferences. + if ((cast(size_t) address) < MEMORY_RESERVED_FOR_NULL_DEREFERENCE) + { + throw new NullPointerError(); + } - return restore; + throw new InvalidPointerError(); } } -else -{ - static assert(false, "Unsupported architecture."); -} -// This should be calculated by druntime. -// TODO: Add a core.memory function for this. -enum PAGE_SIZE = 4096; +version (MemoryAssertSupported) +{ + private __gshared sigaction_t oldSigactionMemoryAssert; // sigaction before calling `registerMemoryAssertHandler` + + /** + * Registers a signal handler for SIGSEGV that turns them into an assertion failure, + * providing a more descriptive error message and stack trace if the program is + * compiled with debug info and D assertions (as opposed to C assertions). + * + * Differences with the `registerMemoryErrorHandler` version are: + * - The handler is registered with SA_ONSTACK, so it can handle stack overflows. + * - It uses `assert(0)` instead of `throw new Error` and doesn't support catching the error. + * - This is a template so that the -check and -checkaction flags of the compiled program are used, + * instead of the ones used for compiling druntime. + * + * Returns: whether the registration was successful + */ + bool registerMemoryAssertHandler()() + { + nothrow @nogc extern(C) + void _d_handleSignalAssert(int signum, siginfo_t* info, void* contextPtr) + { + // Guess the reason for the segfault by seeing if the faulting address + // is close to the stack pointer or the null pointer. + + const void* segfaultingPtr = info.si_addr; + + auto context = cast(ucontext.ucontext_t*) contextPtr; + version (X86_64) + const stackPtr = cast(void*) context.uc_mcontext.gregs[ucontext.REG_RSP]; + else version (X86) + const stackPtr = cast(void*) context.uc_mcontext.gregs[ucontext.REG_ESP]; + else version (ARM) + const stackPtr = cast(void*) context.uc_mcontext.arm_sp; + else version (AArch64) + const stackPtr = cast(void*) context.uc_mcontext.sp; + else version (PPC64) + const stackPtr = cast(void*) context.uc_mcontext.regs.gpr[1]; + else + static assert(false, "Unsupported architecture."); // TODO: other architectures + auto distanceToStack = cast(ptrdiff_t) (stackPtr - segfaultingPtr); + if (distanceToStack < 0) + distanceToStack = -distanceToStack; + + if (stackPtr && distanceToStack <= 4096) + assert(false, "segmentation fault: call stack overflow"); + else if (cast(size_t) segfaultingPtr < MEMORY_RESERVED_FOR_NULL_DEREFERENCE) + assert(false, "segmentation fault: null pointer read/write operation"); + else + assert(false, "segmentation fault: invalid pointer read/write operation"); + } -// The first 64Kb are reserved for detecting null pointer dereferences. -enum MEMORY_RESERVED_FOR_NULL_DEREFERENCE = 4096 * 16; + sigaction_t action; + action.sa_sigaction = &_d_handleSignalAssert; + action.sa_flags = SA_SIGINFO | SA_ONSTACK; + + // Set up alternate stack, because segfaults can be caused by stack overflow, + // in which case the stack is already exhausted + __gshared ubyte[SIGSTKSZ] altStack; + stack_t ss; + ss.ss_sp = altStack.ptr; + ss.ss_size = altStack.length; + ss.ss_flags = 0; + if (sigaltstack(&ss, null) == -1) + return false; + + return !sigaction(SIGSEGV, &action, &oldSigactionMemoryAssert); + } -// User space handler -void sigsegvUserspaceProcess(void* address) -{ - // SEGV_MAPERR, SEGV_ACCERR. - // The first page is protected to detect null dereferences. - if ((cast(size_t) address) < MEMORY_RESERVED_FOR_NULL_DEREFERENCE) + /** + * Revert the memory error handler back to the one from before calling `registerMemoryAssertHandler()`. + * + * Returns: whether the registration of the old handler was successful + */ + bool deregisterMemoryAssertHandler() { - throw new NullPointerError(); + return !sigaction(SIGSEGV, &oldSigactionMemoryAssert, null); } - throw new InvalidPointerError(); + unittest + { + // Testing actual memory errors is done in the test suite + assert(registerMemoryAssertHandler()); + assert(deregisterMemoryAssertHandler()); + } } diff --git a/runtime/druntime/src/importc.h b/runtime/druntime/src/importc.h index 53c7831530..810e13387b 100644 --- a/runtime/druntime/src/importc.h +++ b/runtime/druntime/src/importc.h @@ -169,6 +169,7 @@ typedef unsigned long long __uint64_t; */ #define __STDC_NO_VLA__ 1 +#define _Float16 float #if linux // Microsoft won't allow the following macro // Ubuntu's assert.h uses this #define __PRETTY_FUNCTION__ __func__ diff --git a/runtime/druntime/src/object.d b/runtime/druntime/src/object.d index 0a16735f76..41dc03fed7 100644 --- a/runtime/druntime/src/object.d +++ b/runtime/druntime/src/object.d @@ -1373,8 +1373,16 @@ class TypeInfo_AssociativeArray : TypeInfo override @property inout(TypeInfo) next() nothrow pure inout { return value; } override @property uint flags() nothrow pure const { return 1; } + // TypeInfo entry is generated from the type of this template to help rt/aaA.d + static struct Entry(K, V) + { + K key; + V value; + } + TypeInfo value; TypeInfo key; + TypeInfo entry; override @property size_t talign() nothrow pure const { @@ -3251,6 +3259,10 @@ auto byValue(T : V[K], K, V)(T* aa) pure nothrow @nogc sum += v; assert(sum == 3); + + foreach (ref v; dict.byValue) + v++; + assert(dict == ["k1": 2, "k2": 3]); } /*********************************** @@ -3274,7 +3286,7 @@ auto byValue(T : V[K], K, V)(T* aa) pure nothrow @nogc *--- * * Note that this is a low-level interface to iterating over the associative - * array and is not compatible withth the + * array and is not compatible with the * $(LINK2 $(ROOT_DIR)phobos/std_typecons.html#.Tuple,`Tuple`) type in Phobos. * For compatibility with `Tuple`, use * $(LINK2 $(ROOT_DIR)phobos/std_array.html#.byPair,std.array.byPair) instead. @@ -3338,8 +3350,11 @@ auto byKeyValue(T : V[K], K, V)(T* aa) pure nothrow @nogc assert(e.key[1] == e.value + '0'); sum += e.value; } - assert(sum == 3); + + foreach (e; dict.byKeyValue) + e.value++; + assert(dict == ["k1": 2, "k2": 3]); } /*********************************** @@ -3511,8 +3526,8 @@ Value[] values(T : Value[Key], Value, Key)(T *aa) @property } /*********************************** - * Looks up key; if it exists returns corresponding value else evaluates and - * returns defaultValue. + * If `key` is in `aa`, returns corresponding value; otherwise it evaluates and + * returns `defaultValue`. * Params: * aa = The associative array. * key = The key. @@ -3541,8 +3556,8 @@ inout(V) get(K, V)(inout(V[K])* aa, K key, lazy inout(V) defaultValue) } /*********************************** - * Looks up key; if it exists returns corresponding value else evaluates - * value, adds it to the associative array and returns it. + * If `key` is in `aa`, returns corresponding value; otherwise it evaluates + * `value`, adds it to the associative array and returns it. * Params: * aa = The associative array. * key = The key. @@ -3822,15 +3837,10 @@ template RTInfoImpl(size_t[] pointerBitmap) immutable size_t[pointerBitmap.length] RTInfoImpl = pointerBitmap[]; } -template NoPointersBitmapPayload(size_t N) -{ - enum size_t[N] NoPointersBitmapPayload = 0; -} - template RTInfo(T) { enum pointerBitmap = __traits(getPointerBitmap, T); - static if (pointerBitmap[1 .. $] == NoPointersBitmapPayload!(pointerBitmap.length - 1)) + static if (pointerBitmap[1 .. $] == size_t[pointerBitmap.length - 1].init) enum RTInfo = rtinfoNoPointers; else enum RTInfo = RTInfoImpl!(pointerBitmap).ptr; diff --git a/runtime/druntime/src/rt/aApply.d b/runtime/druntime/src/rt/aApply.d index c59d9dc123..b8e575d20f 100644 --- a/runtime/druntime/src/rt/aApply.d +++ b/runtime/druntime/src/rt/aApply.d @@ -10,6 +10,8 @@ module rt.aApply; import core.internal.utf : decode, toUTF8; +debug (apply) import core.stdc.stdio : printf; + /**********************************************/ /* 1 argument versions */ @@ -76,7 +78,7 @@ extern (C) int _aApplycd1(scope const(char)[] aa, dg_t dg) int result; size_t len = aa.length; - debug(apply) printf("_aApplycd1(), len = %d\n", len); + debug(apply) printf("_aApplycd1(), len = %zd\n", len); for (size_t i = 0; i < len; ) { dchar d = aa[i]; @@ -137,7 +139,7 @@ extern (C) int _aApplywd1(scope const(wchar)[] aa, dg_t dg) int result; size_t len = aa.length; - debug(apply) printf("_aApplywd1(), len = %d\n", len); + debug(apply) printf("_aApplywd1(), len = %zd\n", len); for (size_t i = 0; i < len; ) { dchar d = aa[i]; @@ -198,7 +200,7 @@ extern (C) int _aApplycw1(scope const(char)[] aa, dg_t dg) int result; size_t len = aa.length; - debug(apply) printf("_aApplycw1(), len = %d\n", len); + debug(apply) printf("_aApplycw1(), len = %zd\n", len); for (size_t i = 0; i < len; ) { wchar w = aa[i]; @@ -272,7 +274,7 @@ extern (C) int _aApplywc1(scope const(wchar)[] aa, dg_t dg) int result; size_t len = aa.length; - debug(apply) printf("_aApplywc1(), len = %d\n", len); + debug(apply) printf("_aApplywc1(), len = %zd\n", len); for (size_t i = 0; i < len; ) { wchar w = aa[i]; @@ -351,7 +353,7 @@ extern (C) int _aApplydc1(scope const(dchar)[] aa, dg_t dg) { int result; - debug(apply) printf("_aApplydc1(), len = %d\n", aa.length); + debug(apply) printf("_aApplydc1(), len = %zd\n", aa.length); foreach (dchar d; aa) { if (d & ~0x7F) @@ -427,7 +429,7 @@ extern (C) int _aApplydw1(scope const(dchar)[] aa, dg_t dg) { int result; - debug(apply) printf("_aApplydw1(), len = %d\n", aa.length); + debug(apply) printf("_aApplydw1(), len = %zd\n", aa.length); foreach (dchar d; aa) { wchar w; @@ -513,7 +515,7 @@ extern (C) int _aApplycd2(scope const(char)[] aa, dg2_t dg) int result; size_t len = aa.length; - debug(apply) printf("_aApplycd2(), len = %d\n", len); + debug(apply) printf("_aApplycd2(), len = %zd\n", len); size_t n; for (size_t i = 0; i < len; i += n) { @@ -581,7 +583,7 @@ extern (C) int _aApplywd2(scope const(wchar)[] aa, dg2_t dg) int result; size_t len = aa.length; - debug(apply) printf("_aApplywd2(), len = %d\n", len); + debug(apply) printf("_aApplywd2(), len = %zd\n", len); size_t n; for (size_t i = 0; i < len; i += n) { @@ -649,7 +651,7 @@ extern (C) int _aApplycw2(scope const(char)[] aa, dg2_t dg) int result; size_t len = aa.length; - debug(apply) printf("_aApplycw2(), len = %d\n", len); + debug(apply) printf("_aApplycw2(), len = %zd\n", len); size_t n; for (size_t i = 0; i < len; i += n) { @@ -728,7 +730,7 @@ extern (C) int _aApplywc2(scope const(wchar)[] aa, dg2_t dg) int result; size_t len = aa.length; - debug(apply) printf("_aApplywc2(), len = %d\n", len); + debug(apply) printf("_aApplywc2(), len = %zd\n", len); size_t n; for (size_t i = 0; i < len; i += n) { @@ -813,7 +815,7 @@ extern (C) int _aApplydc2(scope const(dchar)[] aa, dg2_t dg) int result; size_t len = aa.length; - debug(apply) printf("_aApplydc2(), len = %d\n", len); + debug(apply) printf("_aApplydc2(), len = %zd\n", len); for (size_t i = 0; i < len; i++) { dchar d = aa[i]; @@ -891,7 +893,7 @@ unittest extern (C) int _aApplydw2(scope const(dchar)[] aa, dg2_t dg) { int result; - debug(apply) printf("_aApplydw2(), len = %d\n", aa.length); + debug(apply) printf("_aApplydw2(), len = %zd\n", aa.length); foreach (size_t i, dchar d; aa) { wchar w; diff --git a/runtime/druntime/src/rt/aApplyR.d b/runtime/druntime/src/rt/aApplyR.d index 560025c636..14052dfcd4 100644 --- a/runtime/druntime/src/rt/aApplyR.d +++ b/runtime/druntime/src/rt/aApplyR.d @@ -10,6 +10,8 @@ module rt.aApplyR; import core.internal.utf; +debug (apply) import core.stdc.stdio : printf; + /**********************************************/ /* 1 argument versions */ @@ -37,7 +39,7 @@ Returns: extern (C) int _aApplyRcd1(scope const(char)[] aa, dg_t dg) { int result; - debug(apply) printf("_aApplyRcd1(), len = %d\n", aa.length); + debug(apply) printf("_aApplyRcd1(), len = %zd\n", aa.length); for (size_t i = aa.length; i != 0; ) { dchar d; @@ -110,7 +112,7 @@ unittest extern (C) int _aApplyRwd1(scope const(wchar)[] aa, dg_t dg) { int result; - debug(apply) printf("_aApplyRwd1(), len = %d\n", aa.length); + debug(apply) printf("_aApplyRwd1(), len = %zd\n", aa.length); for (size_t i = aa.length; i != 0; ) { dchar d; @@ -173,7 +175,7 @@ unittest extern (C) int _aApplyRcw1(scope const(char)[] aa, dg_t dg) { int result; - debug(apply) printf("_aApplyRcw1(), len = %d\n", aa.length); + debug(apply) printf("_aApplyRcw1(), len = %zd\n", aa.length); for (size_t i = aa.length; i != 0; ) { dchar d; wchar w; @@ -259,7 +261,7 @@ unittest extern (C) int _aApplyRwc1(scope const(wchar)[] aa, dg_t dg) { int result; - debug(apply) printf("_aApplyRwc1(), len = %d\n", aa.length); + debug(apply) printf("_aApplyRwc1(), len = %zd\n", aa.length); for (size_t i = aa.length; i != 0; ) { dchar d; char c; @@ -343,7 +345,7 @@ unittest extern (C) int _aApplyRdc1(scope const(dchar)[] aa, dg_t dg) { int result; - debug(apply) printf("_aApplyRdc1(), len = %d\n", aa.length); + debug(apply) printf("_aApplyRdc1(), len = %zd\n", aa.length); for (size_t i = aa.length; i != 0;) { dchar d = aa[--i]; char c; @@ -421,7 +423,7 @@ unittest extern (C) int _aApplyRdw1(scope const(dchar)[] aa, dg_t dg) { int result; - debug(apply) printf("_aApplyRdw1(), len = %d\n", aa.length); + debug(apply) printf("_aApplyRdw1(), len = %zd\n", aa.length); for (size_t i = aa.length; i != 0; ) { dchar d = aa[--i]; wchar w; @@ -507,7 +509,7 @@ extern (C) int _aApplyRcd2(scope const(char)[] aa, dg2_t dg) size_t i; size_t len = aa.length; - debug(apply) printf("_aApplyRcd2(), len = %d\n", len); + debug(apply) printf("_aApplyRcd2(), len = %zd\n", len); for (i = len; i != 0; ) { dchar d; @@ -581,7 +583,7 @@ unittest extern (C) int _aApplyRwd2(scope const(wchar)[] aa, dg2_t dg) { int result; - debug(apply) printf("_aApplyRwd2(), len = %d\n", aa.length); + debug(apply) printf("_aApplyRwd2(), len = %zd\n", aa.length); for (size_t i = aa.length; i != 0; ) { dchar d; @@ -646,7 +648,7 @@ unittest extern (C) int _aApplyRcw2(scope const(char)[] aa, dg2_t dg) { int result; - debug(apply) printf("_aApplyRcw2(), len = %d\n", aa.length); + debug(apply) printf("_aApplyRcw2(), len = %zd\n", aa.length); for (size_t i = aa.length; i != 0; ) { dchar d; wchar w; @@ -734,7 +736,7 @@ unittest extern (C) int _aApplyRwc2(scope const(wchar)[] aa, dg2_t dg) { int result; - debug(apply) printf("_aApplyRwc2(), len = %d\n", aa.length); + debug(apply) printf("_aApplyRwc2(), len = %zd\n", aa.length); for (size_t i = aa.length; i != 0; ) { dchar d; char c; @@ -820,7 +822,7 @@ unittest extern (C) int _aApplyRdc2(scope const(dchar)[] aa, dg2_t dg) { int result; - debug(apply) printf("_aApplyRdc2(), len = %d\n", aa.length); + debug(apply) printf("_aApplyRdc2(), len = %zd\n", aa.length); for (size_t i = aa.length; i != 0; ) { dchar d = aa[--i]; char c; @@ -899,7 +901,7 @@ unittest extern (C) int _aApplyRdw2(scope const(dchar)[] aa, dg2_t dg) { int result; - debug(apply) printf("_aApplyRdw2(), len = %d\n", aa.length); + debug(apply) printf("_aApplyRdw2(), len = %zd\n", aa.length); for (size_t i = aa.length; i != 0; ) { dchar d = aa[--i]; wchar w; diff --git a/runtime/druntime/src/rt/aaA.d b/runtime/druntime/src/rt/aaA.d index cd20b232bb..500e87208d 100644 --- a/runtime/druntime/src/rt/aaA.d +++ b/runtime/druntime/src/rt/aaA.d @@ -80,13 +80,13 @@ private: if ((ti.key.flags | ti.value.flags) & 1) flags |= Flags.hasPointers; - entryTI = fakeEntryTI(this, ti.key, ti.value); + entryTI = ti.entry; } Bucket[] buckets; uint used; uint deleted; - TypeInfo_Struct entryTI; + const(TypeInfo) entryTI; uint firstUsed; immutable uint keysz; immutable uint valsz; @@ -243,15 +243,6 @@ private void* allocEntry(scope const Impl* aa, scope const void* pkey) return res; } -package void entryDtor(void* p, const TypeInfo_Struct sti) -{ - // key and value type info stored after the TypeInfo_Struct by tiEntry() - auto sizeti = __traits(classInstanceSize, TypeInfo_Struct); - auto extra = cast(const(TypeInfo)*)(cast(void*) sti + sizeti); - extra[0].destroy(p); - extra[1].destroy(p + talign(extra[0].tsize, extra[1].talign)); -} - private bool hasDtor(const TypeInfo ti) pure nothrow { import rt.lifetime : unqualify; @@ -272,132 +263,6 @@ private immutable(void)* getRTInfo(const TypeInfo ti) pure nothrow return isNoClass ? ti.rtInfo() : rtinfoHasPointers; } -// build type info for Entry with additional key and value fields -TypeInfo_Struct fakeEntryTI(ref Impl aa, const TypeInfo keyti, const TypeInfo valti) nothrow -{ - import rt.lifetime : unqualify; - - auto kti = unqualify(keyti); - auto vti = unqualify(valti); - - // figure out whether RTInfo has to be generated (indicated by rtisize > 0) - enum pointersPerWord = 8 * (void*).sizeof * (void*).sizeof; - auto rtinfo = rtinfoNoPointers; - size_t rtisize = 0; - immutable(size_t)* keyinfo = void; - immutable(size_t)* valinfo = void; - if (aa.flags & Impl.Flags.hasPointers) - { - // classes are references - keyinfo = cast(immutable(size_t)*) getRTInfo(keyti); - valinfo = cast(immutable(size_t)*) getRTInfo(valti); - - if (keyinfo is rtinfoHasPointers && valinfo is rtinfoHasPointers) - rtinfo = rtinfoHasPointers; - else - rtisize = 1 + (aa.valoff + aa.valsz + pointersPerWord - 1) / pointersPerWord; - } - bool entryHasDtor = hasDtor(kti) || hasDtor(vti); - if (rtisize == 0 && !entryHasDtor) - return null; - - // save kti and vti after type info for struct - enum sizeti = __traits(classInstanceSize, TypeInfo_Struct); - void* p = GC.malloc(sizeti + (2 + rtisize) * (void*).sizeof); - import core.stdc.string : memcpy; - - memcpy(p, __traits(initSymbol, TypeInfo_Struct).ptr, sizeti); - - auto ti = cast(TypeInfo_Struct) p; - auto extra = cast(TypeInfo*)(p + sizeti); - extra[0] = cast() kti; - extra[1] = cast() vti; - - static immutable tiMangledName = "S2rt3aaA__T5EntryZ"; - ti.mangledName = tiMangledName; - - ti.m_RTInfo = rtisize > 0 ? rtinfoEntry(aa, keyinfo, valinfo, cast(size_t*)(extra + 2), rtisize) : rtinfo; - ti.m_flags = ti.m_RTInfo is rtinfoNoPointers ? cast(TypeInfo_Struct.StructFlags)0 : TypeInfo_Struct.StructFlags.hasPointers; - - // we don't expect the Entry objects to be used outside of this module, so we have control - // over the non-usage of the callback methods and other entries and can keep these null - // xtoHash, xopEquals, xopCmp, xtoString and xpostblit - immutable entrySize = aa.valoff + aa.valsz; - ti.m_init = (cast(ubyte*) null)[0 .. entrySize]; // init length, but not ptr - - if (entryHasDtor) - { - // xdtor needs to be built from the dtors of key and value for the GC - ti.xdtorti = &entryDtor; - ti.m_flags |= TypeInfo_Struct.StructFlags.isDynamicType; - } - - ti.m_align = cast(uint) max(kti.talign, vti.talign); - - return ti; -} - -// build appropriate RTInfo at runtime -immutable(void)* rtinfoEntry(ref Impl aa, immutable(size_t)* keyinfo, - immutable(size_t)* valinfo, size_t* rtinfoData, size_t rtinfoSize) pure nothrow -{ - enum bitsPerWord = 8 * size_t.sizeof; - - rtinfoData[0] = aa.valoff + aa.valsz; - rtinfoData[1..rtinfoSize] = 0; - - void copyKeyInfo(string src)() - { - size_t pos = 1; - size_t keybits = aa.keysz / (void*).sizeof; - while (keybits >= bitsPerWord) - { - rtinfoData[pos] = mixin(src); - keybits -= bitsPerWord; - pos++; - } - if (keybits > 0) - rtinfoData[pos] = mixin(src) & ((cast(size_t) 1 << keybits) - 1); - } - - if (keyinfo is rtinfoHasPointers) - copyKeyInfo!"~cast(size_t) 0"(); - else if (keyinfo !is rtinfoNoPointers) - copyKeyInfo!"keyinfo[pos]"(); - - void copyValInfo(string src)() - { - size_t bitpos = aa.valoff / (void*).sizeof; - size_t pos = 1; - size_t dstpos = 1 + bitpos / bitsPerWord; - size_t begoff = bitpos % bitsPerWord; - size_t valbits = aa.valsz / (void*).sizeof; - size_t endoff = (bitpos + valbits) % bitsPerWord; - for (;;) - { - const bits = bitsPerWord - begoff; - size_t s = mixin(src); - rtinfoData[dstpos] |= s << begoff; - if (begoff > 0 && valbits > bits) - rtinfoData[dstpos+1] |= s >> bits; - if (valbits < bitsPerWord) - break; - valbits -= bitsPerWord; - dstpos++; - pos++; - } - if (endoff > 0) - rtinfoData[dstpos] &= ((cast(size_t) 1 << endoff) - 1); - } - - if (valinfo is rtinfoHasPointers) - copyValInfo!"~cast(size_t) 0"(); - else if (valinfo !is rtinfoNoPointers) - copyValInfo!"valinfo[pos]"(); - - return cast(immutable(void)*) rtinfoData; -} - unittest { void test(K, V)() @@ -416,12 +281,10 @@ unittest if (valrti is rtinfoNoPointers && keyrti is rtinfoNoPointers) { assert(!(impl.flags & Impl.Flags.hasPointers)); - assert(impl.entryTI is null); } else if (valrti is rtinfoHasPointers && keyrti is rtinfoHasPointers) { assert(impl.flags & Impl.Flags.hasPointers); - assert(impl.entryTI is null); } else { @@ -996,7 +859,10 @@ unittest aa1 = null; aa2 = null; aa3 = null; - GC.runFinalizers((cast(char*)(&entryDtor))[0 .. 1]); + auto dtor1 = typeid(TypeInfo_AssociativeArray.Entry!(int, T)).xdtor; + GC.runFinalizers((cast(char*)dtor1)[0 .. 1]); + auto dtor2 = typeid(TypeInfo_AssociativeArray.Entry!(T, int)).xdtor; + GC.runFinalizers((cast(char*)dtor2)[0 .. 1]); assert(T.dtor == 6 && T.postblit == 2); } diff --git a/runtime/druntime/src/rt/adi.d b/runtime/druntime/src/rt/adi.d index ea5a78f9c1..02e4d7719a 100644 --- a/runtime/druntime/src/rt/adi.d +++ b/runtime/druntime/src/rt/adi.d @@ -11,12 +11,9 @@ module rt.adi; -//debug=adi; // uncomment to turn on debugging printf's +// debug = adi; // uncomment to turn on debugging printf's -private -{ - debug(adi) import core.stdc.stdio; -} +debug (adi) import core.stdc.stdio : printf; /*************************************** * Support for array equality test. @@ -27,7 +24,7 @@ private extern (C) int _adEq2(void[] a1, void[] a2, TypeInfo ti) { - debug(adi) printf("_adEq2(a1.length = %d, a2.length = %d)\n", a1.length, a2.length); + debug(adi) printf("_adEq2(a1.length = %zd, a2.length = %zd)\n", a1.length, a2.length); if (a1.length != a2.length) return 0; // not equal if (!ti.equals(&a1, &a2)) diff --git a/runtime/druntime/src/rt/arraycat.d b/runtime/druntime/src/rt/arraycat.d index 9bbf590796..076d7fbabb 100644 --- a/runtime/druntime/src/rt/arraycat.d +++ b/runtime/druntime/src/rt/arraycat.d @@ -10,12 +10,12 @@ module rt.arraycat; -private -{ - import core.stdc.string; - import core.internal.util.array; - debug(PRINTF) import core.stdc.stdio; -} +// debug = PRINTF; + +import core.internal.util.array; +import core.stdc.string : memcpy; + +debug(PRINTF) import core.stdc.stdio : printf; extern (C) @trusted nothrow: @@ -33,8 +33,8 @@ else { void[] _d_arraycopy(size_t size, void[] from, void[] to) { - debug(PRINTF) printf("f = %p,%d, t = %p,%d, size = %d\n", - from.ptr, from.length, to.ptr, to.length, size); + debug(PRINTF) printf("f = %p,%zd, t = %p,%zd, size = %zd\n", + from.ptr, from.length, to.ptr, to.length, size); enforceRawArraysConformable("copy", size, from, to); memcpy(to.ptr, from.ptr, to.length * size); diff --git a/runtime/druntime/src/rt/cast_.d b/runtime/druntime/src/rt/cast_.d index 43ddd5a931..cd110bfc2a 100644 --- a/runtime/druntime/src/rt/cast_.d +++ b/runtime/druntime/src/rt/cast_.d @@ -14,6 +14,8 @@ */ module rt.cast_; +debug(cast_) import core.stdc.stdio : printf; + extern (C): @nogc: nothrow: @@ -60,7 +62,7 @@ Object _d_toObject(return scope void* p) */ if (pi.offset < 0x10000) { - debug(cast_) printf("\tpi.offset = %d\n", pi.offset); + debug(cast_) printf("\tpi.offset = %zd\n", pi.offset); return cast(Object)(p - pi.offset); } return o; @@ -72,19 +74,19 @@ Object _d_toObject(return scope void* p) */ void* _d_interface_cast(void* p, ClassInfo c) { - debug(cast_) printf("_d_interface_cast(p = %p, c = '%.*s')\n", p, c.name); + debug(cast_) printf("_d_interface_cast(p = %p, c = '%.*s')\n", p, cast(int) c.name.length, c.name.ptr); if (!p) return null; Interface* pi = **cast(Interface***) p; - debug(cast_) printf("\tpi.offset = %d\n", pi.offset); + debug(cast_) printf("\tpi.offset = %zd\n", pi.offset); Object o2 = cast(Object)(p - pi.offset); void* res = null; size_t offset = 0; if (o2 && _d_isbaseof2(typeid(o2), c, offset)) { - debug(cast_) printf("\toffset = %d\n", offset); + debug(cast_) printf("\toffset = %zd\n", offset); res = cast(void*) o2 + offset; } debug(cast_) printf("\tresult = %p\n", res); @@ -101,13 +103,13 @@ void* _d_interface_cast(void* p, ClassInfo c) */ void* _d_dynamic_cast(Object o, ClassInfo c) { - debug(cast_) printf("_d_dynamic_cast(o = %p, c = '%.*s')\n", o, c.name); + debug(cast_) printf("_d_dynamic_cast(o = %p, c = '%.*s')\n", o, cast(int) c.name.length, c.name.ptr); void* res = null; size_t offset = 0; if (o && _d_isbaseof2(typeid(o), c, offset)) { - debug(cast_) printf("\toffset = %d\n", offset); + debug(cast_) printf("\toffset = %zd\n", offset); res = cast(void*) o + offset; } debug(cast_) printf("\tresult = %p\n", res); @@ -124,7 +126,7 @@ void* _d_dynamic_cast(Object o, ClassInfo c) */ void* _d_class_cast(Object o, ClassInfo c) { - debug(cast_) printf("_d_cast_cast(o = %p, c = '%.*s', level %d)\n", o, c.name, level); + debug(cast_) printf("_d_cast_cast(o = %p, c = '%.*s')\n", o, cast(int) c.name.length, c.name.ptr); if (!o) return null; @@ -167,7 +169,7 @@ void* _d_paint_cast(Object o, ClassInfo c) { /* If o is really an instance of c, just do a paint */ - auto p = (o && cast(void*)(areClassInfosEqual(typeid(o), c)) ? o : null); + auto p = o && cast(void*)(areClassInfosEqual(typeid(o), c)) ? o : null; debug assert(cast(void*)p is cast(void*)_d_dynamic_cast(o, c)); return cast(void*)p; } diff --git a/runtime/druntime/src/rt/cmath2.d b/runtime/druntime/src/rt/cmath2.d index 1c95d99c37..ac8dae5145 100644 --- a/runtime/druntime/src/rt/cmath2.d +++ b/runtime/druntime/src/rt/cmath2.d @@ -16,7 +16,9 @@ module rt.cmath2; version (LDC) {} else: -import core.stdc.math; +import core.stdc.math : fabs; + +debug import core.stdc.stdio : printf; extern (C): diff --git a/runtime/druntime/src/rt/config.d b/runtime/druntime/src/rt/config.d index 35679c814c..5e5acc492b 100644 --- a/runtime/druntime/src/rt/config.d +++ b/runtime/druntime/src/rt/config.d @@ -179,8 +179,7 @@ string rt_envvarsOption(string opt, scope rt_configCallBack dg) @nogc nothrow var[4 + i] = cast(char) toupper(c); var[4 + opt.length] = 0; - auto p = getenv(var.ptr); - if (p) + if (auto p = getenv(var.ptr)) { string s = dg(cast(string) p[0 .. strlen(p)]); if (s != null) diff --git a/runtime/druntime/src/rt/cover.d b/runtime/druntime/src/rt/cover.d index 94c8ea2bb6..0b1902104c 100644 --- a/runtime/druntime/src/rt/cover.d +++ b/runtime/druntime/src/rt/cover.d @@ -11,25 +11,29 @@ module rt.cover; -import core.internal.util.math : min, max; +import core.internal.utf; +import core.internal.util.math : max, min; +import core.stdc.stdio : EOF, fclose, fgetc, FILE, fileno, fprintf, fread, fseek, ftell, printf, SEEK_END, SEEK_SET, + stderr; +import core.stdc.stdlib : exit, EXIT_FAILURE; -private +version (Windows) { - version (Windows) - { - import core.sys.windows.basetsd /+: HANDLE+/; - import core.sys.windows.winbase /+: LOCKFILE_EXCLUSIVE_LOCK, LockFileEx, OVERLAPPED, SetEndOfFile+/; - } - else version (Posix) - { - import core.sys.posix.fcntl; - import core.sys.posix.unistd; - } - import core.stdc.config : c_long; - import core.stdc.stdio; - import core.stdc.stdlib; - import core.internal.utf; + import core.stdc.stdio : _fdopen, _get_osfhandle, _O_BINARY, _O_CREAT, _O_RDWR, _S_IREAD, _S_IWRITE, _wopen; + import core.sys.windows.basetsd; + import core.sys.windows.winbase; +} +else version (Posix) +{ + import core.stdc.stdio : fopen; + import core.sys.posix.fcntl : O_CREAT, O_RDWR, open, S_IRGRP, S_IROTH, S_IRUSR, S_IWGRP, S_IWOTH, S_IWUSR; + import core.sys.posix.unistd : ftruncate; +} +else + static assert(0, "Unsupported platform"); +private +{ struct BitArray { size_t len; @@ -321,14 +325,14 @@ shared static ~this() version (Windows) SetEndOfFile(handle(fileno(flst))); - else + else version (Posix) ftruncate(fileno(flst), ftell(flst)); } } uint digits(uint number) { - import core.stdc.math; + import core.stdc.math : floor, log10; return number ? cast(uint)floor(log10(number)) + 1 : 1; } @@ -457,17 +461,15 @@ string chomp( string str, string delim = null ) // open/create file for read/write, pointer at beginning FILE* openOrCreateFile(string name) { - import core.internal.utf : toUTF16z; - version (Windows) immutable fd = _wopen(toUTF16z(name), _O_RDWR | _O_CREAT | _O_BINARY, _S_IREAD | _S_IWRITE); - else + else version (Posix) immutable fd = open((name ~ '\0').ptr, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); version (CRuntime_Microsoft) alias fdopen = _fdopen; - version (Posix) - import core.sys.posix.stdio; + else version (Posix) + import core.sys.posix.stdio : fdopen; return fdopen(fd, "r+b"); } @@ -485,15 +487,16 @@ void lockFile(int fd) flock(fd, LOCK_EX); // exclusive lock } else version (Posix) + { + import core.sys.posix.unistd : F_LOCK, lockf; lockf(fd, F_LOCK, 0); // exclusive lock + } else version (Windows) { OVERLAPPED off; // exclusively lock first byte LockFileEx(handle(fd), LOCKFILE_EXCLUSIVE_LOCK, 0, 1, 0, &off); } - else - static assert(0, "unimplemented"); } bool readFile(FILE* file, ref char[] buf) @@ -516,16 +519,13 @@ bool readFile(FILE* file, ref char[] buf) } version (Windows) extern (C) nothrow @nogc FILE* _wfopen(scope const wchar* filename, scope const wchar* mode); -version (Windows) extern (C) int chsize(int fd, c_long size); bool readFile(string name, ref char[] buf) { - import core.internal.utf : toUTF16z; - version (Windows) auto file = _wfopen(toUTF16z(name), "rb"w.ptr); - else + else version (Posix) auto file = fopen((name ~ '\0').ptr, "rb".ptr); if (file is null) return false; scope(exit) fclose(file); diff --git a/runtime/druntime/src/rt/deh.d b/runtime/druntime/src/rt/deh.d index 24fe46c0fd..175382545a 100644 --- a/runtime/druntime/src/rt/deh.d +++ b/runtime/druntime/src/rt/deh.d @@ -52,6 +52,8 @@ extern (C) version (LDC) public import rt.deh_win64_posix; +else version (GNU) + public import gcc.deh; else version (Win32) public import rt.deh_win32; else version (Win64) diff --git a/runtime/druntime/src/rt/deh_win32.d b/runtime/druntime/src/rt/deh_win32.d index 5f6eb932fe..46e222cd07 100644 --- a/runtime/druntime/src/rt/deh_win32.d +++ b/runtime/druntime/src/rt/deh_win32.d @@ -18,7 +18,8 @@ import core.sys.windows.basetsd /+: ULONG_PTR+/; import core.sys.windows.windef /+: BOOL, BYTE, DWORD+/; import core.sys.windows.winnt /+: PVOID+/; import rt.monitor_; -//import core.stdc.stdio; + +debug import core.stdc.stdio : printf; version (D_InlineAsm_X86) { diff --git a/runtime/druntime/src/rt/deh_win64_posix.d b/runtime/druntime/src/rt/deh_win64_posix.d index a1ea1528cd..b5bcdc6321 100644 --- a/runtime/druntime/src/rt/deh_win64_posix.d +++ b/runtime/druntime/src/rt/deh_win64_posix.d @@ -158,7 +158,7 @@ immutable(FuncTable)* __eh_finddata(void *address) immutable(FuncTable)* __eh_finddata(void *address, immutable(FuncTable)* pstart, immutable(FuncTable)* pend) { - debug(PRINTF) printf("FuncTable.sizeof = %p\n", FuncTable.sizeof); + debug(PRINTF) printf("FuncTable.sizeof = %#zx\n", FuncTable.sizeof); debug(PRINTF) printf("__eh_finddata(address = %p)\n", address); debug(PRINTF) printf("_deh_beg = %p, _deh_end = %p\n", pstart, pend); @@ -288,7 +288,7 @@ extern (C) void _d_throwc(Throwable h) break; } - debug(PRINTF) printf("found caller, EBP = %p, retaddr = %p\n", regebp, retaddr); + debug(PRINTF) printf("found caller, EBP = %#zx, retaddr = %#zx\n", regebp, retaddr); //if (++count == 12) *(char*)0=0; auto func_table = __eh_finddata(cast(void *)retaddr); // find static data associated with function auto handler_table = func_table ? func_table.handlertable : null; @@ -314,8 +314,8 @@ extern (C) void _d_throwc(Throwable h) debug(PRINTF) { - printf("retaddr = %p\n", retaddr); - printf("regebp=%p, funcoffset=%p, spoff=x%x, retoffset=x%x\n", + printf("retaddr = %#zx\n", retaddr); + printf("regebp=%#zx, funcoffset=%#zx, spoff=x%x, retoffset=x%x\n", regebp,funcoffset,spoff,retoffset); } @@ -324,11 +324,11 @@ extern (C) void _d_throwc(Throwable h) debug(PRINTF) { - printf("handler_info[%d]:\n", dim); + printf("handler_info[%zd]:\n", dim); for (uint i = 0; i < dim; i++) { auto phi = &handler_table.handler_info.ptr[i]; - printf("\t[%d]: offset = x%04x, endoffset = x%04x, prev_index = %d, cioffset = x%04x, finally_offset = %x\n", + printf("\t[%d]: offset = x%04x, endoffset = x%04x, prev_index = %d, cioffset = x%04x, finally_offset = %zx\n", i, phi.offset, phi.endoffset, phi.prev_index, phi.cioffset, phi.finally_offset); } } @@ -338,7 +338,7 @@ extern (C) void _d_throwc(Throwable h) { auto phi = &handler_table.handler_info.ptr[i]; - debug(PRINTF) printf("i = %d, phi.offset = %04x\n", i, funcoffset + phi.offset); + debug(PRINTF) printf("i = %d, phi.offset = %04zx\n", i, funcoffset + phi.offset); if (retaddr > funcoffset + phi.offset && retaddr <= funcoffset + phi.endoffset) index = i; @@ -348,7 +348,7 @@ extern (C) void _d_throwc(Throwable h) if (dim) { auto phi = &handler_table.handler_info.ptr[index+1]; - debug(PRINTF) printf("next finally_offset %p\n", phi.finally_offset); + debug(PRINTF) printf("next finally_offset %#zx\n", phi.finally_offset); auto prev = cast(InFlight*) &__inflight; auto curr = prev.next; @@ -438,7 +438,7 @@ extern (C) void _d_throwc(Throwable h) // Call finally block // Note that it is unnecessary to adjust the ESP, as the finally block // accesses all items on the stack as relative to EBP. - debug(PRINTF) printf("calling finally_offset %p\n", phi.finally_offset); + debug(PRINTF) printf("calling finally_offset %#zx\n", phi.finally_offset); auto blockaddr = cast(void*)(funcoffset + phi.finally_offset); InFlight inflight; diff --git a/runtime/druntime/src/rt/dmain2.d b/runtime/druntime/src/rt/dmain2.d index 00a8235ae4..a669c7fb29 100644 --- a/runtime/druntime/src/rt/dmain2.d +++ b/runtime/druntime/src/rt/dmain2.d @@ -11,22 +11,24 @@ module rt.dmain2; -import rt.memory; -import rt.sections; import core.atomic; -import core.stdc.stddef; -import core.stdc.stdlib; -import core.stdc.string; -import core.stdc.stdio; // for printf() +import core.internal.parseoptions : rt_parseOption; import core.stdc.errno : errno; +import core.stdc.stdio : fflush, fprintf, fwrite, stderr, stdout; +import core.stdc.stdlib : alloca, EXIT_FAILURE, EXIT_SUCCESS, free, malloc, realloc; +import core.stdc.string : strerror; +import rt.config : rt_cmdline_enabled, rt_configOption; +import rt.memory; +import rt.sections; version (Windows) { - import core.stdc.wchar_; + import core.stdc.stdio : fileno; + import core.stdc.wchar_ : wcslen; import core.sys.windows.basetsd : HANDLE; import core.sys.windows.shellapi : CommandLineToArgvW; - import core.sys.windows.winbase : FreeLibrary, GetCommandLineW, GetProcAddress, - IsDebuggerPresent, LoadLibraryW, LocalFree, WriteFile; + import core.sys.windows.winbase : FreeLibrary, GetCommandLineW, GetProcAddress, IsDebuggerPresent, LoadLibraryW, + LocalFree, WriteFile; import core.sys.windows.wincon : CONSOLE_SCREEN_BUFFER_INFO, GetConsoleOutputCP, GetConsoleScreenBufferInfo; import core.sys.windows.winnls : CP_UTF8, MultiByteToWideChar, WideCharToMultiByte; @@ -34,19 +36,12 @@ version (Windows) import core.sys.windows.winuser : MB_ICONERROR, MessageBoxW; pragma(lib, "shell32.lib"); // needed for CommandLineToArgvW -} -version (FreeBSD) -{ - import core.stdc.fenv; -} -version (NetBSD) -{ - import core.stdc.fenv; + import core.stdc.stdio : _get_osfhandle; } -version (DragonFlyBSD) +else version (Posix) { - import core.stdc.fenv; + import core.stdc.string : strlen; } // not sure why we can't define this in one place, but this is to keep this @@ -428,6 +423,14 @@ private extern (C) int _d_run_main2(char[][] args, size_t totalArgsLength, MainF pop EAX; } } + else version (GNU_InlineAsm) + { + size_t fpu_cw; + asm { "fstcw %0" : "=m" (fpu_cw); } + fpu_cw |= 0b11_00_111111; // 11: use 64 bit extended-precision + // 111111: mask all FP exceptions + asm { "fldcw %0" : "=m" (fpu_cw); } + } } /* Create a copy of args[] on the stack to be used for main, so that rt_args() @@ -441,7 +444,6 @@ private extern (C) int _d_run_main2(char[][] args, size_t totalArgsLength, MainF char[][] argsCopy = buff[0 .. args.length]; auto argBuff = cast(char*) (buff + args.length); size_t j = 0; - import rt.config : rt_cmdline_enabled; bool parseOpts = rt_cmdline_enabled!(); foreach (arg; args) { @@ -465,6 +467,13 @@ private extern (C) int _d_run_main2(char[][] args, size_t totalArgsLength, MainF useExceptionTrap = false; } + version (none) + { + // Causes test failures related to Fibers, not enabled by default yet + import etc.linux.memoryerror; + cast(void) registerMemoryAssertHandler(); + } + void tryExec(scope void delegate() dg) { if (useExceptionTrap) @@ -577,8 +586,6 @@ private void formatThrowable(Throwable t, scope void delegate(in char[] s) nothr private auto parseExceptionOptions() { - import rt.config : rt_configOption; - import core.internal.parseoptions : rt_parseOption; const optName = "trapExceptions"; auto option = rt_configOption(optName); auto trap = rt_trapExceptions; @@ -601,7 +608,7 @@ extern (C) void _d_print_throwable(Throwable t) void sink(in char[] s) scope nothrow { if (!s.length) return; - int swlen = MultiByteToWideChar( + const swlen = MultiByteToWideChar( CP_UTF8, 0, s.ptr, cast(int)s.length, null, 0); if (!swlen) return; @@ -609,7 +616,7 @@ extern (C) void _d_print_throwable(Throwable t) (this.len + swlen + 1) * WCHAR.sizeof); if (!newPtr) return; ptr = newPtr; - auto written = MultiByteToWideChar( + const written = MultiByteToWideChar( CP_UTF8, 0, s.ptr, cast(int)s.length, ptr+len, swlen); len += written; } @@ -621,16 +628,9 @@ extern (C) void _d_print_throwable(Throwable t) HANDLE windowsHandle(int fd) { - version (CRuntime_Microsoft) - return cast(HANDLE)_get_osfhandle(fd); - else - return _fdToHandle(fd); + return cast(HANDLE)_get_osfhandle(fd); } - auto hStdErr = windowsHandle(fileno(stderr)); - CONSOLE_SCREEN_BUFFER_INFO sbi; - bool isConsole = GetConsoleScreenBufferInfo(hStdErr, &sbi) != 0; - // ensure the exception is shown at the beginning of the line, while also // checking whether stderr is a valid file int written = fprintf(stderr, "\n"); @@ -647,22 +647,22 @@ extern (C) void _d_print_throwable(Throwable t) // Avoid static user32.dll dependency for console applications // by loading it dynamically as needed - auto user32 = LoadLibraryW("user32.dll"); - if (user32) + if (auto user32 = LoadLibraryW("user32.dll")) { alias typeof(&MessageBoxW) PMessageBoxW; - auto pMessageBoxW = cast(PMessageBoxW) - GetProcAddress(user32, "MessageBoxW"); - if (pMessageBoxW) + if (auto pMessageBoxW = cast(PMessageBoxW) GetProcAddress(user32, "MessageBoxW")) pMessageBoxW(null, buf.get(), caption.get(), MB_ICONERROR); + FreeLibrary(user32); } - FreeLibrary(user32); caption.free(); buf.free(); } return; } - else if (isConsole) + auto hStdErr = windowsHandle(fileno(stderr)); + CONSOLE_SCREEN_BUFFER_INFO sbi = void; + const isConsole = GetConsoleScreenBufferInfo(hStdErr, &sbi) != 0; + if (isConsole) { WSink buf; formatThrowable(t, &buf.sink); @@ -670,10 +670,9 @@ extern (C) void _d_print_throwable(Throwable t) if (buf.ptr) { uint codepage = GetConsoleOutputCP(); - int slen = WideCharToMultiByte(codepage, 0, + const slen = WideCharToMultiByte(codepage, 0, buf.ptr, cast(int)buf.len, null, 0, null, null); - auto sptr = cast(char*)malloc(slen * char.sizeof); - if (sptr) + if (auto sptr = cast(char*)malloc(slen * char.sizeof)) { WideCharToMultiByte(codepage, 0, buf.ptr, cast(int)buf.len, sptr, slen, null, null); diff --git a/runtime/druntime/src/rt/dwarfeh.d b/runtime/druntime/src/rt/dwarfeh.d index 27023fac44..a78264afef 100644 --- a/runtime/druntime/src/rt/dwarfeh.d +++ b/runtime/druntime/src/rt/dwarfeh.d @@ -11,14 +11,14 @@ module rt.dwarfeh; -// debug = EH_personality; - version (Posix): -import rt.dmain2: _d_print_throwable; +// debug = EH_personality; + import core.internal.backtrace.unwind; -import core.stdc.stdio; -import core.stdc.stdlib; +import core.stdc.stdio : fprintf, printf, stderr; +import core.stdc.stdlib : abort, calloc, free; +import rt.dmain2 : _d_print_throwable; version (LDC) { @@ -136,7 +136,8 @@ debug (EH_personality) { private void writeln(in char* format, ...) @nogc nothrow { - import core.stdc.stdarg; + import core.stdc.stdarg : va_list, va_start; + import core.stdc.stdio : fflush, stdout, vfprintf; va_list args; va_start(args, format); @@ -192,7 +193,7 @@ struct ExceptionHeader auto eh = &ehstorage; if (eh.object) // if in use { - eh = cast(ExceptionHeader*)core.stdc.stdlib.calloc(1, ExceptionHeader.sizeof); + eh = cast(ExceptionHeader*).calloc(1, ExceptionHeader.sizeof); if (!eh) terminate(__LINE__); // out of memory while throwing - not much else can be done } @@ -215,7 +216,7 @@ struct ExceptionHeader */ *eh = ExceptionHeader.init; if (eh != &ehstorage) - core.stdc.stdlib.free(eh); + .free(eh); } /************************* @@ -1216,8 +1217,7 @@ int actionTableLookup(_Unwind_Exception* exceptionObject, uint actionRecordPtr, { // sti is catch clause type_info auto sti = cast(CppTypeInfo)((cast(__cpp_type_info_ptr)cast(void*)ci).ptr); - auto p = getCppPtrToThrownObject(exceptionObject, sti); - if (p) // if found + if (auto p = getCppPtrToThrownObject(exceptionObject, sti)) // if found { auto eh = CppExceptionHeader.toExceptionHeader(exceptionObject); eh.thrownPtr = p; // for __cxa_begin_catch() diff --git a/runtime/druntime/src/rt/ehalloc.d b/runtime/druntime/src/rt/ehalloc.d index 65f92e3d01..7c3f06c33c 100644 --- a/runtime/druntime/src/rt/ehalloc.d +++ b/runtime/druntime/src/rt/ehalloc.d @@ -13,10 +13,7 @@ module rt.ehalloc; //debug = PRINTF; -debug(PRINTF) -{ - import core.stdc.stdio; -} +debug (PRINTF) import core.stdc.stdio : printf; /******************************************** diff --git a/runtime/druntime/src/rt/invariant.d b/runtime/druntime/src/rt/invariant_.d similarity index 70% rename from runtime/druntime/src/rt/invariant.d rename to runtime/druntime/src/rt/invariant_.d index e536196e8c..2a64dc89da 100644 --- a/runtime/druntime/src/rt/invariant.d +++ b/runtime/druntime/src/rt/invariant_.d @@ -4,15 +4,10 @@ * Copyright: Copyright Digital Mars 2007 - 2010. * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). * Authors: Walter Bright - * Source: $(DRUNTIMESRC rt/_invariant.d) - */ - -/* Copyright Digital Mars 2007 - 2010. - * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE or copy at - * http://www.boost.org/LICENSE_1_0.txt) + * Source: $(DRUNTIMESRC rt/_invariant_.d) */ +module rt.invariant_; /** * diff --git a/runtime/druntime/src/rt/lifetime.d b/runtime/druntime/src/rt/lifetime.d index 2a841888bf..892a036a72 100644 --- a/runtime/druntime/src/rt/lifetime.d +++ b/runtime/druntime/src/rt/lifetime.d @@ -13,14 +13,27 @@ module rt.lifetime; import core.attribute : weak; -import core.internal.array.utils : __arrayStart, __arrayClearPad; +import core.checkedint : mulu; +import core.exception : onFinalizeError, onOutOfMemoryError, onUnicodeError; +import core.internal.gc.blockmeta : PAGESIZE; import core.memory; -debug(PRINTF) import core.stdc.stdio; +import core.stdc.stdlib : malloc; +import core.stdc.string : memcpy, memset; static import rt.tlsgc; -alias BlkInfo = GC.BlkInfo; +debug (PRINTF) import core.stdc.stdio : printf; +debug (VALGRIND) import etc.valgrind.valgrind; + alias BlkAttr = GC.BlkAttr; +// for now, all GC array functions are not exposed via core.memory. +extern(C) { + void[] gc_getArrayUsed(void *ptr, bool atomic) nothrow; + bool gc_expandArrayUsed(void[] slice, size_t newUsed, bool atomic) nothrow; + size_t gc_reserveArrayCapacity(void[] slice, size_t request, bool atomic) nothrow; + bool gc_shrinkArrayUsed(void[] slice, size_t existingUsed, bool atomic) nothrow; +} + private { alias bool function(Object) CollectHandler; @@ -28,17 +41,6 @@ private extern (C) void _d_monitordelete(Object h, bool det); - enum : size_t - { - PAGESIZE = 4096, - BIGLENGTHMASK = ~(PAGESIZE - 1), - SMALLPAD = 1, - MEDPAD = ushort.sizeof, - LARGEPREFIX = 16, // 16 bytes padding at the front of the array - LARGEPAD = LARGEPREFIX + 1, - MAXSMALLSIZE = 256-SMALLPAD, - MAXMEDSIZE = (PAGESIZE / 2) - MEDPAD - } } // Now-removed symbol, kept around for ABI @@ -101,8 +103,6 @@ Returns: newly created object pragma(inline, true) private extern (D) Object _d_newclass(bool initialize)(const ClassInfo ci) { - import core.stdc.stdlib; - import core.exception : onOutOfMemoryError; void* p; auto init = ci.initializer; @@ -251,435 +251,22 @@ inout(TypeInfo) unqualify(return scope inout(TypeInfo) cti) pure nothrow @nogc return ti; } -// size used to store the TypeInfo at the end of an allocation for structs that have a destructor -size_t structTypeInfoSize(const TypeInfo ti) pure nothrow @nogc +private uint __typeAttrs(const scope TypeInfo ti, void *copyAttrsFrom = null) pure nothrow { - if (ti && typeid(ti) is typeid(TypeInfo_Struct)) // avoid a complete dynamic type cast + if (copyAttrsFrom) { + // try to copy attrs from the given block + auto info = GC.query(copyAttrsFrom); + if (info.base) + return info.attr; + } + uint attrs = !(ti.flags & 1) ? BlkAttr.NO_SCAN : 0; + if (typeid(ti) is typeid(TypeInfo_Struct)) { auto sti = cast(TypeInfo_Struct)cast(void*)ti; if (sti.xdtor) - return size_t.sizeof; - } - return 0; -} - -/** dummy class used to lock for shared array appending */ -private class ArrayAllocLengthLock -{} - -/** - Set the allocated length of the array block. This is called - any time an array is appended to or its length is set. - - The allocated block looks like this for blocks < PAGESIZE: - - |elem0|elem1|elem2|...|elemN-1|emptyspace|N*elemsize| - - - The size of the allocated length at the end depends on the block size: - - a block of 16 to 256 bytes has an 8-bit length. - - a block with 512 to pagesize/2 bytes has a 16-bit length. - - For blocks >= pagesize, the length is a size_t and is at the beginning of the - block. The reason we have to do this is because the block can extend into - more pages, so we cannot trust the block length if it sits at the end of the - block, because it might have just been extended. If we can prove in the - future that the block is unshared, we may be able to change this, but I'm not - sure it's important. - - In order to do put the length at the front, we have to provide 16 bytes - buffer space in case the block has to be aligned properly. In x86, certain - SSE instructions will only work if the data is 16-byte aligned. In addition, - we need the sentinel byte to prevent accidental pointers to the next block. - Because of the extra overhead, we only do this for page size and above, where - the overhead is minimal compared to the block size. - - So for those blocks, it looks like: - - |N*elemsize|padding|elem0|elem1|...|elemN-1|emptyspace|sentinelbyte| - - where elem0 starts 16 bytes after the first byte. - */ -bool __setArrayAllocLength(ref BlkInfo info, size_t newlength, bool isshared, const TypeInfo tinext, size_t oldlength = ~0) pure nothrow -{ - import core.atomic; - - size_t typeInfoSize = structTypeInfoSize(tinext); - - if (info.size <= 256) - { - import core.checkedint; - - bool overflow; - auto newlength_padded = addu(newlength, - addu(SMALLPAD, typeInfoSize, overflow), - overflow); - - if (newlength_padded > info.size || overflow) - // new size does not fit inside block - return false; - - auto length = cast(ubyte *)(info.base + info.size - typeInfoSize - SMALLPAD); - if (oldlength != ~0) - { - if (isshared) - { - return cas(cast(shared)length, cast(ubyte)oldlength, cast(ubyte)newlength); - } - else - { - if (*length == cast(ubyte)oldlength) - *length = cast(ubyte)newlength; - else - return false; - } - } - else - { - // setting the initial length, no cas needed - *length = cast(ubyte)newlength; - } - if (typeInfoSize) - { - auto typeInfo = cast(TypeInfo*)(info.base + info.size - size_t.sizeof); - *typeInfo = cast() tinext; - } - } - else if (info.size < PAGESIZE) - { - if (newlength + MEDPAD + typeInfoSize > info.size) - // new size does not fit inside block - return false; - auto length = cast(ushort *)(info.base + info.size - typeInfoSize - MEDPAD); - if (oldlength != ~0) - { - if (isshared) - { - return cas(cast(shared)length, cast(ushort)oldlength, cast(ushort)newlength); - } - else - { - if (*length == oldlength) - *length = cast(ushort)newlength; - else - return false; - } - } - else - { - // setting the initial length, no cas needed - *length = cast(ushort)newlength; - } - if (typeInfoSize) - { - auto typeInfo = cast(TypeInfo*)(info.base + info.size - size_t.sizeof); - *typeInfo = cast() tinext; - } - } - else - { - if (newlength + LARGEPAD > info.size) - // new size does not fit inside block - return false; - auto length = cast(size_t *)(info.base); - if (oldlength != ~0) - { - if (isshared) - { - return cas(cast(shared)length, cast(size_t)oldlength, cast(size_t)newlength); - } - else - { - if (*length == oldlength) - *length = newlength; - else - return false; - } - } - else - { - // setting the initial length, no cas needed - *length = newlength; - } - if (typeInfoSize) - { - auto typeInfo = cast(TypeInfo*)(info.base + size_t.sizeof); - *typeInfo = cast()tinext; - } - } - return true; // resize succeeded -} - -/** - get the allocation size of the array for the given block (without padding or type info) - */ -private size_t __arrayAllocLength(ref BlkInfo info, const TypeInfo tinext) pure nothrow -{ - if (info.size <= 256) - return *cast(ubyte *)(info.base + info.size - structTypeInfoSize(tinext) - SMALLPAD); - - if (info.size < PAGESIZE) - return *cast(ushort *)(info.base + info.size - structTypeInfoSize(tinext) - MEDPAD); - - return *cast(size_t *)(info.base); -} - -/** - get the padding required to allocate size bytes. Note that the padding is - NOT included in the passed in size. Therefore, do NOT call this function - with the size of an allocated block. - */ -private size_t __arrayPad(size_t size, const TypeInfo tinext) nothrow pure @trusted -{ - return size > MAXMEDSIZE ? LARGEPAD : ((size > MAXSMALLSIZE ? MEDPAD : SMALLPAD) + structTypeInfoSize(tinext)); -} - -/** - allocate an array memory block by applying the proper padding and - assigning block attributes if not inherited from the existing block - */ -private BlkInfo __arrayAlloc(size_t arrsize, const scope TypeInfo ti, const TypeInfo tinext) nothrow pure -{ - import core.checkedint; - - size_t typeInfoSize = structTypeInfoSize(tinext); - size_t padsize = arrsize > MAXMEDSIZE ? LARGEPAD : ((arrsize > MAXSMALLSIZE ? MEDPAD : SMALLPAD) + typeInfoSize); - - bool overflow; - auto padded_size = addu(arrsize, padsize, overflow); - - if (overflow) - return BlkInfo(); - - uint attr = (!(tinext.flags & 1) ? BlkAttr.NO_SCAN : 0) | BlkAttr.APPENDABLE; - if (typeInfoSize) - attr |= BlkAttr.STRUCTFINAL | BlkAttr.FINALIZE; - - auto bi = GC.qalloc(padded_size, attr, tinext); - __arrayClearPad(bi, arrsize, padsize); - return bi; -} - -private BlkInfo __arrayAlloc(size_t arrsize, ref BlkInfo info, const scope TypeInfo ti, const TypeInfo tinext) -{ - import core.checkedint; - - if (!info.base) - return __arrayAlloc(arrsize, ti, tinext); - - immutable padsize = __arrayPad(arrsize, tinext); - bool overflow; - auto padded_size = addu(arrsize, padsize, overflow); - if (overflow) - { - return BlkInfo(); - } - - auto bi = GC.qalloc(padded_size, info.attr, tinext); - __arrayClearPad(bi, arrsize, padsize); - return bi; -} - -/** - cache for the lookup of the block info - */ -private enum N_CACHE_BLOCKS=8; - -// note this is TLS, so no need to sync. -BlkInfo *__blkcache_storage; - -static if (N_CACHE_BLOCKS==1) -{ - version=single_cache; -} -else -{ - //version=simple_cache; // uncomment to test simple cache strategy - //version=random_cache; // uncomment to test random cache strategy - - // ensure N_CACHE_BLOCKS is power of 2. - static assert(!((N_CACHE_BLOCKS - 1) & N_CACHE_BLOCKS)); - - version (random_cache) - { - int __nextRndNum = 0; - } - int __nextBlkIdx; -} - -@property BlkInfo *__blkcache() nothrow -{ - if (!__blkcache_storage) - { - import core.stdc.stdlib; - import core.stdc.string; - // allocate the block cache for the first time - immutable size = BlkInfo.sizeof * N_CACHE_BLOCKS; - __blkcache_storage = cast(BlkInfo *)malloc(size); - memset(__blkcache_storage, 0, size); - } - return __blkcache_storage; -} - -// called when thread is exiting. -static ~this() -{ - // free the blkcache - if (__blkcache_storage) - { - import core.stdc.stdlib; - free(__blkcache_storage); - __blkcache_storage = null; - } -} - - -// we expect this to be called with the lock in place -void processGCMarks(BlkInfo* cache, scope rt.tlsgc.IsMarkedDg isMarked) nothrow -{ - // called after the mark routine to eliminate block cache data when it - // might be ready to sweep - - debug(PRINTF) printf("processing GC Marks, %x\n", cache); - if (cache) - { - debug(PRINTF) foreach (i; 0 .. N_CACHE_BLOCKS) - { - printf("cache entry %d has base ptr %x\tsize %d\tflags %x\n", i, cache[i].base, cache[i].size, cache[i].attr); - } - auto cache_end = cache + N_CACHE_BLOCKS; - for (;cache < cache_end; ++cache) - { - if (cache.base != null && !isMarked(cache.base)) - { - debug(PRINTF) printf("clearing cache entry at %x\n", cache.base); - cache.base = null; // clear that data. - } - } - } -} - -unittest -{ - // Bugzilla 10701 - segfault in GC - ubyte[] result; result.length = 4096; - GC.free(result.ptr); - GC.collect(); -} - -/** - Get the cached block info of an interior pointer. Returns null if the - interior pointer's block is not cached. - - NOTE: The base ptr in this struct can be cleared asynchronously by the GC, - so any use of the returned BlkInfo should copy it and then check the - base ptr of the copy before actually using it. - - TODO: Change this function so the caller doesn't have to be aware of this - issue. Either return by value and expect the caller to always check - the base ptr as an indication of whether the struct is valid, or set - the BlkInfo as a side-effect and return a bool to indicate success. - */ -BlkInfo *__getBlkInfo(void *interior) nothrow -{ - BlkInfo *ptr = __blkcache; - version (single_cache) - { - if (ptr.base && ptr.base <= interior && (interior - ptr.base) < ptr.size) - return ptr; - return null; // not in cache. - } - else version (simple_cache) - { - foreach (i; 0..N_CACHE_BLOCKS) - { - if (ptr.base && ptr.base <= interior && (interior - ptr.base) < ptr.size) - return ptr; - ptr++; - } - } - else - { - // try to do a smart lookup, using __nextBlkIdx as the "head" - auto curi = ptr + __nextBlkIdx; - for (auto i = curi; i >= ptr; --i) - { - if (i.base && i.base <= interior && cast(size_t)(interior - i.base) < i.size) - return i; - } - - for (auto i = ptr + N_CACHE_BLOCKS - 1; i > curi; --i) - { - if (i.base && i.base <= interior && cast(size_t)(interior - i.base) < i.size) - return i; - } - } - return null; // not in cache. -} - -void __insertBlkInfoCache(BlkInfo bi, BlkInfo *curpos) nothrow -{ - version (single_cache) - { - *__blkcache = bi; - } - else - { - version (simple_cache) - { - if (curpos) - *curpos = bi; - else - { - // note, this is a super-simple algorithm that does not care about - // most recently used. It simply uses a round-robin technique to - // cache block info. This means that the ordering of the cache - // doesn't mean anything. Certain patterns of allocation may - // render the cache near-useless. - __blkcache[__nextBlkIdx] = bi; - __nextBlkIdx = (__nextBlkIdx+1) & (N_CACHE_BLOCKS - 1); - } - } - else version (random_cache) - { - // strategy: if the block currently is in the cache, move the - // current block index to the a random element and evict that - // element. - auto cache = __blkcache; - if (!curpos) - { - __nextBlkIdx = (__nextRndNum = 1664525 * __nextRndNum + 1013904223) & (N_CACHE_BLOCKS - 1); - curpos = cache + __nextBlkIdx; - } - else - { - __nextBlkIdx = curpos - cache; - } - *curpos = bi; - } - else - { - // - // strategy: If the block currently is in the cache, swap it with - // the head element. Otherwise, move the head element up by one, - // and insert it there. - // - auto cache = __blkcache; - if (!curpos) - { - __nextBlkIdx = (__nextBlkIdx+1) & (N_CACHE_BLOCKS - 1); - curpos = cache + __nextBlkIdx; - } - else if (curpos !is cache + __nextBlkIdx) - { - *curpos = cache[__nextBlkIdx]; - curpos = cache + __nextBlkIdx; - } - *curpos = bi; - } + attrs |= BlkAttr.FINALIZE; } + return attrs; } /** @@ -694,54 +281,42 @@ Params: */ extern(C) void _d_arrayshrinkfit(const TypeInfo ti, void[] arr) nothrow { - // note, we do not care about shared. We are setting the length no matter - // what, so no lock is required. - debug(PRINTF) printf("_d_arrayshrinkfit, elemsize = %d, arr.ptr = x%x arr.length = %d\n", ti.next.tsize, arr.ptr, arr.length); + debug(PRINTF) printf("_d_arrayshrinkfit, elemsize = %zd, arr.ptr = %p arr.length = %zd\n", ti.next.tsize, arr.ptr, arr.length); auto tinext = unqualify(ti.next); auto size = tinext.tsize; // array element size - auto cursize = arr.length * size; + auto reqsize = arr.length * size; auto isshared = typeid(ti) is typeid(TypeInfo_Shared); - auto bic = isshared ? null : __getBlkInfo(arr.ptr); - auto info = bic ? *bic : GC.query(arr.ptr); - if (info.base && (info.attr & BlkAttr.APPENDABLE)) - { - auto newsize = (arr.ptr - __arrayStart(info)) + cursize; - debug(PRINTF) printf("setting allocated size to %d\n", (arr.ptr - info.base) + cursize); + auto curArr = gc_getArrayUsed(arr.ptr, isshared); + if (curArr.ptr is null) + // not a valid GC pointer + return; - // destroy structs that become unused memory when array size is shrinked - if (typeid(tinext) is typeid(TypeInfo_Struct)) // avoid a complete dynamic type cast + // align the array. + auto offset = arr.ptr - curArr.ptr; + auto cursize = curArr.length - offset; + if (cursize <= reqsize) + // invalid situation, or no change. + return; + + // if the type has a destructor, destroy elements we are about to remove. + if (typeid(tinext) is typeid(TypeInfo_Struct)) // avoid a complete dynamic type cast + { + auto sti = cast(TypeInfo_Struct)cast(void*)tinext; + if (sti.xdtor) { - auto sti = cast(TypeInfo_Struct)cast(void*)tinext; - if (sti.xdtor) + try { - auto oldsize = __arrayAllocLength(info, tinext); - if (oldsize > cursize) - { - try - { - finalize_array(arr.ptr + cursize, oldsize - cursize, sti); - } - catch (Exception e) - { - import core.exception : onFinalizeError; - onFinalizeError(sti, e); - } - } + finalize_array(arr.ptr + reqsize, cursize - reqsize, sti); + } + catch (Exception e) + { + onFinalizeError(sti, e); } } - // Note: Since we "assume" the append is safe, it means it is not shared. - // Since it is not shared, we also know it won't throw (no lock). - if (!__setArrayAllocLength(info, newsize, false, tinext)) - { - import core.exception : onInvalidMemoryOperationError; - onInvalidMemoryOperationError(); - } - - // cache the block if not already done. - if (!isshared && !bic) - __insertBlkInfoCache(info, null); } + + gc_shrinkArrayUsed(arr.ptr[0 .. reqsize], cursize, isshared); } package bool hasPostblit(in TypeInfo ti) nothrow pure @@ -803,13 +378,7 @@ in } do { - import core.stdc.string; - import core.exception : onOutOfMemoryError; - - // step 1, get the block auto isshared = typeid(ti) is typeid(TypeInfo_Shared); - auto bic = isshared ? null : __getBlkInfo((*p).ptr); - auto info = bic ? *bic : GC.query((*p).ptr); auto tinext = unqualify(ti.next); auto size = tinext.tsize; version (D_InlineAsm_X86) @@ -838,8 +407,6 @@ do } else { - import core.checkedint : mulu; - bool overflow = false; size_t reqsize = mulu(size, newcapacity, overflow); if (!overflow) @@ -850,118 +417,53 @@ Loverflow: assert(0); Lcontinue: - // step 2, get the actual "allocated" size. If the allocated size does not - // match what we expect, then we will need to reallocate anyways. - - // TODO: this probably isn't correct for shared arrays - size_t curallocsize = void; - size_t curcapacity = void; - size_t offset = void; - size_t arraypad = void; - if (info.base && (info.attr & BlkAttr.APPENDABLE)) - { - if (info.size <= 256) - { - arraypad = SMALLPAD + structTypeInfoSize(tinext); - curallocsize = *(cast(ubyte *)(info.base + info.size - arraypad)); - } - else if (info.size < PAGESIZE) - { - arraypad = MEDPAD + structTypeInfoSize(tinext); - curallocsize = *(cast(ushort *)(info.base + info.size - arraypad)); - } - else - { - curallocsize = *(cast(size_t *)(info.base)); - arraypad = LARGEPAD; - } - - - offset = (*p).ptr - __arrayStart(info); - if (offset + (*p).length * size != curallocsize) - { - curcapacity = 0; - } - else - { - // figure out the current capacity of the block from the point - // of view of the array. - curcapacity = info.size - offset - arraypad; - } - } - else - { - curallocsize = curcapacity = offset = 0; - } - debug(PRINTF) printf("_d_arraysetcapacity, p = x%d,%d, newcapacity=%d, info.size=%d, reqsize=%d, curallocsize=%d, curcapacity=%d, offset=%d\n", (*p).ptr, (*p).length, newcapacity, info.size, reqsize, curallocsize, curcapacity, offset); - - if (curcapacity >= reqsize) - { - // no problems, the current allocated size is large enough. - return curcapacity / size; - } - - // step 3, try to extend the array in place. - if (info.size >= PAGESIZE && curcapacity != 0) - { - auto extendsize = reqsize + offset + LARGEPAD - info.size; - auto u = GC.extend(info.base, extendsize, extendsize); - if (u) - { - // extend worked, save the new current allocated size - if (bic) - bic.size = u; // update cache - curcapacity = u - offset - LARGEPAD; - return curcapacity / size; - } - } - - // step 4, if extending doesn't work, allocate a new array with at least the requested allocated size. + // step 1, see if we can ensure the capacity is valid in-place auto datasize = (*p).length * size; - // copy attributes from original block, or from the typeinfo if the - // original block doesn't exist. - info = __arrayAlloc(reqsize, info, ti, tinext); - if (info.base is null) + auto curCapacity = gc_reserveArrayCapacity((*p).ptr[0 .. datasize], reqsize, isshared); + if (curCapacity != 0) + // in-place worked! + return curCapacity / size; + + if (reqsize <= datasize) + // requested size is less than array size, the current array satisfies + // the request. But this is not an appendable GC array, so return 0. + return 0; + + // step 2, if reserving in-place doesn't work, allocate a new array with at + // least the requested allocated size. + auto attrs = __typeAttrs(tinext, (*p).ptr) | BlkAttr.APPENDABLE; + auto ptr = GC.malloc(reqsize, attrs, tinext); + if (ptr is null) goto Loverflow; + // copy the data over. // note that malloc will have initialized the data we did not request to 0. - auto tgt = __arrayStart(info); - memcpy(tgt, (*p).ptr, datasize); + memcpy(ptr, (*p).ptr, datasize); // handle postblit - __doPostblit(tgt, datasize, tinext); + __doPostblit(ptr, datasize, tinext); - if (!(info.attr & BlkAttr.NO_SCAN)) + if (!(attrs & BlkAttr.NO_SCAN)) { // need to memset the newly requested data, except for the data that // malloc returned that we didn't request. - void *endptr = tgt + reqsize; - void *begptr = tgt + datasize; + void *endptr = ptr + reqsize; + void *begptr = ptr + datasize; // sanity check assert(endptr >= begptr); memset(begptr, 0, endptr - begptr); } - // set up the correct length - __setArrayAllocLength(info, datasize, isshared, tinext); - if (!isshared) - __insertBlkInfoCache(info, bic); - - *p = (cast(void*)tgt)[0 .. (*p).length]; + *p = ptr[0 .. (*p).length]; - // determine the padding. This has to be done manually because __arrayPad - // assumes you are not counting the pad size, and info.size does include - // the pad. - if (info.size <= 256) - arraypad = SMALLPAD + structTypeInfoSize(tinext); - else if (info.size < PAGESIZE) - arraypad = MEDPAD + structTypeInfoSize(tinext); - else - arraypad = LARGEPAD; + // set up the correct length. Note that we need to do this here, because + // the GC malloc will automatically set the used size to what we requested. + gc_shrinkArrayUsed(ptr[0 .. datasize], reqsize, isshared); - curcapacity = info.size - arraypad; - return curcapacity / size; + curCapacity = gc_reserveArrayCapacity(ptr[0 .. datasize], 0, isshared); + assert(curCapacity); + return curCapacity / size; } /** @@ -979,12 +481,10 @@ Returns: newly allocated array */ extern (C) void[] _d_newarrayU(const scope TypeInfo ti, size_t length) pure nothrow @weak { - import core.exception : onOutOfMemoryError; - auto tinext = unqualify(ti.next); auto size = tinext.tsize; - debug(PRINTF) printf("_d_newarrayU(length = x%x, size = %d)\n", length, size); + debug(PRINTF) printf("_d_newarrayU(length = x%zx, size = %zd)\n", length, size); if (length == 0 || size == 0) return null; @@ -1010,8 +510,6 @@ extern (C) void[] _d_newarrayU(const scope TypeInfo ti, size_t length) pure noth } else { - import core.checkedint : mulu; - bool overflow = false; size = mulu(size, length, overflow); if (!overflow) @@ -1022,22 +520,16 @@ Loverflow: assert(0); Lcontinue: - auto info = __arrayAlloc(size, ti, tinext); - if (!info.base) + auto ptr = GC.malloc(size, __typeAttrs(tinext) | BlkAttr.APPENDABLE, tinext); + if (!ptr) goto Loverflow; - debug(PRINTF) printf(" p = %p\n", info.base); - // update the length of the array - auto arrstart = __arrayStart(info); - auto isshared = typeid(ti) is typeid(TypeInfo_Shared); - __setArrayAllocLength(info, size, isshared, tinext); - return arrstart[0..length]; + debug(PRINTF) printf(" p = %p\n", ptr); + return ptr[0 .. length]; } /// ditto extern (C) void[] _d_newarrayT(const TypeInfo ti, size_t length) pure nothrow @weak { - import core.stdc.string; - void[] result = _d_newarrayU(ti, length); auto tinext = unqualify(ti.next); auto size = tinext.tsize; @@ -1072,7 +564,6 @@ extern (C) void[] _d_newarrayiT(const TypeInfo ti, size_t length) pure nothrow @ default: { - import core.stdc.string; immutable sz = init.length; for (size_t u = 0; u < size * length; u += sz) memcpy(result.ptr + u, init.ptr, sz); @@ -1093,37 +584,9 @@ Returns: extern (C) void* _d_newitemU(scope const TypeInfo _ti) pure nothrow @weak { auto ti = unqualify(_ti); - auto flags = !(ti.flags & 1) ? BlkAttr.NO_SCAN : 0; - immutable tiSize = structTypeInfoSize(ti); - immutable itemSize = ti.tsize; - immutable size = itemSize + tiSize; - if (tiSize) - flags |= BlkAttr.STRUCTFINAL | BlkAttr.FINALIZE; - - auto blkInf = GC.qalloc(size, flags, ti); - auto p = blkInf.base; + auto flags = __typeAttrs(ti); - if (tiSize) - { - // the GC might not have cleared the padding area in the block - *cast(TypeInfo*)(p + (itemSize & ~(size_t.sizeof - 1))) = null; - *cast(TypeInfo*)(p + blkInf.size - tiSize) = cast() ti; - } - - return p; -} - -debug(PRINTF) -{ - extern(C) void printArrayCache() - { - auto ptr = __blkcache; - printf("CACHE: \n"); - foreach (i; 0 .. N_CACHE_BLOCKS) - { - printf(" %d\taddr:% .8x\tsize:% .10d\tflags:% .8x\n", i, ptr[i].base, ptr[i].size, ptr[i].attr); - } - } + return GC.malloc(ti.tsize, flags, ti); } /** @@ -1183,18 +646,22 @@ extern (C) CollectHandler rt_getCollectHandler() /** * */ -extern (C) int rt_hasFinalizerInSegment(void* p, size_t size, uint attr, scope const(void)[] segment) nothrow +extern (C) int rt_hasFinalizerInSegment(void* p, size_t size, TypeInfo typeInfo, scope const(void)[] segment) nothrow { - if (attr & BlkAttr.STRUCTFINAL) + if (!p) + return false; + + if (typeInfo !is null) { - if (attr & BlkAttr.APPENDABLE) - return hasArrayFinalizerInSegment(p, size, segment); - return hasStructFinalizerInSegment(p, size, segment); + assert(typeid(typeInfo) is typeid(TypeInfo_Struct)); + + auto ti = cast(TypeInfo_Struct)cast(void*)typeInfo; + return cast(size_t)(cast(void*)ti.xdtor - segment.ptr) < segment.length; } // otherwise class auto ppv = cast(void**) p; - if (!p || !*ppv) + if (!*ppv) return false; auto c = *cast(ClassInfo*)*ppv; @@ -1208,71 +675,6 @@ extern (C) int rt_hasFinalizerInSegment(void* p, size_t size, uint attr, scope c return false; } -int hasStructFinalizerInSegment(void* p, size_t size, in void[] segment) nothrow -{ - if (!p) - return false; - - auto ti = *cast(TypeInfo_Struct*)(p + size - size_t.sizeof); - return cast(size_t)(cast(void*)ti.xdtor - segment.ptr) < segment.length; -} - -int hasArrayFinalizerInSegment(void* p, size_t size, in void[] segment) nothrow -{ - if (!p) - return false; - - TypeInfo_Struct si = void; - if (size < PAGESIZE) - si = *cast(TypeInfo_Struct*)(p + size - size_t.sizeof); - else - si = *cast(TypeInfo_Struct*)(p + size_t.sizeof); - - return cast(size_t)(cast(void*)si.xdtor - segment.ptr) < segment.length; -} - -debug (VALGRIND) import etc.valgrind.valgrind; - -// called by the GC -void finalize_array2(void* p, size_t size) nothrow -{ - debug(PRINTF) printf("rt_finalize_array2(p = %p)\n", p); - - TypeInfo_Struct si = void; - debug (VALGRIND) - { - auto block = p[0..size]; - disableAddrReportingInRange(block); - } - if (size <= 256) - { - si = *cast(TypeInfo_Struct*)(p + size - size_t.sizeof); - size = *cast(ubyte*)(p + size - size_t.sizeof - SMALLPAD); - } - else if (size < PAGESIZE) - { - si = *cast(TypeInfo_Struct*)(p + size - size_t.sizeof); - size = *cast(ushort*)(p + size - size_t.sizeof - MEDPAD); - } - else - { - si = *cast(TypeInfo_Struct*)(p + size_t.sizeof); - size = *cast(size_t*)p; - p += LARGEPREFIX; - } - debug (VALGRIND) enableAddrReportingInRange(block); - - try - { - finalize_array(p, size, si); - } - catch (Exception e) - { - import core.exception : onFinalizeError; - onFinalizeError(si, e); - } -} - void finalize_array(void* p, size_t size, const TypeInfo_Struct si) { // Due to the fact that the delete operator calls destructors @@ -1287,18 +689,16 @@ void finalize_array(void* p, size_t size, const TypeInfo_Struct si) } // called by the GC -void finalize_struct(void* p, size_t size) nothrow +void finalize_struct(void* p, TypeInfo_Struct ti) nothrow { debug(PRINTF) printf("finalize_struct(p = %p)\n", p); - auto ti = *cast(TypeInfo_Struct*)(p + size - size_t.sizeof); try { ti.destroy(p); // call destructor } catch (Exception e) { - import core.exception : onFinalizeError; onFinalizeError(ti, e); } } @@ -1339,7 +739,6 @@ extern (C) void rt_finalize2(void* p, bool det = true, bool resetMemory = true) } catch (Exception e) { - import core.exception : onFinalizeError; onFinalizeError(*pc, e); } finally @@ -1354,15 +753,31 @@ extern (C) void rt_finalize(void* p, bool det = true) nothrow rt_finalize2(p, det, true); } -extern (C) void rt_finalizeFromGC(void* p, size_t size, uint attr) nothrow +extern (C) void rt_finalizeFromGC(void* p, size_t size, uint attr, TypeInfo typeInfo) nothrow { // to verify: reset memory necessary? - if (!(attr & BlkAttr.STRUCTFINAL)) + if (typeInfo is null) { rt_finalize2(p, false, false); // class - else if (attr & BlkAttr.APPENDABLE) - finalize_array2(p, size); // array of structs - else - finalize_struct(p, size); // struct + return; + } + + assert(typeid(typeInfo) is typeid(TypeInfo_Struct)); + + auto si = cast(TypeInfo_Struct)cast(void*)typeInfo; + + try + { + if (attr & BlkAttr.APPENDABLE) + { + finalize_array(p, size, si); + } + else + finalize_struct(p, si); // struct + } + catch (Exception e) + { + onFinalizeError(si, e); + } } @@ -1398,21 +813,17 @@ in } do { - import core.stdc.string; - import core.exception : onOutOfMemoryError; - debug(PRINTF) { //printf("_d_arraysetlengthT(p = %p, sizeelem = %d, newlength = %d)\n", p, sizeelem, newlength); if (p) - printf("\tp.ptr = %p, p.length = %d\n", (*p).ptr, (*p).length); + printf("\tp.ptr = %p, p.length = %zd\n", (*p).ptr, (*p).length); } if (newlength <= (*p).length) { *p = (*p)[0 .. newlength]; - void* newdata = (*p).ptr; - return newdata[0 .. newlength]; + return *p; } auto tinext = unqualify(ti.next); size_t sizeelem = tinext.tsize; @@ -1446,7 +857,6 @@ do } else { - import core.checkedint : mulu; const size_t newsize = mulu(sizeelem, newlength, overflow); } if (overflow) @@ -1455,124 +865,42 @@ do assert(0); } - debug(PRINTF) printf("newsize = %x, newlength = %x\n", newsize, newlength); - - const isshared = typeid(ti) is typeid(TypeInfo_Shared); + debug(PRINTF) printf("newsize = %zx, newlength = %zx\n", newsize, newlength); if (!(*p).ptr) { + assert((*p).length == 0); // pointer was null, need to allocate - auto info = __arrayAlloc(newsize, ti, tinext); - if (info.base is null) + auto ptr = GC.malloc(newsize, __typeAttrs(tinext) | BlkAttr.APPENDABLE, tinext); + if (ptr is null) { onOutOfMemoryError(); assert(0); } - __setArrayAllocLength(info, newsize, isshared, tinext); - if (!isshared) - __insertBlkInfoCache(info, null); - void* newdata = cast(byte *)__arrayStart(info); - memset(newdata, 0, newsize); - *p = newdata[0 .. newlength]; + memset(ptr, 0, newsize); + *p = ptr[0 .. newlength]; return *p; } const size_t size = (*p).length * sizeelem; - auto bic = isshared ? null : __getBlkInfo((*p).ptr); - auto info = bic ? *bic : GC.query((*p).ptr); + const isshared = typeid(ti) is typeid(TypeInfo_Shared); /* Attempt to extend past the end of the existing array. * If not possible, allocate new space for entire array and copy. */ - bool allocateAndCopy = false; void* newdata = (*p).ptr; - if (info.base && (info.attr & BlkAttr.APPENDABLE)) + if (!gc_expandArrayUsed(newdata[0 .. size], newsize, isshared)) { - // calculate the extent of the array given the base. - const size_t offset = (*p).ptr - __arrayStart(info); - if (info.size >= PAGESIZE) - { - // size of array is at the front of the block - if (!__setArrayAllocLength(info, newsize + offset, isshared, tinext, size + offset)) - { - // check to see if it failed because there is not - // enough space - if (*(cast(size_t*)info.base) == size + offset) - { - // not enough space, try extending - auto extendsize = newsize + offset + LARGEPAD - info.size; - auto u = GC.extend(info.base, extendsize, extendsize); - if (u) - { - // extend worked, now try setting the length - // again. - info.size = u; - if (__setArrayAllocLength(info, newsize + offset, isshared, tinext, size + offset)) - { - if (!isshared) - __insertBlkInfoCache(info, bic); - memset(newdata + size, 0, newsize - size); - *p = newdata[0 .. newlength]; - return *p; - } - } - } - - // couldn't do it, reallocate - allocateAndCopy = true; - } - else if (!isshared && !bic) - { - // add this to the cache, it wasn't present previously. - __insertBlkInfoCache(info, null); - } - } - else if (!__setArrayAllocLength(info, newsize + offset, isshared, tinext, size + offset)) - { - // could not resize in place - allocateAndCopy = true; - } - else if (!isshared && !bic) - { - // add this to the cache, it wasn't present previously. - __insertBlkInfoCache(info, null); - } - } - else - allocateAndCopy = true; - - if (allocateAndCopy) - { - if (info.base) - { - if (bic) - { - // a chance that flags have changed since this was cached, we should fetch the most recent flags - info.attr = GC.getAttr(info.base) | BlkAttr.APPENDABLE; - } - info = __arrayAlloc(newsize, info, ti, tinext); - } - else - { - info = __arrayAlloc(newsize, ti, tinext); - } - - if (info.base is null) + newdata = GC.malloc(newsize, __typeAttrs(tinext, (*p).ptr) | BlkAttr.APPENDABLE, tinext); + if (newdata is null) { onOutOfMemoryError(); assert(0); } - __setArrayAllocLength(info, newsize, isshared, tinext); - if (!isshared) - __insertBlkInfoCache(info, bic); - newdata = cast(byte *)__arrayStart(info); newdata[0 .. size] = (*p).ptr[0 .. size]; - /* Do postblit processing, as we are making a copy and the - * original array may have references. - * Note that this may throw. - */ + // Do postblit processing, as we are making a copy. __doPostblit(newdata, size, tinext); } @@ -1591,21 +919,17 @@ in } do { - import core.stdc.string; - import core.exception : onOutOfMemoryError; - debug(PRINTF) { - //printf("_d_arraysetlengthiT(p = %p, sizeelem = %d, newlength = %d)\n", p, sizeelem, newlength); + //printf("_d_arraysetlengthT(p = %p, sizeelem = %d, newlength = %d)\n", p, sizeelem, newlength); if (p) - printf("\tp.ptr = %p, p.length = %d\n", (*p).ptr, (*p).length); + printf("\tp.ptr = %p, p.length = %zd\n", (*p).ptr, (*p).length); } if (newlength <= (*p).length) { *p = (*p)[0 .. newlength]; - void* newdata = (*p).ptr; - return newdata[0 .. newlength]; + return *p; } auto tinext = unqualify(ti.next); size_t sizeelem = tinext.tsize; @@ -1639,7 +963,6 @@ do } else { - import core.checkedint : mulu; const size_t newsize = mulu(sizeelem, newlength, overflow); } if (overflow) @@ -1648,9 +971,7 @@ do assert(0); } - debug(PRINTF) printf("newsize = %x, newlength = %x\n", newsize, newlength); - - const isshared = typeid(ti) is typeid(TypeInfo_Shared); + debug(PRINTF) printf("newsize = %zx, newlength = %zx\n", newsize, newlength); static void doInitialize(void *start, void *end, const void[] initializer) { @@ -1671,119 +992,38 @@ do if (!(*p).ptr) { + assert((*p).length == 0); // pointer was null, need to allocate - auto info = __arrayAlloc(newsize, ti, tinext); - if (info.base is null) + auto ptr = GC.malloc(newsize, __typeAttrs(tinext) | BlkAttr.APPENDABLE, tinext); + if (ptr is null) { onOutOfMemoryError(); assert(0); } - __setArrayAllocLength(info, newsize, isshared, tinext); - if (!isshared) - __insertBlkInfoCache(info, null); - void* newdata = cast(byte *)__arrayStart(info); - doInitialize(newdata, newdata + newsize, tinext.initializer); - *p = newdata[0 .. newlength]; + doInitialize(ptr, ptr + newsize, tinext.initializer); + *p = ptr[0 .. newlength]; return *p; } const size_t size = (*p).length * sizeelem; - auto bic = isshared ? null : __getBlkInfo((*p).ptr); - auto info = bic ? *bic : GC.query((*p).ptr); + const isshared = typeid(ti) is typeid(TypeInfo_Shared); /* Attempt to extend past the end of the existing array. * If not possible, allocate new space for entire array and copy. */ - bool allocateAndCopy = false; void* newdata = (*p).ptr; - - if (info.base && (info.attr & BlkAttr.APPENDABLE)) + if (!gc_expandArrayUsed(newdata[0 .. size], newsize, isshared)) { - // calculate the extent of the array given the base. - const size_t offset = (*p).ptr - __arrayStart(info); - if (info.size >= PAGESIZE) - { - // size of array is at the front of the block - if (!__setArrayAllocLength(info, newsize + offset, isshared, tinext, size + offset)) - { - // check to see if it failed because there is not - // enough space - if (*(cast(size_t*)info.base) == size + offset) - { - // not enough space, try extending - auto extendsize = newsize + offset + LARGEPAD - info.size; - auto u = GC.extend(info.base, extendsize, extendsize); - if (u) - { - // extend worked, now try setting the length - // again. - info.size = u; - if (__setArrayAllocLength(info, newsize + offset, isshared, tinext, size + offset)) - { - if (!isshared) - __insertBlkInfoCache(info, bic); - doInitialize(newdata + size, newdata + newsize, tinext.initializer); - *p = newdata[0 .. newlength]; - return *p; - } - } - } - - // couldn't do it, reallocate - allocateAndCopy = true; - } - else if (!isshared && !bic) - { - // add this to the cache, it wasn't present previously. - __insertBlkInfoCache(info, null); - } - } - else if (!__setArrayAllocLength(info, newsize + offset, isshared, tinext, size + offset)) - { - // could not resize in place - allocateAndCopy = true; - } - else if (!isshared && !bic) - { - // add this to the cache, it wasn't present previously. - __insertBlkInfoCache(info, null); - } - } - else - allocateAndCopy = true; - - if (allocateAndCopy) - { - if (info.base) - { - if (bic) - { - // a chance that flags have changed since this was cached, we should fetch the most recent flags - info.attr = GC.getAttr(info.base) | BlkAttr.APPENDABLE; - } - info = __arrayAlloc(newsize, info, ti, tinext); - } - else - { - info = __arrayAlloc(newsize, ti, tinext); - } - - if (info.base is null) + newdata = GC.malloc(newsize, __typeAttrs(tinext, (*p).ptr) | BlkAttr.APPENDABLE, tinext); + if (newdata is null) { onOutOfMemoryError(); assert(0); } - __setArrayAllocLength(info, newsize, isshared, tinext); - if (!isshared) - __insertBlkInfoCache(info, bic); - newdata = cast(byte *)__arrayStart(info); newdata[0 .. size] = (*p).ptr[0 .. size]; - /* Do postblit processing, as we are making a copy and the - * original array may have references. - * Note that this may throw. - */ + // Do postblit processing, as we are making a copy. __doPostblit(newdata, size, tinext); } @@ -1798,85 +1038,65 @@ do Given an array of length `size` that needs to be expanded to `newlength`, compute a new capacity. -Better version by Dave Fladebo: +Better version by Dave Fladebo, enhanced by Steven Schveighoffer: This uses an inverse logorithmic algorithm to pre-allocate a bit more space for larger arrays. -- Arrays smaller than PAGESIZE bytes are left as-is, so for the most -common cases, memory allocation is 1 to 1. The small overhead added -doesn't affect small array perf. (it's virtually the same as -current). -- Larger arrays have some space pre-allocated. +- The maximum "extra" space is about 80% of the requested space. This is for +PAGE size and smaller. - As the arrays grow, the relative pre-allocated space shrinks. -- The logorithmic algorithm allocates relatively more space for -mid-size arrays, making it very fast for medium arrays (for -mid-to-large arrays, this turns out to be quite a bit faster than the -equivalent realloc() code in C, on Linux at least. Small arrays are -just as fast as GCC). - Perhaps most importantly, overall memory usage and stress on the GC is decreased significantly for demanding environments. +- The algorithm is tuned to avoid any division at runtime. Params: newlength = new `.length` - size = old `.length` + elemsize = size of the element in the new array Returns: new capacity for array */ -size_t newCapacity(size_t newlength, size_t size) +size_t newCapacity(size_t newlength, size_t elemsize) { - version (none) - { - size_t newcap = newlength * size; - } - else - { - size_t newcap = newlength * size; - size_t newext = 0; + size_t newcap = newlength * elemsize; - if (newcap > PAGESIZE) + /* + * Max growth factor numerator is 234, so allow for multiplying by 256. + * But also, the resulting size cannot be more than 2x, so prevent + * growing if 2x would fill up the address space (for 32-bit) + */ + enum largestAllowed = (ulong.max >> 8) & (size_t.max >> 1); + if (!newcap || (newcap & ~largestAllowed)) + return newcap; + + /* + * The calculation for "extra" space depends on the requested capacity. + * We use an inverse logarithm of the new capacity to add an extra 15% + * to 83% capacity. Note that normally we humans think in terms of + * percent, but using 128 instead of 100 for the denominator means we + * can avoid all division by simply bit-shifthing. Since there are only + * 64 bits in a long, the bsr of a size_t is going to be 0 - 63. Using + * a lookup table allows us to precalculate the multiplier based on the + * inverse logarithm. The formula rougly is: + * + * newcap = request * (1.0 + min(0.83, 10.0 / (log(request) + 1))) + */ + import core.bitop; + static immutable multTable = (){ + assert(__ctfe); + ulong[size_t.sizeof * 8] result; + foreach (i; 0 .. result.length) { - //double mult2 = 1.0 + (size / log10(pow(newcap * 2.0,2.0))); - - // redo above line using only integer math - - /*static int log2plus1(size_t c) - { int i; - - if (c == 0) - i = -1; - else - for (i = 1; c >>= 1; i++) - { - } - return i; - }*/ - - /* The following setting for mult sets how much bigger - * the new size will be over what is actually needed. - * 100 means the same size, more means proportionally more. - * More means faster but more memory consumption. - */ - //long mult = 100 + (1000L * size) / (6 * log2plus1(newcap)); - //long mult = 100 + (1000L * size) / log2plus1(newcap); - import core.bitop; - long mult = 100 + (1000L) / (bsr(newcap) + 1); - - // testing shows 1.02 for large arrays is about the point of diminishing return - // - // Commented out because the multipler will never be < 102. In order for it to be < 2, - // then 1000L / (bsr(x) + 1) must be > 2. The highest bsr(x) + 1 - // could be is 65 (64th bit set), and 1000L / 64 is much larger - // than 2. We need 500 bit integers for 101 to be achieved :) - /*if (mult < 102) - mult = 102;*/ - /*newext = cast(size_t)((newcap * mult) / 100); - newext -= newext % size;*/ - // This version rounds up to the next element, and avoids using - // mod. - newext = cast(size_t)((newlength * mult + 99) / 100) * size; - debug(PRINTF) printf("mult: %2.2f, alloc: %2.2f\n",mult/100.0,newext / cast(double)size); + auto factor = 128 + 1280 / (i + 1); + result[i] = factor > 234 ? 234 : factor; } - newcap = newext > newcap ? newext : newcap; - debug(PRINTF) printf("newcap = %d, newlength = %d, size = %d\n", newcap, newlength, size); - } + return result; + }(); + + auto mult = multTable[bsr(newcap)]; + + // if this were per cent, then the code would look like: + // ((newlength * mult + 99) / 100) * elemsize + newcap = cast(size_t)((newlength * mult + 127) >> 7) * elemsize; + debug(PRINTF) printf("mult: %2.2f, alloc: %2.2f\n",mult/128.0,newcap / cast(double)elemsize); + debug(PRINTF) printf("newcap = %zd, newlength = %zd, elemsize = %zd\n", newcap, newlength, elemsize); return newcap; } @@ -1895,101 +1115,58 @@ Returns: `px` after being appended to extern (C) byte[] _d_arrayappendcTX(const TypeInfo ti, return scope ref byte[] px, size_t n) @weak { - import core.stdc.string; // This is a cut&paste job from _d_arrayappendT(). Should be refactored. + // Short circuit if no data is being appended. + if (n == 0) + return px; + + // only optimize array append where ti is not a shared type auto tinext = unqualify(ti.next); auto sizeelem = tinext.tsize; // array element size auto isshared = typeid(ti) is typeid(TypeInfo_Shared); - auto bic = isshared ? null : __getBlkInfo(px.ptr); - auto info = bic ? *bic : GC.query(px.ptr); auto length = px.length; auto newlength = length + n; auto newsize = newlength * sizeelem; auto size = length * sizeelem; - size_t newcap = void; // for scratch space - // calculate the extent of the array given the base. - size_t offset = cast(void*)px.ptr - __arrayStart(info); - if (info.base && (info.attr & BlkAttr.APPENDABLE)) + if (!gc_expandArrayUsed(px.ptr[0 .. size], newsize, isshared)) { - if (info.size >= PAGESIZE) + // could not set the size, we must reallocate. + auto newcap = newCapacity(newlength, sizeelem); + auto attrs = __typeAttrs(tinext, px.ptr) | BlkAttr.APPENDABLE; + auto ptr = cast(byte*) GC.malloc(newcap, attrs, tinext); + if (ptr is null) { - // size of array is at the front of the block - if (!__setArrayAllocLength(info, newsize + offset, isshared, tinext, size + offset)) - { - // check to see if it failed because there is not - // enough space - newcap = newCapacity(newlength, sizeelem); - if (*(cast(size_t*)info.base) == size + offset) - { - // not enough space, try extending - auto extendoffset = offset + LARGEPAD - info.size; - auto u = GC.extend(info.base, newsize + extendoffset, newcap + extendoffset); - if (u) - { - // extend worked, now try setting the length - // again. - info.size = u; - if (__setArrayAllocLength(info, newsize + offset, isshared, tinext, size + offset)) - { - if (!isshared) - __insertBlkInfoCache(info, bic); - goto L1; - } - } - } - - // couldn't do it, reallocate - goto L2; - } - else if (!isshared && !bic) - { - __insertBlkInfoCache(info, null); - } - } - else if (!__setArrayAllocLength(info, newsize + offset, isshared, tinext, size + offset)) - { - // could not resize in place - newcap = newCapacity(newlength, sizeelem); - goto L2; + onOutOfMemoryError(); + assert(0); } - else if (!isshared && !bic) + + if (newsize != newcap) { - __insertBlkInfoCache(info, null); + // For small blocks that are always fully scanned, if we allocated more + // capacity than was requested, we are responsible for zeroing that + // memory. + // TODO: should let the GC figure this out, as this property may + // not always hold. + if (!(attrs & BlkAttr.NO_SCAN) && newcap < PAGESIZE) + memset(ptr + newsize, 0, newcap - newsize); + + gc_shrinkArrayUsed(ptr[0 .. newsize], newcap, isshared); } + + memcpy(ptr, px.ptr, size); + + // do potsblit processing. + __doPostblit(ptr, size, tinext); + + px = ptr[0 .. newlength]; + return px; } - else - { - // not appendable or is null - newcap = newCapacity(newlength, sizeelem); - if (info.base) - { - L2: - if (bic) - { - // a chance that flags have changed since this was cached, we should fetch the most recent flags - info.attr = GC.getAttr(info.base) | BlkAttr.APPENDABLE; - } - info = __arrayAlloc(newcap, info, ti, tinext); - } - else - { - info = __arrayAlloc(newcap, ti, tinext); - } - __setArrayAllocLength(info, newsize, isshared, tinext); - if (!isshared) - __insertBlkInfoCache(info, bic); - auto newdata = cast(byte *)__arrayStart(info); - memcpy(newdata, px.ptr, length * sizeelem); - // do postblit processing - __doPostblit(newdata, length * sizeelem, tinext); - (cast(void **)(&px))[1] = newdata; - } - - L1: - *cast(size_t *)&px = newlength; + + // we were able to expand in place, just update the length + px = px.ptr[0 .. newlength]; return px; } @@ -2043,7 +1220,6 @@ extern (C) void[] _d_arrayappendcd(ref byte[] x, dchar c) @weak } else { - import core.exception : onUnicodeError; onUnicodeError("Invalid UTF-8 sequence", 0); // invalid utf character } @@ -2165,18 +1341,14 @@ void* _d_arrayliteralTX(const TypeInfo ti, size_t length) @weak auto sizeelem = tinext.tsize; // array element size void* result; - debug(PRINTF) printf("_d_arrayliteralTX(sizeelem = %d, length = %d)\n", sizeelem, length); + debug(PRINTF) printf("_d_arrayliteralTX(sizeelem = %zd, length = %zd)\n", sizeelem, length); if (length == 0 || sizeelem == 0) - result = null; + return null; else { auto allocsize = length * sizeelem; - auto info = __arrayAlloc(allocsize, ti, tinext); - auto isshared = typeid(ti) is typeid(TypeInfo_Shared); - __setArrayAllocLength(info, allocsize, isshared, tinext); - result = __arrayStart(info); + return GC.malloc(allocsize, __typeAttrs(tinext) | BlkAttr.APPENDABLE, tinext); } - return result; } @@ -2437,7 +1609,7 @@ deprecated unittest dtorCount = 0; const(S1)[] carr1 = new const(S1)[5]; - BlkInfo blkinf1 = GC.query(carr1.ptr); + auto blkinf1 = GC.query(carr1.ptr); GC.runFinalizers((cast(char*)(typeid(S1).xdtor))[0..1]); assert(dtorCount == 5); GC.free(blkinf1.base); @@ -2449,22 +1621,19 @@ deprecated unittest assert(dtorCount == 4); // destructors run explicitely? dtorCount = 0; - BlkInfo blkinf = GC.query(arr2.ptr); + auto blkinf = GC.query(arr2.ptr); GC.runFinalizers((cast(char*)(typeid(S1).xdtor))[0..1]); assert(dtorCount == 6); GC.free(blkinf.base); // associative arrays - import rt.aaA : entryDtor; - // throw away all existing AA entries with dtor - GC.runFinalizers((cast(char*)(&entryDtor))[0..1]); - S1[int] aa1; aa1[0] = S1(0); aa1[1] = S1(1); dtorCount = 0; aa1 = null; - GC.runFinalizers((cast(char*)(&entryDtor))[0..1]); + auto dtor1 = typeid(TypeInfo_AssociativeArray.Entry!(int, S1)).xdtor; + GC.runFinalizers((cast(char*)dtor1)[0..1]); assert(dtorCount == 2); int[S1] aa2; @@ -2473,7 +1642,8 @@ deprecated unittest aa2[S1(2)] = 2; dtorCount = 0; aa2 = null; - GC.runFinalizers((cast(char*)(&entryDtor))[0..1]); + auto dtor2 = typeid(TypeInfo_AssociativeArray.Entry!(S1, int)).xdtor; + GC.runFinalizers((cast(char*)dtor2)[0..1]); assert(dtorCount == 3); S1[2][int] aa3; @@ -2481,7 +1651,8 @@ deprecated unittest aa3[1] = [S1(1),S1(3)]; dtorCount = 0; aa3 = null; - GC.runFinalizers((cast(char*)(&entryDtor))[0..1]); + auto dtor3 = typeid(TypeInfo_AssociativeArray.Entry!(int, S1[2])).xdtor; + GC.runFinalizers((cast(char*)dtor3)[0..1]); assert(dtorCount == 4); } @@ -2543,6 +1714,7 @@ unittest GC.free(larr1); auto larr2 = new S[255]; + import core.internal.gc.blockmeta : LARGEPREFIX; if (cast(void*)larr1 is cast(void*)larr2.ptr - LARGEPREFIX) // reusage not guaranteed { auto ptr = cast(S**)larr1; diff --git a/runtime/druntime/src/rt/minfo.d b/runtime/druntime/src/rt/minfo.d index 9bc1055898..6e9cd1c8fe 100644 --- a/runtime/druntime/src/rt/minfo.d +++ b/runtime/druntime/src/rt/minfo.d @@ -12,10 +12,13 @@ module rt.minfo; -import core.stdc.stdlib; // alloca -import core.stdc.string; // memcpy +import core.stdc.stdio : fprintf, stderr; +import core.stdc.stdlib : free, malloc, realloc; +import core.stdc.string : memcpy, memset; import rt.sections; +debug (printModuleDependencies) import core.stdc.stdio : printf; + enum { MIctorstart = 0x1, // we've started constructing it @@ -179,7 +182,6 @@ struct ModuleGroup switch (cycleHandling) with(OnCycle) { case "deprecate": - import core.stdc.stdio : fprintf, stderr; // Option deprecated in 2.101, remove in 2.111 fprintf(stderr, "`--DRT-oncycle=deprecate` is no longer supported, using `abort` instead\n"); break; @@ -202,8 +204,6 @@ struct ModuleGroup debug (printModuleDependencies) { - import core.stdc.stdio : printf; - foreach (_m; _modules) { printf("%s%s%s:", _m.name.ptr, (_m.flags & MIstandalone) @@ -375,7 +375,6 @@ struct ModuleGroup case print: // print the message buildCycleMessage(idx, midx, (string x) { - import core.stdc.stdio : fprintf, stderr; fprintf(stderr, "%.*s", cast(int) x.length, x.ptr); }); // continue on as if this is correct. @@ -518,7 +517,6 @@ struct ModuleGroup !doSort(MItlsctor | MItlsdtor, _tlsctors)) { // print a warning - import core.stdc.stdio : fprintf, stderr; fprintf(stderr, "Deprecation 16211 warning:\n" ~ "A cycle has been detected in your program that was undetected prior to DMD\n" ~ "2.072. This program will continue, but will not operate when using DMD 2.074\n" diff --git a/runtime/druntime/src/rt/monitor_.d b/runtime/druntime/src/rt/monitor_.d index cbe2a48440..1d4ed2ea8f 100644 --- a/runtime/druntime/src/rt/monitor_.d +++ b/runtime/druntime/src/rt/monitor_.d @@ -8,7 +8,26 @@ */ module rt.monitor_; -import core.atomic, core.stdc.stdlib, core.stdc.string; +import core.atomic; +import core.stdc.stdlib : calloc, free, realloc; +import core.stdc.string : memmove; + +version (Windows) +{ + import core.sys.windows.winbase /+: CRITICAL_SECTION, DeleteCriticalSection, + EnterCriticalSection, InitializeCriticalSection, LeaveCriticalSection+/; +} +else version (Posix) +{ + import core.sys.posix.pthread : pthread_mutex_destroy, pthread_mutex_init, pthread_mutex_lock, + PTHREAD_MUTEX_RECURSIVE, pthread_mutex_unlock, pthread_mutexattr_destroy, pthread_mutexattr_init, + pthread_mutexattr_settype; + import core.sys.posix.sys.types : pthread_mutex_t, pthread_mutexattr_t; +} +else +{ + static assert(0, "Unsupported platform"); +} // NOTE: The dtor callback feature is only supported for monitors that are not // supplied by the user. The assumption is that any object with a user- @@ -27,7 +46,7 @@ do auto m = ensureMonitor(cast(Object) owner); if (m.impl is null) { - atomicOp!("+=")(m.refs, cast(size_t) 1); + atomicOp!"+="(m.refs, size_t(1)); } // Assume the monitor is garbage collected and simply copy the reference. ownee.__monitor = owner.__monitor; @@ -44,7 +63,7 @@ extern (C) void _d_monitordelete(Object h, bool det) // let the GC collect the monitor setMonitor(h, null); } - else if (!atomicOp!("-=")(m.refs, cast(size_t) 1)) + else if (!atomicOp!"-="(m.refs, size_t(1))) { // refcount == 0 means unshared => no synchronization required disposeEvent(cast(Monitor*) m, h); @@ -65,7 +84,7 @@ extern (C) void _d_monitordelete_nogc(Object h) @nogc nothrow // let the GC collect the monitor setMonitor(h, null); } - else if (!atomicOp!("-=")(m.refs, cast(size_t) 1)) + else if (!atomicOp!"-="(m.refs, size_t(1))) { // refcount == 0 means unshared => no synchronization required deleteMonitor(cast(Monitor*) m); @@ -173,9 +192,6 @@ alias DEvent = void delegate(Object); version (Windows) { - import core.sys.windows.winbase /+: CRITICAL_SECTION, DeleteCriticalSection, - EnterCriticalSection, InitializeCriticalSection, LeaveCriticalSection+/; - alias Mutex = CRITICAL_SECTION; alias initMutex = InitializeCriticalSection; @@ -185,8 +201,6 @@ version (Windows) } else version (Posix) { - import core.sys.posix.pthread; - @nogc: alias Mutex = pthread_mutex_t; __gshared pthread_mutexattr_t gattr; @@ -211,10 +225,6 @@ else version (Posix) pthread_mutex_unlock(mtx) && assert(0); } } -else -{ - static assert(0, "Unsupported platform"); -} struct Monitor { @@ -226,12 +236,14 @@ struct Monitor private: +__gshared Mutex gmtx; + @property ref shared(Monitor*) monitor(return scope Object h) pure nothrow @nogc { return *cast(shared Monitor**)&h.__monitor; } -private shared(Monitor)* getMonitor(Object h) pure @nogc +shared(Monitor)* getMonitor(Object h) pure @nogc { return atomicLoad!(MemoryOrder.acq)(h.monitor); } @@ -241,8 +253,6 @@ void setMonitor(Object h, shared(Monitor)* m) pure @nogc atomicStore!(MemoryOrder.rel)(h.monitor, m); } -__gshared Mutex gmtx; - shared(Monitor)* ensureMonitor(Object h) { if (auto m = getMonitor(h)) diff --git a/runtime/druntime/src/rt/msvc_math.d b/runtime/druntime/src/rt/msvc_math.d index bad8448f7d..1d8f94bd66 100644 --- a/runtime/druntime/src/rt/msvc_math.d +++ b/runtime/druntime/src/rt/msvc_math.d @@ -15,7 +15,7 @@ module rt.msvc_math; version (CRuntime_Microsoft): version (X86): -import core.stdc.math; +import cmath = core.stdc.math; extern(C): @trusted: @@ -24,11 +24,11 @@ nothrow: mixin template AltImpl(string baseName) { - mixin("float _msvc_"~baseName~"f(float x) { return cast(float) "~baseName~"(x); }"); + mixin("float _msvc_"~baseName~"f(float x) { return cast(float) cmath."~baseName~"(x); }"); } mixin template AltImpl2(string baseName) { - mixin("float _msvc_"~baseName~"f(float x, float y) { return cast(float) "~baseName~"(x, y); }"); + mixin("float _msvc_"~baseName~"f(float x, float y) { return cast(float) cmath."~baseName~"(x, y); }"); } mixin AltImpl!"acos"; @@ -53,7 +53,7 @@ mixin AltImpl2!"fmod"; float _msvc_modff(float value, float* iptr) { double di; - const result = cast(float) modf(value, &di); + const result = cast(float) cmath.modf(value, &di); *iptr = cast(float) di; return result; } diff --git a/runtime/druntime/src/rt/profilegc.d b/runtime/druntime/src/rt/profilegc.d index b97a5c5437..e7d4f03e53 100644 --- a/runtime/druntime/src/rt/profilegc.d +++ b/runtime/druntime/src/rt/profilegc.d @@ -15,10 +15,9 @@ module rt.profilegc; private: -import core.stdc.errno; -import core.stdc.stdio; -import core.stdc.stdlib; -import core.stdc.string; +import core.stdc.errno : errno; +import core.stdc.stdio : fclose, FILE, fopen, fprintf, snprintf, stderr, stdout; +import core.stdc.stdlib : free, malloc, qsort, realloc; import core.exception : onOutOfMemoryError; import core.internal.container.hashtab; diff --git a/runtime/druntime/src/rt/sections.d b/runtime/druntime/src/rt/sections.d index 7dbfd24a93..90bb112b9d 100644 --- a/runtime/druntime/src/rt/sections.d +++ b/runtime/druntime/src/rt/sections.d @@ -22,7 +22,9 @@ else version (TVOS) else version (WatchOS) version = Darwin; -version (CRuntime_Glibc) +version (GNU) + public import gcc.sections; +else version (CRuntime_Glibc) public import rt.sections_elf_shared; else version (CRuntime_Musl) public import rt.sections_elf_shared; diff --git a/runtime/druntime/src/rt/sections_darwin_64.d b/runtime/druntime/src/rt/sections_darwin_64.d index 825f92dae5..d885621211 100644 --- a/runtime/druntime/src/rt/sections_darwin_64.d +++ b/runtime/druntime/src/rt/sections_darwin_64.d @@ -23,9 +23,14 @@ else version (WatchOS) version (Darwin): version (D_LP64): -import core.sys.darwin.mach.dyld; -import core.sys.darwin.mach.getsect; -import core.sys.posix.pthread; +import core.stdc.stdint : intptr_t, uintptr_t; +import core.sys.darwin.mach.dyld : _dyld_get_image_header, _dyld_image_count; +import core.sys.darwin.mach.getsect : getsectbynamefromheader_64, section_64; +import core.sys.darwin.mach.loader : LC_SEGMENT_64, load_command, mach_header, mach_header_64, MH_MAGIC_64, + S_THREAD_LOCAL_VARIABLES, SECT_BSS, SECT_COMMON, SECT_DATA, SECTION_TYPE, SEG_DATA, segment_command_64, + tlv_descriptor; +import core.sys.posix.pthread : pthread_getspecific; +import core.sys.posix.sys.types : pthread_key_t; import rt.util.utility : safeAssert; @@ -140,7 +145,13 @@ pthread_key_t firstTLVKey(const mach_header_64* header) pure nothrow @nogc if ((section.flags & SECTION_TYPE) != S_THREAD_LOCAL_VARIABLES) continue; - return section.firstTLVDescriptor(slide).key; + // NOTE: macOS 15.4 has started to fill the upper 32 bits of + // the `key` field with an additional number. Using the whole + // 64-bit field as a key results in a segmentation fault. Even + // though none of this appears to be documented anywhere, we + // assume that only the lower 32 bits are used for the actual + // key and this results in binaries that execute normally. + return section.firstTLVDescriptor(slide).key & 0xFFFF_FFFF; } } diff --git a/runtime/druntime/src/rt/sections_elf_shared.d b/runtime/druntime/src/rt/sections_elf_shared.d index 5d26759c19..83c0f236ef 100644 --- a/runtime/druntime/src/rt/sections_elf_shared.d +++ b/runtime/druntime/src/rt/sections_elf_shared.d @@ -5,7 +5,7 @@ * Copyright: Copyright Martin Nowak 2012-2013. * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). * Authors: Martin Nowak - * Source: $(DRUNTIMESRC rt/_sections_linux.d) + * Source: $(DRUNTIMESRC rt/_sections_elf_shared.d) */ module rt.sections_elf_shared; @@ -36,29 +36,42 @@ else enum IsWindows = false; static if (SharedELF || SharedDarwin || IsWindows): +// debug = PRINTF; + version (MIPS32) version = MIPS_Any; version (MIPS64) version = MIPS_Any; version (RISCV32) version = RISCV_Any; version (RISCV64) version = RISCV_Any; -// debug = PRINTF; +import core.internal.container.array; +import core.internal.container.hashtab; import core.internal.elf.dl; import core.memory; -import core.stdc.config; -import core.stdc.stdio; -import core.stdc.stdlib : calloc, exit, free, malloc, EXIT_FAILURE; -import core.stdc.string : strlen; +import core.stdc.config : c_ulong; +import core.stdc.stdlib : calloc, free, malloc; +version (Shared) import core.sync.mutex; +import rt.deh; +import rt.dmain2; +import rt.minfo; +import rt.util.utility : safeAssert; + +version (Posix) +{ + import core.sys.posix.pthread : pthread_mutex_destroy, pthread_mutex_init, pthread_mutex_lock, pthread_mutex_unlock; + import core.sys.posix.sys.types : pthread_mutex_t; +} + version (linux) { - import core.sys.linux.dlfcn; - import core.sys.linux.elf; - import core.sys.linux.link; + import core.sys.linux.dlfcn : Dl_info, dladdr, dlclose, dlinfo, dlopen, RTLD_DI_LINKMAP, RTLD_LAZY, RTLD_NOLOAD; + import core.sys.linux.elf : DT_AUXILIARY, DT_FILTER, DT_NEEDED, DT_STRTAB, PF_W, PF_X, PT_DYNAMIC, PT_LOAD, PT_TLS; + import core.sys.linux.link : ElfW, link_map; } else version (FreeBSD) { - import core.sys.freebsd.dlfcn; - import core.sys.freebsd.sys.elf; - import core.sys.freebsd.sys.link_elf; + import core.sys.freebsd.dlfcn : Dl_info, dladdr, dlclose, dlinfo, dlopen, RTLD_DI_LINKMAP, RTLD_LAZY, RTLD_NOLOAD; + import core.sys.freebsd.sys.elf : DT_AUXILIARY, DT_FILTER, DT_NEEDED, DT_STRTAB, PF_W, PF_X, PT_DYNAMIC, PT_LOAD, PT_TLS; + import core.sys.freebsd.sys.link_elf : ElfW, link_map; } else version (Darwin) { @@ -75,15 +88,15 @@ else version (Darwin) } else version (NetBSD) { - import core.sys.netbsd.dlfcn; - import core.sys.netbsd.sys.elf; - import core.sys.netbsd.sys.link_elf; + import core.sys.netbsd.dlfcn : Dl_info, dladdr, dlclose, dlinfo, dlopen, RTLD_DI_LINKMAP, RTLD_LAZY, RTLD_NOLOAD; + import core.sys.netbsd.sys.elf : DT_AUXILIARY, DT_FILTER, DT_NEEDED, DT_STRTAB, PF_W, PF_X, PT_DYNAMIC, PT_LOAD, PT_TLS; + import core.sys.netbsd.sys.link_elf : ElfW, link_map; } else version (DragonFlyBSD) { - import core.sys.dragonflybsd.dlfcn; - import core.sys.dragonflybsd.sys.elf; - import core.sys.dragonflybsd.sys.link_elf; + import core.sys.dragonflybsd.dlfcn : Dl_info, dladdr, dlclose, dlinfo, dlopen, RTLD_DI_LINKMAP, RTLD_LAZY, RTLD_NOLOAD; + import core.sys.dragonflybsd.sys.elf : DT_AUXILIARY, DT_FILTER, DT_NEEDED, DT_STRTAB, PF_W, PF_X, PT_DYNAMIC, PT_LOAD, PT_TLS; + import core.sys.dragonflybsd.sys.link_elf : ElfW, link_map; } else version (Windows) { @@ -96,13 +109,8 @@ else { static assert(0, "unimplemented"); } -import rt.deh; -import rt.dmain2; -import rt.minfo; -import core.internal.container.array; -import core.internal.container.hashtab; -import rt.util.utility : safeAssert; -version (Shared) import core.sync.mutex; + +debug (PRINTF) import core.stdc.stdio : printf; alias DSO SectionGroup; struct DSO @@ -150,7 +158,7 @@ struct DSO private: - invariant() + invariant { safeAssert(_moduleGroup.modules.length > 0, "No modules for DSO."); version (CRuntime_UClibc) {} else @@ -272,7 +280,7 @@ version (Shared) // interface for core.thread to inherit loaded libraries void* pinLoadedLibraries() nothrow @nogc { - auto res = cast(Array!(ThreadDSO)*)calloc(1, Array!(ThreadDSO).sizeof); + auto res = cast(Array!(ThreadDSO)*).calloc(1, Array!(ThreadDSO).sizeof); res.length = _loadedDSOs.length; foreach (i, ref tdso; _loadedDSOs) { @@ -1259,7 +1267,9 @@ void[] getTLSRange(size_t mod, size_t sz, size_t alignment) nothrow @nogc if (reference != start) { + import core.stdc.stdio : fprintf, stderr; import core.stdc.stdlib : abort; + fprintf(stderr, "ERROR: getTLSRange mismatch - %p\n", start); fprintf(stderr, " vs. %p\n", reference); abort(); diff --git a/runtime/druntime/src/rt/sections_osx_x86.d b/runtime/druntime/src/rt/sections_osx_x86.d index 8178cace76..71395bbc60 100644 --- a/runtime/druntime/src/rt/sections_osx_x86.d +++ b/runtime/druntime/src/rt/sections_osx_x86.d @@ -26,13 +26,21 @@ version (Darwin): version (X86): // debug = PRINTF; -import core.stdc.stdio; -import core.stdc.string, core.stdc.stdlib; -import core.sys.posix.pthread; -import core.sys.darwin.mach.dyld; -import core.sys.darwin.mach.getsect; -import rt.deh, rt.minfo; + import core.internal.container.array; +import core.stdc.stdint : intptr_t; +import core.stdc.stdio : fprintf, perror, stderr; +import core.stdc.stdlib : calloc, free, malloc; +import core.stdc.string : memcpy; +import core.sys.darwin.mach.dyld : _dyld_register_func_for_add_image; +import core.sys.darwin.mach.getsect : getsectbynamefromheader; +import core.sys.darwin.mach.loader : mach_header, MH_MAGIC, SECT_BSS, SECT_COMMON, SECT_DATA, SEG_DATA; +import core.sys.posix.pthread : pthread_getspecific, pthread_key_create, pthread_key_delete, pthread_key_t, + pthread_setspecific; +import rt.deh; +import rt.minfo; + +debug (PRINTF) import core.stdc.stdio : printf; struct SectionGroup { @@ -166,7 +174,6 @@ ref void[] getTLSBlock() nothrow @nogc pary = cast(void[]*).calloc(1, (void[]).sizeof); if (pthread_setspecific(_tlsKey, pary) != 0) { - import core.stdc.stdio; perror("pthread_setspecific failed with"); assert(0); } diff --git a/runtime/druntime/src/rt/sections_osx_x86_64.d b/runtime/druntime/src/rt/sections_osx_x86_64.d index 95608f9ca1..b903959581 100644 --- a/runtime/druntime/src/rt/sections_osx_x86_64.d +++ b/runtime/druntime/src/rt/sections_osx_x86_64.d @@ -26,18 +26,19 @@ version (Darwin): version (X86_64): // debug = PRINTF; -import core.stdc.stdio; -import core.stdc.string, core.stdc.stdlib; -import core.sys.posix.pthread; -import core.sys.darwin.mach.dyld; -import core.sys.darwin.mach.getsect; +import core.internal.container.array; +import core.stdc.stdint : intptr_t; +import core.stdc.stdio : fprintf, stderr; +import core.sys.darwin.mach.dyld : _dyld_register_func_for_add_image; +import core.sys.darwin.mach.getsect : mach_header; import rt.deh; import rt.minfo; import rt.sections_darwin_64; -import core.internal.container.array; import rt.util.utility : safeAssert; +debug (PRINTF) import core.stdc.stdio : printf; + struct SectionGroup { static int opApply(scope int delegate(ref SectionGroup) dg) diff --git a/runtime/druntime/src/rt/sections_solaris.d b/runtime/druntime/src/rt/sections_solaris.d index 84579a5b16..10893435cf 100644 --- a/runtime/druntime/src/rt/sections_solaris.d +++ b/runtime/druntime/src/rt/sections_solaris.d @@ -17,20 +17,18 @@ module rt.sections_solaris; version (LDC) { /* implemented in rt.sections_ldc */ } else: version (Solaris) -{ version = SolarisOrOpenBSD; -} else version (OpenBSD) -{ version = SolarisOrOpenBSD; -} version (SolarisOrOpenBSD): // debug = PRINTF; -debug(PRINTF) import core.stdc.stdio; -import core.stdc.stdlib : malloc, free; -import rt.deh, rt.minfo; + +import rt.deh; +import rt.minfo; + +debug (PRINTF) import core.stdc.stdio : printf; struct SectionGroup { diff --git a/runtime/druntime/src/rt/sections_win64.d b/runtime/druntime/src/rt/sections_win64.d index a1139e9303..c12a87a2d7 100644 --- a/runtime/druntime/src/rt/sections_win64.d +++ b/runtime/druntime/src/rt/sections_win64.d @@ -14,21 +14,21 @@ module rt.sections_win64; version (CRuntime_Microsoft): +version (DigitalMars) version (Win64) version = hasEHTables; + // debug = PRINTF; -debug(PRINTF) import core.stdc.stdio; + +import core.internal.container.array; import core.memory; -import core.stdc.stdlib : calloc, malloc, free; -import core.sys.windows.winbase : FreeLibrary, GetCurrentThreadId, GetModuleHandleExW, - GetProcAddress, LoadLibraryA, LoadLibraryW, - GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT; -import core.sys.windows.winnt : WCHAR, IMAGE_DOS_HEADER, IMAGE_DOS_SIGNATURE, IMAGE_FILE_HEADER, - IMAGE_NT_HEADERS, IMAGE_SECTION_HEADER, IMAGE_TLS_DIRECTORY, IMAGE_DIRECTORY_ENTRY_TLS; +import core.stdc.stdlib : calloc, free, malloc; import core.sys.windows.threadaux; +import core.sys.windows.winbase : FreeLibrary, GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, GetModuleHandleExW, GetProcAddress, LoadLibraryA, LoadLibraryW; +import core.sys.windows.winnt : IMAGE_DIRECTORY_ENTRY_TLS, IMAGE_DOS_HEADER, IMAGE_DOS_SIGNATURE, IMAGE_NT_HEADERS, IMAGE_SECTION_HEADER, IMAGE_TLS_DIRECTORY, WCHAR; import core.thread; -import rt.deh, rt.minfo; -import core.internal.container.array; +import rt.deh; +import rt.minfo; -version (DigitalMars) version (Win64) version = hasEHTables; +debug (PRINTF) import core.stdc.stdio : printf; version (LDC) { /* implemented in rt.sections_elf_shared, we just need some helpers */ } else { @@ -96,7 +96,7 @@ void initSections() nothrow @nogc void initSections(void* handle) nothrow @nogc { - auto sectionGroup = cast(SectionGroup*)calloc(1, SectionGroup.sizeof); + auto sectionGroup = cast(SectionGroup*).calloc(1, SectionGroup.sizeof); sectionGroup._moduleGroup = ModuleGroup(getModuleInfos(handle)); sectionGroup._handle = handle; version (hasEHTables) @@ -127,7 +127,7 @@ void initSections(void* handle) nothrow @nogc if (conservative) { - sectionGroup._gcRanges = (cast(void[]*) malloc((void[]).sizeof))[0..1]; + sectionGroup._gcRanges = (cast(void[]*).malloc((void[]).sizeof))[0..1]; sectionGroup._gcRanges[0] = dataSection; } else @@ -135,9 +135,9 @@ void initSections(void* handle) nothrow @nogc // consolidate GC ranges for pointers in the .data segment void[] dpSection = findImageSection(handle, ".dp"); debug(PRINTF) printf("found .dp section: [%p,+%llx]\n", dpSection.ptr, - cast(ulong)dpSsection.length); + cast(ulong)dpSection.length); auto dp = cast(uint[]) dpSection; - auto ranges = cast(void[]*) malloc(dp.length * (void[]).sizeof); + auto ranges = cast(void[]*).malloc(dp.length * (void[]).sizeof); size_t r = 0; void* prev = null; foreach (off; dp) @@ -218,7 +218,7 @@ version (Shared) auto size = tlsdir.EndAddressOfRawData - tlsdir.StartAddressOfRawData + tlsdir.SizeOfZeroFill; if (conservative) - dg( beg, beg + size); + dg(beg, beg + size); else scanTLSPrecise(cast(uint*)&sec._tpSection[0], cast(uint*)&sec._tpSection[$], beg, dg); } @@ -446,8 +446,7 @@ void* initLibrary(void* mod) // (What? LoadLibrary() is a Windows API call, it shouldn't call rt_init().) if (mod is null) return mod; - gcSetFn gcSet = cast(gcSetFn) GetProcAddress(mod, "gc_setProxy"); - if (gcSet !is null) + if (auto gcSet = cast(gcSetFn) GetProcAddress(mod, "gc_setProxy")) { // BUG: Set proxy, but too late gcSet(gc_getProxy()); } @@ -464,8 +463,7 @@ void* initLibrary(void* mod) */ extern (C) int rt_unloadLibrary(void* ptr) { - gcClrFn gcClr = cast(gcClrFn) GetProcAddress(ptr, "gc_clrProxy"); - if (gcClr !is null) + if (auto gcClr = cast(gcClrFn) GetProcAddress(ptr, "gc_clrProxy")) gcClr(); return FreeLibrary(ptr) != 0; } diff --git a/runtime/druntime/src/rt/tlsgc.d b/runtime/druntime/src/rt/tlsgc.d index b13a1b319c..2b21d1e0e1 100644 --- a/runtime/druntime/src/rt/tlsgc.d +++ b/runtime/druntime/src/rt/tlsgc.d @@ -13,9 +13,9 @@ */ module rt.tlsgc; -import core.stdc.stdlib; - -static import rt.lifetime, rt.sections; +import core.exception : onOutOfMemoryError; +import core.stdc.stdlib : free, malloc; +static import rt.sections; /** * Per thread record to store thread associated data for garbage collection. @@ -23,7 +23,6 @@ static import rt.lifetime, rt.sections; struct Data { typeof(rt.sections.initTLSRanges()) tlsRanges; - rt.lifetime.BlkInfo** blockInfoCache; } /** @@ -33,14 +32,11 @@ struct Data void* init() nothrow @nogc { auto data = cast(Data*).malloc(Data.sizeof); - import core.exception; - if ( data is null ) core.exception.onOutOfMemoryError(); + if ( data is null ) onOutOfMemoryError(); *data = Data.init; // do module specific initialization data.tlsRanges = rt.sections.initTLSRanges(); - data.blockInfoCache = &rt.lifetime.__blkcache_storage; - return data; } @@ -67,16 +63,3 @@ void scan(void* data, scope ScanDg dg) nothrow // do module specific marking rt.sections.scanTLSRanges((cast(Data*)data).tlsRanges, dg); } - -alias int delegate(void* addr) nothrow IsMarkedDg; - -/** - * GC sweep hook, called FOR each thread. Can be used to free - * additional thread local memory or associated data structures. Note - * that only memory allocated from the GC can have marks. - */ -void processGCMarks(void* data, scope IsMarkedDg dg) nothrow -{ - // do module specific sweeping - rt.lifetime.processGCMarks(*(cast(Data*)data).blockInfoCache, dg); -} diff --git a/runtime/druntime/src/rt/trace.d b/runtime/druntime/src/rt/trace.d index 4cbf071f14..18ef2306d0 100644 --- a/runtime/druntime/src/rt/trace.d +++ b/runtime/druntime/src/rt/trace.d @@ -12,13 +12,21 @@ module rt.trace; import core.demangle; -import core.stdc.ctype; -import core.stdc.stdio; -import core.stdc.stdlib; -import core.stdc.string; +import core.stdc.ctype : isalpha, isgraph, isspace; +import core.stdc.stdio : EOF, fclose, fgetc, FILE, fopen, fprintf, stderr, stdout; +import core.stdc.stdlib : exit, EXIT_FAILURE, free, malloc, qsort, realloc, strtoul; +import core.stdc.string : memcmp, memset, strdup, strlen; + +debug import core.stdc.stdio : printf; version (CRuntime_Microsoft) - private alias core.stdc.stdlib._strtoui64 strtoull; +{ + import core.stdc.stdlib : strtoull = _strtoui64; +} +else +{ + import core.stdc.stdlib : strtoull; +} shared static this () { @@ -679,8 +687,7 @@ extern(C) void _c_trace_pro(size_t idlen, char* idptr) extern(C) void _c_trace_epi() { //printf("_c_trace_epi()\n"); - auto tos = trace_tos; - if (tos) + if (auto tos = trace_tos) { timer_t endtime; QueryPerformanceCounter(&endtime); diff --git a/runtime/druntime/src/rt/tracegc.d b/runtime/druntime/src/rt/tracegc.d index c230e21e6e..61947471ad 100644 --- a/runtime/druntime/src/rt/tracegc.d +++ b/runtime/druntime/src/rt/tracegc.d @@ -17,8 +17,6 @@ module rt.tracegc; version (LDC) { /* -profile=gc not supported yet */ } else: -// version = tracegc; - extern (C) void _d_callfinalizer(void* p); extern (C) void _d_callinterfacefinalizer(void *p); extern (C) void _d_delclass(Object* p); @@ -71,19 +69,6 @@ enum accumulator = q{ else string name = ""; - version (tracegc) - { - import core.stdc.stdio; - - printf("%s file = '%.*s' line = %d function = '%.*s' type = %.*s\n", - __FUNCTION__.ptr, - file.length, file.ptr, - line, - funcname.length, funcname.ptr, - name.length, name.ptr - ); - } - ulong currentlyAllocated = GC.allocatedInCurrentThread; scope(exit) diff --git a/runtime/druntime/src/rt/util/typeinfo.d b/runtime/druntime/src/rt/util/typeinfo.d index 730649ef48..9c5af477cb 100644 --- a/runtime/druntime/src/rt/util/typeinfo.d +++ b/runtime/druntime/src/rt/util/typeinfo.d @@ -203,10 +203,10 @@ detect if we need to override. The overriding initializer should be nonzero. private class TypeInfoGeneric(T, Base = T) : Select!(is(T == Base), TypeInfo, TypeInfoGeneric!Base) if (T.sizeof == Base.sizeof && T.alignof == Base.alignof) { - const: nothrow: pure: @trusted: + const: nothrow pure @trusted: // Returns the type name. - override string toString() const pure nothrow @safe { return T.stringof; } + override string toString() const @safe { return T.stringof; } // `getHash` is the same for `Base` and `T`, introduce it just once. static if (is(T == Base)) @@ -390,16 +390,16 @@ unittest // void class TypeInfo_v : TypeInfoGeneric!ubyte { - const: nothrow: pure: @trusted: + const nothrow pure @trusted: - override string toString() const pure nothrow @safe { return "void"; } + override string toString() const @safe { return "void"; } override size_t getHash(scope const void* p) { assert(0); } - override @property uint flags() nothrow pure + override @property uint flags() { return 1; } @@ -640,7 +640,7 @@ class TypeInfo_n : TypeInfo override @property size_t tsize() { return typeof(null).sizeof; } - override const(void)[] initializer() @trusted { return (cast(void *)null)[0 .. size_t.sizeof]; } + override const(void)[] initializer() @trusted { return (cast(void*)null)[0 .. size_t.sizeof]; } override void swap(void*, void*) {} diff --git a/runtime/druntime/src/test_runner.d b/runtime/druntime/src/test_runner.d index c15e10426e..0b98e46729 100644 --- a/runtime/druntime/src/test_runner.d +++ b/runtime/druntime/src/test_runner.d @@ -1,5 +1,5 @@ import core.runtime, core.time : MonoTime; -import core.stdc.stdio; +import core.stdc.stdio : printf; version (ARM) version = ARM_Any; version (AArch64) version = ARM_Any; diff --git a/runtime/druntime/test/aa/Makefile b/runtime/druntime/test/aa/Makefile index ff86fd9fcc..4f696e2da2 100644 --- a/runtime/druntime/test/aa/Makefile +++ b/runtime/druntime/test/aa/Makefile @@ -1,17 +1,3 @@ -include ../common.mak - -TESTS:=test_aa - -.PHONY: all clean -all: $(addprefix $(ROOT)/,$(addsuffix .done,$(TESTS))) +TESTS := test_aa -$(ROOT)/%.done: $(ROOT)/%$(DOTEXE) - @echo Testing $* - $(QUIET)$(TIMELIMIT)$(ROOT)/$* $(RUN_ARGS) - @touch $@ - -$(ROOT)/%$(DOTEXE): $(SRC)/%.d - $(QUIET)$(DMD) $(DFLAGS) -of$@ $< - -clean: - rm -rf $(ROOT) +include ../common.mak diff --git a/runtime/druntime/test/aa/src/test_aa.d b/runtime/druntime/test/aa/src/test_aa.d index 11ad2f90ff..5c3ba05d83 100644 --- a/runtime/druntime/test/aa/src/test_aa.d +++ b/runtime/druntime/test/aa/src/test_aa.d @@ -40,6 +40,7 @@ void main() testZeroSizedValue(); testTombstonePurging(); testClear(); + testTypeInfoCollect(); } void testKeysValues1() @@ -585,8 +586,6 @@ void issue13078() nothrow pure void issue14104() { - import core.stdc.stdio; - alias K = const(ubyte)*; size_t[K] aa; immutable key = cast(K)(cast(size_t) uint.max + 1); @@ -907,3 +906,44 @@ void testClear() assert(aa.length == 1); assert(aa[5] == 6); } + +// https://github.com/dlang/dmd/issues/17503 +void testTypeInfoCollect() +{ + import core.memory; + + static struct S + { + int x; + ~this() {} + } + + static struct AAHolder + { + S[int] aa; + } + + static S* getBadS() + { + auto aaholder = new AAHolder; + aaholder.aa[0] = S(); + auto s = 0 in aaholder.aa; // keep a pointer to the entry + GC.free(aaholder); // but not a pointer to the AA. + return s; + } + + static void stackStomp() + { + import core.stdc.string : memset; + ubyte[4 * 4096] x; + memset(x.ptr, 0, x.sizeof); + } + + auto s = getBadS(); + stackStomp(); // destroy any stale references to the AA or s except in the current frame; + GC.collect(); // BUG: this used to invalidate the fake type info, should no longer do this. + foreach(i; 0 .. 1000) // try to reallocate the freed type info + auto p = new void*[1]; + s = null; // clear any reference to the entry + GC.collect(); // used to segfault. +} diff --git a/runtime/druntime/test/allocations/Makefile b/runtime/druntime/test/allocations/Makefile index 53507da0e5..23dbda555f 100644 --- a/runtime/druntime/test/allocations/Makefile +++ b/runtime/druntime/test/allocations/Makefile @@ -1,28 +1,15 @@ -include ../common.mak - -TESTS:=overflow_from_zero overflow_from_existing alloc_from_assert +TESTS := overflow_from_zero overflow_from_existing alloc_from_assert -DIFF:=diff -SED:=sed - -.PHONY: all clean -all: $(addprefix $(ROOT)/,$(addsuffix .done,$(TESTS))) +include ../common.mak -$(ROOT)/alloc_from_assert.done: $(ROOT)/alloc_from_assert$(DOTEXE) - @echo Testing $* - $(QUIET)$(TIMELIMIT)$(ROOT)/alloc_from_assert $(RUN_ARGS) +$(OBJDIR)/alloc_from_assert.done: $(OBJDIR)/alloc_from_assert$(DOTEXE) + @echo Testing $(&1 1>/dev/null | head -n 2 | grep -qF $(STDERR_EXP) + $(TIMELIMIT)$< 2>&1 1>/dev/null | head -n 2 | grep -qF $(stderr_exp) @touch $@ - -$(ROOT)/unittest_assert$(DOTEXE): DFLAGS+=-unittest -version=CoreUnittest -$(ROOT)/%$(DOTEXE): $(SRC)/%.d - $(QUIET)$(DMD) $(DFLAGS) -of$@ $< - -clean: - rm -rf $(ROOT) diff --git a/runtime/druntime/test/betterc/Makefile b/runtime/druntime/test/betterc/Makefile index 2b5b32b2fd..c200db236e 100644 --- a/runtime/druntime/test/betterc/Makefile +++ b/runtime/druntime/test/betterc/Makefile @@ -1,25 +1,13 @@ -include ../common.mak - TESTS:=test18828 test19416 test19421 test19561 test20088 test20613 test19924 test22336 test19933 +include ../common.mak -.PHONY: all clean -all: $(addprefix $(ROOT)/,$(addsuffix .done,$(TESTS))) - -$(ROOT)/%.done: $(ROOT)/%$(DOTEXE) - @echo Running $* - $(QUIET)$(TIMELIMIT)$(ROOT)/$* $(RUN_ARGS) - @touch $@ +$(OBJDIR)/%.done: extra_dflags += -betterC +$(OBJDIR)/%.done: extra_ldlibs.d = # for the Windows MinGW CI job: -ifneq (,$(findstring -mscrtlib=msvcrt120,$(DFLAGS))) +ifneq (,$(findstring -mscrtlib=msvcrt120,$(DFLAGS) $(LDFLAGS.d))) # DFLAGS=-mscrtlib=msvcrt120 takes precedence over any command line flags, so # specify vcruntime140.lib explicitly for using mingw with Universal CRT. -$(ROOT)/test19933$(DOTEXE): $(SRC)/test19933.d - $(QUIET)$(DMD) $(MODEL_FLAG) -I../../src -betterC -of$@ $< -Lvcruntime140.lib -Llegacy_stdio_definitions.lib -L/NODEFAULTLIB:msvcrt120.lib +$(OBJDIR)/test19933$(DOTEXE): extra_ldflags.d = -L/NODEFAULTLIB:msvcrt120.lib +$(OBJDIR)/test19933$(DOTEXE): extra_ldlibs.d = -Lvcruntime140.lib -Llegacy_stdio_definitions.lib endif - -$(ROOT)/%$(DOTEXE): $(SRC)/%.d - $(QUIET)$(DMD) $(MODEL_FLAG) -I../../src -betterC -of$@ $< - -clean: - rm -rf $(ROOT) diff --git a/runtime/druntime/test/betterc/src/test19933.d b/runtime/druntime/test/betterc/src/test19933.d index a0faadd211..d5c9eacf0d 100644 --- a/runtime/druntime/test/betterc/src/test19933.d +++ b/runtime/druntime/test/betterc/src/test19933.d @@ -2,7 +2,7 @@ // https://issues.dlang.org/show_bug.cgi?id=19933 // https://issues.dlang.org/show_bug.cgi?id=18816 -import core.stdc.stdio; +import core.stdc.stdio : fprintf, stderr; extern(C) int main() { diff --git a/runtime/druntime/test/common.mak b/runtime/druntime/test/common.mak index 82831dd253..fa34733604 100644 --- a/runtime/druntime/test/common.mak +++ b/runtime/druntime/test/common.mak @@ -1,5 +1,5 @@ # set explicitly in the make cmdline in druntime/Makefile (`test/%/.run` rule): -ifneq (,$(findstring ldmd2,$(DMD))) +ifdef IN_LDC # LDC: we have no top makefile, include osmodel.mak for OS and set up bash shell on Windows MODEL:=default include ../../../../dmd/osmodel.mak @@ -17,46 +17,70 @@ TIMELIMIT:= PIC:= SHARED:= -ifeq (,$(findstring ldmd2,$(DMD))) +# Variables that can be specified by users, with the same meaning as used by GNU make +# $(CC) $(CXX) $(DMD) # the compiler +# $(CFLAGS) $(CXXFLAGS) $(DFLAGS) # flags for the compiler +# $(LDFLAGS) ditto $(LDFLAGS.d) # flags for the compiler when it invokes the linker +# $(LDLIBS) ditto $(LDLIBS.d) # library names given to the compiler when invoking the linker +# $(TARGET_ARCH) ditto $(TARGET_ARCH.d) # undocumented but used in the implicit rules + +# Information for writting addition tests: +# +# Each variable above also has a extra_* flavor that can be used by +# the makefiles. CFLAGS et al are meant for users. Do _not_ put flags +# in there unless the flags don't matter. Use extra_cflags for that +# purpose. When writting recipes either use the $(COMPILE.d) or +# $(LINK.cpp) convenience wrappers or make sure that you respect _all_ +# relevant variables. The pattern rules below should handle most cases +# of compilation so you should only need to specify the tests' +# recipes. + +########## Misc setup ########## + +ifndef IN_LDC # Windows: set up bash shell ifeq (windows,$(OS)) include ../../../compiler/src/osmodel.mak endif endif -LDL:=$(subst -L,,$(LINKDL)) # -ldl SRC:=src +VPATH = $(SRC) GENERATED:=./generated ROOT:=$(GENERATED)/$(OS)/$(BUILD)/$(MODEL) -DRUNTIME_IMPLIB:=$(subst .dll,.lib,$(DRUNTIMESO)) +OBJDIR = $(ROOT) -MODEL_FLAG:=$(if $(findstring $(MODEL),default),,-m$(MODEL)) -CFLAGS_BASE:=$(if $(findstring $(OS),windows),/Wall,$(MODEL_FLAG) $(PIC) -Wall) -ifeq (,$(findstring ldmd2,$(DMD))) - ifeq (osx64,$(OS)$(MODEL)) - CFLAGS_BASE+=--target=x86_64-darwin-apple # ARM cpu is not supported by dmd - endif -endif -# LDC: -# * include optional DFLAGS_BASE -# * use `-defaultlib=druntime-ldc [-link-defaultlib-shared]` instead of `-defaultlib= -L$(DRUNTIME[_IMPLIB])` -DFLAGS:=$(MODEL_FLAG) $(PIC) $(if $(findstring ldmd2,$(DMD)),$(DFLAGS_BASE),) -w -I../../src -I../../import -I$(SRC) -defaultlib=$(if $(findstring ldmd2,$(DMD)),druntime-ldc,) -preview=dip1000 $(if $(findstring $(OS),windows),,-L-lpthread -L-lm $(LINKDL)) -# LINK_SHARED may be set by importing makefile -ifeq (,$(findstring ldmd2,$(DMD))) - DFLAGS+=$(if $(LINK_SHARED),-L$(DRUNTIME_IMPLIB) $(if $(findstring $(OS),windows),-dllimport=all),-L$(DRUNTIME)) -else - # LDC: -link-defaultlib-shared takes care of rpath, linking ldc_rt.dso.o etc. - DFLAGS+=$(if $(LINK_SHARED),-link-defaultlib-shared,) -endif -ifeq ($(BUILD),debug) - DFLAGS+=-g -debug $(if $(findstring ldmd2,$(DMD)),-link-defaultlib-debug,) - CFLAGS:=$(CFLAGS_BASE) $(if $(findstring $(OS),windows),/Zi,-g) -else - DFLAGS+=-O -release - CFLAGS:=$(CFLAGS_BASE) $(if $(findstring $(OS),windows),/O2,-O3) +druntime_for_linking := $(if $(LINK_SHARED),$(DRUNTIMESO:.dll=.lib),$(DRUNTIME)) +# Used for -rpath by some tests +druntimeso_dir := $(abspath $(dir $(DRUNTIMESO))) +DRUNTIME_DEP := $(if $(LINK_SHARED),$(DRUNTIMESO),$(DRUNTIME)) +# GNU make says that compiler variables like $(DMD) can contain arguments, technically. +DMD_DEP := $(firstword $(DMD)) +d_platform_libs := $(if $(filter-out windows,$(OS)),-L-lpthread -L-lm $(LINKDL)) + +ifneq ($(strip $(QUIET)),) +.SILENT: endif -CXXFLAGS_BASE:=$(CFLAGS_BASE) -CXXFLAGS:=$(CFLAGS) + +.SUFFIXES: + +########## Default build commands ########## + +# Similar to the implicit rules defined by GNU make +COMPILE.c = $(CC) $(extra_cflags) $(CFLAGS) $(extra_cppflags) $(CPPFLAGS) $(TARGET_ARCH) -c +COMPILE.cpp = $(CXX) $(extra_cxxflags) $(CXXFLAGS) $(extra_cppflags) $(CPPFLAGS) $(TARGET_ARCH) -c +COMPILE.d = $(DMD) $(extra_dflags) $(DFLAGS) $(TARGET_ARCH.d) -c + +LINK.c = $(CC) $(extra_cflags) $(CFLAGS) $(extra_cppflags) $(CPPFLAGS) $(extra_ldflags) $(LDFLAGS) $(TARGET_ARCH) +LINK.cpp = $(CXX) $(extra_cxxflags) $(CXXFLAGS) $(extra_cppflags) $(CPPFLAGS) $(extra_ldflags) $(LDFLAGS) $(TARGET_ARCH) +LINK.d = $(DMD) $(extra_dflags) $(DFLAGS) $(extra_ldflags.d) $(LDFLAGS.d) $(TARGET_ARCH.d) +LINK.o = $(CC) $(extra_ldflags) $(LDFLAGS) $(TARGET_ARCH) + +OUTPUT_OPTION = $(OUTPUT_FLAG)$@ +OUTPUT_OPTION.d = $(OUTPUT_FLAG.d)$@ + +OUTPUT_FLAG = -o #<- important space: OUTPUT_FLAG = "-o " +OUTPUT_FLAG.d = -of= ifeq (windows,$(OS)) DOTEXE:=.exe @@ -69,3 +93,113 @@ else DOTLIB:=.a DOTOBJ:=.o endif + +# Default values for the D counterparts of the standard variables +LDLIBS.d = $(LDLIBS:%=-L%) +TARGET_ARCH.d = $(TARGET_ARCH) + +LDFLAGS.d := $(LDFLAGS) +# LDFLAGS.d == -Wl,-O1 -Wl,--as-needed -Wl,-z,pack-relative-relocs +comma := , +empty := +space := $(empty) $(empty) +LDFLAGS.d := $(subst $(comma),$(space),$(LDFLAGS.d)) +# LDFLAGS.d == -Wl -O1 -Wl --as-needed -z pack-relative-relocs +LDFLAGS.d := $(filter-out -Wl,$(LDFLAGS.d)) +# LDFLAGS.d == -O1 --as-needed -z pack-relative-relocs +LDFLAGS.d := $(LDFLAGS.d:%=-L%) +# LDFLAGS.d == -L-O1 -L--as-needed -L-z -Lpack-relative-relocs + +########## Default pattern rules ########## + +$(OBJDIR)/%$(DOTOBJ): %.c | $(OBJDIR) + $(COMPILE.c) $(OUTPUT_OPTION) $< $(extra_sources) +$(OBJDIR)/%$(DOTOBJ): %.cpp | $(OBJDIR) + $(COMPILE.cpp) $(OUTPUT_OPTION) $< $(extra_sources) +$(OBJDIR)/%$(DOTOBJ): %.d $(DMD_DEP) + $(COMPILE.d) $(OUTPUT_OPTION.d) $< $(extra_sources) + +$(OBJDIR)/%$(DOTEXE): %.c | $(OBJDIR) + $(LINK.c) $< $(extra_sources) $(extra_ldlibs) $(LDLIBS) $(OUTPUT_OPTION) +$(OBJDIR)/%$(DOTEXE): %.cpp | $(OBJDIR) + $(LINK.cpp) $< $(extra_sources) $(extra_ldlibs) $(LDLIBS) $(OUTPUT_OPTION) +$(OBJDIR)/%$(DOTEXE): %.d $(DMD_DEP) $(DRUNTIME_DEP) + $(LINK.d) $< $(extra_sources) $(extra_ldlibs.d) $(LDLIBS.d) $(OUTPUT_OPTION.d) +$(OBJDIR)/%$(DOTEXE): %.o | $(OBJDIR) + $(LINK.o) $< $(extra_sources) $(extra_ldlibs) $(LDLIBS) $(OUTPUT_OPTION) + +########## Default build flags ########## + +ifeq ($(BUILD),debug) + CFLAGS = $(if $(filter windows,$(OS)),/Zi,-g) + CXXFLAGS = $(if $(filter windows,$(OS)),/Zi,-g) + DFLAGS = -g -debug +else + CFLAGS = $(if $(filter windows,$(OS)),/O2,-O3) + CXXFLAGS = $(if $(filter windows,$(OS)),/O2,-O3) + DFLAGS = -O -release +endif +CFLAGS += $(if $(filter windows,$(OS)),/Wall,-Wall) +DFLAGS += -w + +ifdef IN_LDC +# LDC: include optional user DFLAGS_BASE +CFLAGS += $(CFLAGS_BASE) +DFLAGS += $(DFLAGS_BASE) +CXXFLAGS_BASE = $(CFLAGS_BASE) +CXXFLAGS += $(CXXFLAGS_BASE) +endif + +extra_cflags += $(PIC) +extra_cxxflags += $(PIC) +extra_dflags += $(PIC) -I../../src -I../../import -I$(SRC) -preview=dip1000 + +# A lot of the tests perform assert checks. Preserve them even with -release +extra_dflags += -check=assert + +ifdef LINK_SHARED +ifeq ($(OS),windows) +extra_dflags += -dllimport=all +endif +endif + +ifndef IN_LDC +extra_ldlibs.d += -L$(druntime_for_linking) + +extra_ldflags.d += -defaultlib= +extra_ldlibs.d += $(d_platform_libs) +else # IN_LDC + extra_ldflags.d += -defaultlib=druntime-ldc + # -link-defaultlib-shared affects compilation on targets such as Windows + extra_dflags += $(if $(LINK_SHARED),-link-defaultlib-shared) + extra_ldflags.d += $(if $(filter debug,$(BUILD)),-link-defaultlib-debug) +endif # IN_LDC + +model_flag := $(if $(filter-out default,$(MODEL)),-m$(MODEL)) +TARGET_ARCH = $(model_flag) +ifndef IN_LDC +TARGET_ARCH += $(if $(filter osx64,$(OS)$(MODEL)),--target=x86_64-darwin-apple) +endif +TARGET_ARCH.d = $(model_flag) + +########## Other common code ########## + +.PHONY: all cleam +all: $(TESTS:%=$(OBJDIR)/%.done) + +$(OBJDIR)/%.done: $(OBJDIR)/%$(DOTEXE) + @echo Testing $* + $(TIMELIMIT)$< $(run_args) + @touch $@ + +$(OBJDIR): + mkdir -p $(OBJDIR) + +# Preserve the executable files after running the tests +.NOTINTERMEDIATE: $(OBJDIR)/%$(DOTEXE) + +clean: + $(RM) -r $(OBJDIR) + +$(DMD_DEP): ; +$(DRUNTIME_DEP): ; diff --git a/runtime/druntime/test/config/Makefile b/runtime/druntime/test/config/Makefile index fd77e770d2..08ea8b8496 100644 --- a/runtime/druntime/test/config/Makefile +++ b/runtime/druntime/test/config/Makefile @@ -1,29 +1,8 @@ -include ../common.mak - TESTS:=test19433 test20459 test22523 +include ../common.mak -.PHONY: all clean -all: $(addprefix $(ROOT)/,$(addsuffix .done,$(TESTS))) - -$(ROOT)/%$(DOTEXE): $(SRC)/%.d - $(QUIET)$(DMD) $(DFLAGS) -of$@ $< - -$(ROOT)/test19433.done: $(ROOT)/test19433$(DOTEXE) - @echo Testing test19433 - $(QUIET)$(ROOT)/test19433 --DRT-dont-eat-me - @touch $@ - -$(ROOT)/test20459.done: $(ROOT)/test20459$(DOTEXE) - @echo Testing test20459 - $(QUIET)$(ROOT)/test20459 foo bar -- --DRT-gcopts=profile:1 - @touch $@ - - -$(ROOT)/test22523.done: $(SRC)/test22523.d - @echo Testing $< - $(QUIET)$(DMD) $(DFLAGS) -unittest -of$(ROOT)/test22523$(DOTEXE) $< - $(QUIET)$(ROOT)/test22523 -- --DRT-testmode=run-main - @touch $@ +$(ROOT)/test19433.done: run_args = --DRT-dont-eat-me +$(ROOT)/test20459.done: run_args = foo bar -- --DRT-gcopts=profile:1 -clean: - rm -rf $(ROOT) +$(ROOT)/test22523.done: extra_dflags += -unittest +$(ROOT)/test22523.done: run_args = -- --DRT-testmode=run-main diff --git a/runtime/druntime/test/config/src/test22523.d b/runtime/druntime/test/config/src/test22523.d index f3086963f2..680f573ce7 100644 --- a/runtime/druntime/test/config/src/test22523.d +++ b/runtime/druntime/test/config/src/test22523.d @@ -1,6 +1,6 @@ // https://issues.dlang.org/show_bug.cgi?id=22523 -import core.stdc.stdio; +import core.stdc.stdio : puts; int main() { diff --git a/runtime/druntime/test/coverage/Makefile b/runtime/druntime/test/coverage/Makefile index 9af60369e5..5df12bba1a 100644 --- a/runtime/druntime/test/coverage/Makefile +++ b/runtime/druntime/test/coverage/Makefile @@ -1,77 +1,80 @@ +normal := basic +merge := merge merge_true +TESTS := $(normal) $(merge) no_code merge_override include ../common.mak -DFLAGS+=-cov +normal_tests := $(normal:%=$(ROOT)/%.done) +merge_tests := $(merge:%=$(ROOT)/%.done) -NORMAL_TESTS:=$(addprefix $(ROOT)/,$(addsuffix .done,basic)) -MERGE_TESTS:=$(addprefix $(ROOT)/,$(addsuffix .done,merge merge_true)) +extra_dflags += -cov DIFF:=diff --strip-trailing-cr SED:=sed -ifeq ($(OS),$(filter $(OS),freebsd osx)) +ifneq (,$(filter $(OS),freebsd osx)) SED_INPLACE:=-i '' else SED_INPLACE:=-i'' endif -.PHONY: all clean -all: $(NORMAL_TESTS) $(MERGE_TESTS) $(ROOT)/no_code.done $(ROOT)/merge_override.done - -$(NORMAL_TESTS): $(ROOT)/%.done: $(ROOT)/%$(DOTEXE) +$(normal_tests): $(ROOT)/%.done: $(ROOT)/%$(DOTEXE) @echo Testing $* - @rm -f $(ROOT)/src-$*.lst - $(QUIET)$(ROOT)/$* $(ROOT) $(RUN_ARGS) + @$(RM) $(ROOT)/src-$*.lst + $< $(ROOT) ifeq (windows,$(OS)) - $(QUIET)$(SED) $(SED_INPLACE) 's:^src\\:src/:g' $(ROOT)/src-$*.lst + $(SED) $(SED_INPLACE) 's:^src\\:src/:g' $(ROOT)/src-$*.lst endif - $(QUIET)$(DIFF) src-$*.lst.exp $(ROOT)/src-$*.lst + $(DIFF) src-$*.lst.exp $(ROOT)/src-$*.lst @touch $@ -$(MERGE_TESTS): $(ROOT)/%.done: $(ROOT)/%$(DOTEXE) +$(merge_tests): $(ROOT)/%.done: $(ROOT)/%$(DOTEXE) @echo Testing $* - @rm -f $(ROOT)/src-$*.lst - $(QUIET)$(ROOT)/$* $(ROOT) $(RUN_ARGS) - $(QUIET)$(ROOT)/$* $(ROOT) $(RUN_ARGS) + @$(RM) $(ROOT)/src-$*.lst + $< $(ROOT) + $< $(ROOT) ifeq (windows,$(OS)) - $(QUIET)$(SED) $(SED_INPLACE) 's:^src\\:src/:g' $(ROOT)/src-$*.lst + $(SED) $(SED_INPLACE) 's:^src\\:src/:g' $(ROOT)/src-$*.lst endif - $(QUIET)$(DIFF) src-$*.lst.exp $(ROOT)/src-$*.lst + $(DIFF) src-$*.lst.exp $(ROOT)/src-$*.lst @touch $@ -$(ROOT)/merge_override.done: $(ROOT)/%.done: $(ROOT)/%$(DOTEXE) +$(ROOT)/merge_override.done: $(ROOT)/%.done: $(ROOT)/%$(DOTEXE) $(ROOT)/1/$(SRC)/%.d $(ROOT)/2/$(SRC)/%.d src-%.lst_1.exp src-%.lst_2.exp @echo Testing $* - @rm -f $(ROOT)/src-$*.lst - $(QUIET)$(SED) $(SED_INPLACE) 's/CHANGEVAR/CHANGE_VAR/g' src/$*.d - $(QUIET)$(ROOT)/$* $(ROOT) $(RUN_ARGS) - $(QUIET)$(SED) $(SED_INPLACE) 's/CHANGE_VAR/CHANGEVAR/g' src/$*.d + @$(RM) $(ROOT)/src-$*.lst + + $< $(ROOT) $(ROOT)/1 ifeq (windows,$(OS)) - $(QUIET)$(SED) $(SED_INPLACE) 's:^src\\:src/:g' $(ROOT)/src-$*.lst + $(SED) $(SED_INPLACE) 's:^src\\:src/:g' $(ROOT)/src-$*.lst endif - $(QUIET)$(DIFF) src-$*.lst_1.exp $(ROOT)/src-$*.lst - $(QUIET)$(ROOT)/$* $(ROOT) $(RUN_ARGS) - $(QUIET)$(SED) $(SED_INPLACE) 's/CHANGEVAR/CHANGE_VAR/g' src/$*.d + $(DIFF) src-$*.lst_1.exp $(ROOT)/src-$*.lst + + $< $(ROOT) $(ROOT)/2 ifeq (windows,$(OS)) - $(QUIET)$(SED) $(SED_INPLACE) 's:^src\\:src/:g' $(ROOT)/src-$*.lst + $(SED) $(SED_INPLACE) 's:^src\\:src/:g' $(ROOT)/src-$*.lst endif - $(QUIET)$(DIFF) src-$*.lst_2.exp $(ROOT)/src-$*.lst + $(DIFF) src-$*.lst_2.exp $(ROOT)/src-$*.lst + @touch $@ +$(ROOT)/1/$(SRC)/merge_override.d: $(SRC)/merge_override.d | $(ROOT)/1/$(SRC) + cp $< $@ +$(ROOT)/2/$(SRC)/merge_override.d: $(SRC)/merge_override.d | $(ROOT)/2/$(SRC) + cp $< $@ + $(SED) $(SED_INPLACE) 's/CHANGE_VAR/CHANGEVAR/g' $@ +$(ROOT)/1/$(SRC) $(ROOT)/2/$(SRC): + mkdir -p $@ + $(ROOT)/no_code.done: $(ROOT)/%.done: $(ROOT)/%$(DOTEXE) @echo Testing $* - @rm -f $(ROOT)/src-$*.lst - $(QUIET)$(ROOT)/$* $(ROOT) $(RUN_ARGS) - $(QUIET)$(ROOT)/$* $(ROOT) $(RUN_ARGS) + @$(RM) $(ROOT)/src-$*.lst + $< $(ROOT) + $< $(ROOT) ifeq (windows,$(OS)) - $(QUIET)$(SED) $(SED_INPLACE) 's:^src\\:src/:g' $(ROOT)/src-$*.lst - $(QUIET)$(SED) $(SED_INPLACE) 's:^src\\:src/:g' $(ROOT)/src-$*_imp.lst + $(SED) $(SED_INPLACE) 's:^src\\:src/:g' $(ROOT)/src-$*.lst + $(SED) $(SED_INPLACE) 's:^src\\:src/:g' $(ROOT)/src-$*_imp.lst endif - $(QUIET)$(DIFF) src-$*.lst.exp $(ROOT)/src-$*.lst - $(QUIET)$(DIFF) src-$*_imp.lst.exp $(ROOT)/src-$*_imp.lst + $(DIFF) src-$*.lst.exp $(ROOT)/src-$*.lst + $(DIFF) src-$*_imp.lst.exp $(ROOT)/src-$*_imp.lst @touch $@ - $(ROOT)/no_code$(DOTEXE): $(SRC)/no_code_imp.d -$(ROOT)/%$(DOTEXE): $(SRC)/%.d - $(QUIET)$(DMD) $(DFLAGS) -of$@ $^ - -clean: - rm -rf $(ROOT) *.lst +$(ROOT)/no_code$(DOTEXE): extra_sources += $(SRC)/no_code_imp.d diff --git a/runtime/druntime/test/coverage/src-merge_override.lst_1.exp b/runtime/druntime/test/coverage/src-merge_override.lst_1.exp index c9553a091f..6fa34f7309 100644 --- a/runtime/druntime/test/coverage/src-merge_override.lst_1.exp +++ b/runtime/druntime/test/coverage/src-merge_override.lst_1.exp @@ -3,6 +3,7 @@ |void main(string[] args) |{ 1| dmd_coverDestPath(args[1]); + 1| dmd_coverSourcePath(args[2]); | enum CHANGE_VAR = 0; 1| dmd_coverSetMerge(true); |} diff --git a/runtime/druntime/test/coverage/src-merge_override.lst_2.exp b/runtime/druntime/test/coverage/src-merge_override.lst_2.exp index ca38bd62df..0129affc1d 100644 --- a/runtime/druntime/test/coverage/src-merge_override.lst_2.exp +++ b/runtime/druntime/test/coverage/src-merge_override.lst_2.exp @@ -3,6 +3,7 @@ |void main(string[] args) |{ 1| dmd_coverDestPath(args[1]); + 1| dmd_coverSourcePath(args[2]); | enum CHANGEVAR = 0; 1| dmd_coverSetMerge(true); |} diff --git a/runtime/druntime/test/coverage/src/merge_override.d b/runtime/druntime/test/coverage/src/merge_override.d index 3fc6dce423..6a584c3e51 100644 --- a/runtime/druntime/test/coverage/src/merge_override.d +++ b/runtime/druntime/test/coverage/src/merge_override.d @@ -3,6 +3,7 @@ import core.runtime; void main(string[] args) { dmd_coverDestPath(args[1]); + dmd_coverSourcePath(args[2]); enum CHANGE_VAR = 0; dmd_coverSetMerge(true); } diff --git a/runtime/druntime/test/cpuid/Makefile b/runtime/druntime/test/cpuid/Makefile index eec92e31d0..7a25d4024f 100644 --- a/runtime/druntime/test/cpuid/Makefile +++ b/runtime/druntime/test/cpuid/Makefile @@ -1,17 +1,2 @@ -include ../common.mak - TESTS:=cpuid - -.PHONY: all clean -all: $(addprefix $(ROOT)/,$(addsuffix .done,$(TESTS))) - -$(ROOT)/%.done: $(ROOT)/%$(DOTEXE) - @echo Testing $* - $(QUIET)$(TIMELIMIT)$< $(RUN_ARGS) - @touch $@ - -$(ROOT)/%$(DOTEXE): $(SRC)/%.d - $(QUIET)$(DMD) $(DFLAGS) -of$@ $< - -clean: - rm -rf $(ROOT) +include ../common.mak diff --git a/runtime/druntime/test/cpuid/src/cpuid.d b/runtime/druntime/test/cpuid/src/cpuid.d index ef1e124e9e..2f02295227 100644 --- a/runtime/druntime/test/cpuid/src/cpuid.d +++ b/runtime/druntime/test/cpuid/src/cpuid.d @@ -1,6 +1,6 @@ module puid; import core.cpuid; -import core.stdc.stdio; +import core.stdc.stdio : printf; mixin template printFlag(string name) { diff --git a/runtime/druntime/test/cycles/Makefile b/runtime/druntime/test/cycles/Makefile index 948998838a..6260772b96 100644 --- a/runtime/druntime/test/cycles/Makefile +++ b/runtime/druntime/test/cycles/Makefile @@ -1,28 +1,26 @@ -include ../common.mak - TESTS:=cycle_ignore cycle_abort cycle_print cycle_deprecate +include ../common.mak -DIFF:=diff -SED:=sed - -.PHONY: all clean -all: $(addprefix $(ROOT)/,$(addsuffix .done,$(TESTS))) - -$(ROOT)/cycle_ignore.done: RETCODE=0 -$(ROOT)/cycle_ignore.done: LINES=0 -$(ROOT)/cycle_abort.done: RETCODE=1 -$(ROOT)/cycle_abort.done: LINES=$(if $(findstring $(OS),windows),8,7) -$(ROOT)/cycle_print.done: RETCODE=0 -$(ROOT)/cycle_print.done: LINES=6 -$(ROOT)/cycle_deprecate.done: RETCODE=1 -$(ROOT)/cycle_deprecate.done: LINES=$(if $(findstring $(OS),windows),9,8) +$(ROOT)/cycle_ignore.done: retcode=0 +$(ROOT)/cycle_ignore.done: lines=0 +$(ROOT)/cycle_abort.done: retcode=1 +# LINK_SHARED causes the abort message to contain trace lines. +abort_lines := $(if $(filter windows,$(OS)),8,7) +abort_lines := $(if $(LINK_SHARED),,$(abort_lines)) +$(ROOT)/cycle_abort.done: lines=$(abort_lines) +$(ROOT)/cycle_print.done: retcode=0 +$(ROOT)/cycle_print.done: lines=6 +$(ROOT)/cycle_deprecate.done: retcode=1 +# ditto for deprecate +deprecate_lines := $(if $(filter windows,$(OS)),9,8) +deprecate_lines := $(if $(LINK_SHARED),,$(deprecate_lines)) +$(ROOT)/cycle_deprecate.done: lines=$(deprecate_lines) $(ROOT)/%.done: $(ROOT)/test_cycles$(DOTEXE) @echo Testing $* - $(QUIET)$(TIMELIMIT)$(ROOT)/test_cycles --DRT-oncycle=$(patsubst cycle_%.done,%, $(notdir $@)) > $@ 2>&1; test $$? -eq $(RETCODE) - test `cat $@ | wc -l` -eq $(LINES) + $(TIMELIMIT)$< --DRT-oncycle=$(@F:cycle_%.done=%) > $@ 2>&1; test $$? -eq $(retcode) + [ -z "$(lines)" ] || test `cat $@ | wc -l` -eq $(lines) -$(ROOT)/test_cycles$(DOTEXE): $(SRC)/*.d - $(QUIET)$(DMD) $(DFLAGS) -of$@ $^ +$(ROOT)/test_cycles$(DOTEXE): $(SRC)/mod*.d +$(ROOT)/test_cycles$(DOTEXE): private extra_sources = $(wildcard $(SRC)/mod*.d) -clean: - rm -rf $(ROOT) +.DELETE_ON_ERROR: diff --git a/runtime/druntime/test/cycles/src/test_cycles.d b/runtime/druntime/test/cycles/src/test_cycles.d new file mode 100644 index 0000000000..e69de29bb2 diff --git a/runtime/druntime/test/exceptions/Makefile b/runtime/druntime/test/exceptions/Makefile index 2eb5ca0f65..7565e42506 100644 --- a/runtime/druntime/test/exceptions/Makefile +++ b/runtime/druntime/test/exceptions/Makefile @@ -1,124 +1,152 @@ -include ../common.mak +ifdef IN_LDC +# need OS for the conditions below +include ../../../../dmd/osmodel.mak +endif -DIFF:=diff -SED:=sed -GDB:=gdb +ifeq ($(OS),linux) + # FIXME: detect musl libc robustly; just checking Alpine Linux' apk tool for now + ifeq (1,$(shell which apk &>/dev/null && echo 1)) + IS_MUSL:=1 + endif +endif -TESTS=stderr_msg unittest_assert invalid_memory_operation unknown_gc static_dtor \ +TESTS=stderr_msg unittest_assert invalid_memory_operation static_dtor \ future_message refcounted rt_trap_exceptions_drt catch_in_finally \ message_with_null -ifeq ($(OS)-$(BUILD),linux-debug) +# FIXME: segfaults with musl libc +ifneq ($(IS_MUSL),1) +TESTS += unknown_gc +endif + +# fails on 32 bit linux +ifneq ($(OS),linux) +TESTS += assert_fail +endif + +DIFF:=diff +SED:=sed +GDB:=gdb + +ifeq ($(OS),linux) TESTS+=line_trace line_trace_21656 long_backtrace_trunc rt_trap_exceptions cpp_demangle - LINE_TRACE_DFLAGS:=-L--export-dynamic + ifdef IN_LDC + # FIXME: unclear why this fails on AArch64 - the only stderr output is 'Aborted (core dumped)' + ifeq ($(ARCH),aarch64) + TESTS := $(filter-out rt_trap_exceptions,$(TESTS)) + endif + endif + # registerMemoryAssertHandler requires glibc + ifneq ($(IS_MUSL),1) + TESTS+=memoryerror_null_read memoryerror_null_write memoryerror_null_call memoryerror_stackoverflow + endif + line_trace_dflags:=-L--export-dynamic endif + ifeq ($(OS),linux) - # Only add this test if gdb is available. - ifneq (, $(shell which $(GDB))) - TESTS+=rt_trap_exceptions_drt_gdb - endif +# Only add this test if gdb is available. + ifneq (,$(shell which $(GDB) > /dev/null 2>&1 && echo 1)) + TESTS+=rt_trap_exceptions_drt_gdb + endif endif -ifeq ($(OS)-$(BUILD),freebsd-debug) + +ifeq ($(OS),freebsd) TESTS+=line_trace line_trace_21656 long_backtrace_trunc cpp_demangle - LINE_TRACE_DFLAGS:=-L--export-dynamic + line_trace_dflags:=-L--export-dynamic endif -ifeq ($(OS)-$(BUILD),dragonflybsd-debug) +ifeq ($(OS),dragonflybsd) TESTS+=line_trace line_trace_21656 long_backtrace_trunc cpp_demangle - LINE_TRACE_DFLAGS:=-L--export-dynamic + line_trace_dflags:=-L--export-dynamic endif -ifeq ($(OS)-$(BUILD),osx-debug) +ifeq ($(OS),osx) TESTS+=line_trace line_trace_21656 cpp_demangle - LINE_TRACE_DFLAGS:= + line_trace_dflags:= endif -ifeq ($(OS)-$(BUILD),windows-debug) +ifeq ($(OS),windows) TESTS+=winstack endif - -ifeq ($(BUILD),debug) - TESTS+=assert_fail -endif - -.PHONY: all clean -all: $(addprefix $(ROOT)/,$(addsuffix .done,$(TESTS))) +include ../common.mak $(ROOT)/line_trace.done: $(ROOT)/line_trace$(DOTEXE) @echo Testing line_trace - $(QUIET)$(TIMELIMIT)$(ROOT)/line_trace $(RUN_ARGS) > $(ROOT)/line_trace.output - # Use sed to canonicalize line_trace.output and compare against expected output in line_trace.exp - $(QUIET)$(SED) "s|^.*/src/|src/|g; s/\[0x[0-9a-f]*\]/\[ADDR\]/g; s/scope //g; s/Nl//g" $(ROOT)/line_trace.output | $(DIFF) line_trace.exp - - @rm -f $(ROOT)/line_trace.output - @touch $@ + $(TIMELIMIT)$(ROOT)/line_trace > $@ + # Use sed to canonicalize line_trace.done and compare against expected output in line_trace.exp + $(SED) "s|^.*/src/|src/|g; s/\[0x[0-9a-f]*\]/\[ADDR\]/g; s/scope //g; s/Nl//g" $@ | $(DIFF) line_trace.exp - # https://issues.dlang.org/show_bug.cgi?id=21656 $(ROOT)/line_trace_21656.done: $(ROOT)/line_trace$(DOTEXE) @echo Testing line_trace_21656 @mkdir -p $(ROOT)/line_trace_21656 @touch $(ROOT)/line_trace_21656/line_trace - $(QUIET)cd $(ROOT)/line_trace_21656 && PATH="..:$$PATH" $(TIMELIMIT)line_trace $(RUN_ARGS) > line_trace.output - $(QUIET)$(SED) "s|^.*/src/|src/|g; s/\[0x[0-9a-f]*\]/\[ADDR\]/g; s/scope //g; s/Nl//g" $(ROOT)/line_trace_21656/line_trace.output | $(DIFF) line_trace.exp - + cd $(ROOT)/line_trace_21656 && PATH="..:$$PATH" $(TIMELIMIT)line_trace > line_trace.output + $(SED) "s|^.*/src/|src/|g; s/\[0x[0-9a-f]*\]/\[ADDR\]/g; s/scope //g; s/Nl//g" $(ROOT)/line_trace_21656/line_trace.output | $(DIFF) line_trace.exp - @rm -rf $(ROOT)/line_trace_21656 @touch $@ $(ROOT)/long_backtrace_trunc.done: $(ROOT)/long_backtrace_trunc$(DOTEXE) @echo Testing long_backtrace_trunc - $(QUIET)$(TIMELIMIT)$(ROOT)/long_backtrace_trunc $(RUN_ARGS) > $(ROOT)/long_backtrace_trunc.output + $(TIMELIMIT)$(ROOT)/long_backtrace_trunc > $(ROOT)/long_backtrace_trunc.output # Use sed to canonicalize long_backtrace_trunc.output and compare against expected output in long_backtrace_trunc.exp - $(QUIET)$(SED) "s|^.*/src/|src/|g; s/\[0x[0-9a-f]*\]/\[ADDR\]/g; s/scope //g; s/Nl//g" $(ROOT)/long_backtrace_trunc.output | $(DIFF) long_backtrace_trunc.exp - + $(SED) "s|^.*/src/|src/|g; s/\[0x[0-9a-f]*\]/\[ADDR\]/g; s/scope //g; s/Nl//g" $(ROOT)/long_backtrace_trunc.output | $(DIFF) long_backtrace_trunc.exp - @rm -f $(ROOT)/long_backtrace_trunc.output @touch $@ $(ROOT)/chain.done: $(ROOT)/chain$(DOTEXE) @echo Testing chain - $(QUIET)$(TIMELIMIT)$(ROOT)/chain $(RUN_ARGS) > $(ROOT)/chain.output + $(TIMELIMIT)$(ROOT)/chain > $(ROOT)/chain.output @rm -f $(ROOT)/chain.output @touch $@ +$(ROOT)/winstack$(DOTEXE): private extra_dflags += -g $(ROOT)/winstack.done: $(ROOT)/winstack$(DOTEXE) @echo Testing winstack - $(QUIET)$(TIMELIMIT)$(ROOT)/winstack $(RUN_ARGS) + $(TIMELIMIT)$< $(RUN_ARGS) @touch $@ -$(ROOT)/stderr_msg.done: STDERR_EXP="stderr_msg msg" -$(ROOT)/unittest_assert.done: STDERR_EXP="unittest_assert msg" -$(ROOT)/invalid_memory_operation.done: STDERR_EXP="InvalidMemoryOperationError" -$(ROOT)/unknown_gc.done: STDERR_EXP="'unknowngc'" -$(ROOT)/static_dtor.done: STDERR_EXP="dtor_called_more_than_once" -$(ROOT)/static_dtor.done: NEGATE=! -$(ROOT)/future_message.done: STDERR_EXP="exception I have a custom message. exception exception " -$(ROOT)/catch_in_finally.done: STDERR_EXP="success." -$(ROOT)/rt_trap_exceptions.done: STDERR_EXP="object.Exception@src/rt_trap_exceptions.d(12): this will abort" -$(ROOT)/rt_trap_exceptions.done: STDERR_EXP2="src/rt_trap_exceptions.d:8 main" -$(ROOT)/assert_fail.done: STDERR_EXP="success." -$(ROOT)/cpp_demangle.done: STDERR_EXP="thrower(int)" - -$(ROOT)/message_with_null.done: STDERR_EXP=" world" +$(ROOT)/stderr_msg.done: stderr_exp="stderr_msg msg" +$(ROOT)/unittest_assert.done: stderr_exp="unittest_assert msg" +$(ROOT)/invalid_memory_operation.done: stderr_exp="InvalidMemoryOperationError" +$(ROOT)/unknown_gc.done: stderr_exp="'unknowngc'" +$(ROOT)/static_dtor.done: stderr_exp="dtor_called_more_than_once" +$(ROOT)/static_dtor.done: private negate=! +$(ROOT)/future_message.done: stderr_exp="exception I have a custom message. exception exception " +$(ROOT)/catch_in_finally.done: stderr_exp="success." +$(ROOT)/rt_trap_exceptions.done: stderr_exp="object.Exception@src/rt_trap_exceptions.d(12): this will abort" +$(ROOT)/rt_trap_exceptions.done: stderr_exp2="src/rt_trap_exceptions.d:8 main" +$(ROOT)/assert_fail.done: stderr_exp="success." +$(ROOT)/cpp_demangle.done: stderr_exp="thrower(int)" +$(ROOT)/message_with_null.done: stderr_exp=" world" +$(ROOT)/memoryerror_null_read.done: stderr_exp="segmentation fault: null pointer read/write operation" +$(ROOT)/memoryerror_null_write.done: stderr_exp="segmentation fault: null pointer read/write operation" +$(ROOT)/memoryerror_null_call.done: stderr_exp="segmentation fault: null pointer read/write operation" +$(ROOT)/memoryerror_null_call.done: stderr_exp2="uncaught exception reached top of stack" +$(ROOT)/memoryerror_stackoverflow.done: stderr_exp="segmentation fault: call stack overflow" $(ROOT)/%.done: $(ROOT)/%$(DOTEXE) @echo Testing $* - $(QUIET)$(TIMELIMIT)$(ROOT)/$* $(RUN_ARGS) 2>$(ROOT)/$*.stderr || true + $(TIMELIMIT)$< $(run_args) 2>$(ROOT)/$*.stderr || true - @if $(NEGATE) grep -qF $(STDERR_EXP) < $(ROOT)/$*.stderr ; then true ; else \ - echo 'Searched for pattern $(STDERR_EXP), NEGATE = $(NEGATE)' ;\ + @if $(negate) grep -qF $(stderr_exp) $(ROOT)/$*.stderr ; then true ; else \ + echo 'Searched for pattern $(stderr_exp), NEGATE = $(negate)' ;\ tail --bytes=5000 $(ROOT)/$*.stderr ;\ exit 1 ;\ fi - @if [ ! -z $(STDERR_EXP2) ] ; then \ - if $(NEGATE) grep -qF $(STDERR_EXP2) < $(ROOT)/$*.stderr ; then true ; else \ - echo 'Searched for '$(STDERR_EXP2)' NEGATE = $(NEGATE)' ;\ + @if [ ! -z $(stderr_exp2) ] ; then \ + if $(negate) grep -qF $(stderr_exp2) $(ROOT)/$*.stderr ; then true ; else \ + echo 'Searched for '$(stderr_exp2)' NEGATE = $(negate)' ;\ tail --bytes=5000 $(ROOT)/$*.stderr ;\ exit 1 ;\ fi \ fi @touch $@ -$(ROOT)/rt_trap_exceptions_drt.done: STDERR_EXP="uncaught exception\nobject.Exception@rt_trap_exceptions_drt.d(4): exception" -$(ROOT)/rt_trap_exceptions_drt.done: RUN_ARGS="--DRT-trapExceptions=0" -$(ROOT)/rt_trap_exceptions_drt.done: NEGATE=! - +$(ROOT)/rt_trap_exceptions_drt.done: stderr_exp="uncaught exception\nobject.Exception@rt_trap_exceptions_drt.d(4): exception" +$(ROOT)/rt_trap_exceptions_drt.done: run_args="--DRT-trapExceptions=0" +$(ROOT)/rt_trap_exceptions_drt.done: negate=! $(ROOT)/rt_trap_exceptions_drt_gdb.done: $(ROOT)/rt_trap_exceptions_drt$(DOTEXE) @echo Testing rt_trap_exceptions_drt_gdb - $(QUIET)$(TIMELIMIT) $(GDB) -n -ex 'set confirm off' -ex run -ex 'bt full' -ex q --args $< --DRT-trapExceptions=0 \ + $(TIMELIMIT) $(GDB) -n -ex 'set confirm off' -ex run -ex 'bt full' -ex q --args $< --DRT-trapExceptions=0 \ > $(ROOT)/rt_trap_exceptions_drt_gdb.output 2>&1 || true cat $(ROOT)/rt_trap_exceptions_drt_gdb.output grep "\(D main\|_Dmain\) (args=...) at .*rt_trap_exceptions_drt.d:9" > /dev/null < $(ROOT)/rt_trap_exceptions_drt_gdb.output @@ -127,23 +155,22 @@ $(ROOT)/rt_trap_exceptions_drt_gdb.done: $(ROOT)/rt_trap_exceptions_drt$(DOTEXE) @touch $@ $(ROOT)/refcounted.done: $(ROOT)/refcounted$(DOTEXE) - $(QUIET) $< + @echo Testing $( /dev/null 2>&1 && echo 1 + +ifeq ($(has_valgrind),1) +valgrind_new_enough != $(VALGRIND) --version | grep -E 'valgrind-3.([0-9]|1[012])\b' || echo 1 endif -SRC_GC=../../src/core/internal/gc/impl/conservative/gc.d -SRC=$(SRC_GC) ../../src/rt/lifetime.d -# ../../src/object.d causes duplicate symbols -UDFLAGS=$(DFLAGS) -unittest -version=CoreUnittest +ifeq ($(valgrind_new_enough),1) +TESTS += issue22843 +endif -.PHONY: all clean -all: $(addprefix $(ROOT)/,$(addsuffix .done,$(TESTS))) +include ../common.mak +vpath -$(ROOT)/%.done: $(ROOT)/%$(DOTEXE) - @echo Testing $* - $(QUIET)$(TIMELIMIT)$(ROOT)/$* $(RUN_ARGS) - @touch $@ +src_gc := ../../src/core/internal/gc/impl/conservative/gc.d +src_lifetime=$(src_gc) ../../src/rt/lifetime.d +# ../../src/object.d causes duplicate symbols +core_ut = -unittest -version=CoreUnittest # https://issues.dlang.org/show_bug.cgi?id=22843 # needs to run under valgrind # versions before 3.13.0 don't handle clone() correctly, skip them $(ROOT)/issue22843.done: $(ROOT)/issue22843$(DOTEXE) - @echo Testing issue22843 - $(QUIET)if ! command -v valgrind >/dev/null; then \ - echo valgrind not installed, skipping; \ - elif valgrind --version | grep -Eq 'valgrind-3.([0-9]|1[012])\b'; then \ - echo valgrind version too old, skipping; \ - else \ - $(TIMELIMIT)valgrind --quiet --tool=none $(ROOT)/issue22843 $(RUN_ARGS); \ - fi + @echo Testing $(/dev/null && echo 1)) + TESTS := + endif +endif + +# FIXME: fails on macOS arm64, e.g., due to unsupported `_Float16` +ifeq ($(OS)-$(ARCH),osx-aarch64) + TESTS := +endif + +include ../common.mak + +extra_dflags += -d + +ifeq ($(OS),windows) + extra_dflags += -P=/std:c11 + + ifdef IN_LDC + # use Microsoft's cl.exe as preprocessor; clang-cl leads to unsupported `_Float16` etc. + extra_dflags += -gcc=cl + endif +endif diff --git a/runtime/druntime/test/importc_compare/src/importc_compare.d b/runtime/druntime/test/importc_compare/src/importc_compare.d new file mode 100644 index 0000000000..543895874e --- /dev/null +++ b/runtime/druntime/test/importc_compare/src/importc_compare.d @@ -0,0 +1,452 @@ +import core.stdc.stdio : printf; + +version (OSX) + version = Apple; +version (iOS) + version = Apple; +version (TVOS) + version = Apple; +version (WatchOS) + version = Apple; + +/* +This test tries to automatically find types with a wrong size in +druntime C bindings. This is done by also getting type sizes from +C headers using ImportC and comparing them. Differences between the +sizes can have different reasons: + +* Bugs in ImportC (e.g. for bitfields) can result in a wrong size +* Type definitions in druntime can be wrong +* Different preprocessor options could be used, like _FILE_OFFSET_BITS +* Size differences can be fine, because some structs contain a member + for the size or a version, see list growingTypes below + +Members of structs and unions with the same name are also compared. +For types with potential problems a comparison of the layout is printed. + +It is possible, that ImportC and druntime contain the same bug. Those +bugs would not be found by this test. +*/ + +// Types, which can be bigger in newer headers, because they have a size +// or version field. +immutable string[] growingTypes = [ + "core.sys.linux.perf_event.perf_event_attr", +]; + +// List of problems, which are known and should only be treated as +// warnings for now. +immutable ErrorFilter[] knownProblems = [ + ErrorFilter("core.stdc.config.c_long_double", "", "Windows", 32, ""), + ErrorFilter("core.stdc.fenv.fenv_t", "", "FreeBSD", 0, ""), + ErrorFilter("core.stdc.locale.lconv", "", "Apple", 0, "https://issues.dlang.org/show_bug.cgi?id=24652"), + ErrorFilter("core.stdc.locale.lconv", "", "FreeBSD", 0, "https://issues.dlang.org/show_bug.cgi?id=24652"), + ErrorFilter("core.stdc.locale.lconv", "", "Windows", 0, "https://issues.dlang.org/show_bug.cgi?id=24652"), + ErrorFilter("core.stdc.math.double_t", "", "linux", 0, ""), + ErrorFilter("core.stdc.math.float_t", "", "linux", 0, ""), + ErrorFilter("core.stdc.signal.sig_atomic_t", "", "FreeBSD", 0, ""), + ErrorFilter("core.stdc.stdio.FILE", "", "FreeBSD", 0, ""), + ErrorFilter("core.stdc.stdio.FILE", "", "linux", 0, ""), + ErrorFilter("core.stdc.stdio._IO_FILE", "", "linux", 0, ""), + ErrorFilter("core.stdc.stdio.__sFILE", "", "FreeBSD", 0, ""), + ErrorFilter("core.stdc.wchar_.mbstate_t", "", "Apple", 0, ""), + ErrorFilter("core.stdc.wchar_.mbstate_t", "", "Windows", 0, ""), + ErrorFilter("core.sys.linux.perf_event.perf_event_sample_format", "", "linux", 0, ""), + ErrorFilter("core.sys.posix.fcntl.flock", "", "linux", 32, ""), + ErrorFilter("core.sys.posix.sched.sched_param", "", "Apple", 0, ""), + ErrorFilter("core.sys.posix.semaphore.sem_t", "", "FreeBSD", 0, ""), + ErrorFilter("core.sys.posix.semaphore.sem_t", "", "linux", 0, ""), + ErrorFilter("core.sys.posix.signal.siginfo_t", "", "Apple", 0, ""), + ErrorFilter("core.sys.posix.sys.ipc.ipc_perm", "mode", "linux", 0, ""), + ErrorFilter("core.sys.posix.sys.shm.shmatt_t", "", "FreeBSD", 0, ""), + ErrorFilter("core.sys.posix.sys.shm.shmid_ds", "", "FreeBSD", 0, ""), + ErrorFilter("core.sys.posix.sys.types.clock_t", "", "FreeBSD", 0, ""), + ErrorFilter("core.sys.posix.sys.types.pthread_barrierattr_t", "", "linux", 0, "See https://issues.dlang.org/show_bug.cgi?id=24593"), + ErrorFilter("core.sys.posix.sys.types.pthread_barrier_t", "", "linux", 0, "See https://issues.dlang.org/show_bug.cgi?id=24593"), + ErrorFilter("core.sys.posix.sys.types.pthread_key_t", "", "FreeBSD", 0, ""), + ErrorFilter("core.sys.posix.sys.types.pthread_once_t", "", "FreeBSD", 0, ""), + ErrorFilter("core.sys.posix.sys.types.pthread_once_t", "", "FreeBSD", 0, ""), + ErrorFilter("core.sys.posix.sys.types.pthread_rwlockattr_t", "", "linux", 0, "See https://issues.dlang.org/show_bug.cgi?id=24593"), + ErrorFilter("core.sys.posix.sys.types.pthread_rwlock_t", "", "linux", 0, "See https://issues.dlang.org/show_bug.cgi?id=24593"), + ErrorFilter("core.sys.posix.time.timer_t", "", "FreeBSD", 0, ""), + ErrorFilter("core.sys.posix.ucontext.ucontext_t", "", "Apple", 0, ""), + ErrorFilter("core.sys.posix.ucontext.ucontext_t", "", "FreeBSD", 0, ""), +]; + +struct ErrorFilter +{ + string name; + string member; + string version_; + size_t pointerSize; + string description; +} + +immutable ErrorFilter[] knownProblemsFiltered = () { + immutable(ErrorFilter)[] r; + static foreach (errorFilter; knownProblems) + {{ + bool include = true; + static if (errorFilter.version_.length) + mixin("version(" ~ errorFilter.version_ ~ ") {} else include = false;"); + if (errorFilter.pointerSize && errorFilter.pointerSize != (void*).sizeof * 8) + include = false; + if (include) + r ~= errorFilter; + }} + return r; +}(); + +bool isKnownProblem(string name, string member, ref string description) +{ + foreach (errorFilter; knownProblemsFiltered) + { + if (errorFilter.name == name && errorFilter.member == member) + { + description = errorFilter.description; + return true; + } + } + return false; +} + +struct MemberInfo +{ + string name; + size_t offset; + size_t size; + size_t bitoffset; + size_t bitsize; +} +struct RecordInfo +{ + string kind; + string name; + string modulename; + size_t size; + immutable(MemberInfo)[] members; +} + +template collectTypes(string modulename) +{ + immutable RecordInfo[] collectTypes = () { + RecordInfo[] r; + mixin("import M = " ~ modulename ~ ";"); + static foreach (member; __traits(allMembers, M)) + { + static if (__traits(compiles, {__traits(getMember, M, member) x;}) + && __traits(compiles, __traits(getMember, M, member).sizeof)) + {{ + alias T = __traits(getMember, M, member); + string kind; + if (is(T == struct)) + kind = "struct"; + else if (is(T == union)) + kind = "union"; + else if (is(T == enum)) + kind = "enum"; + r ~= RecordInfo(kind, member, modulename, T.sizeof); + static if (is(T == struct) || is(T == union)) + { + static foreach (member2; T.tupleof) + {{ + MemberInfo memberInfo = MemberInfo(__traits(identifier, member2), member2.offsetof, member2.sizeof); + static if (__traits(compiles, __traits(isBitfield, member2))) + { + static if (__traits(isBitfield, member2)) + { + memberInfo.bitoffset = member2.bitoffsetof; + memberInfo.bitsize = member2.bitwidth; + } + } + r[$ - 1].members ~= memberInfo; + }} + } + }} + } + return r; + }(); +} + +// Some helper functions, so this druntime test does not depend on phobos. +const(char)* toStringz(string s) +{ + return (s ~ "\0").ptr; +} +bool startsWith(string a, string b) +{ + return a.length >= b.length && a[0 .. b.length] == b; +} +bool canFind(string a, string b) +{ + foreach (i; 0 .. a.length) + { + if (a[i .. $].startsWith(b)) + return true; + } + return false; +} + +bool isGrowingTypes(string name) +{ + foreach (name2; growingTypes) + if (name2 == name) + return true; + return false; +} + +int main() +{ + RecordInfo[string] importcInfos; + foreach (info; collectTypes!"importc_includes") + { + if (info.name.startsWith("___realtype_")) + importcInfos[info.name[12 .. $]] = info; + else + importcInfos[info.name] = info; + } + + bool anyFailure; + + void checkInfos(immutable RecordInfo[] infosD) + { + foreach (infoD; infosD) + { + auto infoC = infoD.name in importcInfos; + if (!infoC) + { + //printf("Warning: Type %s.%s not found in C\n", infoD.modulename.toStringz, infoD.name.toStringz); + continue; + } + + string problemDescription; + bool typeHasKnownProblem = isKnownProblem(infoD.modulename ~ "." ~ infoD.name, "", problemDescription); + const(char)* errorOrWarning = typeHasKnownProblem ? "Warning".ptr : "Error".ptr; + bool typeHasFailure; + + if (infoC.kind.length && infoD.kind != "enum" && infoD.kind != infoC.kind) + { + printf("%s: Type %s.%s is %s in C (ImportC), but %s in D\n", errorOrWarning, infoD.modulename.toStringz, infoD.name.toStringz, infoC.kind.toStringz, infoD.kind.toStringz); + typeHasFailure = true; + } + bool printLayout; + if (infoD.size > infoC.size || (infoD.size > 0 && infoD.size != infoC.size && !isGrowingTypes(infoD.modulename ~ "." ~ infoD.name))) + { + printf("%s: Type %s %s.%s has size %zd in C (ImportC), but size %zd in D\n", errorOrWarning, infoC.kind.toStringz, infoD.modulename.toStringz, infoD.name.toStringz, infoC.size, infoD.size); + printLayout = true; + typeHasFailure = true; + } + MemberInfo[string] memberByNameC; + foreach (memberC; infoC.members) + memberByNameC[memberC.name] = memberC; + foreach (memberD; infoD.members) + { + if (memberD.name.canFind("reserved")) + continue; + if (memberD.name.canFind("spare")) + continue; + if (memberD.name.canFind("pad")) + continue; + string memberProblemDescription; + bool memberHasKnownProblem = isKnownProblem(infoD.modulename ~ "." ~ infoD.name, memberD.name, memberProblemDescription); + errorOrWarning = (memberHasKnownProblem || typeHasKnownProblem) ? "Warning".ptr : "Error".ptr; + bool memberHasFailure; + auto memberC = memberD.name in memberByNameC; + if (memberC) + { + if (memberC.offset != memberD.offset) + { + printf("%s: Member %s for type %s %s.%s has offset %zd in C (ImportC), but offset %zd in D\n", errorOrWarning, memberD.name.toStringz, infoC.kind.toStringz, infoD.modulename.toStringz, infoD.name.toStringz, memberC.offset, memberD.offset); + memberHasFailure = true; + } + if (memberC.size != memberD.size && (!memberC.bitsize || memberD.bitsize)) + { + printf("%s: Member %s for type %s %s.%s has size %zd in C (ImportC), but size %zd in D\n", errorOrWarning, memberD.name.toStringz, infoC.kind.toStringz, infoD.modulename.toStringz, infoD.name.toStringz, memberC.size, memberD.size); + memberHasFailure = true; + } + } + if (memberHasFailure) + { + printLayout = true; + if (!memberHasKnownProblem) + typeHasFailure = true; + if (memberProblemDescription.length) + printf("Known problem: %.*s\n", cast(int) memberProblemDescription.length, memberProblemDescription.ptr); + } + } + if (typeHasFailure) + { + if (!typeHasKnownProblem) + anyFailure = true; + if (problemDescription.length) + printf("Known problem: %.*s\n", cast(int) problemDescription.length, problemDescription.ptr); + } + if (printLayout && (infoC.members.length || infoD.members.length)) + { + printf(" offset size bitoffset bitsize %20s %20s\n", "ImportC layout".ptr, "D layout".ptr); + void printInfos(MemberInfo m1, MemberInfo m2) + { + MemberInfo m3 = m1.name.length ? m1 : m2; + if (m3.bitsize) + printf(" %5zd %5zd %5zd %5zd %20s %20s\n", m3.offset, m3.size, m3.bitoffset, m3.bitsize, m1.name.toStringz, m2.name.toStringz); + else + printf(" %5zd %5zd %5s %5s %20s %20s\n", m3.offset, m3.size, "".ptr, "".ptr, m1.name.toStringz, m2.name.toStringz); + } + immutable(MemberInfo)[] membersC = infoC.members; + immutable(MemberInfo)[] membersD = infoD.members; + while (membersC.length || membersD.length) + { + if (membersC.length == 0) + { + printInfos(MemberInfo.init, membersD[0]); + membersD = membersD[1 .. $]; + } + else if (membersD.length == 0) + { + printInfos(membersC[0], MemberInfo.init); + membersC = membersC[1 .. $]; + } + else if (membersC[0].offset == membersD[0].offset + && membersC[0].size == membersD[0].size + && membersC[0].bitoffset == membersD[0].bitoffset + && membersC[0].bitsize == membersD[0].bitsize) + { + printInfos(membersC[0], membersD[0]); + membersC = membersC[1 .. $]; + membersD = membersD[1 .. $]; + } + else if (membersD[0].offset < membersC[0].offset) + { + printInfos(MemberInfo.init, membersD[0]); + membersD = membersD[1 .. $]; + } + else + { + printInfos(membersC[0], MemberInfo.init); + membersC = membersC[1 .. $]; + } + } + } + } + } + + static foreach (modulename; [ + "core.stdc.complex", + "core.stdc.stdint", + "core.stdc.stdio", + "core.stdc.signal", + "core.stdc.stdlib", + "core.stdc.limits", + "core.stdc.locale", + "core.stdc.fenv", + "core.stdc.inttypes", + "core.stdc.string", + "core.stdc.wctype", + "core.stdc.config", + "core.stdc.math", + "core.stdc.ctype", + "core.stdc.stddef", + "core.stdc.stdarg", + "core.stdc.tgmath", + "core.stdc.time", + "core.stdc.wchar_", + "core.stdc.errno", + "core.stdc.stdatomic", + "core.stdc.assert_", + "core.stdc.float_", + "core.sys.posix.iconv", + "core.sys.posix.dlfcn", + "core.sys.posix.stdio", + "core.sys.posix.poll", + "core.sys.posix.strings", + "core.sys.posix.utime", + "core.sys.posix.netinet.tcp", + "core.sys.posix.netinet.in_", + "core.sys.posix.arpa.inet", + "core.sys.posix.netdb", + "core.sys.posix.spawn", + "core.sys.posix.setjmp", + "core.sys.posix.ucontext", + "core.sys.posix.pthread", + "core.sys.posix.signal", + "core.sys.posix.stdlib", + "core.sys.posix.syslog", + "core.sys.posix.unistd", + "core.sys.posix.stdc.time", + "core.sys.posix.fcntl", + "core.sys.posix.dirent", + "core.sys.posix.locale", + "core.sys.posix.sys.ioctl", + "core.sys.posix.sys.shm", + "core.sys.posix.sys.resource", + "core.sys.posix.sys.ttycom", + "core.sys.posix.sys.ipc", + "core.sys.posix.sys.un", + "core.sys.posix.sys.utsname", + "core.sys.posix.sys.statvfs", + "core.sys.posix.sys.socket", + "core.sys.posix.sys.mman", + "core.sys.posix.sys.stat", + "core.sys.posix.sys.wait", + "core.sys.posix.sys.filio", + "core.sys.posix.sys.msg", + "core.sys.posix.sys.select", + "core.sys.posix.sys.time", + "core.sys.posix.sys.uio", + "core.sys.posix.sys.ioccom", + "core.sys.posix.sys.types", + "core.sys.posix.net.if_", + "core.sys.posix.inttypes", + "core.sys.posix.libgen", + "core.sys.posix.string", + "core.sys.posix.termios", + "core.sys.posix.aio", + "core.sys.posix.config", + "core.sys.posix.mqueue", + "core.sys.posix.sched", + "core.sys.posix.semaphore", + "core.sys.posix.time", + "core.sys.posix.pwd", + "core.sys.posix.grp", + "core.sys.linux.dlfcn", + "core.sys.linux.stdio", + "core.sys.linux.fs", + "core.sys.linux.netinet.tcp", + "core.sys.linux.netinet.in_", + "core.sys.linux.epoll", + "core.sys.linux.link", + "core.sys.linux.err", + "core.sys.linux.io_uring", + "core.sys.linux.timerfd", + "core.sys.linux.unistd", + "core.sys.linux.fcntl", + "core.sys.linux.sys.file", + "core.sys.linux.sys.auxv", + "core.sys.linux.sys.prctl", + "core.sys.linux.sys.eventfd", + "core.sys.linux.sys.sysinfo", + "core.sys.linux.sys.socket", + "core.sys.linux.sys.mman", + "core.sys.linux.sys.xattr", + "core.sys.linux.sys.signalfd", + "core.sys.linux.sys.time", + "core.sys.linux.sys.inotify", + "core.sys.linux.perf_event", + "core.sys.linux.string", + "core.sys.linux.termios", + "core.sys.linux.config", + "core.sys.linux.tipc", + "core.sys.linux.sched", + "core.sys.linux.elf", + "core.sys.linux.linux.if_packet", + "core.sys.linux.linux.if_arp", + "core.sys.linux.time", + "core.sys.linux.execinfo", + "core.sys.linux.ifaddrs", + "core.sys.linux.errno", + ]) + { + checkInfos(collectTypes!modulename); + } + return anyFailure; +} diff --git a/runtime/druntime/test/importc_compare/src/importc_includes.c b/runtime/druntime/test/importc_compare/src/importc_includes.c new file mode 100644 index 0000000000..a385743bca --- /dev/null +++ b/runtime/druntime/test/importc_compare/src/importc_includes.c @@ -0,0 +1,159 @@ +#define __alignof__ _Alignof + +#define BIONIC_IOCTL_NO_SIGNEDNESS_OVERLOAD + +#undef __SIZEOF_INT128__ + +#ifdef _WIN32 +#define __pragma(a) +#define _Pragma(a) +#endif + +#define _FILE_OFFSET_BITS 64 + +// Skip header ucrt/fenv.h +#define _FENV + +#include +#include +#if __has_include() +#include +#endif +#if __has_include() +#include +#endif +#if __has_include() +#include +#endif +#if __has_include() +#include +#endif +#include +#if __has_include() +#include +#endif +#if __has_include() +#include +#endif +#if __has_include() +#include +#endif +#if __has_include() +#include +#endif +#if __has_include() +#include +#endif +#if __has_include() +#include +#endif +#if __has_include() +#include +#endif +#if __has_include() +#include +#endif +#if __has_include() +#include +#endif +#if __has_include() +#include +#endif +#if __has_include() +#include +#endif +#include +#if __has_include() +#include +#endif +#if __has_include() +#include +#endif +#if __has_include() +#include +#endif +#if __has_include() +#include +#endif +#if __has_include() +#include +#endif +#if __has_include() +#include +#endif +#if __has_include() +#include +#endif +#if __has_include() +#include +#endif +#include +#include +#include +#if __has_include() +#include +#endif +#if __has_include() && !defined(__STDC_NO_ATOMICS__) +#include +#endif +#if __has_include() +#include +#endif +#ifdef linux +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif +#include +#include +#include +#include +#include +#if __has_include() +#include +#endif +#include +#if __has_include() +#include +#endif + +typedef struct stat stat_t; +typedef struct statvfs statvfs_t; +typedef struct timezone timezone_t; +typedef struct sysinfo sysinfo_t; + +typedef unsigned long ulong_t; +typedef long slong_t; +typedef unsigned long c_ulong; +typedef long c_long; +typedef long double c_long_double; +typedef long __c_long; +typedef unsigned long __c_ulong; +typedef long long __c_longlong; +typedef unsigned long long __c_ulonglong; +typedef long cpp_long; +typedef unsigned long cpp_ulong; +typedef long long cpp_longlong; +typedef unsigned long long cpp_ulonglong; +typedef size_t cpp_size_t; +typedef ptrdiff_t cpp_ptrdiff_t; +typedef float _Complex __c_complex_float; +typedef double _Complex __c_complex_double; +typedef long double _Complex __c_complex_real; +typedef float _Complex c_complex_float; +typedef double _Complex c_complex_double; +typedef long double _Complex c_complex_real; + +/* These types are defined as macros on Android and are not usable directly in ImportC. */ +#ifdef ipc_perm +typedef ipc_perm ___realtype_ipc_perm; +#endif +#ifdef shmid_ds +typedef shmid_ds ___realtype_shmid_ds; +#endif diff --git a/runtime/druntime/test/imports/Makefile b/runtime/druntime/test/imports/Makefile index bcd1ae773e..d7275c62c9 100644 --- a/runtime/druntime/test/imports/Makefile +++ b/runtime/druntime/test/imports/Makefile @@ -1,14 +1,7 @@ -include ../common.mak - -TESTS:=bug18193 +TESTS := bug18193 -.PHONY: all clean -all: $(addprefix $(ROOT)/,$(addsuffix .done,$(TESTS))) +include ../common.mak -$(ROOT)/%.done: +$(ROOT)/%.done: %.d $(DMD_DEP) @echo Testing $* - @mkdir -p $(basename $@) - $(QUIET)$(DMD) -version=Shared -o- -deps=$@ -Isrc -I../../import src/$*.d - -clean: - rm -rf $(ROOT) + $(COMPILE.d) -deps=$@ -version=Shared -o- $< diff --git a/runtime/druntime/test/init_fini/Makefile b/runtime/druntime/test/init_fini/Makefile index 0956df48ee..568682b74d 100644 --- a/runtime/druntime/test/init_fini/Makefile +++ b/runtime/druntime/test/init_fini/Makefile @@ -1,17 +1,3 @@ -include ../common.mak - -TESTS:=thread_join runtime_args test18996 custom_gc - -.PHONY: all clean -all: $(addprefix $(ROOT)/,$(addsuffix .done,$(TESTS))) +TESTS := thread_join runtime_args test18996 custom_gc -$(ROOT)/%.done: $(ROOT)/%$(DOTEXE) - @echo Testing $* - $(QUIET)$(TIMELIMIT)$(ROOT)/$* $(RUN_ARGS) - @touch $@ - -$(ROOT)/%$(DOTEXE): $(SRC)/%.d - $(QUIET)$(DMD) $(DFLAGS) -of$@ $< - -clean: - rm -rf $(ROOT) +include ../common.mak diff --git a/runtime/druntime/test/init_fini/src/custom_gc.d b/runtime/druntime/test/init_fini/src/custom_gc.d index ee2dcee92e..b85aa889da 100644 --- a/runtime/druntime/test/init_fini/src/custom_gc.d +++ b/runtime/druntime/test/init_fini/src/custom_gc.d @@ -1,6 +1,6 @@ -import core.gc.registry; import core.gc.gcinterface; -import core.stdc.stdlib; +import core.gc.registry; +import core.stdc.stdlib : calloc, malloc, realloc; static import core.memory; @@ -22,6 +22,12 @@ extern (C) void register_default_gcs() class MallocGC : GC { nothrow @nogc: + // To make sure all allocations are multiples of 8 bytes for alignment + private size_t alignUp(size_t size) + { + return (size + 7) & ~7LU; + } + static GC initialize() { import core.stdc.string : memcpy; @@ -81,21 +87,25 @@ nothrow @nogc: void* malloc(size_t size, uint bits, const TypeInfo ti) nothrow { + size = alignUp(size); return sentinelAdd(.malloc(size + sentinelSize), size); } BlkInfo qalloc(size_t size, uint bits, const scope TypeInfo ti) nothrow { + size = alignUp(size); return BlkInfo(malloc(size, bits, ti), size); } void* calloc(size_t size, uint bits, const TypeInfo ti) nothrow { + size = alignUp(size); return sentinelAdd(.calloc(1, size + sentinelSize), size); } void* realloc(void* p, size_t size, uint bits, const TypeInfo ti) nothrow { + size = alignUp(size); return sentinelAdd(.realloc(p - sentinelSize, size + sentinelSize), size); } @@ -179,6 +189,26 @@ nothrow @nogc: return stats().allocatedInCurrentThread; } + void[] getArrayUsed(void *ptr, bool atomic = false) nothrow + { + return null; + } + + bool expandArrayUsed(void[] slice, size_t newUsed, bool atomic = false) nothrow @safe + { + return false; + } + + size_t reserveArrayCapacity(void[] slice, size_t request, bool atomic = false) nothrow @safe + { + return 0; + } + + bool shrinkArrayUsed(void[] slice, size_t existingUsed, bool atomic = false) nothrow + { + return false; + } + private: // doesn't care for alignment static void* sentinelAdd(void* p, size_t value) diff --git a/runtime/druntime/test/init_fini/src/thread_join.d b/runtime/druntime/test/init_fini/src/thread_join.d index a40cd5ebe0..b7700429bd 100644 --- a/runtime/druntime/test/init_fini/src/thread_join.d +++ b/runtime/druntime/test/init_fini/src/thread_join.d @@ -1,7 +1,6 @@ // Bugzilla 11309 - std.concurrency: OwnerTerminated message doesn't work // We need to assure that the thread dtors of parent threads run before the thread dtors of the child threads. import core.thread, core.sync.semaphore; -import core.stdc.stdio; __gshared Semaphore sem; diff --git a/runtime/druntime/test/lifetime/Makefile b/runtime/druntime/test/lifetime/Makefile index c8cb5895cd..64869ddb04 100644 --- a/runtime/druntime/test/lifetime/Makefile +++ b/runtime/druntime/test/lifetime/Makefile @@ -1,17 +1,3 @@ -include ../common.mak - -TESTS:=large_aggregate_destroy_21097 - -.PHONY: all clean -all: $(addprefix $(ROOT)/,$(addsuffix .done,$(TESTS))) +TESTS := large_aggregate_destroy_21097 -$(ROOT)/%.done: $(ROOT)/%$(DOTEXE) - @echo Testing $* - $(QUIET)$(TIMELIMIT)$(ROOT)/$* $(RUN_ARGS) - @touch $@ - -$(ROOT)/%$(DOTEXE): $(SRC)/%.d - $(QUIET)$(DMD) $(DFLAGS) -of$@ $< - -clean: - rm -rf $(ROOT) +include ../common.mak diff --git a/runtime/druntime/test/nogc.d b/runtime/druntime/test/nogc.d deleted file mode 100644 index b3f75804a9..0000000000 --- a/runtime/druntime/test/nogc.d +++ /dev/null @@ -1,5 +0,0 @@ -extern(C) __gshared string[] rt_options = [ "gcopt=gc:non-existing" ]; - -void main() @nogc -{ -} diff --git a/runtime/druntime/test/profile/Makefile b/runtime/druntime/test/profile/Makefile index 4c263336e3..08318a8c58 100644 --- a/runtime/druntime/test/profile/Makefile +++ b/runtime/druntime/test/profile/Makefile @@ -1,7 +1,11 @@ +TESTS := profile profilegc both +# LDC doesn't support -profile=gc yet +ifdef IN_LDC +TESTS := $(filter-out profilegc both,$(TESTS)) +endif + include ../common.mak -# LDC doesn't support -profile=gc yet -TESTS:=profile $(if $(findstring ldmd2,$(DMD)),,profilegc both) DIFF:=diff --strip-trailing-cr GREP:=grep @@ -19,55 +23,45 @@ else ifneq (windows,$(OS)) # already using a bash shell on Windows via common.ma SHELL=/bin/bash endif -.PHONY: all clean -all: $(addprefix $(ROOT)/,$(addsuffix .done,$(TESTS))) - -# LDC: enable assertions for BUILD=RELEASE (=> `-O -release`) -$(ROOT)/profile.done: DFLAGS+=-profile $(if $(findstring ldmd2,$(DMD)),-check=assert=on,) $(ROOT)/profile.done: $(ROOT)/%.done: $(ROOT)/%$(DOTEXE) @echo Testing $* @rm -f $(ROOT)/mytrace.log $(ROOT)/mytrace.def - $(QUIET)$(TIMELIMIT)$(ROOT)/$* $(ROOT)/mytrace.log $(ROOT)/mytrace.def - $(QUIET)$(GREP) -q '1 .*_Dmain' $(ROOT)/mytrace.log - $(QUIET)$(GREP) -q '1000 .*uint profile.foo(uint)' $(ROOT)/mytrace.log - $(QUIET) cat $(ROOT)/mytrace.def - $(QUIET) sort $(ROOT)/mytrace.def -o $(ROOT)/mytrace.def - $(QUIET) (sort mytrace.def.exp | $(DIFF) - $(ROOT)/mytrace.def) || (sort mytrace.releaseopt.def.exp | $(DIFF) - $(ROOT)/mytrace.def) + $(TIMELIMIT)$(ROOT)/$* $(ROOT)/mytrace.log $(ROOT)/mytrace.def + $(GREP) -q '1 .*_Dmain' $(ROOT)/mytrace.log + $(GREP) -q '1000 .*uint profile.foo(uint)' $(ROOT)/mytrace.log + cat $(ROOT)/mytrace.def + sort $(ROOT)/mytrace.def -o $(ROOT)/mytrace.def + (sort mytrace.def.exp | $(DIFF) - $(ROOT)/mytrace.def) || (sort mytrace.releaseopt.def.exp | $(DIFF) - $(ROOT)/mytrace.def) @touch $@ +$(ROOT)/profile$(DOTEXE): extra_dflags += -profile -$(ROOT)/profilegc.done: DFLAGS+=-profile=gc $(ROOT)/profilegc.done: $(ROOT)/%.done: $(ROOT)/%$(DOTEXE) @echo Testing $* @rm -f $(ROOT)/myprofilegc.log - $(QUIET)$(TIMELIMIT)$(ROOT)/$* $(ROOT)/myprofilegc.log - $(QUIET)$(DIFF) \ + $(TIMELIMIT)$(ROOT)/$* $(ROOT)/myprofilegc.log + $(DIFF) \ <($(GREP) -vF 'core.' myprofilegc.log.$(OS).$(shell echo $(MODEL) | cut -c 1-2).exp) \ <($(GREP) -vF 'core.' $(ROOT)/myprofilegc.log) @touch $@ +$(ROOT)/profilegc$(DOTEXE): extra_dflags += -profile=gc -$(ROOT)/both.done: DFLAGS+=-profile -profile=gc $(ROOT)/both.done: $(ROOT)/%.done: $(ROOT)/%$(DOTEXE) @echo Testing $* @rm -f $(ROOT)/both.log $(ROOT)/both.def $(ROOT)/bothgc.log - $(QUIET)$(TIMELIMIT)$(ROOT)/$* $(ROOT)/both.log $(ROOT)/both.def $(ROOT)/bothgc.log - $(QUIET)$(GREP) -q '1 .*_Dmain' $(ROOT)/both.log - $(QUIET)$(GREP) -q '1000 .*both.Num\* both.foo(uint)' $(ROOT)/both.log - $(QUIET) cat $(ROOT)/both.def - $(QUIET) sort $(ROOT)/both.def -o $(ROOT)/both.def - $(QUIET)(sort bothnew.def.exp | $(DIFF) - $(ROOT)/both.def) + $(TIMELIMIT)$(ROOT)/$* $(ROOT)/both.log $(ROOT)/both.def $(ROOT)/bothgc.log + $(GREP) -q '1 .*_Dmain' $(ROOT)/both.log + $(GREP) -q '1000 .*both.Num\* both.foo(uint)' $(ROOT)/both.log + cat $(ROOT)/both.def + sort $(ROOT)/both.def -o $(ROOT)/both.def + (sort bothnew.def.exp | $(DIFF) - $(ROOT)/both.def) ifeq (windows,$(OS)) - $(QUIET)$(DIFF) \ + $(DIFF) \ <($(GREP) -vF 'core.' bothgc.log.exp) \ <($(GREP) -vF 'core.' $(ROOT)/bothgc.log | $(SED) 's: src\\\\: src/:g') else - $(QUIET)$(DIFF) \ + $(DIFF) \ <($(GREP) -vF 'core.' bothgc.log.exp) \ <($(GREP) -vF 'core.' $(ROOT)/bothgc.log) endif @touch $@ - -$(ROOT)/%$(DOTEXE): $(SRC)/%.d - $(QUIET)$(DMD) $(DFLAGS) -of$(ROOT)/$* $< - -clean: - rm -rf $(ROOT) *.log *.def +$(ROOT)/both$(DOTEXE): extra_dflags += -profile -profile=gc diff --git a/runtime/druntime/test/profile/bothnew.def.exp b/runtime/druntime/test/profile/bothnew.def.exp index c6b652b5b4..91683fc148 100644 --- a/runtime/druntime/test/profile/bothnew.def.exp +++ b/runtime/druntime/test/profile/bothnew.def.exp @@ -6,3 +6,4 @@ FUNCTIONS _D4core8internal8lifetime__T18emplaceInitializerTS4both3NumZQBgFNaNbNiNeMKQzZv _D4core8lifetime__T11_d_newitemTTS4both3NumZQzFNaNbNeZPQw _D4core8lifetime__T16_d_newitemTTraceTS4both3NumZQBeFNaNbNeAyaiQeZPQBd + _D4core8lifetime__T16_d_newitemTTraceTS4both3NumZQBeFNeAyaiQeZ8typeNameFNbNeC15TypeInfo_StructZQBo diff --git a/runtime/druntime/test/profile/myprofilegc.log.freebsd.32.exp b/runtime/druntime/test/profile/myprofilegc.log.freebsd.32.exp index 8a04f4737a..0053d15aaa 100644 --- a/runtime/druntime/test/profile/myprofilegc.log.freebsd.32.exp +++ b/runtime/druntime/test/profile/myprofilegc.log.freebsd.32.exp @@ -1,21 +1,21 @@ bytes allocated, allocations, type, function, file:line - 256 1 immutable(char)[][int] D main src/profilegc.d:23 - 128 1 float D main src/profilegc.d:18 - 128 1 int D main src/profilegc.d:15 + 160 1 immutable(char)[][int] profilegc.main src/profilegc.d:23 + 128 1 float profilegc.main src/profilegc.d:18 + 128 1 int profilegc.main src/profilegc.d:15 64 1 double[] profilegc.main src/profilegc.d:56 - 48 1 float[] D main src/profilegc.d:42 - 48 1 int[] D main src/profilegc.d:41 + 48 1 float[] profilegc.main src/profilegc.d:42 + 48 1 int[] profilegc.main src/profilegc.d:41 32 1 void[] profilegc.main src/profilegc.d:55 - 16 1 C D main src/profilegc.d:12 - 16 1 char[] D main src/profilegc.d:34 - 16 1 char[] D main src/profilegc.d:36 + 16 1 C profilegc.main src/profilegc.d:12 + 16 1 char[] profilegc.main src/profilegc.d:34 + 16 1 char[] profilegc.main src/profilegc.d:36 16 1 closure profilegc.main.foo src/profilegc.d:45 - 16 1 float D main src/profilegc.d:16 - 16 1 float D main src/profilegc.d:17 - 16 1 int D main src/profilegc.d:13 - 16 1 int D main src/profilegc.d:14 - 16 1 uint[] D main src/profilegc.d:15 - 16 1 uint[] D main src/profilegc.d:18 - 16 1 int[] D main src/profilegc.d:22 - 16 1 int[] D main src/profilegc.d:37 - 16 1 wchar[] D main src/profilegc.d:35 + 16 1 float profilegc.main src/profilegc.d:16 + 16 1 float profilegc.main src/profilegc.d:17 + 16 1 int profilegc.main src/profilegc.d:13 + 16 1 int profilegc.main src/profilegc.d:14 + 16 1 uint[] profilegc.main src/profilegc.d:15 + 16 1 uint[] profilegc.main src/profilegc.d:18 + 16 1 int[] profilegc.main src/profilegc.d:22 + 16 1 int[] profilegc.main src/profilegc.d:37 + 16 1 wchar[] profilegc.main src/profilegc.d:35 diff --git a/runtime/druntime/test/profile/myprofilegc.log.freebsd.64.exp b/runtime/druntime/test/profile/myprofilegc.log.freebsd.64.exp index e3e2476a5a..97c1aaa253 100644 --- a/runtime/druntime/test/profile/myprofilegc.log.freebsd.64.exp +++ b/runtime/druntime/test/profile/myprofilegc.log.freebsd.64.exp @@ -1,21 +1,21 @@ bytes allocated, allocations, type, function, file:line - 496 1 immutable(char)[][int] D main src/profilegc.d:23 - 160 1 float D main src/profilegc.d:18 - 160 1 int D main src/profilegc.d:15 + 320 1 immutable(char)[][int] profilegc.main src/profilegc.d:23 + 160 1 float profilegc.main src/profilegc.d:18 + 160 1 int profilegc.main src/profilegc.d:15 64 1 double[] profilegc.main src/profilegc.d:56 - 48 1 float[] D main src/profilegc.d:42 - 48 1 int[] D main src/profilegc.d:41 - 32 1 C D main src/profilegc.d:12 - 32 1 ulong[] D main src/profilegc.d:15 - 32 1 ulong[] D main src/profilegc.d:18 + 48 1 float[] profilegc.main src/profilegc.d:42 + 48 1 int[] profilegc.main src/profilegc.d:41 + 32 1 C profilegc.main src/profilegc.d:12 + 32 1 ulong[] profilegc.main src/profilegc.d:15 + 32 1 ulong[] profilegc.main src/profilegc.d:18 32 1 void[] profilegc.main src/profilegc.d:55 - 16 1 char[] D main src/profilegc.d:34 - 16 1 char[] D main src/profilegc.d:36 + 16 1 char[] profilegc.main src/profilegc.d:34 + 16 1 char[] profilegc.main src/profilegc.d:36 16 1 closure profilegc.main.foo src/profilegc.d:45 - 16 1 float D main src/profilegc.d:16 - 16 1 float D main src/profilegc.d:17 - 16 1 int D main src/profilegc.d:13 - 16 1 int D main src/profilegc.d:14 - 16 1 int[] D main src/profilegc.d:22 - 16 1 int[] D main src/profilegc.d:37 - 16 1 wchar[] D main src/profilegc.d:35 + 16 1 float profilegc.main src/profilegc.d:16 + 16 1 float profilegc.main src/profilegc.d:17 + 16 1 int profilegc.main src/profilegc.d:13 + 16 1 int profilegc.main src/profilegc.d:14 + 16 1 int[] profilegc.main src/profilegc.d:22 + 16 1 int[] profilegc.main src/profilegc.d:37 + 16 1 wchar[] profilegc.main src/profilegc.d:35 diff --git a/runtime/druntime/test/profile/myprofilegc.log.linux.32.exp b/runtime/druntime/test/profile/myprofilegc.log.linux.32.exp index 9d929c3cca..2dc8e5ccf3 100644 --- a/runtime/druntime/test/profile/myprofilegc.log.linux.32.exp +++ b/runtime/druntime/test/profile/myprofilegc.log.linux.32.exp @@ -1,21 +1,21 @@ bytes allocated, allocations, type, function, file:line - 256 1 immutable(char)[][int] D main src/profilegc.d:23 - 128 1 float D main src/profilegc.d:18 - 128 1 int D main src/profilegc.d:15 + 160 1 immutable(char)[][int] profilegc.main src/profilegc.d:23 + 128 1 float profilegc.main src/profilegc.d:18 + 128 1 int profilegc.main src/profilegc.d:15 64 1 double[] profilegc.main src/profilegc.d:56 - 48 1 float[] D main src/profilegc.d:42 - 48 1 int[] D main src/profilegc.d:41 + 48 1 float[] profilegc.main src/profilegc.d:42 + 48 1 int[] profilegc.main src/profilegc.d:41 32 1 void[] profilegc.main src/profilegc.d:55 - 16 1 C D main src/profilegc.d:12 - 16 1 char[] D main src/profilegc.d:34 - 16 1 char[] D main src/profilegc.d:36 + 16 1 C profilegc.main src/profilegc.d:12 + 16 1 char[] profilegc.main src/profilegc.d:34 + 16 1 char[] profilegc.main src/profilegc.d:36 16 1 closure profilegc.main.foo src/profilegc.d:45 - 16 1 float D main src/profilegc.d:16 - 16 1 float D main src/profilegc.d:17 - 16 1 int D main src/profilegc.d:13 - 16 1 int D main src/profilegc.d:14 - 16 1 int[] D main src/profilegc.d:22 - 16 1 int[] D main src/profilegc.d:37 - 16 1 uint[] D main src/profilegc.d:15 - 16 1 uint[] D main src/profilegc.d:18 - 16 1 wchar[] D main src/profilegc.d:35 + 16 1 float profilegc.main src/profilegc.d:16 + 16 1 float profilegc.main src/profilegc.d:17 + 16 1 int profilegc.main src/profilegc.d:13 + 16 1 int profilegc.main src/profilegc.d:14 + 16 1 int[] profilegc.main src/profilegc.d:22 + 16 1 int[] profilegc.main src/profilegc.d:37 + 16 1 uint[] profilegc.main src/profilegc.d:15 + 16 1 uint[] profilegc.main src/profilegc.d:18 + 16 1 wchar[] profilegc.main src/profilegc.d:35 diff --git a/runtime/druntime/test/profile/myprofilegc.log.linux.64.exp b/runtime/druntime/test/profile/myprofilegc.log.linux.64.exp index e3e2476a5a..97c1aaa253 100644 --- a/runtime/druntime/test/profile/myprofilegc.log.linux.64.exp +++ b/runtime/druntime/test/profile/myprofilegc.log.linux.64.exp @@ -1,21 +1,21 @@ bytes allocated, allocations, type, function, file:line - 496 1 immutable(char)[][int] D main src/profilegc.d:23 - 160 1 float D main src/profilegc.d:18 - 160 1 int D main src/profilegc.d:15 + 320 1 immutable(char)[][int] profilegc.main src/profilegc.d:23 + 160 1 float profilegc.main src/profilegc.d:18 + 160 1 int profilegc.main src/profilegc.d:15 64 1 double[] profilegc.main src/profilegc.d:56 - 48 1 float[] D main src/profilegc.d:42 - 48 1 int[] D main src/profilegc.d:41 - 32 1 C D main src/profilegc.d:12 - 32 1 ulong[] D main src/profilegc.d:15 - 32 1 ulong[] D main src/profilegc.d:18 + 48 1 float[] profilegc.main src/profilegc.d:42 + 48 1 int[] profilegc.main src/profilegc.d:41 + 32 1 C profilegc.main src/profilegc.d:12 + 32 1 ulong[] profilegc.main src/profilegc.d:15 + 32 1 ulong[] profilegc.main src/profilegc.d:18 32 1 void[] profilegc.main src/profilegc.d:55 - 16 1 char[] D main src/profilegc.d:34 - 16 1 char[] D main src/profilegc.d:36 + 16 1 char[] profilegc.main src/profilegc.d:34 + 16 1 char[] profilegc.main src/profilegc.d:36 16 1 closure profilegc.main.foo src/profilegc.d:45 - 16 1 float D main src/profilegc.d:16 - 16 1 float D main src/profilegc.d:17 - 16 1 int D main src/profilegc.d:13 - 16 1 int D main src/profilegc.d:14 - 16 1 int[] D main src/profilegc.d:22 - 16 1 int[] D main src/profilegc.d:37 - 16 1 wchar[] D main src/profilegc.d:35 + 16 1 float profilegc.main src/profilegc.d:16 + 16 1 float profilegc.main src/profilegc.d:17 + 16 1 int profilegc.main src/profilegc.d:13 + 16 1 int profilegc.main src/profilegc.d:14 + 16 1 int[] profilegc.main src/profilegc.d:22 + 16 1 int[] profilegc.main src/profilegc.d:37 + 16 1 wchar[] profilegc.main src/profilegc.d:35 diff --git a/runtime/druntime/test/profile/myprofilegc.log.osx.32.exp b/runtime/druntime/test/profile/myprofilegc.log.osx.32.exp index e5afe3345e..1cea04eced 100644 --- a/runtime/druntime/test/profile/myprofilegc.log.osx.32.exp +++ b/runtime/druntime/test/profile/myprofilegc.log.osx.32.exp @@ -1,21 +1,21 @@ bytes allocated, allocations, type, function, file:line - 176 1 immutable(char)[][int] D main src/profilegc.d:23 - 128 1 float D main src/profilegc.d:18 - 128 1 int D main src/profilegc.d:15 - 64 1 float[] D main src/profilegc.d:42 - 64 1 int[] D main src/profilegc.d:41 + 160 1 immutable(char)[][int] profilegc.main src/profilegc.d:23 + 128 1 float profilegc.main src/profilegc.d:18 + 128 1 int profilegc.main src/profilegc.d:15 + 64 1 float[] profilegc.main src/profilegc.d:42 + 64 1 int[] profilegc.main src/profilegc.d:41 64 1 double[] profilegc.main src/profilegc.d:56 32 1 void[] profilegc.main src/profilegc.d:55 - 16 1 C D main src/profilegc.d:12 - 16 1 char[] D main src/profilegc.d:34 - 16 1 char[] D main src/profilegc.d:36 + 16 1 C profilegc.main src/profilegc.d:12 + 16 1 char[] profilegc.main src/profilegc.d:34 + 16 1 char[] profilegc.main src/profilegc.d:36 16 1 closure profilegc.main.foo src/profilegc.d:45 - 16 1 float D main src/profilegc.d:16 - 16 1 float D main src/profilegc.d:17 - 16 1 int D main src/profilegc.d:13 - 16 1 int D main src/profilegc.d:14 - 16 1 uint[] D main src/profilegc.d:15 - 16 1 uint[] D main src/profilegc.d:18 - 16 1 int[] D main src/profilegc.d:22 - 16 1 int[] D main src/profilegc.d:37 - 16 1 wchar[] D main src/profilegc.d:35 + 16 1 float profilegc.main src/profilegc.d:16 + 16 1 float profilegc.main src/profilegc.d:17 + 16 1 int profilegc.main src/profilegc.d:13 + 16 1 int profilegc.main src/profilegc.d:14 + 16 1 uint[] profilegc.main src/profilegc.d:15 + 16 1 uint[] profilegc.main src/profilegc.d:18 + 16 1 int[] profilegc.main src/profilegc.d:22 + 16 1 int[] profilegc.main src/profilegc.d:37 + 16 1 wchar[] profilegc.main src/profilegc.d:35 diff --git a/runtime/druntime/test/profile/myprofilegc.log.osx.64.exp b/runtime/druntime/test/profile/myprofilegc.log.osx.64.exp index e3e2476a5a..97c1aaa253 100644 --- a/runtime/druntime/test/profile/myprofilegc.log.osx.64.exp +++ b/runtime/druntime/test/profile/myprofilegc.log.osx.64.exp @@ -1,21 +1,21 @@ bytes allocated, allocations, type, function, file:line - 496 1 immutable(char)[][int] D main src/profilegc.d:23 - 160 1 float D main src/profilegc.d:18 - 160 1 int D main src/profilegc.d:15 + 320 1 immutable(char)[][int] profilegc.main src/profilegc.d:23 + 160 1 float profilegc.main src/profilegc.d:18 + 160 1 int profilegc.main src/profilegc.d:15 64 1 double[] profilegc.main src/profilegc.d:56 - 48 1 float[] D main src/profilegc.d:42 - 48 1 int[] D main src/profilegc.d:41 - 32 1 C D main src/profilegc.d:12 - 32 1 ulong[] D main src/profilegc.d:15 - 32 1 ulong[] D main src/profilegc.d:18 + 48 1 float[] profilegc.main src/profilegc.d:42 + 48 1 int[] profilegc.main src/profilegc.d:41 + 32 1 C profilegc.main src/profilegc.d:12 + 32 1 ulong[] profilegc.main src/profilegc.d:15 + 32 1 ulong[] profilegc.main src/profilegc.d:18 32 1 void[] profilegc.main src/profilegc.d:55 - 16 1 char[] D main src/profilegc.d:34 - 16 1 char[] D main src/profilegc.d:36 + 16 1 char[] profilegc.main src/profilegc.d:34 + 16 1 char[] profilegc.main src/profilegc.d:36 16 1 closure profilegc.main.foo src/profilegc.d:45 - 16 1 float D main src/profilegc.d:16 - 16 1 float D main src/profilegc.d:17 - 16 1 int D main src/profilegc.d:13 - 16 1 int D main src/profilegc.d:14 - 16 1 int[] D main src/profilegc.d:22 - 16 1 int[] D main src/profilegc.d:37 - 16 1 wchar[] D main src/profilegc.d:35 + 16 1 float profilegc.main src/profilegc.d:16 + 16 1 float profilegc.main src/profilegc.d:17 + 16 1 int profilegc.main src/profilegc.d:13 + 16 1 int profilegc.main src/profilegc.d:14 + 16 1 int[] profilegc.main src/profilegc.d:22 + 16 1 int[] profilegc.main src/profilegc.d:37 + 16 1 wchar[] profilegc.main src/profilegc.d:35 diff --git a/runtime/druntime/test/profile/myprofilegc.log.windows.32.exp b/runtime/druntime/test/profile/myprofilegc.log.windows.32.exp index 8b4cdf6e75..cf29c52048 100644 --- a/runtime/druntime/test/profile/myprofilegc.log.windows.32.exp +++ b/runtime/druntime/test/profile/myprofilegc.log.windows.32.exp @@ -1,21 +1,21 @@ bytes allocated, allocations, type, function, file:line - 256 1 immutable(char)[][int] D main src\profilegc.d:23 - 128 1 float D main src\profilegc.d:18 - 128 1 int D main src\profilegc.d:15 + 160 1 immutable(char)[][int] profilegc.main src\profilegc.d:23 + 128 1 float profilegc.main src\profilegc.d:18 + 128 1 int profilegc.main src\profilegc.d:15 64 1 double[] profilegc.main src\profilegc.d:56 - 48 1 float[] D main src\profilegc.d:42 - 48 1 int[] D main src\profilegc.d:41 + 48 1 float[] profilegc.main src\profilegc.d:42 + 48 1 int[] profilegc.main src\profilegc.d:41 32 1 void[] profilegc.main src\profilegc.d:55 - 16 1 C D main src\profilegc.d:12 - 16 1 char[] D main src\profilegc.d:34 - 16 1 char[] D main src\profilegc.d:36 + 16 1 C profilegc.main src\profilegc.d:12 + 16 1 char[] profilegc.main src\profilegc.d:34 + 16 1 char[] profilegc.main src\profilegc.d:36 16 1 closure profilegc.main.foo src\profilegc.d:45 - 16 1 float D main src\profilegc.d:16 - 16 1 float D main src\profilegc.d:17 - 16 1 int D main src\profilegc.d:13 - 16 1 int D main src\profilegc.d:14 - 16 1 int[] D main src\profilegc.d:22 - 16 1 int[] D main src\profilegc.d:37 - 16 1 uint[] D main src\profilegc.d:15 - 16 1 uint[] D main src\profilegc.d:18 - 16 1 wchar[] D main src\profilegc.d:35 + 16 1 float profilegc.main src\profilegc.d:16 + 16 1 float profilegc.main src\profilegc.d:17 + 16 1 int profilegc.main src\profilegc.d:13 + 16 1 int profilegc.main src\profilegc.d:14 + 16 1 int[] profilegc.main src\profilegc.d:22 + 16 1 int[] profilegc.main src\profilegc.d:37 + 16 1 uint[] profilegc.main src\profilegc.d:15 + 16 1 uint[] profilegc.main src\profilegc.d:18 + 16 1 wchar[] profilegc.main src\profilegc.d:35 diff --git a/runtime/druntime/test/profile/myprofilegc.log.windows.64.exp b/runtime/druntime/test/profile/myprofilegc.log.windows.64.exp index 05408557a8..631c404941 100644 --- a/runtime/druntime/test/profile/myprofilegc.log.windows.64.exp +++ b/runtime/druntime/test/profile/myprofilegc.log.windows.64.exp @@ -1,21 +1,21 @@ bytes allocated, allocations, type, function, file:line - 496 1 immutable(char)[][int] D main src\profilegc.d:23 - 160 1 float D main src\profilegc.d:18 - 160 1 int D main src\profilegc.d:15 + 320 1 immutable(char)[][int] profilegc.main src\profilegc.d:23 + 160 1 float profilegc.main src\profilegc.d:18 + 160 1 int profilegc.main src\profilegc.d:15 64 1 double[] profilegc.main src\profilegc.d:56 - 48 1 float[] D main src\profilegc.d:42 - 48 1 int[] D main src\profilegc.d:41 - 32 1 C D main src\profilegc.d:12 - 32 1 ulong[] D main src\profilegc.d:15 - 32 1 ulong[] D main src\profilegc.d:18 + 48 1 float[] profilegc.main src\profilegc.d:42 + 48 1 int[] profilegc.main src\profilegc.d:41 + 32 1 C profilegc.main src\profilegc.d:12 + 32 1 ulong[] profilegc.main src\profilegc.d:15 + 32 1 ulong[] profilegc.main src\profilegc.d:18 32 1 void[] profilegc.main src\profilegc.d:55 - 16 1 char[] D main src\profilegc.d:34 - 16 1 char[] D main src\profilegc.d:36 + 16 1 char[] profilegc.main src\profilegc.d:34 + 16 1 char[] profilegc.main src\profilegc.d:36 16 1 closure profilegc.main.foo src\profilegc.d:45 - 16 1 float D main src\profilegc.d:16 - 16 1 float D main src\profilegc.d:17 - 16 1 int D main src\profilegc.d:13 - 16 1 int D main src\profilegc.d:14 - 16 1 int[] D main src\profilegc.d:22 - 16 1 int[] D main src\profilegc.d:37 - 16 1 wchar[] D main src\profilegc.d:35 + 16 1 float profilegc.main src\profilegc.d:16 + 16 1 float profilegc.main src\profilegc.d:17 + 16 1 int profilegc.main src\profilegc.d:13 + 16 1 int profilegc.main src\profilegc.d:14 + 16 1 int[] profilegc.main src\profilegc.d:22 + 16 1 int[] profilegc.main src\profilegc.d:37 + 16 1 wchar[] profilegc.main src\profilegc.d:35 diff --git a/runtime/druntime/test/shared/Makefile b/runtime/druntime/test/shared/Makefile index 405cccb362..4b120bf5a8 100644 --- a/runtime/druntime/test/shared/Makefile +++ b/runtime/druntime/test/shared/Makefile @@ -1,11 +1,12 @@ -# SHARED (from druntime/Makefile) is `1` for platforms supporting a shared druntime library, otherwise empty -LINK_SHARED:=$(SHARED) +override LINK_SHARED := $(SHARED) -include ../common.mak # affected by LINK_SHARED! +ifdef IN_LDC +# need OS for the conditions below +include ../../../../dmd/osmodel.mak +endif ifneq (,$(LINK_SHARED)) - # LDC: enable ~all tests on Windows too - ifeq (,$(findstring ldmd2,$(DMD))) + ifndef IN_LDC # TODO: enable tests on Windows ifeq (windows,$(OS)) TESTS:=link linkD linkDR loadDR @@ -14,6 +15,7 @@ ifneq (,$(LINK_SHARED)) link_linkdep load_linkdep link_loaddep load_loaddep load_13414 endif else + # LDC: enable ~all tests on Windows too TESTS:=link load linkD linkDR loadDR host finalize dynamiccast \ link_linkdep link_loaddep load_loaddep load_13414 # FIXME: `load_linkdep` needs a non-dummy `getDependencies()` in rt.sections_elf_shared, @@ -23,134 +25,148 @@ ifneq (,$(LINK_SHARED)) TESTS+=load_linkdep endif endif + + ifeq (windows,$(OS)) + TESTS += dll_gc_proxy_teardown dll_gc_proxy_teardown_nounload + endif endif endif + # there are extra tests for Windows, not requiring a druntime DLL ifeq (windows,$(OS)) TESTS+=loadlibwin dllrefcount dllgc dynamiccast endif -DOTIMPLIB:=$(if $(findstring $(OS),windows),.lib,$(DOTDLL)) +include ../common.mak -.PHONY: all clean -all: $(addprefix $(ROOT)/,$(addsuffix .done,$(TESTS))) $(if $(findstring ldmd2,$(DMD)),$(if $(findstring $(OS),windows),dll_gc_proxy_teardown,),) +abs_root := $(abspath $(ROOT)) +# on posix we link directly to the .so +# on windows you have to link the .lib which references the .dll +for_linking := $(if $(filter windows,$(OS)),.lib,$(DOTDLL)) ifeq (windows,$(OS)) # extra tests on Windows -ifneq (,$(findstring ldmd2,$(DMD))) - # LDC: required for executables to implicitly dllimport DLL data symbols - DFLAGS+=-dllimport=all -else # DMD +ifndef IN_LDC ifeq ($(SHARED),1) # dmd -shared does not (yet) imply -visibility=public -$(ROOT)/%$(DOTDLL): DFLAGS += -visibility=public +$(ROOT)/%$(DOTDLL): private extra_dflags += -visibility=public -DFLAGS+=-version=SharedRuntime -PATH:=$(dir $(DRUNTIMESO));$(PATH) +extra_dflags += -version=SharedRuntime +PATH := $(dir $(DRUNTIMESO));$(PATH) endif -endif # end DMD - -$(ROOT)/dllrefcount$(DOTEXE): $(SRC)/dllrefcount.d - $(QUIET)$(DMD) $(DFLAGS) -of$@ $< - -$(ROOT)/loadlibwin$(DOTEXE): $(SRC)/loadlibwin.d - $(QUIET)$(DMD) $(DFLAGS) -of$@ $< - -# LDC: this test is designed for .exe & .dll with separate druntimes -ifneq (,$(findstring ldmd2,$(DMD))) -$(ROOT)/dllgc$(DOTEXE): DFLAGS+=-link-defaultlib-shared=false -dllimport=none endif -$(ROOT)/dllgc$(DOTEXE): $(SRC)/dllgc.d - $(QUIET)$(DMD) $(DFLAGS) -version=DLL -shared -of$(ROOT)/dllgc$(DOTDLL) $< - $(QUIET)$(DMD) $(DFLAGS) -of$@ $< +# LDC: this test is designed for .exe & .dll with separate druntimes +$(ROOT)/dllgc$(DOTEXE) $(ROOT)/dllgc$(DOTDLL): private extra_dflags += -link-defaultlib-shared=false -dllimport=none # LDC addition: test teardown with separate druntimes, with the DLL using the .exe GC -ifneq (,$(findstring ldmd2,$(DMD))) -dll_gc_proxy_teardown: DFLAGS+=-link-defaultlib-shared=false -dllimport=none -dll_gc_proxy_teardown: $(SRC)/dll_gc_proxy_teardown.d - $(QUIET)$(DMD) $(DFLAGS) -shared -L/EXPORT:gc_setProxy -L/EXPORT:gc_clrProxy -version=DLL -of$(ROOT)/dll_gc_proxy_teardown$(DOTDLL) $< - $(QUIET)$(DMD) $(DFLAGS) -shared -L/EXPORT:gc_setProxy -L/EXPORT:gc_clrProxy -version=DLL -version=NoUnload -of$(ROOT)/dll_gc_proxy_teardown_nounload$(DOTDLL) $< - $(QUIET)$(DMD) $(DFLAGS) -of$(ROOT)/load_dll_gc_proxy_teardown$(DOTEXE) $< - $(QUIET)$(DMD) $(DFLAGS) -version=NoUnload -of$(ROOT)/load_dll_gc_proxy_teardown_nounload$(DOTEXE) $< - $(QUIET)$(ROOT)/load_dll_gc_proxy_teardown$(DOTEXE) - $(QUIET)$(ROOT)/load_dll_gc_proxy_teardown_nounload$(DOTEXE) -endif - +dgpt = dll_gc_proxy_teardown dll_gc_proxy_teardown_nounload +$(dgpt:%=$(OBJDIR)/%$(DOTDLL)): private extra_ldflags.d += -L/EXPORT:gc_setProxy -L/EXPORT:gc_clrProxy +$(dgpt:%=$(OBJDIR)/%$(DOTDLL)): private extra_dflags += -version=DLL +$(dgpt:%=$(OBJDIR)/%$(DOTEXE)) $(dgpt:%=$(OBJDIR)/%$(DOTDLL)): private extra_dflags += -link-defaultlib-shared=false -dllimport=none +$(dgpt:%=$(OBJDIR)/%.done): $(OBJDIR)/%.done: $(OBJDIR)/%$(DOTDLL) + +$(OBJDIR)/dll_gc_proxy_teardown_nounload%: private extra_dflags += -version=NoUnload +$(OBJDIR)/dll_gc_proxy_teardown_nounload$(DOTDLL): dll_gc_proxy_teardown.d + $(LINK.d) -shared $< $(extra_sources) $(extra_ldlibs.d) $(LDLIBS.d) $(OUTPUT_OPTION.d) +$(OBJDIR)/dll_gc_proxy_teardown_nounload$(DOTEXE): dll_gc_proxy_teardown.d + $(LINK.d) $< $(extra_sources) $(extra_ldlibs.d) $(LDLIBS.d) $(OUTPUT_OPTION.d) + +$(ROOT)/dllgc.done: $(ROOT)/dllgc$(DOTDLL) +$(ROOT)/dllgc$(DOTDLL): extra_dflags += -version=DLL endif # Windows -$(ROOT)/loadDR.done $(ROOT)/host.done: RUN_ARGS:=$(DRUNTIMESO:.lib=.dll) - -$(ROOT)/%.done: $(ROOT)/%$(DOTEXE) +$(ROOT)/dynamiccast.done: $(ROOT)/%.done: $(ROOT)/%$(DOTEXE) $(ROOT)/%$(DOTDLL) @echo Testing $* - $(QUIET)$(TIMELIMIT)$< $(RUN_ARGS) + $(RM) $(ROOT)/dynamiccast_end{bar,main} + $(TIMELIMIT)$< + test -f $(ROOT)/dynamiccast_endbar + test -f $(ROOT)/dynamiccast_endmain @touch $@ +$(ROOT)/dynamiccast$(DOTEXE): private extra_ldlibs.d += $(LINKDL) +$(ROOT)/dynamiccast$(DOTDLL): private extra_dflags += -version=DLL -$(ROOT)/dynamiccast.done: $(ROOT)/%.done: $(ROOT)/%$(DOTEXE) - @echo Testing $* - $(QUIET)rm -f $(ROOT)/dynamiccast_end{bar,main} - $(QUIET)$(TIMELIMIT)$< $(RUN_ARGS) - $(QUIET)test -f $(ROOT)/dynamiccast_endbar - $(QUIET)test -f $(ROOT)/dynamiccast_endmain - @touch $@ +# Avoid a race condition that I sometimes hit with make -j8. +# Maybe temporary file collisions when invoking the linker? +$(OBJDIR)/dynamiccast$(DOTEXE): $(OBJDIR)/dynamiccast$(DOTDLL) -$(ROOT)/link$(DOTEXE): $(SRC)/link.d $(ROOT)/lib$(DOTDLL) $(DRUNTIMESO) - $(QUIET)$(DMD) $(DFLAGS) -of$@ $< -L$(ROOT)/lib$(DOTIMPLIB) +$(OBJDIR)/link$(DOTEXE): $(OBJDIR)/lib$(for_linking) lib.d +$(OBJDIR)/link$(DOTEXE): private extra_ldlibs.d += $(abs_root)/lib$(for_linking) -$(ROOT)/link_linkdep$(DOTEXE): $(SRC)/link_linkdep.d $(ROOT)/lib$(DOTDLL) $(ROOT)/liblinkdep$(DOTDLL) $(DRUNTIMESO) - $(QUIET)$(DMD) $(DFLAGS) -of$@ $< $(LINKFLAGS) -L$(ROOT)/liblinkdep$(DOTIMPLIB) -L$(ROOT)/lib$(DOTIMPLIB) +$(ROOT)/liblinkdep$(DOTDLL): $(OBJDIR)/lib$(for_linking) lib.d +$(ROOT)/liblinkdep$(DOTDLL): private extra_ldlibs.d += $(abs_root)/lib$(for_linking) -$(ROOT)/load_linkdep$(DOTEXE): $(SRC)/load_linkdep.d $(ROOT)/lib$(DOTDLL) $(ROOT)/liblinkdep$(DOTDLL) $(DRUNTIMESO) - $(QUIET)$(DMD) $(DFLAGS) -of$@ $< $(LINKFLAGS) $(LINKDL) +$(ROOT)/libloaddep$(DOTDLL): private extra_ldlibs.d += $(LINKDL) -$(ROOT)/link_loaddep$(DOTEXE): $(SRC)/link_loaddep.d $(ROOT)/lib$(DOTDLL) $(ROOT)/libloaddep$(DOTDLL) $(DRUNTIMESO) - $(QUIET)$(DMD) $(DFLAGS) -of$@ $< $(LINKFLAGS) -L$(ROOT)/libloaddep$(DOTIMPLIB) +$(ROOT)/link_linkdep$(DOTEXE): $(ROOT)/liblinkdep$(for_linking) liblinkdep.d +$(ROOT)/link_linkdep$(DOTEXE): private extra_ldlibs.d += $(abs_root)/liblinkdep$(for_linking) -$(ROOT)/load_loaddep$(DOTEXE): $(SRC)/load_loaddep.d $(ROOT)/lib$(DOTDLL) $(ROOT)/libloaddep$(DOTDLL) $(DRUNTIMESO) - $(QUIET)$(DMD) $(DFLAGS) -of$@ $< $(LINKFLAGS) $(LINKDL) +# dlopens lib.so through libloaddep +$(ROOT)/link_loaddep.done: $(ROOT)/lib$(DOTDLL) +$(ROOT)/link_loaddep$(DOTEXE): $(ROOT)/libloaddep$(for_linking) libloaddep.d utils.di +$(ROOT)/link_loaddep$(DOTEXE): private extra_ldlibs.d += $(abs_root)/libloaddep$(for_linking) -$(ROOT)/load$(DOTEXE) $(ROOT)/finalize$(DOTEXE): $(ROOT)/%$(DOTEXE): $(SRC)/%.d $(ROOT)/lib$(DOTDLL) $(DRUNTIMESO) - $(QUIET)$(DMD) $(DFLAGS) -of$@ $< $(LINKDL) +# dlopens liblinkdep.so +$(ROOT)/load_linkdep.done: $(ROOT)/liblinkdep$(DOTDLL) +$(ROOT)/load_linkdep$(DOTEXE): utils.di +$(ROOT)/load_linkdep$(DOTEXE): private extra_ldlibs.d += $(LINKDL) -$(ROOT)/load_13414$(DOTEXE): $(ROOT)/%$(DOTEXE): $(SRC)/%.d $(ROOT)/lib_13414$(DOTDLL) $(DRUNTIMESO) - $(QUIET)$(DMD) $(DFLAGS) -of$@ $< $(LINKDL) +# dlopens libloaddep.so and runs code that will dlopen lib.so +$(ROOT)/load_loaddep.done: $(ROOT)/lib$(DOTDLL) $(ROOT)/libloaddep$(DOTDLL) +$(ROOT)/load_loaddep$(DOTEXE): utils.di +$(ROOT)/load_loaddep$(DOTEXE): private extra_ldlibs.d += $(LINKDL) -$(ROOT)/dynamiccast$(DOTEXE): $(SRC)/dynamiccast.d $(ROOT)/dynamiccast$(DOTDLL) $(if $(LINK_SHARED),$(DRUNTIMESO),$(DRUNTIME)) - $(QUIET)$(DMD) $(DFLAGS) -of$@ $(SRC)/dynamiccast.d $(LINKDL) +$(ROOT)/load.done: $(ROOT)/lib$(DOTDLL) +$(ROOT)/load$(DOTEXE): utils.di +$(ROOT)/load$(DOTEXE): private extra_ldlibs.d += $(LINKDL) -$(ROOT)/dynamiccast$(DOTDLL): $(SRC)/dynamiccast.d $(if $(LINK_SHARED),$(DRUNTIMESO),$(DRUNTIME)) - $(QUIET)$(DMD) $(DFLAGS) -of$@ $< -version=DLL -shared $(LINKDL) +$(ROOT)/finalize.done: $(ROOT)/lib$(DOTDLL) +$(ROOT)/finalize$(DOTEXE): utils.di +$(ROOT)/finalize$(DOTEXE): private extra_ldlibs.d += $(LINKDL) + +$(ROOT)/load_13414.done: $(ROOT)/lib_13414$(DOTDLL) +$(ROOT)/load_13414$(DOTEXE): utils.di +$(ROOT)/load_13414$(DOTEXE): private extra_ldlibs.d += $(LINKDL) ifeq (windows,$(OS)) - CC:=cl - CC_OUTFLAG:=/Fe - # additionally specify the .obj output directory to prevent collisions - CC_EXTRAS:=/Fo$(ROOT)/ -else - CC_OUTFLAG:=-o - CC_EXTRAS:=$(LDL) -pthread + CC := cl + OUTPUT_FLAG := /Fe + # we additionally specify the .obj output path (/Fo) to prevent collisions + extra_cflags += /Fo$(OBJDIR)/ endif -$(ROOT)/linkD$(DOTEXE): $(SRC)/linkD.c $(ROOT)/lib$(DOTDLL) $(DRUNTIMESO) - $(QUIET)$(CC) $(CFLAGS) $(CC_OUTFLAG)$@ $< $(ROOT)/lib$(DOTIMPLIB) $(CC_EXTRAS) +# $(LINKDL) == -L-ldl => $(ldl) == -ldl +ldl := $(LINKDL:-L%=%) + +$(ROOT)/linkD$(DOTEXE): $(ROOT)/lib$(for_linking) +$(ROOT)/linkD$(DOTEXE): private extra_ldlibs += $(abs_root)/lib$(for_linking) -$(ROOT)/linkDR$(DOTEXE): $(SRC)/linkDR.c $(ROOT)/lib$(DOTDLL) $(DRUNTIMESO) - $(QUIET)$(CC) $(CFLAGS) $(CC_OUTFLAG)$@ $< $(DRUNTIME_IMPLIB) $(CC_EXTRAS) +$(ROOT)/linkDR.done: $(ROOT)/lib$(DOTDLL) +$(ROOT)/linkDR$(DOTEXE): $(DRUNTIME_DEP) utils.h +$(ROOT)/linkDR$(DOTEXE): private extra_ldlibs += $(ldl) $(druntime_for_linking) +ifneq ($(OS),windows) +$(ROOT)/linkDR$(DOTEXE): private extra_ldflags += -Wl,-rpath,$(druntimeso_dir) +endif -$(ROOT)/loadDR$(DOTEXE): $(SRC)/loadDR.c $(ROOT)/lib$(DOTDLL) $(DRUNTIMESO) - $(QUIET)$(CC) $(CFLAGS) $(CC_OUTFLAG)$@ $< $(CC_EXTRAS) +$(ROOT)/loadDR.done: $(ROOT)/lib$(DOTDLL) $(DRUNTIMESO) +$(ROOT)/loadDR.done: private run_args = $(DRUNTIMESO) +$(ROOT)/loadDR$(DOTEXE): utils.h +$(ROOT)/loadDR$(DOTEXE): private extra_ldlibs += $(ldl) +ifeq ($(IS_MUSL),1) +$(ROOT)/loadDR$(DOTEXE): private extra_ldflags += -Wl,-rpath,$(druntimeso_dir) +endif -$(ROOT)/host$(DOTEXE): $(SRC)/host.c $(ROOT)/plugin1$(DOTDLL) $(ROOT)/plugin2$(DOTDLL) - $(QUIET)$(CC) $(CFLAGS) $(CC_OUTFLAG)$@ $< $(CC_EXTRAS) +$(ROOT)/host.done: $(DRUNTIMESO) $(ROOT)/plugin1$(DOTDLL) $(ROOT)/plugin2$(DOTDLL) +$(ROOT)/host.done: private run_args = $(DRUNTIMESO) +$(ROOT)/host$(DOTEXE): utils.h +$(ROOT)/host$(DOTEXE): private extra_ldlibs += $(ldl) -$(ROOT)/liblinkdep$(DOTDLL): $(ROOT)/lib$(DOTDLL) -$(ROOT)/liblinkdep$(DOTDLL): DFLAGS+=-L$(ROOT)/lib$(DOTIMPLIB) +$(ROOT)/plugin1$(DOTDLL) $(ROOT)/plugin2$(DOTDLL): $(ROOT)/plugin$(DOTDLL) + cp $< $@ -$(ROOT)/plugin1$(DOTDLL) $(ROOT)/plugin2$(DOTDLL): $(SRC)/plugin.d $(DRUNTIMESO) - $(QUIET)$(DMD) -shared $(DFLAGS) -of$@ $< +########## default rule for building a shared library ########## -$(ROOT)/%$(DOTDLL): $(SRC)/%.d $(DRUNTIMESO) - $(QUIET)$(DMD) -shared $(DFLAGS) -of$@ $< $(LINKDL) +$(ROOT)/%$(DOTDLL): %.d $(DMD_DEP) $(DRUNTIME_DEP) + $(LINK.d) -shared $< $(extra_ldlibs.d) $(LDLIBS.d) $(OUTPUT_OPTION.d) -clean: - rm -rf $(ROOT) +$(ROOT)/%$(for_linking): $(ROOT)/%$(DOTDLL) ; diff --git a/runtime/druntime/test/shared/src/finalize.d b/runtime/druntime/test/shared/src/finalize.d index 7a3cfeb7af..15ecc589ba 100644 --- a/runtime/druntime/test/shared/src/finalize.d +++ b/runtime/druntime/test/shared/src/finalize.d @@ -1,6 +1,5 @@ import core.runtime; -import core.stdc.stdio; -import core.stdc.string; +import core.stdc.string : strrchr; import core.thread; void runTest() diff --git a/runtime/druntime/test/shared/src/load.d b/runtime/druntime/test/shared/src/load.d index c49f546f15..a36fcffefc 100644 --- a/runtime/druntime/test/shared/src/load.d +++ b/runtime/druntime/test/shared/src/load.d @@ -1,6 +1,5 @@ import core.runtime; -import core.stdc.stdio; -import core.stdc.string; +import core.stdc.string : strrchr; import core.thread; version (DragonFlyBSD) import core.sys.dragonflybsd.dlfcn : RTLD_NOLOAD; @@ -147,7 +146,7 @@ void main(string[] args) } else { - import core.sys.posix.dlfcn; + import core.sys.posix.dlfcn : dlopen, RTLD_LAZY; assert(dlopen(name.ptr, RTLD_LAZY | RTLD_NOLOAD) is null); } name = name[0 .. $-1]; diff --git a/runtime/druntime/test/shared/src/load_13414.d b/runtime/druntime/test/shared/src/load_13414.d index db2f1eb143..cdd7d9c1ec 100644 --- a/runtime/druntime/test/shared/src/load_13414.d +++ b/runtime/druntime/test/shared/src/load_13414.d @@ -1,6 +1,6 @@ -import core.runtime; import core.atomic; -import core.stdc.string; +import core.runtime; +import core.stdc.string : strrchr; shared uint tlsDtor, dtor; void staticDtorHook() { atomicOp!"+="(tlsDtor, 1); } diff --git a/runtime/druntime/test/shared/src/load_linkdep.d b/runtime/druntime/test/shared/src/load_linkdep.d index ceb31a3cb6..c86f71789a 100644 --- a/runtime/druntime/test/shared/src/load_linkdep.d +++ b/runtime/druntime/test/shared/src/load_linkdep.d @@ -1,5 +1,5 @@ import core.runtime; -import core.stdc.string; +import core.stdc.string : strrchr; extern(C) alias RunDepTests = int function(); diff --git a/runtime/druntime/test/shared/src/load_loaddep.d b/runtime/druntime/test/shared/src/load_loaddep.d index 3410f93142..fb0335faea 100644 --- a/runtime/druntime/test/shared/src/load_loaddep.d +++ b/runtime/druntime/test/shared/src/load_loaddep.d @@ -1,5 +1,5 @@ import core.runtime; -import core.stdc.string; +import core.stdc.string : strrchr; extern(C) alias RunDepTests = int function(const char*); diff --git a/runtime/druntime/test/stdcpp/GNUmakefile.posix b/runtime/druntime/test/stdcpp/GNUmakefile.posix new file mode 100644 index 0000000000..76a4e4eb10 --- /dev/null +++ b/runtime/druntime/test/stdcpp/GNUmakefile.posix @@ -0,0 +1,84 @@ +ifdef IN_LDC +# need OS for the conditions below +include ../../../../dmd/osmodel.mak +endif + +hascpp17 != echo wow | $(CXX) -std=c++17 -E -xc++ - > /dev/null 2>&1 && echo yes + +TESTS98:=allocator new utility +TESTS11:=array +TESTS17:=string_view +TESTSOLDABI:= + +ifeq (osx,$(OS)) + TESTS11+=memory +# TESTS98+=string +# TESTS98+=vector +endif +ifeq (linux,$(OS)) + TESTS11+=exception typeinfo + TESTS98+=typeinfo +# TESTS98+=string +# TESTS98+=vector + TESTSOLDABI+=string +endif +ifeq (freebsd,$(OS)) + TESTS11+=memory + TESTS98+=string +# TESTS98+=vector +endif + +# some build machines have ancient compilers, so we need to disable C++17 tests +ifneq (yes,$(hascpp17)) + TESTS17:= +endif + +TESTS := $(TESTS98:=_98) $(TESTS11:=_11) $(TESTS17:=_17) $(TESTSOLDABI:=_oldabi) + +include ../common.mak + +# -L-lm -L-lpthread => -lm -lpthread +d_platform_libs_cc_form := $(d_platform_libs:-L%=%) + +$(OBJDIR)/%_98_d$(DOTOBJ): %_test.d $(DMD_DEP) + $(COMPILE.d) $(OUTPUT_OPTION.d) $< $(extra_sources) +$(OBJDIR)/%_98_d$(DOTOBJ): private extra_dflags += -extern-std=c++98 + +$(OBJDIR)/%_11_d$(DOTOBJ): %_test.d $(DMD_DEP) + $(COMPILE.d) $(OUTPUT_OPTION.d) $< $(extra_sources) +$(OBJDIR)/%_11_d$(DOTOBJ): private extra_dflags += -extern-std=c++11 + +$(OBJDIR)/%_17_d$(DOTOBJ): %_test.d $(DMD_DEP) + $(COMPILE.d) $(OUTPUT_OPTION.d) $< $(extra_sources) +$(OBJDIR)/%_17_d$(DOTOBJ): private extra_dflags += -extern-std=c++17 + +$(OBJDIR)/%_oldabi_d$(DOTOBJ): %_test.d $(DMD_DEP) + $(COMPILE.d) $(OUTPUT_OPTION.d) $< $(extra_sources) +$(OBJDIR)/%_oldabi_d$(DOTOBJ): private extra_dflags += -version=_GLIBCXX_USE_CXX98_ABI + +$(OBJDIR)/%_d$(DOTOBJ): private extra_dflags += -main -unittest -version=CoreUnittest + +$(OBJDIR)/%_98$(DOTEXE): %.cpp $(OBJDIR)/%_98_d$(DOTOBJ) $(DRUNTIME_DEP) + $(LINK.cpp) $< $(OBJDIR)/$*_98_d$(DOTOBJ) $(extra_sources) $(extra_ldlibs) $(LDLIBS) $(OUTPUT_OPTION) +$(OBJDIR)/%_98$(DOTEXE): private extra_cxxflags += -std=c++98 + +$(OBJDIR)/%_11$(DOTEXE): %.cpp $(OBJDIR)/%_11_d$(DOTOBJ) $(DRUNTIME_DEP) + $(LINK.cpp) $< $(OBJDIR)/$*_11_d$(DOTOBJ) $(extra_sources) $(extra_ldlibs) $(LDLIBS) $(OUTPUT_OPTION) +$(OBJDIR)/%_11$(DOTEXE): private extra_cxxflags += -std=c++11 + +$(OBJDIR)/%_17$(DOTEXE): %.cpp $(OBJDIR)/%_17_d$(DOTOBJ) $(DRUNTIME_DEP) + $(LINK.cpp) $< $(OBJDIR)/$*_17_d$(DOTOBJ) $(extra_sources) $(extra_ldlibs) $(LDLIBS) $(OUTPUT_OPTION) +$(OBJDIR)/%_17$(DOTEXE): private extra_cxxflags += -std=c++17 + +$(OBJDIR)/%_oldabi$(DOTEXE): %.cpp $(OBJDIR)/%_oldabi_d$(DOTOBJ) $(DRUNTIME_DEP) + $(LINK.cpp) $< $(OBJDIR)/$*_oldabi_d$(DOTOBJ) $(extra_sources) $(extra_ldlibs) $(LDLIBS) $(OUTPUT_OPTION) +$(OBJDIR)/%_oldabi$(DOTEXE): private extra_cppflags += -D_GLIBCXX_USE_CXX11_ABI=0 + +$(OBJDIR)/%$(DOTEXE): private extra_ldlibs += $(druntime_for_linking) $(d_platform_libs_cc_form) +$(OBJDIR)/%$(DOTEXE): private extra_ldflags += -Wl,-rpath,$(druntimeso_dir) + +short_test_names = 98 11 17 oldabi +.NOTINTERMEDIATE: \ + $(short_test_names:%=$(OBJDIR)/\%_%_d$(DOTOBJ)) \ + $(short_test_names:%=$(OBJDIR)/\%_%$(DOTEXE)) +# .NOTINTERMEDIATE: $(OBJDIR)/%_98_d$(DOTOBJ) $(OBJDIR)/T_98$(DOTEXE) ... diff --git a/runtime/druntime/test/stdcpp/GNUmakefile.windows b/runtime/druntime/test/stdcpp/GNUmakefile.windows new file mode 100644 index 0000000000..db7612dce5 --- /dev/null +++ b/runtime/druntime/test/stdcpp/GNUmakefile.windows @@ -0,0 +1,55 @@ +TESTS:=allocator array memory new string utility vector + +CC := cl + +msc_ver:=$(strip $(shell $(CC) /nologo /EP msc_ver.c)) +ifeq (1,$(intcmp $(msc_ver),1900,0,1,1)) + extra_cxxflags += /std:c++17 + extra_dflags += -extern-std=c++17 + TESTS+=string_view +endif + +TESTS := $(TESTS:=_mt) $(TESTS:=_md) $(TESTS:=_mtd) $(TESTS:=_mdd) + +include ../common.mak + +CXX := cl +OUTPUT_FLAG := /Fo +extra_cxxflags += /nologo /EHsc +extra_dflags += -main -unittest -version=CoreUnittest -version=_MSC_VER_$(msc_ver) + +$(OBJDIR)/%_mt_cpp$(DOTOBJ): %.cpp | $(OBJDIR) + $(COMPILE.cpp) $(OUTPUT_OPTION) $< $(extra_sources) +$(OBJDIR)/%_mt_cpp$(DOTOBJ): private extra_cxxflags += /MT +$(OBJDIR)/%_md_cpp$(DOTOBJ): %.cpp | $(OBJDIR) + $(COMPILE.cpp) $(OUTPUT_OPTION) $< $(extra_sources) +$(OBJDIR)/%_md_cpp$(DOTOBJ): private extra_cxxflags += /MD +$(OBJDIR)/%_mtd_cpp$(DOTOBJ): %.cpp | $(OBJDIR) + $(COMPILE.cpp) $(OUTPUT_OPTION) $< $(extra_sources) +$(OBJDIR)/%_mtd_cpp$(DOTOBJ): private extra_cxxflags += /MTd +$(OBJDIR)/%_mdd_cpp$(DOTOBJ): %.cpp | $(OBJDIR) + $(COMPILE.cpp) $(OUTPUT_OPTION) $< $(extra_sources) +$(OBJDIR)/%_mdd_cpp$(DOTOBJ): private extra_cxxflags += /MDd + +# Change the PDB file output to avoid collisions: +# fatal error C1041: cannot open program database '...\test\stdcpp\vc140.pdb'; if multiple CL.EXE write to the same .PDB file, please use /FS. +$(OBJDIR)/%_cpp$(DOTOBJ): private extra_cxxflags += /Fd$(@:$(DOTOBJ)=) + +$(OBJDIR)/%_mt$(DOTEXE): %_test.d $(OBJDIR)/%_mt_cpp$(DOTOBJ) $(DMD_DEP) $(DRUNTIME_DEP) + $(LINK.d) $< $(OBJDIR)/$*_mt_cpp$(DOTOBJ) $(extra_sources) $(extra_ldlibs.d) $(LDLIBS.d) $(OUTPUT_OPTION.d) +$(OBJDIR)/%_mt$(DOTEXE): private extra_ldflags.d += -mscrtlib=libcmt +$(OBJDIR)/%_md$(DOTEXE): %_test.d $(OBJDIR)/%_md_cpp$(DOTOBJ) $(DMD_DEP) $(DRUNTIME_DEP) + $(LINK.d) $< $(OBJDIR)/$*_md_cpp$(DOTOBJ) $(extra_sources) $(extra_ldlibs.d) $(LDLIBS.d) $(OUTPUT_OPTION.d) +$(OBJDIR)/%_md$(DOTEXE): private extra_ldflags.d += -mscrtlib=msvcrt +$(OBJDIR)/%_mtd$(DOTEXE): %_test.d $(OBJDIR)/%_mtd_cpp$(DOTOBJ) $(DMD_DEP) $(DRUNTIME_DEP) + $(LINK.d) $< $(OBJDIR)/$*_mtd_cpp$(DOTOBJ) $(extra_sources) $(extra_ldlibs.d) $(LDLIBS.d) $(OUTPUT_OPTION.d) +$(OBJDIR)/%_mtd$(DOTEXE): private extra_ldflags.d += -mscrtlib=libcmtd +$(OBJDIR)/%_mdd$(DOTEXE): %_test.d $(OBJDIR)/%_mdd_cpp$(DOTOBJ) $(DMD_DEP) $(DRUNTIME_DEP) + $(LINK.d) $< $(OBJDIR)/$*_mdd_cpp$(DOTOBJ) $(extra_sources) $(extra_ldlibs.d) $(LDLIBS.d) $(OUTPUT_OPTION.d) +$(OBJDIR)/%_mdd$(DOTEXE): private extra_ldflags.d += -mscrtlib=msvcrtd + +short_test_names = mt md mtd mdd +.NOTINTERMEDIATE: \ + $(short_test_names:%=$(OBJDIR)/\%_%_cpp$(DOTOBJ)) \ + $(short_test_names:%=$(OBJDIR)/\%_%$(DOTEXE)) +# .NOTINTERMEDIATE: $(OBJDIR)/%_mt_cpp$(DOTOBJ) $(OBJDIR)/%_$(DOTEXE) ... diff --git a/runtime/druntime/test/stdcpp/Makefile b/runtime/druntime/test/stdcpp/Makefile index 3d55bf54a2..295b40f9b3 100644 --- a/runtime/druntime/test/stdcpp/Makefile +++ b/runtime/druntime/test/stdcpp/Makefile @@ -1,121 +1 @@ -include ../common.mak - -.PHONY: all clean - -ifeq (windows,$(OS)) - -CXX:=cl -EXTRA_CXXFLAGS:=/nologo /EHsc -EXTRA_DFLAGS:= - -TESTS:=allocator array memory new string utility vector - -MSC_VER:=$(strip $(shell $(CC) /nologo /EP msc_ver.c)) -ifeq ($(shell test $(MSC_VER) -gt 1900; echo $$?),0) - EXTRA_CXXFLAGS+=/std:c++17 - EXTRA_DFLAGS+=-extern-std=c++17 - TESTS+=string_view -endif - -all: $(addprefix $(ROOT)/,$(TESTS)) - -$(ROOT)/%: $(SRC)/%.cpp $(SRC)/%_test.d - @echo Testing $* - @mkdir -p $(dir $@) - - $(QUIET)$(CXX) /MT $(CXXFLAGS_BASE) $(EXTRA_CXXFLAGS) -c /Fo$@_cpp$(DOTOBJ) $< - $(QUIET)$(DMD) -mscrtlib=libcmt $(DFLAGS) $(EXTRA_DFLAGS) -main -unittest -version=CoreUnittest -version=_MSC_VER_$(MSC_VER) -of$@$(DOTEXE) $@_cpp$(DOTOBJ) $(SRC)/$*_test.d - $(QUIET)$(TIMELIMIT)$@ $(RUN_ARGS) - - $(QUIET)$(CXX) /MD $(CXXFLAGS_BASE) $(EXTRA_CXXFLAGS) -c /Fo$@_cpp$(DOTOBJ) $< - $(QUIET)$(DMD) -mscrtlib=msvcrt $(DFLAGS) $(EXTRA_DFLAGS) -main -unittest -version=CoreUnittest -version=_MSC_VER_$(MSC_VER) -of$@$(DOTEXE) $@_cpp$(DOTOBJ) $(SRC)/$*_test.d - $(QUIET)$(TIMELIMIT)$@ $(RUN_ARGS) - - $(QUIET)$(CXX) /MTd $(CXXFLAGS_BASE) $(EXTRA_CXXFLAGS) -c /Fo$@_cpp$(DOTOBJ) $< - $(QUIET)$(DMD) -mscrtlib=libcmtd $(DFLAGS) $(EXTRA_DFLAGS) -main -unittest -version=CoreUnittest -version=_MSC_VER_$(MSC_VER) -of$@$(DOTEXE) $@_cpp$(DOTOBJ) $(SRC)/$*_test.d - $(QUIET)$(TIMELIMIT)$@ $(RUN_ARGS) - - $(QUIET)$(CXX) /MDd $(CXXFLAGS_BASE) $(EXTRA_CXXFLAGS) -c /Fo$@_cpp$(DOTOBJ) $< - $(QUIET)$(DMD) -mscrtlib=msvcrtd $(DFLAGS) $(EXTRA_DFLAGS) -main -unittest -version=CoreUnittest -version=_MSC_VER_$(MSC_VER) -of$@$(DOTEXE) $@_cpp$(DOTOBJ) $(SRC)/$*_test.d - $(QUIET)$(TIMELIMIT)$@ $(RUN_ARGS) - -else # Posix: - -HASCPP17:=`echo wow | $(CXX) -std=c++17 -E -xc++ - > /dev/null 2>&1 && echo yes` - -TESTS:=allocator new utility -TESTS11:=array -TESTS17:=string_view -OLDABITESTS:= - -ifeq (osx,$(OS)) - TESTS11+=memory -# TESTS+=string -# TESTS+=vector -endif -ifeq (linux,$(OS)) - TESTS11+=exception typeinfo - TESTS+=typeinfo -# TESTS+=string -# TESTS+=vector - OLDABITESTS+=string -endif -ifeq (freebsd,$(OS)) - TESTS11+=memory - TESTS+=string -# TESTS+=vector -endif - -# some build machines have ancient compilers, so we need to disable C++17 tests -ifneq (yes,$(HASCPP17)) - TESTS17:= -endif - -all: $(addprefix $(ROOT)/,$(addsuffix .done,$(TESTS))) $(addprefix $(ROOT)/,$(addsuffix _11.done,$(TESTS11))) $(addprefix $(ROOT)/,$(addsuffix _17.done,$(TESTS17))) $(addprefix $(ROOT)/,$(addsuffix _old.done,$(OLDABITESTS))) - -# run C++98 tests -$(ROOT)/%.done: $(ROOT)/%$(DOTEXE) - @echo Testing $* - $(QUIET)$(TIMELIMIT)$(ROOT)/$* $(RUN_ARGS) - @touch $@ -# run C++11 tests -$(ROOT)/%_11.done: $(ROOT)/%_11$(DOTEXE) - @echo Testing $*_11 - $(QUIET)$(TIMELIMIT)$(ROOT)/$*_11 $(RUN_ARGS) - @touch $@ -# run C++17 tests -$(ROOT)/%_17.done: $(ROOT)/%_17$(DOTEXE) - @echo Testing $*_17 - $(QUIET)$(TIMELIMIT)$(ROOT)/$*_17 $(RUN_ARGS) - @touch $@ -# run libstdc++ _GLIBCXX_USE_CXX11_ABI=0 tests -$(ROOT)/%_old.done: $(ROOT)/%_old$(DOTEXE) - @echo Testing $*_old - $(QUIET)$(TIMELIMIT)$(ROOT)/$*_old $(RUN_ARGS) - @touch $@ - -# build C++98 tests -$(ROOT)/%$(DOTEXE): $(SRC)/%.cpp $(SRC)/%_test.d - @mkdir -p $(dir $@) - $(QUIET)$(DMD) $(DFLAGS) -extern-std=c++98 -main -unittest -version=CoreUnittest -c -of=$(ROOT)/$*_d$(DOTOBJ) $(SRC)/$*_test.d - $(QUIET)$(CXX) $(CXXFLAGS_BASE) -std=c++98 -o $@ $< $(ROOT)/$*_d$(DOTOBJ) $(DRUNTIME) -lpthread $(LDL) -# build C++11 tests -$(ROOT)/%_11$(DOTEXE): $(SRC)/%.cpp $(SRC)/%_test.d - @mkdir -p $(dir $@) - $(QUIET)$(DMD) $(DFLAGS) -extern-std=c++11 -main -unittest -version=CoreUnittest -c -of=$(ROOT)/$*_11_d$(DOTOBJ) $(SRC)/$*_test.d - $(QUIET)$(CXX) $(CXXFLAGS_BASE) -std=c++11 -o $@ $< $(ROOT)/$*_11_d$(DOTOBJ) $(DRUNTIME) -lpthread $(LDL) -# build C++17 tests -$(ROOT)/%_17$(DOTEXE): $(SRC)/%.cpp $(SRC)/%_test.d - @mkdir -p $(dir $@) - $(QUIET)$(DMD) $(DFLAGS) -extern-std=c++17 -main -unittest -version=CoreUnittest -c -of=$(ROOT)/$*_17_d$(DOTOBJ) $(SRC)/$*_test.d - $(QUIET)$(CXX) $(CXXFLAGS_BASE) -std=c++17 -o $@ $< $(ROOT)/$*_17_d$(DOTOBJ) $(DRUNTIME) -lpthread $(LDL) -# build libstdc++ _GLIBCXX_USE_CXX11_ABI=0 tests -$(ROOT)/%_old$(DOTEXE): $(SRC)/%.cpp $(SRC)/%_test.d - @mkdir -p $(dir $@) - $(QUIET)$(DMD) $(DFLAGS) -version=_GLIBCXX_USE_CXX98_ABI -main -unittest -version=CoreUnittest -c -of=$(ROOT)/$*_old_d$(DOTOBJ) $(SRC)/$*_test.d - $(QUIET)$(CXX) $(CXXFLAGS_BASE) -D_GLIBCXX_USE_CXX11_ABI=0 -o $@ $< $(ROOT)/$*_old_d$(DOTOBJ) $(DRUNTIME) -lpthread $(LDL) - -endif # end Posix - -clean: - rm -rf $(ROOT) +include GNUmakefile.$(if $(filter windows,$(OS)),windows,posix) diff --git a/runtime/druntime/test/stdcpp/src/array.cpp b/runtime/druntime/test/stdcpp/src/array.cpp index e9c31be97e..c7053a41f5 100644 --- a/runtime/druntime/test/stdcpp/src/array.cpp +++ b/runtime/druntime/test/stdcpp/src/array.cpp @@ -14,7 +14,10 @@ std::array& sumOfElements_ref(std::array& arr) std::array sumOfElements_val(std::array arr) { - int r = sumOfElements_ref(arr)[0] + fromC_ref(arr)[0] + fromC_val(arr)[0]; + int r = 0; + r += sumOfElements_ref(arr)[0]; + r += fromC_ref(arr)[0]; + r += fromC_val(arr)[0]; arr.fill(r); return arr; } diff --git a/runtime/druntime/test/stdcpp/src/array_test.d b/runtime/druntime/test/stdcpp/src/array_test.d index d38f02e4c9..824eee6f26 100644 --- a/runtime/druntime/test/stdcpp/src/array_test.d +++ b/runtime/druntime/test/stdcpp/src/array_test.d @@ -1,6 +1,6 @@ import core.stdcpp.array; -extern (C++) int test_array() +unittest { array!(int, 5) arr; arr[] = [0, 2, 3, 4, 5]; @@ -12,7 +12,9 @@ extern (C++) int test_array() assert(arr.empty == false); assert(arr.front == 1); - assert(sumOfElements_val(arr)[0] == 160); + assert(arrayMethodsTests(arr)[0] == 10); + + assert(sumOfElements_val(arr)[0] == 465); assert(sumOfElements_ref(arr)[0] == 15); array!(int, 0) arr2; @@ -21,8 +23,24 @@ extern (C++) int test_array() assert(arr2.max_size == 0); assert(arr2.empty == true); assert(arr2[] == []); +} + +array!(int, 5) arrayMethodsTests(array!(int, 5) arr) { + assert(arr[] == [1, 2, 3, 4, 5]); + assert(arr.front == 1); + assert(arr.back == 5); + assert(arr.at(2) == 3); - return 0; + arr.fill(2); + + int r; + foreach (e; arr) + r += e; + + assert(r == 10); + + arr[] = r; + return arr; } @@ -35,19 +53,9 @@ ref array!(int, 5) sumOfElements_ref(return ref array!(int, 5) arr); // test the ABI for calls from C++ array!(int, 5) fromC_val(array!(int, 5) arr) { - assert(arr[] == [1, 2, 3, 4, 5]); - assert(arr.front == 1); - assert(arr.back == 5); - assert(arr.at(2) == 3); - - arr.fill(2); - int r; foreach (e; arr) r += e; - - assert(r == 10); - arr[] = r; return arr; } diff --git a/runtime/druntime/test/thread/Makefile b/runtime/druntime/test/thread/Makefile index fbdefffa7c..07fb08617c 100644 --- a/runtime/druntime/test/thread/Makefile +++ b/runtime/druntime/test/thread/Makefile @@ -1,46 +1,21 @@ -include ../common.mak +ifdef IN_LDC +# need OS for the conditions below +include ../../../../dmd/osmodel.mak +endif -TESTS:=tlsgc_sections test_import tlsstack join_detach +TESTS := tlsgc_sections test_import tlsstack +# join_detach is currently disabled +#TESTS += join_detach # some .d files support Posix only ifneq ($(OS),windows) - TEST+=fiber_guard_page external_threads + TESTS += fiber_guard_page external_threads endif -.PHONY: all clean -all: $(addprefix $(ROOT)/,$(addsuffix .done,$(TESTS))) +include ../common.mak # segfault || bus error (OSX) $(ROOT)/fiber_guard_page.done: $(ROOT)/%.done : $(ROOT)/%$(DOTEXE) @echo Testing $* - $(QUIET)$(TIMELIMIT)$(ROOT)/$* $(RUN_ARGS); rc=$$?; [ $$rc -eq 139 ] || [ $$rc -eq 138 ] - @touch $@ - -$(ROOT)/external_threads.done: $(ROOT)/%.done : $(ROOT)/%$(DOTEXE) - @echo Testing $* - $(QUIET)$(TIMELIMIT)$(ROOT)/$* - @touch $@ - -$(ROOT)/tlsgc_sections.done: $(ROOT)/%.done : $(ROOT)/%$(DOTEXE) - @echo Testing $* - $(QUIET)$(TIMELIMIT)$(ROOT)/$* + $(TIMELIMIT)$<; rc=$$?; [ $$rc -eq 139 ] || [ $$rc -eq 138 ] @touch $@ - -$(ROOT)/test_import.done: $(ROOT)/%.done : $(ROOT)/%$(DOTEXE) - @echo Testing $* - $(QUIET)$(TIMELIMIT)$(ROOT)/$* - @touch $@ - -$(ROOT)/tlsstack.done: $(ROOT)/%.done : $(ROOT)/%$(DOTEXE) - @echo Testing $* - $(QUIET)$(TIMELIMIT)$(ROOT)/$* - @touch $@ - -$(ROOT)/join_detach.done: $(ROOT)/%.done : $(ROOT)/%$(DOTEXE) - @echo Testing $* is currently disabled! - -$(ROOT)/%$(DOTEXE): $(SRC)/%.d - $(QUIET)$(DMD) $(DFLAGS) -of$@ $< - -clean: - rm -rf $(ROOT) diff --git a/runtime/druntime/test/thread/src/external_threads.d b/runtime/druntime/test/thread/src/external_threads.d index 9c98a3fa13..a59fc76dbb 100644 --- a/runtime/druntime/test/thread/src/external_threads.d +++ b/runtime/druntime/test/thread/src/external_threads.d @@ -1,5 +1,6 @@ -import core.sys.posix.pthread; import core.memory; +import core.sys.posix.pthread : pthread_create, pthread_join; +import core.sys.posix.sys.types : pthread_t; import core.thread; extern (C) void rt_moduleTlsCtor(); diff --git a/runtime/druntime/test/thread/src/fiber_guard_page.d b/runtime/druntime/test/thread/src/fiber_guard_page.d index f19ddae7c3..6527a590cc 100644 --- a/runtime/druntime/test/thread/src/fiber_guard_page.d +++ b/runtime/druntime/test/thread/src/fiber_guard_page.d @@ -1,5 +1,5 @@ +import core.sys.posix.sys.mman : MAP_ANON, MAP_PRIVATE, mmap, PROT_READ, PROT_WRITE; import core.thread; -import core.sys.posix.sys.mman; version (LDC) import ldc.attributes; else struct optStrategy { string a; } diff --git a/runtime/druntime/test/thread/src/tlsgc_sections.d b/runtime/druntime/test/thread/src/tlsgc_sections.d index 1bd3f26cff..b0807ec6ff 100644 --- a/runtime/druntime/test/thread/src/tlsgc_sections.d +++ b/runtime/druntime/test/thread/src/tlsgc_sections.d @@ -11,7 +11,7 @@ class C { ~this() { - import core.stdc.stdlib; + import core.stdc.stdlib : abort; abort(); // this gets triggered although the instance always stays referenced } } diff --git a/runtime/druntime/test/thread/src/tlsstack.d b/runtime/druntime/test/thread/src/tlsstack.d index dbd93213bf..7b76f22fa9 100644 --- a/runtime/druntime/test/thread/src/tlsstack.d +++ b/runtime/druntime/test/thread/src/tlsstack.d @@ -1,6 +1,6 @@ module core.thread.test; // needs access to getStackTop()/getStackBottom() -import core.stdc.stdio; +import core.stdc.stdio : printf; import core.thread; ubyte[16384] data; diff --git a/runtime/druntime/test/traits/Makefile b/runtime/druntime/test/traits/Makefile index 610f7d5319..3f836a3adf 100644 --- a/runtime/druntime/test/traits/Makefile +++ b/runtime/druntime/test/traits/Makefile @@ -1,17 +1,3 @@ -include ../common.mak - -TESTS:=all_satisfy - -.PHONY: all clean -all: $(addprefix $(ROOT)/,$(addsuffix .done,$(TESTS))) +TESTS := all_satisfy -$(ROOT)/%.done: $(ROOT)/%$(DOTEXE) - @echo Testing $* - $(QUIET)$(TIMELIMIT)$(ROOT)/$* $(RUN_ARGS) - @touch $@ - -$(ROOT)/%$(DOTEXE): $(SRC)/%.d - $(QUIET)$(DMD) $(DFLAGS) -of$@ $< - -clean: - rm -rf $(ROOT) +include ../common.mak diff --git a/runtime/druntime/test/typeinfo/Makefile b/runtime/druntime/test/typeinfo/Makefile index 338f19c810..b3dc1d9c78 100644 --- a/runtime/druntime/test/typeinfo/Makefile +++ b/runtime/druntime/test/typeinfo/Makefile @@ -1,17 +1,3 @@ -include ../common.mak - -TESTS:=comparison isbaseof enum_ - -.PHONY: all clean -all: $(addprefix $(ROOT)/,$(addsuffix .done,$(TESTS))) +TESTS := comparison isbaseof enum_ -$(ROOT)/%.done: $(ROOT)/%$(DOTEXE) - @echo Testing $* - $(QUIET)$(TIMELIMIT)$(ROOT)/$* $(RUN_ARGS) - @touch $@ - -$(ROOT)/%$(DOTEXE): $(SRC)/%.d - $(QUIET)$(DMD) $(DFLAGS) -of$@ $< - -clean: - rm -rf $(ROOT) +include ../common.mak diff --git a/runtime/druntime/test/unittest/Makefile b/runtime/druntime/test/unittest/Makefile index bb4551ff3f..3914f721bc 100644 --- a/runtime/druntime/test/unittest/Makefile +++ b/runtime/druntime/test/unittest/Makefile @@ -1,45 +1,43 @@ -include ../common.mak - -TESTS:=good goodn bad badn na - -DIFF:=diff -SED:=sed - -.PHONY: all clean -all: $(addprefix $(ROOT)/,$(addsuffix .done,$(TESTS))) - -$(ROOT)/good.done: HASMAIN=0 -$(ROOT)/goodn.done: HASMAIN=0 -$(ROOT)/bad.done: HASMAIN=0 -$(ROOT)/badn.done: HASMAIN=0 -$(ROOT)/na.done: HASMAIN=1 +TESTS := good goodn bad badn na -$(ROOT)/good.done: RETCODE=0 -$(ROOT)/goodn.done: RETCODE=0 -$(ROOT)/bad.done: RETCODE=1 -$(ROOT)/badn.done: RETCODE=1 -$(ROOT)/na.done: RETCODE=0 - -$(ROOT)/good.done: VER=PassNoPrintout -$(ROOT)/goodn.done: VER=GoodTests -$(ROOT)/bad.done: VER=FailNoPrintout -$(ROOT)/badn.done: VER=FailedTests -$(ROOT)/na.done: VER=NoTests - -$(ROOT)/good.done: TESTTEXT= -$(ROOT)/goodn.done: TESTTEXT=passed -$(ROOT)/bad.done: TESTTEXT= -$(ROOT)/badn.done: TESTTEXT=FAILED -$(ROOT)/na.done: TESTTEXT= +include ../common.mak -$(ROOT)/%.done: customhandler.d +$(ROOT)/good.done: hasmain=0 +$(ROOT)/goodn.done: hasmain=0 +$(ROOT)/bad.done: hasmain=0 +$(ROOT)/badn.done: hasmain=0 +$(ROOT)/na.done: hasmain=1 + +$(ROOT)/good.done: retcode=0 +$(ROOT)/goodn.done: retcode=0 +$(ROOT)/bad.done: retcode=1 +$(ROOT)/badn.done: retcode=1 +$(ROOT)/na.done: retcode=0 + +$(ROOT)/good.done: testtext= +$(ROOT)/goodn.done: testtext=passed +$(ROOT)/bad.done: testtext= +$(ROOT)/badn.done: testtext=FAILED +$(ROOT)/na.done: testtext= + +$(ROOT)/good$(DOTEXE): private extra_dflags += -version=PassNoPrintout +$(ROOT)/goodn$(DOTEXE): private extra_dflags += -version=GoodTests +$(ROOT)/bad$(DOTEXE): private extra_dflags += -version=FailNoPrintout +$(ROOT)/badn$(DOTEXE): private extra_dflags += -version=FailedTests +$(ROOT)/na$(DOTEXE): private extra_dflags += -version=NoTests + +# $(OBJDIR)/good.done $(OBJDIR)/bad.done [...]: $(OBJDIR)/%.done: $(OBJDIR)/%$(DOTEXE): +# +# Forces the tests to be matched by this rule instead of being matched +# by the `$(OBJDIR)/%$(DOTEXE)` (`$(OBJDIR)/%` on posix) rule at the +# end: +$(TESTS:%=$(OBJDIR)/%.done): $(OBJDIR)/%.done: $(OBJDIR)/%$(DOTEXE) @echo Testing $* - $(QUIET)$(DMD) $(DFLAGS) -of$(ROOT)/tester_$(patsubst %.done,%, $(notdir $@))$(DOTEXE) customhandler.d -version=$(VER) - $(QUIET)$(TIMELIMIT)$(ROOT)/tester_$(patsubst %.done,%, $(notdir $@)) > $@ 2>&1; test $$? -eq $(RETCODE) - $(QUIET)test $(HASMAIN) -eq 0 || grep -q main $@ - $(QUIET)test $(HASMAIN) -eq 1 || ! grep -q main $@ - $(QUIET)test -z "$(TESTTEXT)" || grep -q "$(TESTTEXT) unittests" $@ - $(QUIET)test -n "$(TESTTEXT)" || ! grep -q "unittests" $@ - -clean: - rm -rf $(ROOT) + $(TIMELIMIT)$< > $@ 2>&1; test $$? -eq $(retcode) + test $(hasmain) -eq 0 || grep -q main $@ + test $(hasmain) -eq 1 || ! grep -q main $@ + test -z "$(testtext)" || grep -q "$(testtext) unittests" $@ + test -n "$(testtext)" || ! grep -q "unittests" $@ + +$(OBJDIR)/%$(DOTEXE): customhandler.d + $(LINK.d) $< $(extra_sources) $(extra_ldlibs.d) $(LDLIBS.d) $(OUTPUT_OPTION.d) diff --git a/runtime/druntime/test/unittest/customhandler.d b/runtime/druntime/test/unittest/customhandler.d index f5a04350d9..2beeb78ad8 100644 --- a/runtime/druntime/test/unittest/customhandler.d +++ b/runtime/druntime/test/unittest/customhandler.d @@ -16,6 +16,6 @@ shared static this() void main() { - import core.stdc.stdio; + import core.stdc.stdio : fprintf, stderr; fprintf(stderr, "main\n"); } diff --git a/runtime/druntime/test/uuid/Makefile b/runtime/druntime/test/uuid/Makefile index 0c52b8f273..c3cdf1dd67 100644 --- a/runtime/druntime/test/uuid/Makefile +++ b/runtime/druntime/test/uuid/Makefile @@ -1,10 +1,5 @@ -include ../common.mak - -.PHONY: all clean -all: $(ROOT)/test$(DOTEXE) +TESTS := test -$(ROOT)/%$(DOTEXE): %.d - $(QUIET)$(DMD) $(DFLAGS) -of$@ $< uuid.lib +include ../common.mak -clean: - rm -rf $(ROOT) +$(OBJDIR)/test$(DOTEXE): private extra_ldlibs.d += uuid.lib diff --git a/runtime/druntime/test/valgrind/Makefile b/runtime/druntime/test/valgrind/Makefile index 47f1792a98..7ee1ad8caa 100644 --- a/runtime/druntime/test/valgrind/Makefile +++ b/runtime/druntime/test/valgrind/Makefile @@ -1,35 +1,25 @@ +VALGRIND = valgrind +has_valgrind != command -v $(VALGRIND) > /dev/null 2>&1 && echo 1 + +ifeq ($(has_valgrind),1) +TESTS := ok_append no_use_after_free no_oob no_oob_sentinel no_use_after_gc +endif + include ../common.mak -TESTS:=ok_append no_use_after_free no_oob no_oob_sentinel no_use_after_gc +$(OBJDIR)/ok_%.done: private negate := +$(OBJDIR)/no_%.done: private negate := ! -GC_SRC:= \ +$(OBJDIR)/%.done: $(OBJDIR)/%$(DOTEXE) + @echo Testing $< + $(negate) $(TIMELIMIT)$(VALGRIND) --quiet --tool=memcheck --error-exitcode=8 $< + @touch $@ + +gc_src := \ ../../src/core/internal/gc/impl/conservative/gc.d \ ../../src/etc/valgrind/valgrind.d \ ../../src/rt/lifetime.d -.PHONY: all clean -all: $(addprefix $(ROOT)/,$(addsuffix .done,$(TESTS))) - -$(ROOT)/ok_%.done: $(ROOT)/ok_%$(DOTEXE) - @echo Testing ok_$* - $(QUIET)if ! command -v valgrind >/dev/null; then \ - echo valgrind not installed, skipping; \ - else \ - $(TIMELIMIT)valgrind --quiet --tool=memcheck --error-exitcode=8 $(ROOT)/ok_$* $(RUN_ARGS); \ - fi - $(QUIET)touch $@ - -$(ROOT)/no_%.done: $(ROOT)/no_%$(DOTEXE) - @echo Testing no_$* - $(QUIET)if ! command -v valgrind >/dev/null; then \ - echo valgrind not installed, skipping; \ - else \ - ( ! $(TIMELIMIT)valgrind --quiet --tool=memcheck --error-exitcode=8 $(ROOT)/no_$* $(RUN_ARGS); ) \ - fi - $(QUIET)touch $@ - -$(ROOT)/%$(DOTEXE): $(SRC)/%.d $(GC_SRC) - $(QUIET)$(DMD) -debug=VALGRIND -debug=SENTINEL $(DFLAGS) -of$@ $< $(GC_SRC) - -clean: - rm -rf $(ROOT) +$(OBJDIR)/%$(DOTEXE): $(gc_src) +$(OBJDIR)/%$(DOTEXE): private extra_sources += $(gc_src) +$(OBJDIR)/%$(DOTEXE): private extra_dflags += -debug=VALGRIND -debug=SENTINEL diff --git a/runtime/phobos b/runtime/phobos index 29e889e658..39f1725829 160000 --- a/runtime/phobos +++ b/runtime/phobos @@ -1 +1 @@ -Subproject commit 29e889e658424eb629fec8bb3bc664e88a1bb16d +Subproject commit 39f17258290770b97ef332d1707ce3739e8104cf diff --git a/tests/codegen/attr_targetoptions.d b/tests/codegen/attr_targetoptions.d index 8e6e21f2e2..07822b753e 100644 --- a/tests/codegen/attr_targetoptions.d +++ b/tests/codegen/attr_targetoptions.d @@ -1,9 +1,5 @@ // Tests that our TargetMachine options are added as function attributes -// RUN: %ldc -c -output-ll -of=%t.ll %s -// RUN: FileCheck %s --check-prefix=COMMON --check-prefix=WITH_FP < %t.ll -// RUN: %ldc -c -output-ll -of=%t.ll %s -O2 -// RUN: FileCheck %s --check-prefix=COMMON --check-prefix=NO_FP < %t.ll // RUN: %ldc -c -output-ll -of=%t.ll %s -O2 -frame-pointer=all // RUN: FileCheck %s --check-prefix=COMMON --check-prefix=WITH_FP < %t.ll // RUN: %ldc -c -output-ll -of=%t.ll %s -frame-pointer=none -mattr=test diff --git a/tests/codegen/exception_stack_trace.d b/tests/codegen/exception_stack_trace.d index 71002d74e9..94f916a80c 100644 --- a/tests/codegen/exception_stack_trace.d +++ b/tests/codegen/exception_stack_trace.d @@ -1,4 +1,4 @@ -// RUN: %ldc -g -frame-pointer=all -link-defaultlib-debug %s -of=%t%exe +// RUN: %ldc -g %s -of=%t%exe // RUN: %t%exe | FileCheck %s void bar() diff --git a/tests/codegen/frame_pointer_x86.d b/tests/codegen/frame_pointer_x86.d index c0450591f7..17cd4f0b3b 100644 --- a/tests/codegen/frame_pointer_x86.d +++ b/tests/codegen/frame_pointer_x86.d @@ -1,12 +1,12 @@ // REQUIRES: target_X86 -// RUN: %ldc -c -mtriple=x86_64 -output-s -of=%t.s %s +// RUN: %ldc -c -mtriple=x86_64-linux-gnu -output-s -of=%t.s %s // RUN: FileCheck %s --check-prefixes=COMMON,FP < %t.s -// RUN: %ldc -c -mtriple=x86_64 -output-s -of=%t.s %s -O2 +// RUN: %ldc -c -mtriple=x86_64-linux-gnu -output-s -of=%t.s %s -O2 // RUN: FileCheck %s --check-prefixes=COMMON,NO_FP < %t.s -// RUN: %ldc -c -mtriple=x86_64 -output-s -of=%t.s %s -O2 -frame-pointer=all +// RUN: %ldc -c -mtriple=x86_64-linux-gnu -output-s -of=%t.s %s -O2 -frame-pointer=all // RUN: FileCheck %s --check-prefixes=COMMON,FP < %t.s -// RUN: %ldc -c -mtriple=x86_64 -output-s -of=%t.s %s -frame-pointer=none +// RUN: %ldc -c -mtriple=x86_64-linux-gnu -output-s -of=%t.s %s -frame-pointer=none // RUN: FileCheck %s --check-prefixes=COMMON,NO_FP < %t.s // COMMON-LABEL: _D17frame_pointer_x8613inlineAsmLeafFZv: diff --git a/tests/codegen/gh2537.d b/tests/codegen/gh2537.d deleted file mode 100644 index fc338dab0c..0000000000 --- a/tests/codegen/gh2537.d +++ /dev/null @@ -1,25 +0,0 @@ -// RUN: %ldc -run %s - -void main() -{ - int[string] aa = [ "one": 123 ]; - typeof(null) nul; - - auto sum = nul + nul; - auto diff = nul - nul; - - assert(aa + nul == aa); - assert(nul + aa == aa); - assert(aa - nul == aa); - assert(nul - aa == aa); - - static assert(!__traits(compiles, nul * nul)); - static assert(!__traits(compiles, aa * nul)); - static assert(!__traits(compiles, nul / nul)); - static assert(!__traits(compiles, aa / nul)); - static assert(!__traits(compiles, nul % nul)); - static assert(!__traits(compiles, aa % nul)); - - static assert(!__traits(compiles, nul & nul)); - static assert(!__traits(compiles, aa | nul)); -} diff --git a/tests/codegen/gh3094.d b/tests/codegen/gh3094.d index 7226558050..db5e79e8e4 100644 --- a/tests/codegen/gh3094.d +++ b/tests/codegen/gh3094.d @@ -1,10 +1,8 @@ // RUN: %ldc -run %s -int bar() { return 0; } - void foo(void delegate() sink) { - return (bar(), sink()); + return sink(); } void main() diff --git a/tests/codegen/vector_abi_x86.d b/tests/codegen/vector_abi_x86.d index 41fc2bcc1c..44a92fcb2a 100644 --- a/tests/codegen/vector_abi_x86.d +++ b/tests/codegen/vector_abi_x86.d @@ -3,10 +3,10 @@ // REQUIRES: host_X86 -// RUN: %ldc -O -output-s -m32 -of=%t_32.s %s -mattr=+sse2 +// RUN: %ldc -O -frame-pointer=none -output-s -m32 -of=%t_32.s %s -mattr=+sse2 // RUN: FileCheck --check-prefix=COMMON %s < %t_32.s -// RUN: %ldc -O -output-s -m64 -of=%t_64.s %s +// RUN: %ldc -O -frame-pointer=none -output-s -m64 -of=%t_64.s %s // RUN: FileCheck --check-prefix=COMMON --check-prefix=X64 %s < %t_64.s import core.simd; diff --git a/tests/compilable/gh3162.d b/tests/compilable/gh3162.d deleted file mode 100644 index 5f0e103ca1..0000000000 --- a/tests/compilable/gh3162.d +++ /dev/null @@ -1,13 +0,0 @@ -// RUN: %ldc -run %s - -shared struct Queue -{ - int[int] map; -} - -void main() -{ - auto queue = Queue(); - ( cast(int[int]) queue.map )[1] = 2; - assert(queue.map[1] == 2); -} diff --git a/tests/dmd/README.md b/tests/dmd/README.md index 75855a35aa..fe9e0cc6db 100644 --- a/tests/dmd/README.md +++ b/tests/dmd/README.md @@ -337,6 +337,13 @@ The following is a list of all available settings: of -Xi (see test/tools/sanitize_json.d) arguments: none + - sanitize_timetrace: Parse output of -ftime-trace profiling data, + extract event name / detail strings, and sort them. + Raw profiler output can't be tested against because it + contains non-deterministic timing data, or implementation + details such as semantic analysis order and memory usage, + so use this when writing a test for -ftime-trace. + - remove_lines: Remove lines matching a given regex arguments: the regex note: patterns containing ')' must be quoted diff --git a/tests/dmd/compilable/aligndefault.d b/tests/dmd/compilable/aligndefault.d new file mode 100644 index 0000000000..5c66bffdc2 --- /dev/null +++ b/tests/dmd/compilable/aligndefault.d @@ -0,0 +1,28 @@ + +struct S +{ + align(1) + { + short x1; + int y1; + long z1; + + align(default) + { + short x; + int y; + long z; + } + } +} + +void fun() +{ + static assert(S.x1.alignof == 1); + static assert(S.y1.alignof == 1); + static assert(S.z1.alignof == 1); + + static assert(S.x.alignof == short.alignof); + static assert(S.y.alignof == int.alignof); + static assert(S.z.alignof == long.alignof); +} diff --git a/tests/dmd/compilable/b16976.d b/tests/dmd/compilable/b16976.d index f5f45ef907..fd9c539226 100644 --- a/tests/dmd/compilable/b16976.d +++ b/tests/dmd/compilable/b16976.d @@ -1,4 +1,4 @@ -/* REQUIRED_ARGS: -m64 +/* REQUIRED_ARGS: -verrors=simple -m64 TEST_OUTPUT: --- compilable/b16976.d(33): Deprecation: foreach: loop index implicitly converted from `size_t` to `int` diff --git a/tests/dmd/compilable/betterCinline.d b/tests/dmd/compilable/betterCinline.d new file mode 100644 index 0000000000..31cc8498b0 --- /dev/null +++ b/tests/dmd/compilable/betterCinline.d @@ -0,0 +1,22 @@ +/* REQUIRED_ARGS: -betterC -inline + PERMUTE_ARGS: +*/ + +struct InvBoneBindInfo +{ +} + + +struct Test(Value) +{ + void test() + { + auto t = Value.init; + } +} + +extern(C) void main() +{ + Test!(InvBoneBindInfo[32]) test; + test.test(); +} diff --git a/tests/dmd/compilable/chkformat.d b/tests/dmd/compilable/chkformat.d index ccbe974f40..5c352c1891 100644 --- a/tests/dmd/compilable/chkformat.d +++ b/tests/dmd/compilable/chkformat.d @@ -1,9 +1,10 @@ // https://issues.dlang.org/show_bug.cgi?id=20643 // https://issues.dlang.org/show_bug.cgi?id=20644 +// REQUIRED_ARGS: -verrors=simple /* TEST_OUTPUT: ---- -compilable/chkformat.d(14): Deprecation: more format specifiers than 0 arguments +compilable/chkformat.d(15): Deprecation: more format specifiers than 0 arguments ---- */ import core.stdc.stdio; diff --git a/tests/dmd/compilable/compile1.d b/tests/dmd/compilable/compile1.d index 44ac3fa527..0e7e9de649 100644 --- a/tests/dmd/compilable/compile1.d +++ b/tests/dmd/compilable/compile1.d @@ -1,9 +1,10 @@ // COMPILABLE_MATH_TEST // PERMUTE_ARGS: +// REQUIRED_ARGS: -verrors=simple // EXTRA_FILES: imports/a12506.d /* TEST_OUTPUT: --- -compilable/compile1.d(230): Deprecation: use of complex type `cdouble` is deprecated, use `std.complex.Complex!(double)` instead +compilable/compile1.d(231): Deprecation: use of complex type `cdouble` is deprecated, use `std.complex.Complex!(double)` instead --- */ @@ -863,7 +864,7 @@ S14166 s14166; struct X14166 { this(int) { } X14166 opAssign(int) { return this; } } X14166[int] aa14166; -X14166[int] makeAA14166() { return aa14166; } +ref X14166[int] makeAA14166() { return aa14166; } struct Tup14166(T...) { T field; alias field this; } Tup14166!(int, int) tup14166; diff --git a/tests/dmd/compilable/copyCtor2.d b/tests/dmd/compilable/copyCtor2.d new file mode 100644 index 0000000000..084ef3b3f2 --- /dev/null +++ b/tests/dmd/compilable/copyCtor2.d @@ -0,0 +1,14 @@ +/* This used to not be allowed + * https://github.com/dlang/dmd/pull/20634 + */ + +struct A +{ + this (ref shared A a) immutable {} +} + +struct B +{ + A a; + this(immutable B b) shared {} +} diff --git a/tests/dmd/compilable/cppmangle.d b/tests/dmd/compilable/cppmangle.d index c1ca34f43d..b6b0740a89 100644 --- a/tests/dmd/compilable/cppmangle.d +++ b/tests/dmd/compilable/cppmangle.d @@ -689,6 +689,8 @@ extern (C++) class C18890_2 Agg s; } +void test18890() +{ version (CppMangle_Itanium) { static assert(C18890.__dtor.mangleof == "_ZN6C18890D1Ev"); @@ -713,6 +715,7 @@ version (CppMangle_MSVC) static assert(C18890_2.__xdtor.mangleof == "??_GC18890_2@@UEAAPEAXI@Z"); } } +} /**************************************/ // https://issues.dlang.org/show_bug.cgi?id=18891 @@ -727,6 +730,8 @@ extern (C++) class C18891 Agg s; } +void test18891() +{ version (CppMangle_Itanium) { static assert(C18891.__dtor.mangleof == "_ZN6C18891D1Ev"); @@ -745,6 +750,7 @@ version (CppMangle_MSVC) static assert(C18891.__xdtor.mangleof == "??_GC18891@@UEAAPEAXI@Z"); } } +} /**************************************/ // Test C++ operator mangling diff --git a/tests/dmd/compilable/ctod.i b/tests/dmd/compilable/ctod.i index b836dcaf8d..d1529de47d 100644 --- a/tests/dmd/compilable/ctod.i +++ b/tests/dmd/compilable/ctod.i @@ -31,11 +31,36 @@ extern (C) { A, } + struct S24326 + { + int x = void; + } + const(S24326) fun(int y); + struct foo + { + int x = void; + } + alias weird = int[(cast(foo*)cast(void*)0).x.sizeof]; + alias ULONG = ulong; + alias ULONG_Deluxe = ulong; + alias ULONG_PTR = ulong*; + alias Callback = void* function(); + struct Test + { + ULONG_Deluxe d = void; + ULONG_Deluxe* p = void; + ULONG_PTR q = void; + Callback cb = void; + } + extern __gshared int[cast(ULONG)3] arr; /+enum int __DATE__ = 1+/; /+enum int __TIME__ = 1+/; /+enum int __TIMESTAMP__ = 1+/; /+enum int __EOF__ = 1+/; /+enum int __VENDOR__ = 1+/; + enum int DEF = 123; + enum int SQL_DRIVER_STMT_ATTR_BASE = 16384; + enum int ABC = 64; } --- */ @@ -76,3 +101,33 @@ typedef S T; // https://issues.dlang.org/show_bug.cgi?id=24326 enum { A }; + +// https://issues.dlang.org/show_bug.cgi?id=24670 +struct S24326 { int x; }; +const struct S24326 fun(int y); + +// https://issues.dlang.org/show_bug.cgi?id=24375 +struct foo { + int x; +}; +typedef int weird[sizeof(((struct foo *)((void*)0))->x)]; + +// https://github.com/dlang/dmd/issues/20889 +typedef unsigned long long ULONG; +typedef ULONG ULONG_Deluxe; +typedef ULONG_Deluxe *ULONG_PTR; +typedef void *(*Callback)(); + +struct Test +{ + ULONG_Deluxe d; + ULONG_Deluxe *p; + ULONG_PTR q; + Callback cb; +}; + +int arr[(ULONG) 3]; + +#define DEF 123 +#define SQL_DRIVER_STMT_ATTR_BASE 0x00004000 // 32-bit +#define ABC 64 diff --git a/tests/dmd/compilable/ddoc10236.d b/tests/dmd/compilable/ddoc10236.d index c27289472e..41d63cd1e1 100644 --- a/tests/dmd/compilable/ddoc10236.d +++ b/tests/dmd/compilable/ddoc10236.d @@ -1,5 +1,5 @@ // PERMUTE_ARGS: -// REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -wi -o- +// REQUIRED_ARGS: -verrors=simple -D -Dd${RESULTS_DIR}/compilable -wi -o- /* TEST_OUTPUT: diff --git a/tests/dmd/compilable/ddoc10236b.d b/tests/dmd/compilable/ddoc10236b.d index 85783e8d14..ce3264ef25 100644 --- a/tests/dmd/compilable/ddoc10236b.d +++ b/tests/dmd/compilable/ddoc10236b.d @@ -1,5 +1,5 @@ // PERMUTE_ARGS: -// REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -wi -o- +// REQUIRED_ARGS: -verrors=simple -D -Dd${RESULTS_DIR}/compilable -wi -o- /* TEST_OUTPUT: diff --git a/tests/dmd/compilable/ddoc13502.d b/tests/dmd/compilable/ddoc13502.d index 93f383fea9..3bfb617345 100644 --- a/tests/dmd/compilable/ddoc13502.d +++ b/tests/dmd/compilable/ddoc13502.d @@ -1,5 +1,5 @@ // PERMUTE_ARGS: -// REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -wi -o- +// REQUIRED_ARGS: -verrors=simple -D -Dd${RESULTS_DIR}/compilable -wi -o- /* TEST_OUTPUT: --- diff --git a/tests/dmd/compilable/ddoc24871.d b/tests/dmd/compilable/ddoc24871.d new file mode 100644 index 0000000000..8e1c227e03 --- /dev/null +++ b/tests/dmd/compilable/ddoc24871.d @@ -0,0 +1,18 @@ +// PERMUTE_ARGS: +// REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -o- +// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh +import std.stdio; + +/// Example +/// --- +/// void main() { +/// foreach (i; 0..10) { +/// writeln("Hello, world!"); +/// } +/// } +/// --- +void main() { + + writeln("Hello, World!"); + +} diff --git a/tests/dmd/compilable/ddoc4899.d b/tests/dmd/compilable/ddoc4899.d index b5cfa86367..44999ddeeb 100644 --- a/tests/dmd/compilable/ddoc4899.d +++ b/tests/dmd/compilable/ddoc4899.d @@ -1,5 +1,5 @@ // PERMUTE_ARGS: -// REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -wi -o- +// REQUIRED_ARGS: -verrors=simple -D -Dd${RESULTS_DIR}/compilable -wi -o- /* TEST_OUTPUT: diff --git a/tests/dmd/compilable/depmsg.d b/tests/dmd/compilable/depmsg.d index 9b005d8ff7..2c29e05a58 100644 --- a/tests/dmd/compilable/depmsg.d +++ b/tests/dmd/compilable/depmsg.d @@ -1,5 +1,5 @@ /* -REQUIRED_ARGS: -dw +REQUIRED_ARGS: -verrors=simple -dw TEST_OUTPUT: --- compilable/depmsg.d(39): Deprecation: struct `depmsg.main.Inner.A` is deprecated - With message! diff --git a/tests/dmd/compilable/deprecated_override.d b/tests/dmd/compilable/deprecated_override.d index c9da34dbfa..193cea9d0d 100644 --- a/tests/dmd/compilable/deprecated_override.d +++ b/tests/dmd/compilable/deprecated_override.d @@ -1,5 +1,5 @@ // https://issues.dlang.org/show_bug.cgi?id=22668 - +// REQUIRED_ARGS: -verrors=simple // Overrides with same deprecated'ness are allowed class SameParent diff --git a/tests/dmd/compilable/deprecationlimit.d b/tests/dmd/compilable/deprecationlimit.d index 8ee7ab650f..2b7d45c393 100644 --- a/tests/dmd/compilable/deprecationlimit.d +++ b/tests/dmd/compilable/deprecationlimit.d @@ -1,5 +1,5 @@ /* -REQUIRED_ARGS: -verrors=3 +REQUIRED_ARGS: -verrors=simple -verrors=3 TEST_OUTPUT: --- compilable/deprecationlimit.d(18): Deprecation: function `deprecationlimit.f` is deprecated diff --git a/tests/dmd/compilable/diag20916.d b/tests/dmd/compilable/diag20916.d index 1993e0e020..aa30c94fcf 100644 --- a/tests/dmd/compilable/diag20916.d +++ b/tests/dmd/compilable/diag20916.d @@ -1,3 +1,4 @@ +// REQUIRED_ARGS: -verrors=simple /* TEST_OUTPUT: --- diff --git a/tests/dmd/compilable/dmdcliflags.sh b/tests/dmd/compilable/dmdcliflags.sh index 56d8736ed5..15e80c2079 100644 --- a/tests/dmd/compilable/dmdcliflags.sh +++ b/tests/dmd/compilable/dmdcliflags.sh @@ -162,4 +162,4 @@ echo "$output" | grep "Only a number between 0 and 100 can be passed to \`-cov=< output="$(! $DMD -verrors=foo 2>&1)" echo "$output" | grep "Error: switch \`-verrors=foo\` is invalid" -echo "$output" | grep "Only number, \`spec\`, or \`context\` are allowed for \`-verrors\`" +echo "$output" | grep "Only a number, \`spec\`, \`simple\`, or \`context\` are allowed for \`-verrors\`" diff --git a/tests/dmd/compilable/dtoh_invalid_identifiers.d b/tests/dmd/compilable/dtoh_invalid_identifiers.d index 28c79088a2..1179f66203 100644 --- a/tests/dmd/compilable/dtoh_invalid_identifiers.d +++ b/tests/dmd/compilable/dtoh_invalid_identifiers.d @@ -1,5 +1,5 @@ /+ -REQUIRED_ARGS: -HC -c -o- -wi -extern-std=c++20 +REQUIRED_ARGS: -verrors=simple -HC -c -o- -wi -extern-std=c++20 PERMUTE_ARGS: TEST_OUTPUT: --- diff --git a/tests/dmd/compilable/dtorfields_deprecation.d b/tests/dmd/compilable/dtorfields_deprecation.d deleted file mode 100644 index 83014e38e4..0000000000 --- a/tests/dmd/compilable/dtorfields_deprecation.d +++ /dev/null @@ -1,49 +0,0 @@ -/** -Checks that code still compiles when -preview=dtorfields is enabled by default -but issues an appropriate deprecation message. - -Remove this test when the deprecations period ends, see visit(CtorDeclaration) -in semantic3.d - -TEST_OUTPUT: ---- -compilable/dtorfields_deprecation.d(30): Deprecation: `dtorfields_deprecation.Pure.this` has stricter attributes than its destructor (`pure`) -compilable/dtorfields_deprecation.d(30): The destructor will be called if an exception is thrown -compilable/dtorfields_deprecation.d(30): Either make the constructor `nothrow` or adjust the field destructors -compilable/dtorfields_deprecation.d(42): Deprecation: `dtorfields_deprecation.NoGC.this` has stricter attributes than its destructor (`@nogc`) -compilable/dtorfields_deprecation.d(42): The destructor will be called if an exception is thrown -compilable/dtorfields_deprecation.d(42): Either make the constructor `nothrow` or adjust the field destructors -compilable/dtorfields_deprecation.d(48): Deprecation: `dtorfields_deprecation.Safe.this` has stricter attributes than its destructor (`@system`) -compilable/dtorfields_deprecation.d(48): The destructor will be called if an exception is thrown -compilable/dtorfields_deprecation.d(48): Either make the constructor `nothrow` or adjust the field destructors ---- -**/ - -struct HasDtor -{ - ~this() {} -} - -struct Pure -{ - HasDtor member; - this(int) pure {} -} - -struct Nothrow -{ - HasDtor member; - this(int) nothrow {} -} - -struct NoGC -{ - HasDtor member; - this(int) @nogc {} -} - -struct Safe -{ - HasDtor member; - this(int) @safe {} -} diff --git a/tests/dmd/compilable/extra-files/ddoc10.html b/tests/dmd/compilable/extra-files/ddoc10.html index fe0044d28e..0665100b20 100644 --- a/tests/dmd/compilable/extra-files/ddoc10.html +++ b/tests/dmd/compilable/extra-files/ddoc10.html @@ -1088,7 +1088,7 @@

Declaration

- const pure nothrow this(long ticks); + pure nothrow this(long ticks) const;

@@ -1113,7 +1113,7 @@

Declaration

- const pure nothrow void foo(long ticks); + pure nothrow void foo(long ticks) const;

diff --git a/tests/dmd/compilable/extra-files/ddoc10869.html b/tests/dmd/compilable/extra-files/ddoc10869.html index 1d7365d242..a8d0c86fab 100644 --- a/tests/dmd/compilable/extra-files/ddoc10869.html +++ b/tests/dmd/compilable/extra-files/ddoc10869.html @@ -552,7 +552,7 @@

Declaration

- const void c1Foo(); + void c1Foo() const;

@@ -577,7 +577,7 @@

Declaration

- immutable void i1Foo(); + void i1Foo() immutable;

@@ -602,7 +602,7 @@

Declaration

- immutable void c2Foo(); + void c2Foo() immutable;

@@ -627,7 +627,7 @@

Declaration

- immutable void i2Foo(); + void i2Foo() immutable;

diff --git a/tests/dmd/compilable/extra-files/ddoc24871.html b/tests/dmd/compilable/extra-files/ddoc24871.html new file mode 100644 index 0000000000..c8e4504211 --- /dev/null +++ b/tests/dmd/compilable/extra-files/ddoc24871.html @@ -0,0 +1,576 @@ + + + + + + ddoc24871 + + + +
+
+

ddoc24871

+
+
+
+
    +
  • +
    +
    + main +
    +
    +
    +
    +

    Declaration

    +
    +

    + + void main(); + + +

    +
    +
    +
    +
    +
    +
    +
    +

    + Example + +

    +
    +
    +
      +
    1. void main() {
      +	foreach (i; 0..10) {
      +		writeln("Hello, world!");
      +	}
      +}
      +
    2. +
    +
    +
    +
    + +

    +
    + +
    + +
    + +
  • +
+
+
+
+
+
+ + diff --git a/tests/dmd/compilable/extra-files/header1.d b/tests/dmd/compilable/extra-files/header1.d index 9756264fcc..ee698f16dc 100644 --- a/tests/dmd/compilable/extra-files/header1.d +++ b/tests/dmd/compilable/extra-files/header1.d @@ -564,6 +564,11 @@ ref int* foo(scope return ref int* a) @safe struct SafeS { + this(int[1] x) scope {} + this(int[2] x) return scope {} + this(int[3] x) scope return {} + this(int[4] x) return {} + @safe: ref SafeS foo() return { diff --git a/tests/dmd/compilable/extra-files/header1.di b/tests/dmd/compilable/extra-files/header1.di index 91422fed8a..3e25efb488 100644 --- a/tests/dmd/compilable/extra-files/header1.di +++ b/tests/dmd/compilable/extra-files/header1.di @@ -359,8 +359,8 @@ int bar11(T)() } struct S6360 { - const pure nothrow @property long weeks1(); - const pure nothrow @property long weeks2(); + pure nothrow @property long weeks1() const; + pure nothrow @property long weeks2() const; } struct S12 { @@ -369,10 +369,10 @@ struct S12 } struct T12 { - immutable this()(int args) + this()(int args) immutable { } - immutable this(A...)(A args) + this(A...)(A args) immutable { } } @@ -502,7 +502,7 @@ size_t magic(); class Foo2A { immutable(FooA) Dummy = new immutable(FooA); - private immutable pure nothrow @nogc @safe this(); + private pure nothrow @nogc @safe this() immutable; } struct Foo3A(T) { @@ -514,6 +514,10 @@ ref @safe int foo(return ref int a); ref @safe int* foo(return ref scope int* a); struct SafeS { + this(int[1] x) scope; + this(int[2] x) return scope; + this(int[3] x) scope return; + this(int[4] x) return; @safe { ref SafeS foo() return; @@ -560,4 +564,4 @@ interface I12344 assert(result > 0); } ; -} +} \ No newline at end of file diff --git a/tests/dmd/compilable/extra-files/header1i.di b/tests/dmd/compilable/extra-files/header1i.di index a77f3cb094..ee37e60ef3 100644 --- a/tests/dmd/compilable/extra-files/header1i.di +++ b/tests/dmd/compilable/extra-files/header1i.di @@ -452,11 +452,11 @@ int bar11(T)() } struct S6360 { - const pure nothrow @property long weeks1() + pure nothrow @property long weeks1() const { return 0; } - const pure nothrow @property long weeks2() + pure nothrow @property long weeks2() const { return 0; } @@ -472,10 +472,10 @@ struct S12 } struct T12 { - immutable this()(int args) + this()(int args) immutable { } - immutable this(A...)(A args) + this(A...)(A args) immutable { } } @@ -624,7 +624,7 @@ size_t magic() class Foo2A { immutable(FooA) Dummy = new immutable(FooA); - private immutable pure nothrow @nogc @safe this() + private pure nothrow @nogc @safe this() immutable { } } @@ -647,6 +647,18 @@ ref @safe int* foo(return ref scope int* a) } struct SafeS { + this(int[1] x) scope + { + } + this(int[2] x) return scope + { + } + this(int[3] x) scope return + { + } + this(int[4] x) return + { + } @safe { ref SafeS foo() return @@ -715,4 +727,4 @@ interface I12344 assert(result > 0); } ; -} +} \ No newline at end of file diff --git a/tests/dmd/compilable/extra-files/json.json b/tests/dmd/compilable/extra-files/json.json index 1ff4b7ee4e..a4e715edc8 100644 --- a/tests/dmd/compilable/extra-files/json.json +++ b/tests/dmd/compilable/extra-files/json.json @@ -23,7 +23,7 @@ "endline": 9, "kind": "static constructor", "line": 9, - "name": "_staticCtor_L9_C1", + "name": "static this", "protection": "public", "storageClass": [ "static" @@ -49,7 +49,7 @@ "endline": 11, "kind": "static destructor", "line": 11, - "name": "_staticDtor_L11_C1", + "name": "static ~this", "protection": "public", "storageClass": [ "static" @@ -77,7 +77,7 @@ "endline": 16, "kind": "static constructor", "line": 16, - "name": "_staticCtor_L16_C5", + "name": "static this", "storageClass": [ "static" ] @@ -99,7 +99,7 @@ "endline": 18, "kind": "static destructor", "line": 18, - "name": "_staticDtor_L18_C5", + "name": "static ~this", "storageClass": [ "static" ] @@ -147,7 +147,7 @@ "endline": 26, "kind": "static constructor", "line": 26, - "name": "_staticCtor_L26_C5", + "name": "static this", "protection": "public", "storageClass": [ "static" @@ -173,7 +173,7 @@ "endline": 28, "kind": "static destructor", "line": 28, - "name": "_staticDtor_L28_C5", + "name": "static ~this", "protection": "public", "storageClass": [ "static" @@ -385,7 +385,7 @@ }, { "kind": "alias", - "name": "__xdtor", + "name": "~this", "protection": "public" } ], @@ -1186,4 +1186,4 @@ ], "name": "json" } -] +] \ No newline at end of file diff --git a/tests/dmd/compilable/extra-files/vcg-ast.d.cg b/tests/dmd/compilable/extra-files/vcg-ast.d.cg index c8e338a262..c60768ee54 100644 --- a/tests/dmd/compilable/extra-files/vcg-ast.d.cg +++ b/tests/dmd/compilable/extra-files/vcg-ast.d.cg @@ -70,13 +70,13 @@ class C : Object { } { - const ref const(int) r = __result; + ref const const(int) r = __result; { } } assert(true); { - const ref const(int) r = __result; + ref const const(int) r = __result; assert(true); } } @@ -162,11 +162,6 @@ RTInfo!(C) { enum immutable(void)* RTInfo = null; -} -NoPointersBitmapPayload!1$?:32=u|64=LU$ -{ - enum $?:32=uint|64=ulong$[1] NoPointersBitmapPayload = [0$?:32=u|64=LU$]; - } values!(__c_wchar_t) { diff --git a/tests/dmd/compilable/ftimetrace.d b/tests/dmd/compilable/ftimetrace.d new file mode 100644 index 0000000000..40affa6f0d --- /dev/null +++ b/tests/dmd/compilable/ftimetrace.d @@ -0,0 +1,38 @@ +/** +REQUIRED_ARGS: -ftime-trace -ftime-trace-file=- -ftime-trace-granularity=0 +TRANSFORM_OUTPUT: sanitize_timetrace +TEST_OUTPUT: +--- +Code generation, +Codegen: function add, object.add +Codegen: function fun, object.fun +Codegen: module object, object +Ctfe: add(4, 8), add(4, 8) +Ctfe: call add, object.add(4, 8) +Generate IR, object +Import object.object, object.object +Optimize, $r:.*ftimetrace_0\.o(bj)?$ +Parse: Module object, object +Parsing, +Prune object file cache, +Sema1: Module object, object +Sema2: add, object.add +Sema2: fun, object.fun +Sema3: add, object.add +Sema3: fun, object.fun +Semantic analysis, +Write file(s), $r:.*ftimetrace_0\.o(bj)?$ +--- +*/ + +module object; // Don't clutter time trace output with object.d + +void fun() +{ + enum z = add(4, 8); +} + +int add(int x, int y) +{ + return x + y; +} diff --git a/tests/dmd/compilable/header18365.d b/tests/dmd/compilable/header18365.d index 7e51fb26cc..8846c86c44 100644 --- a/tests/dmd/compilable/header18365.d +++ b/tests/dmd/compilable/header18365.d @@ -13,7 +13,7 @@ struct FullCaseEntry ubyte n; ubyte size; ubyte entry_len; - auto const pure nothrow @nogc @property @trusted value() return + auto pure nothrow @nogc @property @trusted value() const return { return seq[0..entry_len]; } diff --git a/tests/dmd/compilable/imports/defines.c b/tests/dmd/compilable/imports/defines.c index 6b0746f8ff..420679a4fc 100644 --- a/tests/dmd/compilable/imports/defines.c +++ b/tests/dmd/compilable/imports/defines.c @@ -55,3 +55,11 @@ int pr16199c() { return 8; } + +// https://issues.dlang.org/show_bug.cgi?id=24639 +#define NEGATIVE_I32 -1 +#define NEGATIVE_U32 -2U +#define NEGATIVE_I64 -3LL +#define NEGATIVE_U64 -4LLU +#define NEGATIVE_F32 -5.0f +#define NEGATIVE_F64 -6.0 diff --git a/tests/dmd/compilable/imports/imp23812.c b/tests/dmd/compilable/imports/imp23812.c new file mode 100644 index 0000000000..26a600a776 --- /dev/null +++ b/tests/dmd/compilable/imports/imp23812.c @@ -0,0 +1,35 @@ + +void funcDefault(void); + +#pragma attribute(push, nothrow) +void funcNothrow(void); +#pragma attribute(pop) + +#pragma attribute(push, nogc) +void funcNogc(void); +#pragma attribute(pop) + +#pragma attribute(push, pure) +void funcPure(void); +#pragma attribute(pop) + +#pragma attribute(push, nothrow, nogc) +void funcNothrowNogc(void); +#pragma attribute(pop) + +void funcDefault2(void); + +#pragma attribute(push, nothrow) +#pragma attribute(push, nogc) +void funcNothrowNogc2(void); +#pragma attribute(pop) +void funcNothrow2(void); +#pragma attribute(pop) + +#pragma attribute(push, nothrow) +void funcWithCallback(void (*f)(void)); +struct Callbacks +{ + void (*f)(void); +}; +#pragma attribute(pop) diff --git a/tests/dmd/compilable/imports/test21098_phobos.d b/tests/dmd/compilable/imports/test21098_phobos.d new file mode 100644 index 0000000000..29c77eb047 --- /dev/null +++ b/tests/dmd/compilable/imports/test21098_phobos.d @@ -0,0 +1,77 @@ +struct Nullable(T) +{ + static struct DontCallDestructorT + { + T payload; + } + + DontCallDestructorT _value; + + string toString() const + { + Appender!string app; + formatValueImpl(app, _value); + return null; + } +} + + + +struct Appender(A) +{ + InPlaceAppender!A impl; +} + +struct InPlaceAppender(T) +{ + static void toStringImpl(const T[] data) + { + string app; + formatValue(app, data); + } +} + + + +void formatValueImpl(Writer, T)(Writer, const(T)) {} + +void formatValueImpl(Writer, T)(Writer w, T obj) +if (is(T == U[], U)) +{ + formatValue(w, obj[0]); +} + +enum HasToStringResult +{ + none, + bla +} + +template hasToString(T) +{ + static if (is(typeof( + (T val) { + val.toString(s); + }))) + enum hasToString = HasToStringResult.bla; + else + enum hasToString = HasToStringResult.none; +} + +void formatValueImpl(Writer, T)(ref Writer w, T val) +if (is(T == struct) || is(T == union)) +{ + static if (hasToString!T) + int dummy; + formatElement(w, val.tupleof); +} + +void formatElement(Writer, T)(Writer w, T val) +{ + formatValueImpl(w, val); +} + +void formatValue(Writer, T)(Writer w, T val) +{ + formatValueImpl(w, val); +} diff --git a/tests/dmd/compilable/imports/test21098b.d b/tests/dmd/compilable/imports/test21098b.d new file mode 100644 index 0000000000..74c9fa80c8 --- /dev/null +++ b/tests/dmd/compilable/imports/test21098b.d @@ -0,0 +1,12 @@ +import imports.test21098_phobos : Appender, Nullable; + +struct Type { + Nullable!(Type[]) templateArgs; +} + +Type[] parseDeclarations() { + Appender!(Type[]) members; + return null; +} + +enum ast = parseDeclarations(); diff --git a/tests/dmd/compilable/interpret3.d b/tests/dmd/compilable/interpret3.d index 69f65b1cbd..f62147a0db 100644 --- a/tests/dmd/compilable/interpret3.d +++ b/tests/dmd/compilable/interpret3.d @@ -1,8 +1,9 @@ // PERMUTE_ARGS: -inline +// REQUIRED_ARGS: -verrors=simple /* TEST_OUTPUT: --- -compilable/interpret3.d(6350): Deprecation: identity comparison of static arrays implicitly coerces them to slices, which are compared by reference +compilable/interpret3.d(6351): Deprecation: identity comparison of static arrays implicitly coerces them to slices, which are compared by reference --- */ @@ -6239,9 +6240,9 @@ struct Coord13831 struct Chunk13831 { - this(Coord13831) + this(Coord13831 coord) { - coord = coord; + this.coord = coord; } Coord13831 coord; diff --git a/tests/dmd/compilable/isZeroInit.d b/tests/dmd/compilable/isZeroInit.d index a64420febc..342dc07bf0 100644 --- a/tests/dmd/compilable/isZeroInit.d +++ b/tests/dmd/compilable/isZeroInit.d @@ -78,3 +78,45 @@ static if (is(Vector!(int[4]))) static assert(__traits(isZeroInit, Holder!(Vector!(int[4]), 0))); static assert(!__traits(isZeroInit, Holder!(Vector!(int[4]), 1))); } + +// https://issues.dlang.org/show_bug.cgi?id=24776 +struct S6 { + union { + int i1; + float f1; + } +} +static assert(__traits(isZeroInit, S6)); + +struct S7 +{ + union { + float f2; + int i2; + } +} +static assert(!__traits(isZeroInit, S7)); + +// https://issues.dlang.org/show_bug.cgi?id=23841 +union U +{ + float x = 0; + float y; +} +static assert(__traits(isZeroInit, U)); + +union U2 +{ + float x; + int y; +} +static assert(!__traits(isZeroInit, U2)); + +struct S8 { + int[0] dummy; // same offset as anon union, but doesn't overlap; should be ignored anyway for zero-init check + union { + float f; // is the first member of the anon union and must be checked + int i; + } +} +static assert(!__traits(isZeroInit, S8)); diff --git a/tests/dmd/compilable/issue24830.sh b/tests/dmd/compilable/issue24830.sh new file mode 100755 index 0000000000..26864c2449 --- /dev/null +++ b/tests/dmd/compilable/issue24830.sh @@ -0,0 +1,34 @@ +#! /usr/bin/env bash + +TEST_DIR=${OUTPUT_BASE} +# create two modules each depending on each other. +# They both do the same template instantiation. +D_FILE1=$TEST_DIR/first.d +D_FILE2=$TEST_DIR/second.d +D_OBJ1=$TEST_DIR/first${OBJ} +D_OBJ2=$TEST_DIR/second${OBJ} +APP=$TEST_DIR/app + +mkdir -p $TEST_DIR + +cat >$D_FILE1 <$D_FILE2 < r.popFront))); + +// https://issues.dlang.org/show_bug.cgi?id=24883 +int toString(Writer)(ref Writer sink) => 3; +int toString(void delegate(scope const(char)[]) sink) => 4; +void put() {} +static assert(toString(dst => put()) == 4); diff --git a/tests/dmd/compilable/sarif_success_test.d b/tests/dmd/compilable/sarif_success_test.d new file mode 100644 index 0000000000..315f5bbd7d --- /dev/null +++ b/tests/dmd/compilable/sarif_success_test.d @@ -0,0 +1,28 @@ +// REQUIRED_ARGS: -verror-style=sarif +/* +TEST_OUTPUT: +--- +{ + "version": "2.1.0", + "$schema": "https://schemastore.azurewebsites.net/schemas/json/sarif-2.1.0.json", + "runs": [{ + "tool": { + "driver": { + "name": "LDC", + "version": "$r:\d+\.\d+\.\d+$", + "informationUri": "https://dlang.org/dmd.html" + } + }, + "invocations": [{ + "executionSuccessful": true + }], + "results": [ + ] + }] +} +--- +*/ + +void main() { + int x = 5; +} diff --git a/tests/dmd/compilable/scope.d b/tests/dmd/compilable/scope.d index 44f54f7468..06549941d2 100644 --- a/tests/dmd/compilable/scope.d +++ b/tests/dmd/compilable/scope.d @@ -262,3 +262,116 @@ struct S23669 a.length += 1; } } + +/************************************/ + +// Calling `hasPointers` in escape.d at some point caused +// a "circular `typeof` definition" error in std.typecons +// https://github.com/dlang/dmd/pull/16719 +struct Unique +{ + int* p; + alias ValueType = typeof({ return p; } ()); + static if (is(ValueType == int*)) {} +} + +// Without handling tuple expansion in escape.d, this error occurs: +// Error: returning `(ref Tuple!(int, int) __tup2 = this.tuple.get();) , &__tup2.__expand_field_0` escapes a reference to local variable `__tup2` +struct Tuple(Types...) +{ + Types expand; + ref Tuple!Types get() { return this; } +} + +struct S2 +{ + Tuple!(int, int) tuple; + int* f() return { return &tuple.get().expand[0]; } +} + +/************************************/ + +// https://issues.dlang.org/show_bug.cgi?id=23300 + +auto array(Range)(Range r) +{ + int[] result; + foreach (e; r) + result ~= e; + return result; +} + +struct Range +{ + int* ptr; + int front = 3; + enum empty = true; + void popFront() @safe scope {} +} + +auto f() @safe +{ + scope Range r; + return r.array; +} + +/************************************/ + +// Test that there's no endless loop forwarding `__appendtmp1 => __appendtmp1.this(null)` +struct SD +{ + int* p; + this(int*) return scope {} +} + +auto fsd() @safe +{ + SD[] a; + a ~= SD(null); +} + +/************************************/ + +// Test that `return scope` inference from assignment doesn't force the function to be `scope` +struct Scape +{ + int* p; + + this()(int* input) + { + p = input; // return scope inferred here + notScope(); // not-scope inferred here + } + + void notScope() @safe + { + static int* g; + g = p; + } +} + +@safe testScape() +{ + Scape(null); +} + +/************************************/ + +// Test `scope` inference in presence of nested function returning `this`: +// `save()` can still be called on a `scope Result` +struct Result +{ + int* source; + auto save() + { + int* saveI() { return this.source; } + auto saveResult = Result(saveI()); + } +} + +@safe escapeNested() +{ + int s; + auto r = Result(&s); + r.save(); +} diff --git a/tests/dmd/compilable/shortened_methods.d b/tests/dmd/compilable/shortened_methods.d index 785cb8e5cd..76a4c8a55d 100644 --- a/tests/dmd/compilable/shortened_methods.d +++ b/tests/dmd/compilable/shortened_methods.d @@ -13,6 +13,10 @@ class A { // or normal method defintions bool isNull() => this is null; + + this() {} + this(int x) { _x = x; } + this(float y) => this(cast(int) y); } class B : A{ @@ -36,3 +40,12 @@ void func() { // Issue 24088 - https://issues.dlang.org/show_bug.cgi?id=24088 S!int f() => S!int(); } + +struct T +{ + void inc() {} + this(this) => inc(); + + void free() {} + ~this() => free(); +} diff --git a/tests/dmd/compilable/stdcheaders.c b/tests/dmd/compilable/stdcheaders.c index 51405c8226..8e9f3126d0 100644 --- a/tests/dmd/compilable/stdcheaders.c +++ b/tests/dmd/compilable/stdcheaders.c @@ -32,12 +32,10 @@ #include #include -#ifndef __APPLE__ // /Applications/Xcode-14.2.0.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/tgmath.h(39): Error: named parameter required before `...` #include #ifndef _MSC_VER // C:\Program Files (x86)\Windows Kits\10\include\10.0.26100.0\ucrt\corecrt_math.h(93): Error: reinterpretation through overlapped field `f` is not allowed in CTFE float x = NAN; #endif -#endif #ifndef _MSC_VER // setjmp.h(51): Error: missing tag `identifier` after `struct #include diff --git a/tests/dmd/compilable/sw_transition_complex.d b/tests/dmd/compilable/sw_transition_complex.d index 3b27356033..bcce94b6fe 100644 --- a/tests/dmd/compilable/sw_transition_complex.d +++ b/tests/dmd/compilable/sw_transition_complex.d @@ -1,5 +1,5 @@ // PERMUTE_ARGS: -// REQUIRED_ARGS: -unittest -verrors=0 +// REQUIRED_ARGS: -verrors=simple -unittest -verrors=0 /* TEST_OUTPUT: diff --git a/tests/dmd/compilable/test12567c.d b/tests/dmd/compilable/test12567c.d index 9a686df555..1e3f01b9ab 100644 --- a/tests/dmd/compilable/test12567c.d +++ b/tests/dmd/compilable/test12567c.d @@ -1,4 +1,4 @@ -// REQUIRED_ARGS: +// REQUIRED_ARGS: -verrors=simple // EXTRA_FILES: imports/a12567.d // PERMUTE_ARGS: /* diff --git a/tests/dmd/compilable/test16776.d b/tests/dmd/compilable/test16776.d new file mode 100644 index 0000000000..9ebd515d8b --- /dev/null +++ b/tests/dmd/compilable/test16776.d @@ -0,0 +1,8 @@ +// ARG_SETS: -i +// PERMUTE_ARGS: +// LINK: +import imports.cstuff3; +void main() +{ + int s = squared(2); +} diff --git a/tests/dmd/compilable/test19227.d b/tests/dmd/compilable/test19227.d index 6ff5349f33..2a1d325d75 100644 --- a/tests/dmd/compilable/test19227.d +++ b/tests/dmd/compilable/test19227.d @@ -1,9 +1,8 @@ // https://issues.dlang.org/show_bug.cgi?id=19227 +// REQUIRED_ARGS: -verrors=simple /* TEST_OUTPUT: --- -compilable/test19227.d(18): Deprecation: use of complex type `cfloat` is deprecated, use `std.complex.Complex!(float)` instead -Deprecation: use of complex type `const(cfloat)` is deprecated, use `std.complex.Complex!(float)` instead -Deprecation: use of complex type `const(cfloat)` is deprecated, use `std.complex.Complex!(float)` instead +compilable/test19227.d(17): Deprecation: use of complex type `cfloat` is deprecated, use `std.complex.Complex!(float)` instead Deprecation: use of complex type `const(cfloat)` is deprecated, use `std.complex.Complex!(float)` instead --- */ diff --git a/tests/dmd/compilable/test19609.d b/tests/dmd/compilable/test19609.d index df8f891ec8..b23eece668 100644 --- a/tests/dmd/compilable/test19609.d +++ b/tests/dmd/compilable/test19609.d @@ -1,11 +1,12 @@ // https://issues.dlang.org/show_bug.cgi?id=19609 +// REQUIRED_ARGS: -verrors=simple // EXTRA_FILES: imports/test19609a.d imports/test19609b.d imports/test19609c.d /* TEST_OUTPUT: --- -compilable/test19609.d(11): Deprecation: module `imports.test19609a` is deprecated -compilable/test19609.d(12): Deprecation: module `imports.test19609b` is deprecated - hello -compilable/test19609.d(13): Deprecation: module `imports.test19609c` is deprecated +compilable/test19609.d(12): Deprecation: module `imports.test19609a` is deprecated +compilable/test19609.d(13): Deprecation: module `imports.test19609b` is deprecated - hello +compilable/test19609.d(14): Deprecation: module `imports.test19609c` is deprecated --- */ import imports.test19609a; diff --git a/tests/dmd/compilable/test20063.d b/tests/dmd/compilable/test20063.d index 468f93d718..258f466992 100644 --- a/tests/dmd/compilable/test20063.d +++ b/tests/dmd/compilable/test20063.d @@ -1,8 +1,9 @@ // DISABLED: LDC +// REQUIRED_ARGS: -verrors=simple /* TEST_OUTPUT: --- -compilable/test20063.d(10): Deprecation: function `test20063.main.f!(delegate () pure nothrow @safe => new C).f` function requires a dual-context, which is deprecated -compilable/test20063.d(19): instantiated from here: `f!(delegate () pure nothrow @safe => new C)` +compilable/test20063.d(11): Deprecation: function `test20063.main.f!(delegate () pure nothrow @safe => new C).f` function requires a dual-context, which is deprecated +compilable/test20063.d(20): instantiated from here: `f!(delegate () pure nothrow @safe => new C)` --- */ diff --git a/tests/dmd/compilable/test21098.d b/tests/dmd/compilable/test21098.d new file mode 100644 index 0000000000..9b02b7b4d6 --- /dev/null +++ b/tests/dmd/compilable/test21098.d @@ -0,0 +1,4 @@ +// https://github.com/dlang/dmd/issues/21098 + +// EXTRA_FILES: imports/test21098b.d imports/test21098_phobos.d +import imports.test21098b; diff --git a/tests/dmd/compilable/test21153.d b/tests/dmd/compilable/test21153.d new file mode 100644 index 0000000000..cd92a31240 --- /dev/null +++ b/tests/dmd/compilable/test21153.d @@ -0,0 +1,8 @@ +// https://github.com/dlang/dmd/issues/21153 +alias AliasSeq(TList...) = TList; +class DataClass; +void reduce(DataClass[] r) +{ + alias Args = AliasSeq!(DataClass); + Args result = r[0]; +} diff --git a/tests/dmd/compilable/test21177.d b/tests/dmd/compilable/test21177.d index b485304c18..a75a0d5263 100644 --- a/tests/dmd/compilable/test21177.d +++ b/tests/dmd/compilable/test21177.d @@ -1,5 +1,6 @@ // https://issues.dlang.org/show_bug.cgi?id=21177 /* +REQUIRED_ARGS: -verrors=simple DISABLED: win TEST_OUTPUT: --- diff --git a/tests/dmd/compilable/test21179.d b/tests/dmd/compilable/test21179.d new file mode 100644 index 0000000000..78bdffda55 --- /dev/null +++ b/tests/dmd/compilable/test21179.d @@ -0,0 +1,11 @@ +// https://github.com/dlang/dmd/issues/21179 + +void bigEndianToNative(ubyte[2] a) {} + +void main() +{ + ubyte[] arr; + const ubyte[2] bytes; + bigEndianToNative(bytes); + auto b = cast(const ubyte[2][]) arr; +} diff --git a/tests/dmd/compilable/test21514.d b/tests/dmd/compilable/test21514.d index 4eb80509f3..4ea5377dc4 100644 --- a/tests/dmd/compilable/test21514.d +++ b/tests/dmd/compilable/test21514.d @@ -1,15 +1,16 @@ // https://issues.dlang.org/show_bug.cgi?id=21514 +// REQUIRED_ARGS: -verrors=simple // DISABLED: win32 win64 /* TEST_OUTPUT: --- -compilable/test21514.d(16): Deprecation: use of complex type `cdouble` is deprecated, use `std.complex.Complex!(double)` instead -compilable/test21514.d(16): Deprecation: use of complex type `cdouble` is deprecated, use `std.complex.Complex!(double)` instead -compilable/test21514.d(17): Deprecation: use of complex type `creal` is deprecated, use `std.complex.Complex!(real)` instead -compilable/test21514.d(17): Deprecation: use of complex type `creal` is deprecated, use `std.complex.Complex!(real)` instead -compilable/test21514.d(19): Deprecation: use of complex type `cdouble` is deprecated, use `std.complex.Complex!(double)` instead -compilable/test21514.d(19): Deprecation: use of complex type `cdouble` is deprecated, use `std.complex.Complex!(double)` instead -compilable/test21514.d(20): Deprecation: use of complex type `creal` is deprecated, use `std.complex.Complex!(real)` instead -compilable/test21514.d(20): Deprecation: use of complex type `creal` is deprecated, use `std.complex.Complex!(real)` instead +compilable/test21514.d(17): Deprecation: use of complex type `cdouble` is deprecated, use `std.complex.Complex!(double)` instead +compilable/test21514.d(17): Deprecation: use of complex type `cdouble` is deprecated, use `std.complex.Complex!(double)` instead +compilable/test21514.d(18): Deprecation: use of complex type `creal` is deprecated, use `std.complex.Complex!(real)` instead +compilable/test21514.d(18): Deprecation: use of complex type `creal` is deprecated, use `std.complex.Complex!(real)` instead +compilable/test21514.d(20): Deprecation: use of complex type `cdouble` is deprecated, use `std.complex.Complex!(double)` instead +compilable/test21514.d(20): Deprecation: use of complex type `cdouble` is deprecated, use `std.complex.Complex!(double)` instead +compilable/test21514.d(21): Deprecation: use of complex type `creal` is deprecated, use `std.complex.Complex!(real)` instead +compilable/test21514.d(21): Deprecation: use of complex type `creal` is deprecated, use `std.complex.Complex!(real)` instead --- */ diff --git a/tests/dmd/compilable/test22510.d b/tests/dmd/compilable/test22510.d index af5d0a433e..1207bf0226 100644 --- a/tests/dmd/compilable/test22510.d +++ b/tests/dmd/compilable/test22510.d @@ -7,7 +7,7 @@ struct S @disable this(this); this (scope ref inout S) inout { - this.b = b; + this.b = 0; } } diff --git a/tests/dmd/compilable/test22626.d b/tests/dmd/compilable/test22626.d index 5f72eb635c..099adc8e1a 100644 --- a/tests/dmd/compilable/test22626.d +++ b/tests/dmd/compilable/test22626.d @@ -20,4 +20,11 @@ class Oops // this shouldn't compile `k` is a field of class `Oops` static assert (!__traits(compiles, k = 2)); } + + // https://issues.dlang.org/show_bug.cgi?id=24705 + synchronized void foo(int n) + { + // Error: direct access to shared `n` is not allowed, see `core.atomic` + int a = n; + } } diff --git a/tests/dmd/compilable/test23097.d b/tests/dmd/compilable/test23097.d index 092bd774f2..333b2f2b8b 100644 --- a/tests/dmd/compilable/test23097.d +++ b/tests/dmd/compilable/test23097.d @@ -1,5 +1,5 @@ /* https://issues.dlang.org/show_bug.cgi?id=23097 -REQUIRED_ARGS: -verrors=spec +REQUIRED_ARGS: -verrors=simple -verrors=spec TEST_OUTPUT: --- (spec:2) compilable/test23097.d(14): Error: `inout` constructor `test23097.S23097.this` creates const object, not mutable diff --git a/tests/dmd/compilable/test23812.d b/tests/dmd/compilable/test23812.d new file mode 100644 index 0000000000..502a9d0a2a --- /dev/null +++ b/tests/dmd/compilable/test23812.d @@ -0,0 +1,75 @@ +// EXTRA_FILES: imports/imp23812.c + +import imports.imp23812; + +void callDefault() +{ + funcDefault(); + funcDefault2(); +} + +static assert(!__traits(compiles, () nothrow { funcDefault(); } )); +static assert(!__traits(compiles, () @nogc { funcDefault(); } )); +static assert(!__traits(compiles, () pure { funcDefault(); } )); + +static assert(!__traits(compiles, () nothrow { funcDefault2(); } )); +static assert(!__traits(compiles, () @nogc { funcDefault2(); } )); +static assert(!__traits(compiles, () pure { funcDefault2(); } )); + +void callNothrow() nothrow +{ + funcNothrow(); + funcNothrow2(); +} + +static assert(!__traits(compiles, () @nogc { funcNothrow(); } )); +static assert(!__traits(compiles, () pure { funcNothrow(); } )); + +static assert(!__traits(compiles, () @nogc { funcNothrow2(); } )); +static assert(!__traits(compiles, () pure { funcNothrow2(); } )); + +void callNogc() @nogc +{ + funcNogc(); +} + +static assert(!__traits(compiles, () nothrow { funcNogc(); } )); +static assert(!__traits(compiles, () pure { funcNogc(); } )); + +void callPure() pure +{ + funcPure(); +} + +static assert(!__traits(compiles, () nothrow { funcPure(); } )); +static assert(!__traits(compiles, () @nogc { funcPure(); } )); + +void callNothrowNogc() nothrow @nogc +{ + funcNothrowNogc(); + funcNothrowNogc2(); +} + +static assert(!__traits(compiles, () pure { funcNothrowNogc(); } )); + +static assert(!__traits(compiles, () pure { funcNothrowNogc2(); } )); + +extern(C) void callbackDefault() +{ +} + +extern(C) void callbackNothrow() nothrow +{ +} + +void callFuncWithCallback() nothrow +{ + funcWithCallback(&callbackNothrow); + + Callbacks callbacks; + callbacks.f = &callbackNothrow; +} + +static assert(!__traits(compiles, () { funcWithCallback(&callbackDefault); } )); + +static assert(!__traits(compiles, () { Callbacks callbacks; callbacks.f = &callbackDefault; } )); diff --git a/tests/dmd/compilable/test24733.c b/tests/dmd/compilable/test24733.c new file mode 100644 index 0000000000..84a07850ce --- /dev/null +++ b/tests/dmd/compilable/test24733.c @@ -0,0 +1,21 @@ +struct S1 {unsigned char c;int i;}; +#pragma pack(push, 2) +struct S2 {unsigned char c;int i;}; +#pragma pack(push, 1) +struct S3 {unsigned char c;int i;}; +#pragma pack(pop) +struct S4 {unsigned char c;int i;}; +#pragma pack(pop) +struct S5 {unsigned char c;int i;}; + +_Static_assert(_Alignof(struct S1) == 4, "alignof S1"); +_Static_assert(_Alignof(struct S2) == 2, "alignof S2"); +_Static_assert(_Alignof(struct S3) == 1, "alignof S3"); +_Static_assert(_Alignof(struct S4) == 2, "alignof S4"); +_Static_assert(_Alignof(struct S5) == 4, "alignof S5"); + +_Static_assert(sizeof(struct S1) == 8, "sizeof S1"); +_Static_assert(sizeof(struct S2) == 6, "sizeof S2"); +_Static_assert(sizeof(struct S3) == 5, "sizeof S3"); +_Static_assert(sizeof(struct S4) == 6, "sizeof S4"); +_Static_assert(sizeof(struct S5) == 8, "sizeof S5"); diff --git a/tests/dmd/compilable/test24762.d b/tests/dmd/compilable/test24762.d new file mode 100644 index 0000000000..91df3c57ab --- /dev/null +++ b/tests/dmd/compilable/test24762.d @@ -0,0 +1,11 @@ +// https://issues.dlang.org/show_bug.cgi?id=24762 + +struct S { int m; } + +string m() { return "m"; } +@nogc void f() +{ + S s; + enum t = m(); + auto x = __traits(getMember, s, m()); // Error: `@nogc` function `nogc.f` cannot call non-@nogc function `nogc.m` +} diff --git a/tests/dmd/compilable/test324.d b/tests/dmd/compilable/test324.d index 01ae9aeb64..51a757ffc7 100644 --- a/tests/dmd/compilable/test324.d +++ b/tests/dmd/compilable/test324.d @@ -1,12 +1,13 @@ // DISABLED: LDC +// REQUIRED_ARGS: -verrors=simple /* TEST_OUTPUT: --- -compilable/test324.d(17): Deprecation: function `test324.main.doStuff!((i) +compilable/test324.d(18): Deprecation: function `test324.main.doStuff!((i) { return i; } ).doStuff` function requires a dual-context, which is deprecated -compilable/test324.d(23): instantiated from here: `doStuff!((i) +compilable/test324.d(24): instantiated from here: `doStuff!((i) { return i; } diff --git a/tests/dmd/compilable/test9701.d b/tests/dmd/compilable/test9701.d index 68055c4dd4..81b56bdb65 100644 --- a/tests/dmd/compilable/test9701.d +++ b/tests/dmd/compilable/test9701.d @@ -1,9 +1,10 @@ // https://issues.dlang.org/show_bug.cgi?id=9701 +// REQUIRED_ARGS: -verrors=simple /* TEST_OUTPUT: --- -compilable/test9701.d(68): Deprecation: enum member `test9701.Enum.value7` is deprecated -compilable/test9701.d(68): Deprecation: enum member `test9701.Enum.value8` is deprecated - message +compilable/test9701.d(69): Deprecation: enum member `test9701.Enum.value7` is deprecated +compilable/test9701.d(69): Deprecation: enum member `test9701.Enum.value8` is deprecated - message --- */ diff --git a/tests/dmd/compilable/testInference.d b/tests/dmd/compilable/testInference.d index bddbaf4294..5091b99896 100644 --- a/tests/dmd/compilable/testInference.d +++ b/tests/dmd/compilable/testInference.d @@ -817,4 +817,22 @@ void test13840() nothrow {} } -// Add more tests regarding inferences later. +/***************************************************/ +// https://github.com/dlang/dmd/pull/20685 + +struct T1 +{ + int a; + inout this(ref inout T1 t) @nogc nothrow pure { a = t.a; } +} + +struct S1 +{ + T1 t; // generate copy constructor, infer @nogc nothrow pure +} + +void test1() @nogc nothrow pure +{ + S1 s; + S1 t = s; +} diff --git a/tests/dmd/compilable/testVRP.d b/tests/dmd/compilable/testVRP.d index bdd72d0962..33d366d172 100644 --- a/tests/dmd/compilable/testVRP.d +++ b/tests/dmd/compilable/testVRP.d @@ -4,6 +4,7 @@ // https://issues.dlang.org/show_bug.cgi?id=3147 // https://issues.dlang.org/show_bug.cgi?id=6000 // https://issues.dlang.org/show_bug.cgi?id=5225 +// https://issues.dlang.org/show_bug.cgi?id=24855 void add() { @@ -100,6 +101,10 @@ void divideFail() short w; byte y; static assert(!__traits(compiles, y = w / -1)); + static assert(!__traits(compiles, y = y / w)); + + short z; + static assert(!__traits(compiles, z = w / z + 1)); } void plus1Fail() diff --git a/tests/dmd/compilable/testcolor.sh b/tests/dmd/compilable/testcolor.sh index 8d0d070046..f8b12f2e60 100755 --- a/tests/dmd/compilable/testcolor.sh +++ b/tests/dmd/compilable/testcolor.sh @@ -15,17 +15,24 @@ compare() fi } -normalize() { tr -d "\n\r" ; } +normalize() +{ + if uname | grep -qi "freebsd"; then + tr -d "\n\r" | sed 's/void foo() {} void main() { goo(); }//; s/[[:space:]]\+\^$//' + else + tr -d "\n\r" | sed -E 's/void foo\(\) \{\} void main\(\) \{ goo\(\); \}//; s/\s+\^$//' + fi +} check() { local actual expected - actual=$(echo "$2" | ("$DMD" -c -o- "$1" - 2>&1 || true) | normalize) + actual=$(echo "$2" | ("$DMD" -c -o- -verrors=simple "$1" - 2>&1 || true) | normalize) compare "$actual" "$3" } -expectedWithoutColor=__stdin.d\(2\):\ Error:\ no\ identifier\ for\ declarator\ \`test\` -expectedWithColor=$'\033'\[1m__stdin.d\(2\):\ $'\033'\[1\;31mError:\ $'\033'\[mno\ identifier\ for\ declarator\ \`$'\033'\[0\;36m$'\033'\[m$'\033'\[1mtest$'\033'\[0\;36m$'\033'\[m\` +expectedWithoutColor='__stdin.d(2): Error: variable name expected after type `test`, not `End of File`' +expectedWithColor=$'\E[1m__stdin.d(2): \E[1;31mError: \E[mvariable name expected after type `\E[0;36m\E[m\E[1mtest\E[0;36m\E[m`, not `\E[0;36m\E[m\E[1mEnd\E[0;36m \E[m\E[1mof\E[0;36m \E[m\E[1mFile\E[0;36m\E[m`' check -c "test" "$expectedWithoutColor" check -color=auto "test" "$expectedWithoutColor" @@ -40,7 +47,7 @@ check -color=off "$gooCode" "$gooExpectedWithoutColor" if [[ "$(script --version)" == script\ from\ util-linux\ * ]] then - actual="$(SHELL="$(command -v bash)" TERM="faketerm" script -q -c "echo test | ( $DMD -c -o- -)" /dev/null | normalize)" || true + actual="$(SHELL="$(command -v bash)" TERM="faketerm" script -q -c "echo test | ( $DMD -c -o- -verrors=simple -)" /dev/null | normalize)" || true # Weird results for WSL, probably some environmental issue if uname -a | grep -i linux | grep -i microsoft &> /dev/null diff --git a/tests/dmd/compilable/testcstuff2.c b/tests/dmd/compilable/testcstuff2.c index 2af2bec2b4..5fab55d57a 100644 --- a/tests/dmd/compilable/testcstuff2.c +++ b/tests/dmd/compilable/testcstuff2.c @@ -137,6 +137,10 @@ struct S21968 extern int test21970a; extern char *test21970b; +/***************************************************/ +// https://issues.dlang.org/show_bug.cgi?id=24447 +extern int x = 3; + /***************************************************/ // https://issues.dlang.org/show_bug.cgi?id=21973 diff --git a/tests/dmd/compilable/testdefines.d b/tests/dmd/compilable/testdefines.d index 060e962920..58eefb7256 100644 --- a/tests/dmd/compilable/testdefines.d +++ b/tests/dmd/compilable/testdefines.d @@ -25,3 +25,17 @@ static assert(pr16199_ice == 3); static assert(pr16199d() == 7); static assert(pr16199c() == 8); + +// https://issues.dlang.org/show_bug.cgi?id=24639 +static assert(NEGATIVE_I32 == -1); +static assert(NEGATIVE_U32 == cast(uint)-2); +static assert(NEGATIVE_I64 == -3); +static assert(NEGATIVE_U64 == cast(ulong)-4L); +static assert(NEGATIVE_F32 == -5f); +static assert(NEGATIVE_F64 == -6); +static assert(is(typeof(NEGATIVE_I32) == int)); +static assert(is(typeof(NEGATIVE_U32) == uint)); +static assert(is(typeof(NEGATIVE_I64) == long)); +static assert(is(typeof(NEGATIVE_U64) == ulong)); +static assert(is(typeof(NEGATIVE_F32) == float)); +static assert(is(typeof(NEGATIVE_F64) == double)); diff --git a/tests/dmd/compilable/traits.d b/tests/dmd/compilable/traits.d index d5e2cb0f22..09538b1dd4 100644 --- a/tests/dmd/compilable/traits.d +++ b/tests/dmd/compilable/traits.d @@ -137,6 +137,8 @@ struct DisabledPostblit struct NoCpCtor { } class C19902 { } +struct MoveCtor { this(MoveCtor) { } } + static assert(__traits(hasCopyConstructor, S)); static assert(__traits(hasCopyConstructor, OuterS.S)); static assert(__traits(hasCopyConstructor, OuterS)); @@ -147,6 +149,8 @@ static assert(__traits(hasCopyConstructor, U!S)); static assert(!__traits(hasPostblit, U!S)); static assert(__traits(hasPostblit, SPostblit)); static assert(!__traits(hasCopyConstructor, SPostblit)); +static assert(__traits(hasMoveConstructor, MoveCtor)); +static assert(!__traits(hasMoveConstructor, NoCpCtor)); static assert(!__traits(hasCopyConstructor, NoCpCtor)); static assert(!__traits(hasCopyConstructor, C19902)); diff --git a/tests/dmd/compilable/udamodule1.d b/tests/dmd/compilable/udamodule1.d index 434cf5125b..76e9fb7488 100644 --- a/tests/dmd/compilable/udamodule1.d +++ b/tests/dmd/compilable/udamodule1.d @@ -1,4 +1,4 @@ -// REQUIRED_ARGS: +// REQUIRED_ARGS: -verrors=simple // PERMUTE_ARGS: // EXTRA_FILES: imports/udamodule1.d /* diff --git a/tests/dmd/compilable/verrors_spec.d b/tests/dmd/compilable/verrors_spec.d index b858008922..9768ba10da 100644 --- a/tests/dmd/compilable/verrors_spec.d +++ b/tests/dmd/compilable/verrors_spec.d @@ -1,6 +1,6 @@ /* PERMUTE_ARGS: -REQUIRED_ARGS: -verrors=spec +REQUIRED_ARGS: -verrors=simple -verrors=spec TEST_OUTPUT: --- (spec:1) compilable/verrors_spec.d(13): Error: cannot implicitly convert expression `& i` of type `int*` to `int` diff --git a/tests/dmd/dub_package/avg.d b/tests/dmd/dub_package/avg.d index 9be5a0a391..13b1495ec1 100755 --- a/tests/dmd/dub_package/avg.d +++ b/tests/dmd/dub_package/avg.d @@ -13,7 +13,7 @@ import dmd.astbase; import dmd.errorsink; import dmd.parse; import dmd.target; -import dmd.transitivevisitor; +import dmd.visitor.transitive; import dmd.globals; import dmd.id; diff --git a/tests/dmd/dub_package/impvisitor.d b/tests/dmd/dub_package/impvisitor.d index 8619748f94..a578deaf77 100755 --- a/tests/dmd/dub_package/impvisitor.d +++ b/tests/dmd/dub_package/impvisitor.d @@ -3,8 +3,8 @@ dependency "dmd" path="../../.." +/ -import dmd.permissivevisitor; -import dmd.transitivevisitor; +import dmd.visitor.permissive; +import dmd.visitor.transitive; import dmd.tokens; import dmd.common.outbuffer; diff --git a/tests/dmd/dub_package/retrieve_scope.d b/tests/dmd/dub_package/retrieve_scope.d index 05dd643e06..8eb1decd42 100755 --- a/tests/dmd/dub_package/retrieve_scope.d +++ b/tests/dmd/dub_package/retrieve_scope.d @@ -67,7 +67,7 @@ int main() global.path.push((dmdParentDir ~ "/druntime/import" ~ '\0').ptr); // comment for error output in parsing & semantic - diagnosticHandler = (const ref Loc location, + diagnosticHandler = (const ref SourceLoc location, Color headerColor, const(char)* header, const(char)* messageFormat, diff --git a/tests/dmd/fail_compilation/alias_instance_member.d b/tests/dmd/fail_compilation/alias_instance_member.d index 9f3729ed23..8ebdbbb131 100644 --- a/tests/dmd/fail_compilation/alias_instance_member.d +++ b/tests/dmd/fail_compilation/alias_instance_member.d @@ -1,7 +1,7 @@ /* TEST_OUTPUT: --- -fail_compilation/alias_instance_member.d(18): Error: cannot alias member of variable `that` +fail_compilation/alias_instance_member.d(18): Error: cannot alias variable member `v` of variable `that` fail_compilation/alias_instance_member.d(18): Use `typeof(that)` instead to preserve behaviour --- */ @@ -17,6 +17,7 @@ struct Foo alias a = this.v; // OK alias b = that.v; assert(&a is &b); + alias b2 = typeof(that).v; // OK } } diff --git a/tests/dmd/fail_compilation/alias_instance_member2.d b/tests/dmd/fail_compilation/alias_instance_member2.d index 752ef140e5..f31cb81257 100644 --- a/tests/dmd/fail_compilation/alias_instance_member2.d +++ b/tests/dmd/fail_compilation/alias_instance_member2.d @@ -1,8 +1,10 @@ /* TEST_OUTPUT: --- -fail_compilation/alias_instance_member2.d(20): Error: cannot alias member of variable `f` -fail_compilation/alias_instance_member2.d(20): Use `typeof(f)` instead to preserve behaviour +fail_compilation/alias_instance_member2.d(26): Error: cannot alias variable member `v` of variable `f` +fail_compilation/alias_instance_member2.d(26): Use `typeof(f)` instead to preserve behaviour +fail_compilation/alias_instance_member2.d(30): Error: cannot alias function member `fun` of variable `f` +fail_compilation/alias_instance_member2.d(30): Use `typeof(f)` instead to preserve behaviour --- */ @@ -12,10 +14,19 @@ module aim; struct Foo { int v; + static int w; + enum x = 5; + void fun() {} + static void gun() {} } struct Bar { Foo f; alias v = f.v; + alias v2 = typeof(f).v; // OK + alias w = f.w; // OK + alias x = f.x; // OK + alias fun = f.fun; + alias gun = f.gun; // OK } diff --git a/tests/dmd/fail_compilation/attributediagnostic.d b/tests/dmd/fail_compilation/attributediagnostic.d index 523a183d76..2a3cfee337 100644 --- a/tests/dmd/fail_compilation/attributediagnostic.d +++ b/tests/dmd/fail_compilation/attributediagnostic.d @@ -1,20 +1,17 @@ /* TEST_OUTPUT: --- -fail_compilation/attributediagnostic.d(24): Error: `@safe` function `attributediagnostic.layer2` cannot call `@system` function `attributediagnostic.layer1` -fail_compilation/attributediagnostic.d(26): which calls `attributediagnostic.layer0` -fail_compilation/attributediagnostic.d(28): which calls `attributediagnostic.system` -fail_compilation/attributediagnostic.d(30): which wasn't inferred `@safe` because of: -fail_compilation/attributediagnostic.d(30): `asm` statement is assumed to be `@system` - mark it with `@trusted` if it is not -fail_compilation/attributediagnostic.d(25): `attributediagnostic.layer1` is declared here -fail_compilation/attributediagnostic.d(46): Error: `@safe` function `D main` cannot call `@system` function `attributediagnostic.system1` -fail_compilation/attributediagnostic.d(35): which wasn't inferred `@safe` because of: -fail_compilation/attributediagnostic.d(35): cast from `uint` to `int*` not allowed in safe code -fail_compilation/attributediagnostic.d(33): `attributediagnostic.system1` is declared here -fail_compilation/attributediagnostic.d(47): Error: `@safe` function `D main` cannot call `@system` function `attributediagnostic.system2` -fail_compilation/attributediagnostic.d(41): which wasn't inferred `@safe` because of: -fail_compilation/attributediagnostic.d(41): `@safe` function `system2` cannot call `@system` `fsys` -fail_compilation/attributediagnostic.d(39): `attributediagnostic.system2` is declared here +fail_compilation/attributediagnostic.d(21): Error: `@safe` function `attributediagnostic.layer2` cannot call `@system` function `attributediagnostic.layer1` +fail_compilation/attributediagnostic.d(23): which calls `layer0` +fail_compilation/attributediagnostic.d(25): which calls `system` +fail_compilation/attributediagnostic.d(27): and executing an `asm` statement without `@trusted` annotation makes it fail to infer `@safe` +fail_compilation/attributediagnostic.d(22): `attributediagnostic.layer1` is declared here +fail_compilation/attributediagnostic.d(43): Error: `@safe` function `D main` cannot call `@system` function `attributediagnostic.system1` +fail_compilation/attributediagnostic.d(32): and cast from `uint` to `int*` makes it fail to infer `@safe` +fail_compilation/attributediagnostic.d(30): `attributediagnostic.system1` is declared here +fail_compilation/attributediagnostic.d(44): Error: `@safe` function `D main` cannot call `@system` function `attributediagnostic.system2` +fail_compilation/attributediagnostic.d(38): and calling `@system` `fsys` makes it fail to infer `@safe` +fail_compilation/attributediagnostic.d(36): `attributediagnostic.system2` is declared here --- */ diff --git a/tests/dmd/fail_compilation/attributediagnostic_nogc.d b/tests/dmd/fail_compilation/attributediagnostic_nogc.d index e3dbee899f..f54a4e91d9 100644 --- a/tests/dmd/fail_compilation/attributediagnostic_nogc.d +++ b/tests/dmd/fail_compilation/attributediagnostic_nogc.d @@ -2,19 +2,15 @@ TEST_OUTPUT: --- fail_compilation/attributediagnostic_nogc.d(21): Error: `@nogc` function `attributediagnostic_nogc.layer2` cannot call non-@nogc function `attributediagnostic_nogc.layer1` -fail_compilation/attributediagnostic_nogc.d(22): which calls `attributediagnostic_nogc.layer0` -fail_compilation/attributediagnostic_nogc.d(23): which calls `attributediagnostic_nogc.gc` -fail_compilation/attributediagnostic_nogc.d(27): which wasn't inferred `@nogc` because of: -fail_compilation/attributediagnostic_nogc.d(27): `asm` statement in function `attributediagnostic_nogc.gc` is assumed to use the GC - mark it with `@nogc` if it does not +fail_compilation/attributediagnostic_nogc.d(22): which calls `layer0` +fail_compilation/attributediagnostic_nogc.d(23): which calls `gc` +fail_compilation/attributediagnostic_nogc.d(27): and executing an `asm` statement without `@nogc` annotation makes it fail to infer `@nogc` fail_compilation/attributediagnostic_nogc.d(43): Error: `@nogc` function `D main` cannot call non-@nogc function `attributediagnostic_nogc.gc1` -fail_compilation/attributediagnostic_nogc.d(32): which wasn't inferred `@nogc` because of: -fail_compilation/attributediagnostic_nogc.d(32): cannot use `new` in `@nogc` function `attributediagnostic_nogc.gc1` +fail_compilation/attributediagnostic_nogc.d(32): and allocating with `new` makes it fail to infer `@nogc` fail_compilation/attributediagnostic_nogc.d(44): Error: `@nogc` function `D main` cannot call non-@nogc function `attributediagnostic_nogc.gc2` -fail_compilation/attributediagnostic_nogc.d(38): which wasn't inferred `@nogc` because of: -fail_compilation/attributediagnostic_nogc.d(38): `@nogc` function `attributediagnostic_nogc.gc2` cannot call non-@nogc `fgc` +fail_compilation/attributediagnostic_nogc.d(38): and calling non-@nogc `fgc` makes it fail to infer `@nogc` fail_compilation/attributediagnostic_nogc.d(45): Error: `@nogc` function `D main` cannot call non-@nogc function `attributediagnostic_nogc.gcClosure` -fail_compilation/attributediagnostic_nogc.d(48): which wasn't inferred `@nogc` because of: -fail_compilation/attributediagnostic_nogc.d(48): function `attributediagnostic_nogc.gcClosure` is `@nogc` yet allocates closure for `gcClosure()` with the GC +fail_compilation/attributediagnostic_nogc.d(48): and allocating a closure for `gcClosure()` makes it fail to infer `@nogc` --- */ #line 18 diff --git a/tests/dmd/fail_compilation/attributediagnostic_nothrow.d b/tests/dmd/fail_compilation/attributediagnostic_nothrow.d index 7fea322441..3b5c17a0aa 100644 --- a/tests/dmd/fail_compilation/attributediagnostic_nothrow.d +++ b/tests/dmd/fail_compilation/attributediagnostic_nothrow.d @@ -1,17 +1,15 @@ /* TEST_OUTPUT: --- -fail_compilation/attributediagnostic_nothrow.d(21): Error: function `attributediagnostic_nothrow.layer1` is not `nothrow` -fail_compilation/attributediagnostic_nothrow.d(22): which calls `attributediagnostic_nothrow.layer0` -fail_compilation/attributediagnostic_nothrow.d(23): which calls `attributediagnostic_nothrow.gc` -fail_compilation/attributediagnostic_nothrow.d(27): which wasn't inferred `nothrow` because of: -fail_compilation/attributediagnostic_nothrow.d(27): `asm` statement is assumed to throw - mark it with `nothrow` if it does not -fail_compilation/attributediagnostic_nothrow.d(21): Error: function `attributediagnostic_nothrow.layer2` may throw but is marked as `nothrow` -fail_compilation/attributediagnostic_nothrow.d(43): Error: function `attributediagnostic_nothrow.gc1` is not `nothrow` -fail_compilation/attributediagnostic_nothrow.d(32): which wasn't inferred `nothrow` because of: -fail_compilation/attributediagnostic_nothrow.d(32): `object.Exception` is thrown but not caught -fail_compilation/attributediagnostic_nothrow.d(44): Error: function `attributediagnostic_nothrow.gc2` is not `nothrow` -fail_compilation/attributediagnostic_nothrow.d(41): Error: function `D main` may throw but is marked as `nothrow` +fail_compilation/attributediagnostic_nothrow.d(19): Error: function `attributediagnostic_nothrow.layer1` is not `nothrow` +fail_compilation/attributediagnostic_nothrow.d(20): which calls `layer0` +fail_compilation/attributediagnostic_nothrow.d(21): which calls `gc` +fail_compilation/attributediagnostic_nothrow.d(25): and executing an `asm` statement without a `nothrow` annotation makes it fail to infer `nothrow` +fail_compilation/attributediagnostic_nothrow.d(19): Error: function `attributediagnostic_nothrow.layer2` may throw but is marked as `nothrow` +fail_compilation/attributediagnostic_nothrow.d(41): Error: function `attributediagnostic_nothrow.gc1` is not `nothrow` +fail_compilation/attributediagnostic_nothrow.d(30): and `object.Exception` being thrown but not caught makes it fail to infer `nothrow` +fail_compilation/attributediagnostic_nothrow.d(42): Error: function `attributediagnostic_nothrow.gc2` is not `nothrow` +fail_compilation/attributediagnostic_nothrow.d(39): Error: function `D main` may throw but is marked as `nothrow` --- */ diff --git a/tests/dmd/fail_compilation/attributediagnostic_pure.d b/tests/dmd/fail_compilation/attributediagnostic_pure.d index a120dabf85..ef3863c475 100644 --- a/tests/dmd/fail_compilation/attributediagnostic_pure.d +++ b/tests/dmd/fail_compilation/attributediagnostic_pure.d @@ -1,9 +1,8 @@ /* TEST_OUTPUT: --- -fail_compilation/attributediagnostic_pure.d(20): Error: `pure` function `D main` cannot call impure function `attributediagnostic_pure.gc` -fail_compilation/attributediagnostic_pure.d(15): which wasn't inferred `pure` because of: -fail_compilation/attributediagnostic_pure.d(15): `asm` statement is assumed to be impure - mark it with `pure` if it is not +fail_compilation/attributediagnostic_pure.d(19): Error: `pure` function `D main` cannot call impure function `attributediagnostic_pure.gc` +fail_compilation/attributediagnostic_pure.d(14): and executing an `asm` statement without `pure` annotation makes it fail to infer `pure` --- */ diff --git a/tests/dmd/fail_compilation/auto_ref_inout.d b/tests/dmd/fail_compilation/auto_ref_inout.d new file mode 100644 index 0000000000..8d53012a06 --- /dev/null +++ b/tests/dmd/fail_compilation/auto_ref_inout.d @@ -0,0 +1,12 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/auto_ref_inout.d(12): Error: template `f` is not callable using argument types `!()(int)` +fail_compilation/auto_ref_inout.d(10): Candidate is: `f(T)(auto ref inout T a, auto ref inout T b)` +--- +*/ + +// https://issues.dlang.org/show_bug.cgi?id=24707 +auto ref inout(T) f(T)(auto ref inout T a, auto ref inout T b); + +enum e = f(5); diff --git a/tests/dmd/fail_compilation/b17285.d b/tests/dmd/fail_compilation/b17285.d index 7b79cf0666..9bf5d96cb2 100644 --- a/tests/dmd/fail_compilation/b17285.d +++ b/tests/dmd/fail_compilation/b17285.d @@ -1,9 +1,10 @@ /* TEST_OUTPUT: --- -fail_compilation/b17285.d(14): Error: type `ONE` has no value -fail_compilation/b17285.d(14): Error: type `TWO` has no value -fail_compilation/b17285.d(14): Error: cannot implicitly convert expression `ONE` of type `b17285.ONE` to `int` +fail_compilation/b17285.d(15): Error: type `ONE` has no value +fail_compilation/b17285.d(15): perhaps use `ONE.init` +fail_compilation/b17285.d(15): Error: type `TWO` has no value +fail_compilation/b17285.d(15): Error: cannot implicitly convert expression `ONE` of type `b17285.ONE` to `int` --- */ diff --git a/tests/dmd/fail_compilation/b19523.d b/tests/dmd/fail_compilation/b19523.d index 3b2a5e0f22..5ab491faea 100644 --- a/tests/dmd/fail_compilation/b19523.d +++ b/tests/dmd/fail_compilation/b19523.d @@ -3,7 +3,7 @@ TEST_OUTPUT: ---- fail_compilation/b19523.d(13): Error: undefined identifier `SomeStruct` fail_compilation/b19523.d(14): Error: function `foo` is not callable using argument types `(_error_)` -fail_compilation/b19523.d(14): cannot pass argument `__lambda_L14_C6` of type `_error_` to parameter `int delegate() arg` +fail_compilation/b19523.d(14): cannot pass argument `__error` of type `_error_` to parameter `int delegate() arg` fail_compilation/b19523.d(19): `b19523.foo(int delegate() arg)` declared here ---- */ diff --git a/tests/dmd/fail_compilation/binexperr.d b/tests/dmd/fail_compilation/binexperr.d new file mode 100644 index 0000000000..695b2746b1 --- /dev/null +++ b/tests/dmd/fail_compilation/binexperr.d @@ -0,0 +1,14 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/binexperr.d(12): Error: expression expected, not `)` +fail_compilation/binexperr.d(12): Error: missing closing `)` after `if (A1 * (__error)` +--- +*/ + +void main() +{ + struct A1 {} + if (A1*) {} + return; +} diff --git a/tests/dmd/fail_compilation/biterrors5.d b/tests/dmd/fail_compilation/biterrors5.d new file mode 100644 index 0000000000..2665d69cc9 --- /dev/null +++ b/tests/dmd/fail_compilation/biterrors5.d @@ -0,0 +1,25 @@ +/* REQUIRED_ARGS: -preview=bitfields + * TEST_OUTPUT: +--- +fail_compilation/biterrors5.d(23): Error: bitfield symbol expected not struct `biterrors5.S` +fail_compilation/biterrors5.d(24): Error: bitfield symbol expected not variable `biterrors5.test0.i` +--- +*/ + +struct S +{ + int a,b; + int :2, c:3; +} + +static assert(__traits(getBitfieldOffset, S.b) == 0); +static assert(__traits(getBitfieldWidth, S.b) == 32); +static assert(__traits(getBitfieldOffset, S.c) == 2); +static assert(__traits(getBitfieldWidth, S.c) == 3); + +void test0() +{ + int i; + i = __traits(getBitfieldOffset, S); + i = __traits(getBitfieldOffset, i); +} diff --git a/tests/dmd/fail_compilation/bool_cast.d b/tests/dmd/fail_compilation/bool_cast.d index 830360a74c..e648429ce1 100644 --- a/tests/dmd/fail_compilation/bool_cast.d +++ b/tests/dmd/fail_compilation/bool_cast.d @@ -2,11 +2,11 @@ REQUIRED_ARGS: -de -preview=dip1000 TEST_OUTPUT: --- -fail_compilation/bool_cast.d(17): Deprecation: cast from `ubyte[]` to `bool[]` not allowed in safe code +fail_compilation/bool_cast.d(17): Deprecation: cast from `ubyte[]` to `bool[]` will become `@system` in a future release fail_compilation/bool_cast.d(17): Source element may have bytes which are not 0 or 1 -fail_compilation/bool_cast.d(22): Deprecation: cast from `int*` to `bool*` not allowed in safe code +fail_compilation/bool_cast.d(22): Deprecation: cast from `int*` to `bool*` will become `@system` in a future release fail_compilation/bool_cast.d(22): Source element may have bytes which are not 0 or 1 -fail_compilation/bool_cast.d(24): Deprecation: cast from `bool*` to `byte*` not allowed in safe code +fail_compilation/bool_cast.d(24): Deprecation: cast from `bool*` to `byte*` will become `@system` in a future release fail_compilation/bool_cast.d(24): Target element could be assigned a byte which is not 0 or 1 --- */ diff --git a/tests/dmd/fail_compilation/bug19569.d b/tests/dmd/fail_compilation/bug19569.d index a314142a53..1bf6fb5abd 100644 --- a/tests/dmd/fail_compilation/bug19569.d +++ b/tests/dmd/fail_compilation/bug19569.d @@ -1,51 +1,51 @@ /* TEST_OUTPUT: --- -fail_compilation/bug19569.d(70): Error: `bug19569.test0` called with argument types `()` matches both: +fail_compilation/bug19569.d(70): Error: `bug19569.test0` called with argument types `()` matches multiple overloads exactly: fail_compilation/bug19569.d(56): `bug19569.test0()` and: fail_compilation/bug19569.d(57): `bug19569.test0()` -fail_compilation/bug19569.d(71): Error: `bug19569.test1` called with argument types `()` matches both: +fail_compilation/bug19569.d(71): Error: `bug19569.test1` called with argument types `()` matches multiple overloads exactly: fail_compilation/bug19569.d(59): `bug19569.test1()` and: fail_compilation/bug19569.d(60): `bug19569.test1()` -fail_compilation/bug19569.d(72): Error: `bug19569.test2` called with argument types `()` matches both: +fail_compilation/bug19569.d(72): Error: `bug19569.test2` called with argument types `()` matches multiple overloads exactly: fail_compilation/bug19569.d(62): `bug19569.test2!().test2()` and: fail_compilation/bug19569.d(63): `bug19569.test2!().test2()` -fail_compilation/bug19569.d(73): Error: `bug19569.test3` called with argument types `()` matches both: +fail_compilation/bug19569.d(73): Error: `bug19569.test3` called with argument types `()` matches multiple overloads exactly: fail_compilation/bug19569.d(65): `bug19569.test3!().test3()` and: fail_compilation/bug19569.d(66): `bug19569.test3!().test3()` -fail_compilation/bug19569.d(78): Error: `bug19569.test0` called with argument types `()` matches both: +fail_compilation/bug19569.d(78): Error: `bug19569.test0` called with argument types `()` matches multiple overloads exactly: fail_compilation/bug19569.d(56): `bug19569.test0()` and: fail_compilation/bug19569.d(57): `bug19569.test0()` -fail_compilation/bug19569.d(79): Error: `bug19569.test1` called with argument types `()` matches both: +fail_compilation/bug19569.d(79): Error: `bug19569.test1` called with argument types `()` matches multiple overloads exactly: fail_compilation/bug19569.d(59): `bug19569.test1()` and: fail_compilation/bug19569.d(60): `bug19569.test1()` -fail_compilation/bug19569.d(80): Error: `bug19569.test2` called with argument types `()` matches both: +fail_compilation/bug19569.d(80): Error: `bug19569.test2` called with argument types `()` matches multiple overloads exactly: fail_compilation/bug19569.d(62): `bug19569.test2!().test2()` and: fail_compilation/bug19569.d(63): `bug19569.test2!().test2()` -fail_compilation/bug19569.d(81): Error: `bug19569.test3` called with argument types `()` matches both: +fail_compilation/bug19569.d(81): Error: `bug19569.test3` called with argument types `()` matches multiple overloads exactly: fail_compilation/bug19569.d(65): `bug19569.test3!().test3()` and: fail_compilation/bug19569.d(66): `bug19569.test3!().test3()` -fail_compilation/bug19569.d(86): Error: `bug19569.test0` called with argument types `()` matches both: +fail_compilation/bug19569.d(86): Error: `bug19569.test0` called with argument types `()` matches multiple overloads exactly: fail_compilation/bug19569.d(56): `bug19569.test0()` and: fail_compilation/bug19569.d(57): `bug19569.test0()` -fail_compilation/bug19569.d(87): Error: `bug19569.test1` called with argument types `()` matches both: +fail_compilation/bug19569.d(87): Error: `bug19569.test1` called with argument types `()` matches multiple overloads exactly: fail_compilation/bug19569.d(59): `bug19569.test1()` and: fail_compilation/bug19569.d(60): `bug19569.test1()` -fail_compilation/bug19569.d(88): Error: `bug19569.test2` called with argument types `()` matches both: +fail_compilation/bug19569.d(88): Error: `bug19569.test2` called with argument types `()` matches multiple overloads exactly: fail_compilation/bug19569.d(62): `bug19569.test2!().test2()` and: fail_compilation/bug19569.d(63): `bug19569.test2!().test2()` -fail_compilation/bug19569.d(89): Error: `bug19569.test3` called with argument types `()` matches both: +fail_compilation/bug19569.d(89): Error: `bug19569.test3` called with argument types `()` matches multiple overloads exactly: fail_compilation/bug19569.d(65): `bug19569.test3!().test3()` and: fail_compilation/bug19569.d(66): `bug19569.test3!().test3()` diff --git a/tests/dmd/fail_compilation/bug9631.d b/tests/dmd/fail_compilation/bug9631.d index 833cc95c0f..157a07584e 100644 --- a/tests/dmd/fail_compilation/bug9631.d +++ b/tests/dmd/fail_compilation/bug9631.d @@ -4,7 +4,7 @@ TEST_OUTPUT: fail_compilation/bug9631.d(20): Error: cannot implicitly convert expression `F()` of type `bug9631.T1!().F` to `bug9631.T2!().F` --- */ -// DISABLED: win32 + template T1() { struct F { } @@ -19,11 +19,11 @@ void main() { T2!().F x = T1!().F(); } - /* TEST_OUTPUT: --- -fail_compilation/bug9631.d(41): Error: incompatible types for `(x) == (y)`: `bug9631.S` and `bug9631.tem!().S` +fail_compilation/bug9631.d(41): Error: no operator `==` for type `S` +fail_compilation/bug9631.d(30): perhaps overload it with `bool opEquals(S other) const {}` --- */ @@ -65,7 +65,7 @@ TEST_OUTPUT: fail_compilation/bug9631.d(80): Error: function `f` is not callable using argument types `(int, S)` fail_compilation/bug9631.d(80): cannot pass argument `y` of type `bug9631.tem!().S` to parameter `bug9631.S s` fail_compilation/bug9631.d(79): `bug9631.arg.f(int i, S s)` declared here -fail_compilation/bug9631.d(81): Error: function literal `__lambda_L81_C5(S s)` is not callable using argument types `(S)` +fail_compilation/bug9631.d(81): Error: function literal `(S s) { }` is not callable using argument types `(S)` fail_compilation/bug9631.d(81): cannot pass argument `x` of type `bug9631.S` to parameter `bug9631.tem!().S s` fail_compilation/bug9631.d(87): Error: constructor `bug9631.arg.A.this(S __param_0)` is not callable using argument types `(S)` fail_compilation/bug9631.d(87): cannot pass argument `S(0)` of type `bug9631.tem!().S` to parameter `bug9631.S __param_0` diff --git a/tests/dmd/fail_compilation/cast_qual.d b/tests/dmd/fail_compilation/cast_qual.d index 5821dc1499..500724d7a5 100644 --- a/tests/dmd/fail_compilation/cast_qual.d +++ b/tests/dmd/fail_compilation/cast_qual.d @@ -2,9 +2,9 @@ REQUIRED_ARGS: -preview=dip1000 -de TEST_OUTPUT: --- -fail_compilation/cast_qual.d(17): Deprecation: cast from `const(int)` to `int` cannot be used as an lvalue in @safe code -fail_compilation/cast_qual.d(19): Deprecation: cast from `const(int)` to `int` cannot be used as an lvalue in @safe code -fail_compilation/cast_qual.d(25): Error: cast from `const(Object)` to `object.Object` not allowed in safe code +fail_compilation/cast_qual.d(17): Deprecation: using the result of a cast from `const(int)` to `int` as an lvalue will become `@system` in a future release +fail_compilation/cast_qual.d(19): Deprecation: using the result of a cast from `const(int)` to `int` as an lvalue will become `@system` in a future release +fail_compilation/cast_qual.d(25): Error: cast from `const(Object)` to `object.Object` is not allowed in a `@safe` function fail_compilation/cast_qual.d(25): Incompatible type qualifier --- */ diff --git a/tests/dmd/fail_compilation/cenums.c b/tests/dmd/fail_compilation/cenums.c index 570a0de10b..b8c4157ba5 100644 --- a/tests/dmd/fail_compilation/cenums.c +++ b/tests/dmd/fail_compilation/cenums.c @@ -3,7 +3,6 @@ fail_compilation/cenums.c(202): Error: `enum E2` is incomplete without members fail_compilation/cenums.c(303): Error: redeclaring `union E3` as `enum E3` fail_compilation/cenums.c(502): Error: enum member `cenums.test5.F.a` conflicts with enum member `cenums.test5.F.a` at fail_compilation/cenums.c(502) -fail_compilation/cenums.c(502): Error: enum member `cenums.test5.F.a` conflicts with enum member `cenums.test5.F.a` at fail_compilation/cenums.c(502) --- */ diff --git a/tests/dmd/fail_compilation/code_global_scope.d b/tests/dmd/fail_compilation/code_global_scope.d new file mode 100644 index 0000000000..4db84c02e7 --- /dev/null +++ b/tests/dmd/fail_compilation/code_global_scope.d @@ -0,0 +1,26 @@ +/** +TEST_OUTPUT: +--- +fail_compilation/code_global_scope.d(18): Error: `switch` statement must be inside function scope +fail_compilation/code_global_scope.d(19): Error: `do` statement must be inside function scope +fail_compilation/code_global_scope.d(20): Error: `foreach` statement must be inside function scope +fail_compilation/code_global_scope.d(21): Error: `while` statement must be inside function scope +fail_compilation/code_global_scope.d(22): Error: `if` statement must be inside function scope +fail_compilation/code_global_scope.d(23): Error: `return` statement must be inside function scope +fail_compilation/code_global_scope.d(24): Error: `goto` statement must be inside function scope +fail_compilation/code_global_scope.d(25): Error: `continue` statement must be inside function scope +fail_compilation/code_global_scope.d(26): Error: `break` statement must be inside function scope +--- +*/ + + + +switch s; +do d; +foreach (i; 0 .. 4) {} +while (x) {} +if (y) {} +return 0; +goto A; +continue B; +break; diff --git a/tests/dmd/fail_compilation/constraints_defs.d b/tests/dmd/fail_compilation/constraints_defs.d index b3a335b4e2..2a9aff6dcd 100755 --- a/tests/dmd/fail_compilation/constraints_defs.d +++ b/tests/dmd/fail_compilation/constraints_defs.d @@ -5,7 +5,7 @@ TEST_OUTPUT: fail_compilation/constraints_defs.d(49): Error: template instance `constraints_defs.main.def!(int, 0, (a) => a)` does not match template declaration `def(T, int i = 5, alias R)()` with `T = int, i = 0, - R = __lambda_L49_C18` + R = (a) => a` must satisfy the following constraint: ` N!T` fail_compilation/constraints_defs.d(50): Error: template instance `imports.constraints.defa!int` does not match template declaration `defa(T, U = int)()` diff --git a/tests/dmd/fail_compilation/constraints_tmpl.d b/tests/dmd/fail_compilation/constraints_tmpl.d index fee7a3e3e7..1c4b97a99f 100755 --- a/tests/dmd/fail_compilation/constraints_tmpl.d +++ b/tests/dmd/fail_compilation/constraints_tmpl.d @@ -22,7 +22,7 @@ fail_compilation/constraints_tmpl.d(41): Error: template instance `imports.const ` N!T N!U` fail_compilation/constraints_tmpl.d(43): Error: template instance `constraints_tmpl.main.lambda!((a) => a)` does not match template declaration `lambda(alias pred)()` - with `pred = __lambda_L43_C13` + with `pred = (a) => a` must satisfy the following constraint: ` N!int` --- diff --git a/tests/dmd/fail_compilation/cpp_cast.d b/tests/dmd/fail_compilation/cpp_cast.d index 3802ebd5e9..3fc82b1b9e 100644 --- a/tests/dmd/fail_compilation/cpp_cast.d +++ b/tests/dmd/fail_compilation/cpp_cast.d @@ -3,9 +3,9 @@ REQUIRED_ARGS: -de TEST_OUTPUT: --- -fail_compilation/cpp_cast.d(19): Error: cast from `cpp_cast.I` to `cpp_cast.C` not allowed in safe code +fail_compilation/cpp_cast.d(19): Error: cast from `cpp_cast.I` to `cpp_cast.C` is not allowed in a `@safe` function fail_compilation/cpp_cast.d(19): No dynamic type information for extern(C++) classes -fail_compilation/cpp_cast.d(21): Deprecation: cast from `cpp_cast.C` to `cpp_cast.D` not allowed in safe code +fail_compilation/cpp_cast.d(21): Deprecation: cast from `cpp_cast.C` to `cpp_cast.D` will become `@system` in a future release fail_compilation/cpp_cast.d(21): No dynamic type information for extern(C++) classes --- */ diff --git a/tests/dmd/fail_compilation/cppeh1.d b/tests/dmd/fail_compilation/cppeh1.d index 9ef0ab3c79..c64d3bf2b7 100644 --- a/tests/dmd/fail_compilation/cppeh1.d +++ b/tests/dmd/fail_compilation/cppeh1.d @@ -2,7 +2,7 @@ /* TEST_OUTPUT: --- -fail_compilation/cppeh1.d(26): Error: cannot catch C++ class objects in `@safe` code +fail_compilation/cppeh1.d(26): Error: catching C++ class objects is not allowed in a `@safe` function --- */ diff --git a/tests/dmd/fail_compilation/cppmangle.d b/tests/dmd/fail_compilation/cppmangle.d index b3c89b46d2..479e6e924d 100644 --- a/tests/dmd/fail_compilation/cppmangle.d +++ b/tests/dmd/fail_compilation/cppmangle.d @@ -1,10 +1,10 @@ /* TEST_OUTPUT: --- -fail_compilation/cppmangle.d(11): Error: expected valid identifier for C++ namespace but got `` -fail_compilation/cppmangle.d(15): Error: expected valid identifier for C++ namespace but got `0num` +fail_compilation/cppmangle.d(11): Error: expected valid identifier for C++ namespace but got `""` +fail_compilation/cppmangle.d(15): Error: expected valid identifier for C++ namespace but got `"0num"` fail_compilation/cppmangle.d(19): Error: compile time string constant (or sequence) expected, not `2` -fail_compilation/cppmangle.d(23): Error: expected valid identifier for C++ namespace but got `invalid@namespace` +fail_compilation/cppmangle.d(23): Error: expected valid identifier for C++ namespace but got `"invalid@namespace"` --- */ diff --git a/tests/dmd/fail_compilation/cppvar.d b/tests/dmd/fail_compilation/cppvar.d index 213b54d10f..f336e35d69 100644 --- a/tests/dmd/fail_compilation/cppvar.d +++ b/tests/dmd/fail_compilation/cppvar.d @@ -9,7 +9,7 @@ fail_compilation/cppvar.d(21): Error: variable `cppvar.staticVar` cannot have `e fail_compilation/cppvar.d(21): perhaps declare it as `__gshared` instead fail_compilation/cppvar.d(22): Error: variable `cppvar.sharedVar` cannot have `extern(C++)` linkage because it is `shared` fail_compilation/cppvar.d(22): perhaps declare it as `__gshared` instead -fail_compilation/cppvar.d(30): Error: delegate `cppvar.__lambda_L30_C46` cannot return type `bool[3]` because its linkage is `extern(C++)` +fail_compilation/cppvar.d(30): Error: delegate `() { bool[3] a = false; return a; }` cannot return type `bool[3]` because its linkage is `extern(C++)` --- */ #line 10 diff --git a/tests/dmd/fail_compilation/ctor_self_assignment.d b/tests/dmd/fail_compilation/ctor_self_assignment.d new file mode 100644 index 0000000000..9d424b1e86 --- /dev/null +++ b/tests/dmd/fail_compilation/ctor_self_assignment.d @@ -0,0 +1,23 @@ +/** +REQUIRED_ARGS: -de +TEST_OUTPUT: +--- +fail_compilation/ctor_self_assignment.d(17): Deprecation: cannot initialize field `location` with itself +fail_compilation/ctor_self_assignment.d(15): did you mean to use parameter `locaction`? +--- +*/ +// https://forum.dlang.org/post/teghfhpmvkdcfwfeovua@forum.dlang.org + +alias Location = int; + +struct Node +{ + this(Location locaction, uint f) + { + this.location = location; + this.f = f; + } + + Location location; + uint f; +} diff --git a/tests/dmd/fail_compilation/dep_d1_ops.d b/tests/dmd/fail_compilation/dep_d1_ops.d index 19c64752aa..83e700632a 100644 --- a/tests/dmd/fail_compilation/dep_d1_ops.d +++ b/tests/dmd/fail_compilation/dep_d1_ops.d @@ -1,52 +1,176 @@ /* -REQUIRED_ARGS: -de +REQUIRED_ARGS: TEST_OUTPUT: --- -fail_compilation/dep_d1_ops.d(105): Error: `opAdd` is obsolete. Use `opBinary(string op)(...) if (op == "+")` instead. -fail_compilation/dep_d1_ops.d(106): Error: `opAdd_r` is obsolete. Use `opBinaryRight(string op)(...) if (op == "+")` instead. -fail_compilation/dep_d1_ops.d(107): Error: `opSub` is obsolete. Use `opBinary(string op)(...) if (op == "-")` instead. -fail_compilation/dep_d1_ops.d(108): Error: `opSub_r` is obsolete. Use `opBinaryRight(string op)(...) if (op == "-")` instead. -fail_compilation/dep_d1_ops.d(109): Error: `opMul` is obsolete. Use `opBinary(string op)(...) if (op == "*")` instead. -fail_compilation/dep_d1_ops.d(110): Error: `opMul_r` is obsolete. Use `opBinaryRight(string op)(...) if (op == "*")` instead. -fail_compilation/dep_d1_ops.d(111): Error: `opDiv` is obsolete. Use `opBinary(string op)(...) if (op == "/")` instead. -fail_compilation/dep_d1_ops.d(112): Error: `opDiv_r` is obsolete. Use `opBinaryRight(string op)(...) if (op == "/")` instead. -fail_compilation/dep_d1_ops.d(113): Error: `opMod` is obsolete. Use `opBinary(string op)(...) if (op == "%")` instead. -fail_compilation/dep_d1_ops.d(114): Error: `opMod_r` is obsolete. Use `opBinaryRight(string op)(...) if (op == "%")` instead. -fail_compilation/dep_d1_ops.d(116): Error: `opAnd` is obsolete. Use `opBinary(string op)(...) if (op == "&")` instead. -fail_compilation/dep_d1_ops.d(117): Error: `opOr` is obsolete. Use `opBinary(string op)(...) if (op == "|")` instead. -fail_compilation/dep_d1_ops.d(118): Error: `opXor` is obsolete. Use `opBinary(string op)(...) if (op == "^")` instead. -fail_compilation/dep_d1_ops.d(120): Error: `opShl` is obsolete. Use `opBinary(string op)(...) if (op == "<<")` instead. -fail_compilation/dep_d1_ops.d(121): Error: `opShl_r` is obsolete. Use `opBinaryRight(string op)(...) if (op == "<<")` instead. -fail_compilation/dep_d1_ops.d(122): Error: `opShr` is obsolete. Use `opBinary(string op)(...) if (op == ">>")` instead. -fail_compilation/dep_d1_ops.d(123): Error: `opShr_r` is obsolete. Use `opBinaryRight(string op)(...) if (op == ">>")` instead. -fail_compilation/dep_d1_ops.d(124): Error: `opUShr` is obsolete. Use `opBinary(string op)(...) if (op == ">>>")` instead. -fail_compilation/dep_d1_ops.d(125): Error: `opUShr_r` is obsolete. Use `opBinaryRight(string op)(...) if (op == ">>>")` instead. -fail_compilation/dep_d1_ops.d(127): Error: `opCat` is obsolete. Use `opBinary(string op)(...) if (op == "~")` instead. -fail_compilation/dep_d1_ops.d(128): Error: `opCat_r` is obsolete. Use `opBinaryRight(string op)(...) if (op == "~")` instead. -fail_compilation/dep_d1_ops.d(130): Error: `opNeg` is obsolete. Use `opUnary(string op)() if (op == "-")` instead. -fail_compilation/dep_d1_ops.d(131): Error: `opCom` is obsolete. Use `opUnary(string op)() if (op == "~")` instead. -fail_compilation/dep_d1_ops.d(132): Error: `opPostInc` is obsolete. Use `opUnary(string op)() if (op == "++")` instead. -fail_compilation/dep_d1_ops.d(133): Error: `opPostDec` is obsolete. Use `opUnary(string op)() if (op == "--")` instead. -fail_compilation/dep_d1_ops.d(134): Error: `opStar` is obsolete. Use `opUnary(string op)() if (op == "*")` instead. -fail_compilation/dep_d1_ops.d(136): Error: `opIn` is obsolete. Use `opBinary(string op)(...) if (op == "in")` instead. -fail_compilation/dep_d1_ops.d(137): Error: `opIn_r` is obsolete. Use `opBinaryRight(string op)(...) if (op == "in")` instead. -fail_compilation/dep_d1_ops.d(139): Error: `opAddAssign` is obsolete. Use `opOpAssign(string op)(...) if (op == "+")` instead. -fail_compilation/dep_d1_ops.d(140): Error: `opSubAssign` is obsolete. Use `opOpAssign(string op)(...) if (op == "-")` instead. -fail_compilation/dep_d1_ops.d(141): Error: `opMulAssign` is obsolete. Use `opOpAssign(string op)(...) if (op == "*")` instead. -fail_compilation/dep_d1_ops.d(142): Error: `opDivAssign` is obsolete. Use `opOpAssign(string op)(...) if (op == "/")` instead. -fail_compilation/dep_d1_ops.d(143): Error: `opModAssign` is obsolete. Use `opOpAssign(string op)(...) if (op == "%")` instead. -fail_compilation/dep_d1_ops.d(144): Error: `opAndAssign` is obsolete. Use `opOpAssign(string op)(...) if (op == "&")` instead. -fail_compilation/dep_d1_ops.d(145): Error: `opOrAssign` is obsolete. Use `opOpAssign(string op)(...) if (op == "|")` instead. -fail_compilation/dep_d1_ops.d(146): Error: `opXorAssign` is obsolete. Use `opOpAssign(string op)(...) if (op == "^")` instead. -fail_compilation/dep_d1_ops.d(147): Error: `opShlAssign` is obsolete. Use `opOpAssign(string op)(...) if (op == "<<")` instead. -fail_compilation/dep_d1_ops.d(148): Error: `opShrAssign` is obsolete. Use `opOpAssign(string op)(...) if (op == ">>")` instead. -fail_compilation/dep_d1_ops.d(149): Error: `opUShrAssign` is obsolete. Use `opOpAssign(string op)(...) if (op == ">>>")` instead. -fail_compilation/dep_d1_ops.d(150): Error: `opCatAssign` is obsolete. Use `opOpAssign(string op)(...) if (op == "~")` instead. -fail_compilation/dep_d1_ops.d(158): Error: `opCom` is obsolete. Use `opUnary(string op)() if (op == "~")` instead. +fail_compilation/dep_d1_ops.d(281): Error: operator `+` is not defined for type `S` +fail_compilation/dep_d1_ops.d(174): perhaps overload the operator with `auto opBinary(string op : "+")(int rhs) {}` +fail_compilation/dep_d1_ops.d(282): Error: operator `+` is not defined for type `S` +fail_compilation/dep_d1_ops.d(174): perhaps overload the operator with `auto opBinaryRight(string op : "+")(int rhs) {}` +fail_compilation/dep_d1_ops.d(283): Error: operator `-` is not defined for type `S` +fail_compilation/dep_d1_ops.d(174): perhaps overload the operator with `auto opBinary(string op : "-")(int rhs) {}` +fail_compilation/dep_d1_ops.d(284): Error: operator `-` is not defined for type `S` +fail_compilation/dep_d1_ops.d(174): perhaps overload the operator with `auto opBinaryRight(string op : "-")(int rhs) {}` +fail_compilation/dep_d1_ops.d(285): Error: operator `*` is not defined for type `S` +fail_compilation/dep_d1_ops.d(174): perhaps overload the operator with `auto opBinary(string op : "*")(int rhs) {}` +fail_compilation/dep_d1_ops.d(286): Error: operator `*` is not defined for type `S` +fail_compilation/dep_d1_ops.d(174): perhaps overload the operator with `auto opBinaryRight(string op : "*")(int rhs) {}` +fail_compilation/dep_d1_ops.d(287): Error: operator `/` is not defined for type `S` +fail_compilation/dep_d1_ops.d(174): perhaps overload the operator with `auto opBinary(string op : "/")(int rhs) {}` +fail_compilation/dep_d1_ops.d(288): Error: operator `/` is not defined for type `S` +fail_compilation/dep_d1_ops.d(174): perhaps overload the operator with `auto opBinaryRight(string op : "/")(int rhs) {}` +fail_compilation/dep_d1_ops.d(289): Error: operator `%` is not defined for type `S` +fail_compilation/dep_d1_ops.d(174): perhaps overload the operator with `auto opBinary(string op : "%")(int rhs) {}` +fail_compilation/dep_d1_ops.d(290): Error: operator `%` is not defined for type `S` +fail_compilation/dep_d1_ops.d(174): perhaps overload the operator with `auto opBinaryRight(string op : "%")(int rhs) {}` +fail_compilation/dep_d1_ops.d(292): Error: operator `&` is not defined for type `S` +fail_compilation/dep_d1_ops.d(174): perhaps overload the operator with `auto opBinary(string op : "&")(int rhs) {}` +fail_compilation/dep_d1_ops.d(293): Error: operator `|` is not defined for type `S` +fail_compilation/dep_d1_ops.d(174): perhaps overload the operator with `auto opBinary(string op : "|")(int rhs) {}` +fail_compilation/dep_d1_ops.d(294): Error: operator `^` is not defined for type `S` +fail_compilation/dep_d1_ops.d(174): perhaps overload the operator with `auto opBinary(string op : "^")(int rhs) {}` +fail_compilation/dep_d1_ops.d(296): Error: operator `<<` is not defined for type `S` +fail_compilation/dep_d1_ops.d(174): perhaps overload the operator with `auto opBinary(string op : "<<")(int rhs) {}` +fail_compilation/dep_d1_ops.d(297): Error: operator `<<` is not defined for type `S` +fail_compilation/dep_d1_ops.d(174): perhaps overload the operator with `auto opBinaryRight(string op : "<<")(int rhs) {}` +fail_compilation/dep_d1_ops.d(298): Error: operator `>>` is not defined for type `S` +fail_compilation/dep_d1_ops.d(174): perhaps overload the operator with `auto opBinary(string op : ">>")(int rhs) {}` +fail_compilation/dep_d1_ops.d(299): Error: operator `>>` is not defined for type `S` +fail_compilation/dep_d1_ops.d(174): perhaps overload the operator with `auto opBinaryRight(string op : ">>")(int rhs) {}` +fail_compilation/dep_d1_ops.d(300): Error: operator `>>>` is not defined for type `S` +fail_compilation/dep_d1_ops.d(174): perhaps overload the operator with `auto opBinary(string op : ">>>")(int rhs) {}` +fail_compilation/dep_d1_ops.d(301): Error: operator `>>>` is not defined for type `S` +fail_compilation/dep_d1_ops.d(174): perhaps overload the operator with `auto opBinaryRight(string op : ">>>")(int rhs) {}` +fail_compilation/dep_d1_ops.d(303): Error: operator `~` is not defined for type `S` +fail_compilation/dep_d1_ops.d(174): perhaps overload the operator with `auto opBinary(string op : "~")(int rhs) {}` +fail_compilation/dep_d1_ops.d(304): Error: operator `~` is not defined for type `S` +fail_compilation/dep_d1_ops.d(174): perhaps overload the operator with `auto opBinaryRight(string op : "~")(int rhs) {}` +fail_compilation/dep_d1_ops.d(306): Error: operator `+` is not defined for `S` +fail_compilation/dep_d1_ops.d(174): perhaps overload the operator with `auto opUnary(string op : "+")() {}` +fail_compilation/dep_d1_ops.d(307): Error: operator `-` is not defined for `S` +fail_compilation/dep_d1_ops.d(174): perhaps overload the operator with `auto opUnary(string op : "-")() {}` +fail_compilation/dep_d1_ops.d(308): Error: operator `~` is not defined for `S` +fail_compilation/dep_d1_ops.d(174): perhaps overload the operator with `auto opUnary(string op : "~")() {}` +fail_compilation/dep_d1_ops.d(309): Error: operator `++` not supported for `s` of type `S` +fail_compilation/dep_d1_ops.d(174): perhaps implement `auto opUnary(string op : "++")() {}` or `auto opOpAssign(string op : "+")(int) {}` +fail_compilation/dep_d1_ops.d(310): Error: operator `--` not supported for `s` of type `S` +fail_compilation/dep_d1_ops.d(174): perhaps implement `auto opUnary(string op : "--")() {}` or `auto opOpAssign(string op : "-")(int) {}` +fail_compilation/dep_d1_ops.d(311): Error: operator `*` is not defined for `S` +fail_compilation/dep_d1_ops.d(174): perhaps overload the operator with `auto opUnary(string op : "*")() {}` +fail_compilation/dep_d1_ops.d(313): Error: operator `in` is not defined for type `S` +fail_compilation/dep_d1_ops.d(174): perhaps overload the operator with `auto opBinary(string op : "in")(int rhs) {}` +fail_compilation/dep_d1_ops.d(314): Error: operator `in` is not defined for type `S` +fail_compilation/dep_d1_ops.d(174): perhaps overload the operator with `auto opBinaryRight(string op : "in")(int rhs) {}` +fail_compilation/dep_d1_ops.d(316): Error: operator `+=` not supported for `s` of type `S` +fail_compilation/dep_d1_ops.d(174): perhaps implement `auto opOpAssign(string op : "+")(int) {}` +fail_compilation/dep_d1_ops.d(317): Error: operator `-=` not supported for `s` of type `S` +fail_compilation/dep_d1_ops.d(174): perhaps implement `auto opOpAssign(string op : "-")(int) {}` +fail_compilation/dep_d1_ops.d(318): Error: operator `*=` not supported for `s` of type `S` +fail_compilation/dep_d1_ops.d(174): perhaps implement `auto opOpAssign(string op : "*")(int) {}` +fail_compilation/dep_d1_ops.d(319): Error: operator `/=` not supported for `s` of type `S` +fail_compilation/dep_d1_ops.d(174): perhaps implement `auto opOpAssign(string op : "/")(int) {}` +fail_compilation/dep_d1_ops.d(320): Error: operator `%=` not supported for `s` of type `S` +fail_compilation/dep_d1_ops.d(174): perhaps implement `auto opOpAssign(string op : "%")(int) {}` +fail_compilation/dep_d1_ops.d(321): Error: operator `&=` not supported for `s` of type `S` +fail_compilation/dep_d1_ops.d(174): perhaps implement `auto opOpAssign(string op : "&")(int) {}` +fail_compilation/dep_d1_ops.d(322): Error: operator `|=` not supported for `s` of type `S` +fail_compilation/dep_d1_ops.d(174): perhaps implement `auto opOpAssign(string op : "|")(int) {}` +fail_compilation/dep_d1_ops.d(323): Error: operator `^=` not supported for `s` of type `S` +fail_compilation/dep_d1_ops.d(174): perhaps implement `auto opOpAssign(string op : "^")(int) {}` +fail_compilation/dep_d1_ops.d(324): Error: operator `<<=` not supported for `s` of type `S` +fail_compilation/dep_d1_ops.d(174): perhaps implement `auto opOpAssign(string op : "<<")(int) {}` +fail_compilation/dep_d1_ops.d(325): Error: operator `>>=` not supported for `s` of type `S` +fail_compilation/dep_d1_ops.d(174): perhaps implement `auto opOpAssign(string op : ">>")(int) {}` +fail_compilation/dep_d1_ops.d(326): Error: operator `>>>=` not supported for `s` of type `S` +fail_compilation/dep_d1_ops.d(174): perhaps implement `auto opOpAssign(string op : ">>>")(int) {}` +fail_compilation/dep_d1_ops.d(327): Error: operator `~=` not supported for `s` of type `S` +fail_compilation/dep_d1_ops.d(174): perhaps implement `auto opOpAssign(string op : "~")(int) {}` +fail_compilation/dep_d1_ops.d(331): Error: operator `+` is not defined for type `dep_d1_ops.C` +fail_compilation/dep_d1_ops.d(225): perhaps overload the operator with `auto opBinary(string op : "+")(int rhs) {}` +fail_compilation/dep_d1_ops.d(332): Error: operator `+` is not defined for type `dep_d1_ops.C` +fail_compilation/dep_d1_ops.d(225): perhaps overload the operator with `auto opBinaryRight(string op : "+")(int rhs) {}` +fail_compilation/dep_d1_ops.d(333): Error: operator `-` is not defined for type `dep_d1_ops.C` +fail_compilation/dep_d1_ops.d(225): perhaps overload the operator with `auto opBinary(string op : "-")(int rhs) {}` +fail_compilation/dep_d1_ops.d(334): Error: operator `-` is not defined for type `dep_d1_ops.C` +fail_compilation/dep_d1_ops.d(225): perhaps overload the operator with `auto opBinaryRight(string op : "-")(int rhs) {}` +fail_compilation/dep_d1_ops.d(335): Error: operator `*` is not defined for type `dep_d1_ops.C` +fail_compilation/dep_d1_ops.d(225): perhaps overload the operator with `auto opBinary(string op : "*")(int rhs) {}` +fail_compilation/dep_d1_ops.d(336): Error: operator `*` is not defined for type `dep_d1_ops.C` +fail_compilation/dep_d1_ops.d(225): perhaps overload the operator with `auto opBinaryRight(string op : "*")(int rhs) {}` +fail_compilation/dep_d1_ops.d(337): Error: operator `/` is not defined for type `dep_d1_ops.C` +fail_compilation/dep_d1_ops.d(225): perhaps overload the operator with `auto opBinary(string op : "/")(int rhs) {}` +fail_compilation/dep_d1_ops.d(338): Error: operator `/` is not defined for type `dep_d1_ops.C` +fail_compilation/dep_d1_ops.d(225): perhaps overload the operator with `auto opBinaryRight(string op : "/")(int rhs) {}` +fail_compilation/dep_d1_ops.d(339): Error: operator `%` is not defined for type `dep_d1_ops.C` +fail_compilation/dep_d1_ops.d(225): perhaps overload the operator with `auto opBinary(string op : "%")(int rhs) {}` +fail_compilation/dep_d1_ops.d(340): Error: operator `%` is not defined for type `dep_d1_ops.C` +fail_compilation/dep_d1_ops.d(225): perhaps overload the operator with `auto opBinaryRight(string op : "%")(int rhs) {}` +fail_compilation/dep_d1_ops.d(342): Error: operator `&` is not defined for type `dep_d1_ops.C` +fail_compilation/dep_d1_ops.d(225): perhaps overload the operator with `auto opBinary(string op : "&")(int rhs) {}` +fail_compilation/dep_d1_ops.d(343): Error: operator `|` is not defined for type `dep_d1_ops.C` +fail_compilation/dep_d1_ops.d(225): perhaps overload the operator with `auto opBinary(string op : "|")(int rhs) {}` +fail_compilation/dep_d1_ops.d(344): Error: operator `^` is not defined for type `dep_d1_ops.C` +fail_compilation/dep_d1_ops.d(225): perhaps overload the operator with `auto opBinary(string op : "^")(int rhs) {}` +fail_compilation/dep_d1_ops.d(346): Error: operator `<<` is not defined for type `dep_d1_ops.C` +fail_compilation/dep_d1_ops.d(225): perhaps overload the operator with `auto opBinary(string op : "<<")(int rhs) {}` +fail_compilation/dep_d1_ops.d(347): Error: operator `<<` is not defined for type `dep_d1_ops.C` +fail_compilation/dep_d1_ops.d(225): perhaps overload the operator with `auto opBinaryRight(string op : "<<")(int rhs) {}` +fail_compilation/dep_d1_ops.d(348): Error: operator `>>` is not defined for type `dep_d1_ops.C` +fail_compilation/dep_d1_ops.d(225): perhaps overload the operator with `auto opBinary(string op : ">>")(int rhs) {}` +fail_compilation/dep_d1_ops.d(349): Error: operator `>>` is not defined for type `dep_d1_ops.C` +fail_compilation/dep_d1_ops.d(225): perhaps overload the operator with `auto opBinaryRight(string op : ">>")(int rhs) {}` +fail_compilation/dep_d1_ops.d(350): Error: operator `>>>` is not defined for type `dep_d1_ops.C` +fail_compilation/dep_d1_ops.d(225): perhaps overload the operator with `auto opBinary(string op : ">>>")(int rhs) {}` +fail_compilation/dep_d1_ops.d(351): Error: operator `>>>` is not defined for type `dep_d1_ops.C` +fail_compilation/dep_d1_ops.d(225): perhaps overload the operator with `auto opBinaryRight(string op : ">>>")(int rhs) {}` +fail_compilation/dep_d1_ops.d(353): Error: operator `~` is not defined for type `dep_d1_ops.C` +fail_compilation/dep_d1_ops.d(225): perhaps overload the operator with `auto opBinary(string op : "~")(int rhs) {}` +fail_compilation/dep_d1_ops.d(354): Error: operator `~` is not defined for type `dep_d1_ops.C` +fail_compilation/dep_d1_ops.d(225): perhaps overload the operator with `auto opBinaryRight(string op : "~")(int rhs) {}` +fail_compilation/dep_d1_ops.d(356): Error: operator `+` is not defined for `C` +fail_compilation/dep_d1_ops.d(225): perhaps overload the operator with `auto opUnary(string op : "+")() {}` +fail_compilation/dep_d1_ops.d(357): Error: operator `-` is not defined for `C` +fail_compilation/dep_d1_ops.d(225): perhaps overload the operator with `auto opUnary(string op : "-")() {}` +fail_compilation/dep_d1_ops.d(358): Error: operator `~` is not defined for `C` +fail_compilation/dep_d1_ops.d(225): perhaps overload the operator with `auto opUnary(string op : "~")() {}` +fail_compilation/dep_d1_ops.d(359): Error: operator `++` not supported for `c` of type `C` +fail_compilation/dep_d1_ops.d(225): perhaps implement `auto opUnary(string op : "++")() {}` or `auto opOpAssign(string op : "+")(int) {}` +fail_compilation/dep_d1_ops.d(360): Error: operator `--` not supported for `c` of type `C` +fail_compilation/dep_d1_ops.d(225): perhaps implement `auto opUnary(string op : "--")() {}` or `auto opOpAssign(string op : "-")(int) {}` +fail_compilation/dep_d1_ops.d(361): Error: operator `*` is not defined for `C` +fail_compilation/dep_d1_ops.d(225): perhaps overload the operator with `auto opUnary(string op : "*")() {}` +fail_compilation/dep_d1_ops.d(363): Error: operator `in` is not defined for type `dep_d1_ops.C` +fail_compilation/dep_d1_ops.d(225): perhaps overload the operator with `auto opBinary(string op : "in")(int rhs) {}` +fail_compilation/dep_d1_ops.d(364): Error: operator `in` is not defined for type `dep_d1_ops.C` +fail_compilation/dep_d1_ops.d(225): perhaps overload the operator with `auto opBinaryRight(string op : "in")(int rhs) {}` +fail_compilation/dep_d1_ops.d(366): Error: operator `+=` not supported for `c` of type `C` +fail_compilation/dep_d1_ops.d(225): perhaps implement `auto opOpAssign(string op : "+")(int) {}` +fail_compilation/dep_d1_ops.d(367): Error: operator `-=` not supported for `c` of type `C` +fail_compilation/dep_d1_ops.d(225): perhaps implement `auto opOpAssign(string op : "-")(int) {}` +fail_compilation/dep_d1_ops.d(368): Error: operator `*=` not supported for `c` of type `C` +fail_compilation/dep_d1_ops.d(225): perhaps implement `auto opOpAssign(string op : "*")(int) {}` +fail_compilation/dep_d1_ops.d(369): Error: operator `/=` not supported for `c` of type `C` +fail_compilation/dep_d1_ops.d(225): perhaps implement `auto opOpAssign(string op : "/")(int) {}` +fail_compilation/dep_d1_ops.d(370): Error: operator `%=` not supported for `c` of type `C` +fail_compilation/dep_d1_ops.d(225): perhaps implement `auto opOpAssign(string op : "%")(int) {}` +fail_compilation/dep_d1_ops.d(371): Error: operator `&=` not supported for `c` of type `C` +fail_compilation/dep_d1_ops.d(225): perhaps implement `auto opOpAssign(string op : "&")(int) {}` +fail_compilation/dep_d1_ops.d(372): Error: operator `|=` not supported for `c` of type `C` +fail_compilation/dep_d1_ops.d(225): perhaps implement `auto opOpAssign(string op : "|")(int) {}` +fail_compilation/dep_d1_ops.d(373): Error: operator `^=` not supported for `c` of type `C` +fail_compilation/dep_d1_ops.d(225): perhaps implement `auto opOpAssign(string op : "^")(int) {}` +fail_compilation/dep_d1_ops.d(374): Error: operator `<<=` not supported for `c` of type `C` +fail_compilation/dep_d1_ops.d(225): perhaps implement `auto opOpAssign(string op : "<<")(int) {}` +fail_compilation/dep_d1_ops.d(375): Error: operator `>>=` not supported for `c` of type `C` +fail_compilation/dep_d1_ops.d(225): perhaps implement `auto opOpAssign(string op : ">>")(int) {}` +fail_compilation/dep_d1_ops.d(376): Error: operator `>>>=` not supported for `c` of type `C` +fail_compilation/dep_d1_ops.d(225): perhaps implement `auto opOpAssign(string op : ">>>")(int) {}` +fail_compilation/dep_d1_ops.d(377): Error: operator `~=` not supported for `c` of type `C` +fail_compilation/dep_d1_ops.d(225): perhaps implement `auto opOpAssign(string op : "~")(int) {}` +fail_compilation/dep_d1_ops.d(386): Error: operator `~` is not defined for `NoDeprecation` +fail_compilation/dep_d1_ops.d(390): perhaps overload the operator with `auto opUnary(string op : "~")() {}` --- */ -#line 50 struct S { int opAdd(int i) { return 0; } @@ -74,6 +198,58 @@ struct S int opCat(int i) { return 0; } int opCat_r(int i) { return 0; } + int opPos() { return 0; } + int opNeg() { return 0; } + int opCom() { return 0; } + int opPostInc() { return 0; } + int opPostDec() { return 0; } + int opStar() { return 0; } + + int opIn(int i) { return 0; } + int opIn_r(int i) { return 0; } + + int opAddAssign(int i) { return 0; } + int opSubAssign(int i) { return 0; } + int opMulAssign(int i) { return 0; } + int opDivAssign(int i) { return 0; } + int opModAssign(int i) { return 0; } + int opAndAssign(int i) { return 0; } + int opOrAssign(int i) { return 0; } + int opXorAssign(int i) { return 0; } + int opShlAssign(int i) { return 0; } + int opShrAssign(int i) { return 0; } + int opUShrAssign(int i) { return 0; } + int opCatAssign(int i) { return 0; } +} + +class C +{ + int opAdd(int i) { return 0; } + int opAdd_r(int i) { return 0; } + int opSub(int i) { return 0; } + int opSub_r(int i) { return 0; } + int opMul(int i) { return 0; } + int opMul_r(int i) { return 0; } + int opDiv(int i) { return 0; } + int opDiv_r(int i) { return 0; } + int opMod(int i) { return 0; } + int opMod_r(int i) { return 0; } + + int opAnd(int i) { return 0; } + int opOr(int i) { return 0; } + int opXor(int i) { return 0; } + + int opShl(int i) { return 0; } + int opShl_r(int i) { return 0; } + int opShr(int i) { return 0; } + int opShr_r(int i) { return 0; } + int opUShr(int i) { return 0; } + int opUShr_r(int i) { return 0; } + + int opCat(int i) { return 0; } + int opCat_r(int i) { return 0; } + + int opPos() { return 0; } int opNeg() { return 0; } int opCom() { return 0; } int opPostInc() { return 0; } @@ -99,55 +275,107 @@ struct S void main() { - S s; int i; + { + S s; + i = s + 1; + i = 1 + s; + i = s - 1; + i = 1 - s; + i = s * 1; + i = 1 * s; + i = s / 1; + i = 1 / s; + i = s % 1; + i = 1 % s; + + i = s & 1; + i = s | 1; + i = s ^ 1; + + i = s << 1; + i = 1 << s; + i = s >> 1; + i = 1 >> s; + i = s >>> 1; + i = 1 >>> s; + + i = s ~ 1; + i = 1 ~ s; + + i = +s; + i = -s; + i = ~s; + s++; + s--; + i = *s; + + i = s in 1; + i = 1 in s; + + s += 1; + s -= 1; + s *= 1; + s /= 1; + s %= 1; + s &= 1; + s |= 1; + s ^= 1; + s <<= 1; + s >>= 1; + s >>>= 1; + s ~= 1; + } + { + C c; + i = c + 1; + i = 1 + c; + i = c - 1; + i = 1 - c; + i = c * 1; + i = 1 * c; + i = c / 1; + i = 1 / c; + i = c % 1; + i = 1 % c; + + i = c & 1; + i = c | 1; + i = c ^ 1; + + i = c << 1; + i = 1 << c; + i = c >> 1; + i = 1 >> c; + i = c >>> 1; + i = 1 >>> c; - i = s + 1; - i = 1 + s; - i = s - 1; - i = 1 - s; - i = s * 1; - i = 1 * s; - i = s / 1; - i = 1 / s; - i = s % 1; - i = 1 % s; - - i = s & 1; - i = s | 1; - i = s ^ 1; - - i = s << 1; - i = 1 << s; - i = s >> 1; - i = 1 >> s; - i = s >>> 1; - i = 1 >>> s; - - i = s ~ 1; - i = 1 ~ s; - - i = -s; - i = ~s; - s++; - s--; - i = *s; - - i = s in 1; - i = 1 in s; - - s += 1; - s -= 1; - s *= 1; - s /= 1; - s %= 1; - s &= 1; - s |= 1; - s ^= 1; - s <<= 1; - s >>= 1; - s >>>= 1; - s ~= 1; + i = c ~ 1; + i = 1 ~ c; + + i = +c; + i = -c; + i = ~c; + c++; + c--; + i = *c; + + i = c in 1; + i = 1 in c; + + c += 1; + c -= 1; + c *= 1; + c /= 1; + c %= 1; + c &= 1; + c |= 1; + c ^= 1; + c <<= 1; + c >>= 1; + c >>>= 1; + c ~= 1; + } scope nd = new NoDeprecation; assert((42 in nd) == 0); diff --git a/tests/dmd/fail_compilation/deprecate12979d.d b/tests/dmd/fail_compilation/deprecate12979d.d index 54c71ec828..6cb01f72e5 100644 --- a/tests/dmd/fail_compilation/deprecate12979d.d +++ b/tests/dmd/fail_compilation/deprecate12979d.d @@ -2,7 +2,7 @@ /* DISABLED: LDC_not_x86 TEST_OUTPUT: --- -fail_compilation/deprecate12979d.d(11): Error: `asm` statement is assumed to be `@system` - mark it with `@trusted` if it is not +fail_compilation/deprecate12979d.d(11): Error: executing an `asm` statement without `@trusted` annotation is not allowed in a `@safe` function --- */ diff --git a/tests/dmd/fail_compilation/deprecateopdot.d b/tests/dmd/fail_compilation/deprecateopdot.d deleted file mode 100644 index 46c949376e..0000000000 --- a/tests/dmd/fail_compilation/deprecateopdot.d +++ /dev/null @@ -1,30 +0,0 @@ -/* -REQUIRED_ARGS: -de -TEST_OUTPUT: ---- -fail_compilation/deprecateopdot.d(27): Error: `opDot` is obsolete. Use `alias this` -fail_compilation/deprecateopdot.d(28): Error: `opDot` is obsolete. Use `alias this` -fail_compilation/deprecateopdot.d(29): Error: `opDot` is obsolete. Use `alias this` ---- -*/ -struct S6 -{ - int a, b; -} -struct T6 -{ - S6 s; - - S6* opDot() return - { - return &s; - } -} - -void test6() -{ - T6 t; - t.a = 4; - assert(t.a == 4); - t.b = 5; -} diff --git a/tests/dmd/fail_compilation/diag10319.d b/tests/dmd/fail_compilation/diag10319.d index efc818f30b..3083192950 100644 --- a/tests/dmd/fail_compilation/diag10319.d +++ b/tests/dmd/fail_compilation/diag10319.d @@ -1,21 +1,18 @@ /* TEST_OUTPUT: --- -fail_compilation/diag10319.d(33): Error: `pure` function `D main` cannot call impure function `diag10319.foo` -fail_compilation/diag10319.d(33): Error: `@safe` function `D main` cannot call `@system` function `diag10319.foo` -fail_compilation/diag10319.d(22): `diag10319.foo` is declared here -fail_compilation/diag10319.d(34): Error: `pure` function `D main` cannot call impure function `diag10319.bar!int.bar` -fail_compilation/diag10319.d(26): which wasn't inferred `pure` because of: -fail_compilation/diag10319.d(26): `pure` function `diag10319.bar!int.bar` cannot access mutable static data `g` -fail_compilation/diag10319.d(34): Error: `@safe` function `D main` cannot call `@system` function `diag10319.bar!int.bar` -fail_compilation/diag10319.d(27): which wasn't inferred `@safe` because of: -fail_compilation/diag10319.d(27): cannot take address of local `x` in `@safe` function `bar` -fail_compilation/diag10319.d(24): `diag10319.bar!int.bar` is declared here -fail_compilation/diag10319.d(33): Error: function `diag10319.foo` is not `nothrow` -fail_compilation/diag10319.d(34): Error: function `diag10319.bar!int.bar` is not `nothrow` -fail_compilation/diag10319.d(28): which wasn't inferred `nothrow` because of: -fail_compilation/diag10319.d(28): `object.Exception` is thrown but not caught -fail_compilation/diag10319.d(31): Error: function `D main` may throw but is marked as `nothrow` +fail_compilation/diag10319.d(30): Error: `pure` function `D main` cannot call impure function `diag10319.foo` +fail_compilation/diag10319.d(30): Error: `@safe` function `D main` cannot call `@system` function `diag10319.foo` +fail_compilation/diag10319.d(19): `diag10319.foo` is declared here +fail_compilation/diag10319.d(31): Error: `pure` function `D main` cannot call impure function `diag10319.bar!int.bar` +fail_compilation/diag10319.d(23): and accessing mutable static data `g` makes it fail to infer `pure` +fail_compilation/diag10319.d(31): Error: `@safe` function `D main` cannot call `@system` function `diag10319.bar!int.bar` +fail_compilation/diag10319.d(24): and taking the address of stack-allocated local variable `x` makes it fail to infer `@safe` +fail_compilation/diag10319.d(21): `diag10319.bar!int.bar` is declared here +fail_compilation/diag10319.d(30): Error: function `diag10319.foo` is not `nothrow` +fail_compilation/diag10319.d(31): Error: function `diag10319.bar!int.bar` is not `nothrow` +fail_compilation/diag10319.d(25): and `object.Exception` being thrown but not caught makes it fail to infer `nothrow` +fail_compilation/diag10319.d(28): Error: function `D main` may throw but is marked as `nothrow` --- */ diff --git a/tests/dmd/fail_compilation/diag10359.d b/tests/dmd/fail_compilation/diag10359.d index 142cec98e0..39a7877508 100644 --- a/tests/dmd/fail_compilation/diag10359.d +++ b/tests/dmd/fail_compilation/diag10359.d @@ -1,7 +1,7 @@ /* TEST_OUTPUT: --- -fail_compilation/diag10359.d(10): Error: pointer slicing not allowed in safe functions +fail_compilation/diag10359.d(10): Error: pointer slicing is not allowed in a `@safe` function --- */ diff --git a/tests/dmd/fail_compilation/diag11198.d b/tests/dmd/fail_compilation/diag11198.d index 1be0f1e85a..7f8979ccb3 100644 --- a/tests/dmd/fail_compilation/diag11198.d +++ b/tests/dmd/fail_compilation/diag11198.d @@ -1,14 +1,10 @@ /* TEST_OUTPUT: --- -fail_compilation/diag11198.d(17): Error: version `blah` declaration must be at module level -fail_compilation/diag11198.d(18): Error: debug `blah` declaration must be at module level -fail_compilation/diag11198.d(19): Deprecation: `version = ` is deprecated, use version identifiers instead -fail_compilation/diag11198.d(19): Error: version `1` level declaration must be at module level -fail_compilation/diag11198.d(20): Deprecation: `debug = ` is deprecated, use debug identifiers instead -fail_compilation/diag11198.d(20): Error: debug `2` level declaration must be at module level -fail_compilation/diag11198.d(21): Error: identifier or integer expected, not `""` -fail_compilation/diag11198.d(22): Error: identifier or integer expected, not `""` +fail_compilation/diag11198.d(13): Error: version `blah` declaration must be at module level +fail_compilation/diag11198.d(14): Error: debug `blah` declaration must be at module level +fail_compilation/diag11198.d(15): Error: identifier expected, not `""` +fail_compilation/diag11198.d(16): Error: identifier expected, not `""` --- */ @@ -16,8 +12,6 @@ void main() { version = blah; debug = blah; - version = 1; - debug = 2; version = ""; debug = ""; } diff --git a/tests/dmd/fail_compilation/diag11769.d b/tests/dmd/fail_compilation/diag11769.d index 75047f5378..9f9ed351dd 100644 --- a/tests/dmd/fail_compilation/diag11769.d +++ b/tests/dmd/fail_compilation/diag11769.d @@ -1,7 +1,7 @@ /* TEST_OUTPUT: --- -fail_compilation/diag11769.d(18): Error: `diag11769.foo!string.bar` called with argument types `(string)` matches both: +fail_compilation/diag11769.d(18): Error: `diag11769.foo!string.bar` called with argument types `(string)` matches multiple overloads after implicit conversions: fail_compilation/diag11769.d(13): `diag11769.foo!string.bar(wstring __param_0)` and: fail_compilation/diag11769.d(14): `diag11769.foo!string.bar(dstring __param_0)` diff --git a/tests/dmd/fail_compilation/diag12063.d b/tests/dmd/fail_compilation/diag12063.d index 3e9535ab42..a87d5bf833 100644 --- a/tests/dmd/fail_compilation/diag12063.d +++ b/tests/dmd/fail_compilation/diag12063.d @@ -1,13 +1,15 @@ /* TEST_OUTPUT: --- -fail_compilation/diag12063.d(19): Error: cannot check `diag12063.Bar.b` value for overflow -fail_compilation/diag12063.d(16): Error: no property `max` for type `Foo`, perhaps `import std.algorithm;` is needed? -fail_compilation/diag12063.d(19): Error: cannot generate value for `diag12063.Bar.b` -fail_compilation/diag12063.d(19): Error: incompatible types for `(Foo()) + (1)`: `Bar` and `int` -fail_compilation/diag12063.d(29): Error: cannot check `diag12063.b` value for overflow -fail_compilation/diag12063.d(29): Error: incompatible types for `(S()) == (1)`: `S` and `int` -fail_compilation/diag12063.d(38): Error: enum member `diag12063.d` initialization with `__anonymous.c+1` causes overflow for type `Q` +fail_compilation/diag12063.d(21): Error: cannot check `diag12063.Bar.b` value for overflow +fail_compilation/diag12063.d(18): Error: no property `max` for type `Foo`, perhaps `import std.algorithm;` is needed? +fail_compilation/diag12063.d(21): Error: cannot generate value for `diag12063.Bar.b` +fail_compilation/diag12063.d(21): Error: operator `+` is not defined for type `Bar` +fail_compilation/diag12063.d(16): perhaps overload the operator with `auto opBinary(string op : "+")(int rhs) {}` +fail_compilation/diag12063.d(31): Error: cannot check `diag12063.b` value for overflow +fail_compilation/diag12063.d(31): Error: no operator `==` for type `S` +fail_compilation/diag12063.d(24): perhaps overload it with `bool opEquals(int other) const {}` +fail_compilation/diag12063.d(40): Error: enum member `diag12063.d` initialization with `__anonymous.c+1` causes overflow for type `Q` --- */ diff --git a/tests/dmd/fail_compilation/diag12829.d b/tests/dmd/fail_compilation/diag12829.d index 0ed04d3a74..3a24180af8 100644 --- a/tests/dmd/fail_compilation/diag12829.d +++ b/tests/dmd/fail_compilation/diag12829.d @@ -2,12 +2,12 @@ TEST_OUTPUT: --- fail_compilation/diag12829.d(15): Error: function `diag12829.test1` is `@nogc` yet allocates closure for `test1()` with the GC -fail_compilation/diag12829.d(18): delegate `diag12829.test1.__lambda_L18_C33` closes over variable `x` +fail_compilation/diag12829.d(18): delegate `() { int y = x; }` closes over variable `x` fail_compilation/diag12829.d(17): `x` declared here -fail_compilation/diag12829.d(22): function `diag12829.test1.bar` closes over variable `x` +fail_compilation/diag12829.d(22): function `bar` closes over variable `x` fail_compilation/diag12829.d(17): `x` declared here fail_compilation/diag12829.d(29): Error: function `diag12829.test2` is `@nogc` yet allocates closure for `test2()` with the GC -fail_compilation/diag12829.d(34): function `diag12829.test2.S.foo` closes over variable `x` +fail_compilation/diag12829.d(34): function `foo` closes over variable `x` fail_compilation/diag12829.d(31): `x` declared here --- */ diff --git a/tests/dmd/fail_compilation/diag13320.d b/tests/dmd/fail_compilation/diag13320.d index 2808606bdc..40b99ab39d 100644 --- a/tests/dmd/fail_compilation/diag13320.d +++ b/tests/dmd/fail_compilation/diag13320.d @@ -1,7 +1,8 @@ /* TEST_OUTPUT: --- -fail_compilation/diag13320.d(13): Error: `f` is not a scalar, it is a `Foo` +fail_compilation/diag13320.d(14): Error: operator `++` not supported for `f` of type `Foo` +fail_compilation/diag13320.d(9): perhaps implement `auto opUnary(string op : "++")() {}` or `auto opOpAssign(string op : "+")(int) {}` --- */ diff --git a/tests/dmd/fail_compilation/diag14145.d b/tests/dmd/fail_compilation/diag14145.d index fee73078e3..b2044b31db 100644 --- a/tests/dmd/fail_compilation/diag14145.d +++ b/tests/dmd/fail_compilation/diag14145.d @@ -1,11 +1,9 @@ /* TEST_OUTPUT: --- -fail_compilation/diag14145.d(16): Error: no property `i` for `_` of type `diag14145.main.Capture!(i)` -fail_compilation/diag14145.d(16): potentially malformed `opDispatch`. Use an explicit instantiation to get a better error message -fail_compilation/diag14145.d(26): struct `Capture` defined here -fail_compilation/diag14145.d(35): Error: expression `*this.ptr` of type `shared(int)` is not implicitly convertible to return type `ref int` -fail_compilation/diag14145.d(17): Error: template instance `diag14145.main.Capture!(i).Capture.opDispatch!"i"` error instantiating +fail_compilation/diag14145.d(14): Error: no property `i` for `_` of type `diag14145.main.Capture!(i)` +fail_compilation/diag14145.d(33): Error: expression `*this.ptr` of type `shared(int)` is not implicitly convertible to return type `ref int` +fail_compilation/diag14145.d(14): Error: template instance `diag14145.main.Capture!(i).Capture.opDispatch!"i"` error instantiating --- */ diff --git a/tests/dmd/fail_compilation/diag16499.d b/tests/dmd/fail_compilation/diag16499.d index 63b4b3c527..f3757954d4 100644 --- a/tests/dmd/fail_compilation/diag16499.d +++ b/tests/dmd/fail_compilation/diag16499.d @@ -1,8 +1,10 @@ /* TEST_OUTPUT: --- -fail_compilation/diag16499.d(22): Error: incompatible types for `(2) in (foo)`: `int` and `A` -fail_compilation/diag16499.d(24): Error: incompatible types for `(1.0) in (bar)`: `double` and `B` +fail_compilation/diag16499.d(24): Error: operator `in` is not defined for type `A` +fail_compilation/diag16499.d(11): perhaps overload the operator with `auto opBinaryRight(string op : "in")(int rhs) {}` +fail_compilation/diag16499.d(26): Error: operator `in` is not defined for type `B` +fail_compilation/diag16499.d(12): perhaps overload the operator with `auto opBinaryRight(string op : "in")(double rhs) {}` --- */ diff --git a/tests/dmd/fail_compilation/diag20888.d b/tests/dmd/fail_compilation/diag20888.d new file mode 100644 index 0000000000..84af42533f --- /dev/null +++ b/tests/dmd/fail_compilation/diag20888.d @@ -0,0 +1,71 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/diag20888.d(24): Error: return value `callback` of type `int function()` does not match return type `int`, and cannot be implicitly converted +fail_compilation/diag20888.d(24): Did you intend to call the function pointer? +fail_compilation/diag20888.d(29): Error: return value `s` of type `string` does not match return type `int`, and cannot be implicitly converted +fail_compilation/diag20888.d(34): Error: return value `callback` of type `int delegate()` does not match return type `int`, and cannot be implicitly converted +fail_compilation/diag20888.d(34): Did you intend to call the delegate? +fail_compilation/diag20888.d(39): Error: return value `callback` of type `int delegate()` does not match return type `int`, and cannot be implicitly converted +fail_compilation/diag20888.d(39): Did you intend to call the delegate? +fail_compilation/diag20888.d(44): Error: return value `callback` of type `int delegate()*` does not match return type `int`, and cannot be implicitly converted +fail_compilation/diag20888.d(49): Error: return value `callback` of type `int delegate()` does not match return type `string`, and cannot be implicitly converted +fail_compilation/diag20888.d(54): Error: return value `() => 3755` of type `int function() pure nothrow @nogc @safe` does not match return type `int`, and cannot be implicitly converted +fail_compilation/diag20888.d(54): Did you intend to call the function pointer? +fail_compilation/diag20888.d(59): Error: `return` expression expected +fail_compilation/diag20888.d(64): Error: cannot return non-void from `void` function +fail_compilation/diag20888.d(70): Error: return value `() => i` of type `int delegate() pure nothrow @nogc @safe` does not match return type `int`, and cannot be implicitly converted +fail_compilation/diag20888.d(70): Did you intend to call the delegate? +--- +*/ + +int alpha(int function() callback) +{ + return callback; +} + +int beta(string s) +{ + return s; +} + +int gamma(int delegate() callback) +{ + return callback; +} + +int delta(int delegate() callback) +{ + return callback; +} + +int epsilon(int delegate()* callback) +{ + return callback; // no supplemental yet +} + +string zeta(int delegate() callback) +{ + return callback; +} + +int eta() +{ + return () => 0xEAB; +} + +int theta() +{ + return; +} + +void iota() +{ + return 0xEAB; +} + +int kappa() +{ + int i = 0xEAB; + return () { return i; }; +} diff --git a/tests/dmd/fail_compilation/diag23295.d b/tests/dmd/fail_compilation/diag23295.d index a0bfe88396..4f996b6dae 100644 --- a/tests/dmd/fail_compilation/diag23295.d +++ b/tests/dmd/fail_compilation/diag23295.d @@ -2,10 +2,10 @@ REQUIRED_ARGS: -preview=dip1000 TEST_OUTPUT: --- -fail_compilation/diag23295.d(21): Error: scope variable `x` assigned to non-scope parameter `y` calling `foo` +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(24): Error: scope variable `ex` assigned to non-scope parameter `e` calling `thro` +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` --- */ diff --git a/tests/dmd/fail_compilation/diag9620.d b/tests/dmd/fail_compilation/diag9620.d index 4af87df0a2..87ec1c5a98 100644 --- a/tests/dmd/fail_compilation/diag9620.d +++ b/tests/dmd/fail_compilation/diag9620.d @@ -1,10 +1,9 @@ /* TEST_OUTPUT: --- -fail_compilation/diag9620.d(20): Error: `pure` function `diag9620.main.bar` cannot call impure function `diag9620.foo1` -fail_compilation/diag9620.d(21): Error: `pure` function `diag9620.main.bar` cannot call impure function `diag9620.foo2!().foo2` -fail_compilation/diag9620.d(14): which wasn't inferred `pure` because of: -fail_compilation/diag9620.d(14): `pure` function `diag9620.foo2!().foo2` cannot access mutable static data `x` +fail_compilation/diag9620.d(19): Error: `pure` function `diag9620.main.bar` cannot call impure function `diag9620.foo1` +fail_compilation/diag9620.d(20): Error: `pure` function `diag9620.main.bar` cannot call impure function `diag9620.foo2!().foo2` +fail_compilation/diag9620.d(13): and accessing mutable static data `x` makes it fail to infer `pure` --- */ diff --git a/tests/dmd/fail_compilation/diag9679.d b/tests/dmd/fail_compilation/diag9679.d index 85923b7189..22e94e5759 100644 --- a/tests/dmd/fail_compilation/diag9679.d +++ b/tests/dmd/fail_compilation/diag9679.d @@ -1,8 +1,12 @@ -/* +/* REQUIRED_ARGS: -verrors=0 TEST_OUTPUT: --- -fail_compilation/diag9679.d(11): Error: variable `diag9679.main.n` - only parameters, functions and `foreach` declarations can be `ref` -fail_compilation/diag9679.d(12): Error: variable `diag9679.main.n` - storage class `auto` has no effect if type is not inferred, did you mean `scope`? +fail_compilation/diag9679.d(93): Deprecation: `auto` and `ref` storage classes should be adjacent +fail_compilation/diag9679.d(93): Deprecation: `auto` and `ref` storage classes should be adjacent +fail_compilation/diag9679.d(15): Error: rvalue `1` cannot be assigned to `ref n` +fail_compilation/diag9679.d(16): Error: variable `diag9679.main.n` - storage class `auto` has no effect if type is not inferred, did you mean `scope`? +fail_compilation/diag9679.d(17): Error: variable `diag9679.main.S.a` - field declarations cannot be `ref` +fail_compilation/diag9679.d(24): Error: returning `r` escapes a reference to local variable `i` --- */ @@ -10,4 +14,84 @@ void main() { if (ref n = 1) {} if (auto int n = 1) {} + struct S { ref int a; } +} + +ref int test2() +{ + int i; + ref r = i; + return r; +} + +ref int test3() +{ + extern int i; + ref r = i; + return r; +} + +struct S { int a; } + +void test4() +{ + S s; + ref int r1 = s.a; + r1 = 3; + __gshared S t2; + ref int r2 = t2.a; + static S t3; + ref int r3 = t3.a; + extern S t4; + ref int r4 = t4.a; +} + +/* TEST_OUTPUT: +--- +fail_compilation/diag9679.d(66): Error: variable `diag9679.test5.r5` - initializer is required for `ref` variable +fail_compilation/diag9679.d(66): Error: rvalue `0` cannot be assigned to `ref r5` +fail_compilation/diag9679.d(71): Error: rvalue `4` cannot be assigned to `ref x` +fail_compilation/diag9679.d(72): Error: returning `x` escapes a reference to local variable `x` +fail_compilation/diag9679.d(77): Error: type `immutable(int)` cannot be assigned to `ref int x` +fail_compilation/diag9679.d(84): Error: returning `x` escapes a reference to local variable `x` +fail_compilation/diag9679.d(89): Error: variable `diag9679.test9.x` - void initializer not allowed for `ref` variable +fail_compilation/diag9679.d(90): Error: variable `diag9679.test9.y` - void initializer not allowed for `ref` variable +fail_compilation/diag9679.d(96): Error: variable `x` - `auto ref` variable must have `auto` and `ref` adjacent +--- +*/ + + +void test5() +{ + ref int r5; +} + +ref int test6() +{ + ref int x = 4; + return x; +} + +void test7(immutable int y) +{ + ref int x = y; + x = 5; +} + +ref int test8() +{ + auto ref int x = 3; + return x; +} + +void test9() +{ + ref int x = void; + auto ref int y = void; +} + +void testKeywordOrder()(ref auto int x, auto const ref float y) {} +void testKeywordOrder() +{ + ref auto int x = 3; } diff --git a/tests/dmd/fail_compilation/diag_class_alloc.d b/tests/dmd/fail_compilation/diag_class_alloc.d deleted file mode 100644 index 326d82e071..0000000000 --- a/tests/dmd/fail_compilation/diag_class_alloc.d +++ /dev/null @@ -1,19 +0,0 @@ -/* -TEST_OUTPUT: ---- -fail_compilation/diag_class_alloc.d(15): Error: `new` allocator must be annotated with `@disabled` -fail_compilation/diag_class_alloc.d(16): Deprecation: `new` allocator with non-empty parameter list is deprecated -fail_compilation/diag_class_alloc.d(16): Deprecation: `new` allocator with function definition is deprecated ---- -*/ - -// This test exists to ensure class allocators and deallocators emit an appropriate error message. -// This test can be deleted when class allocators and deallocators are removed from the language. - -class C -{ - new(size_t size) // error message - { - return malloc(size); - } -} diff --git a/tests/dmd/fail_compilation/diag_debug_conditional.d b/tests/dmd/fail_compilation/diag_debug_conditional.d index 99884c70c4..51df97c803 100644 --- a/tests/dmd/fail_compilation/diag_debug_conditional.d +++ b/tests/dmd/fail_compilation/diag_debug_conditional.d @@ -1,8 +1,8 @@ /** TEST_OUTPUT: --- -fail_compilation/diag_debug_conditional.d(1): Error: identifier or integer expected inside `debug(...)`, not `alias` -fail_compilation/diag_debug_conditional.d(2): Error: identifier or integer expected inside `version(...)`, not `alias` +fail_compilation/diag_debug_conditional.d(1): Error: identifier expected inside `debug(...)`, not `alias` +fail_compilation/diag_debug_conditional.d(2): Error: identifier expected inside `version(...)`, not `alias` fail_compilation/diag_debug_conditional.d(3): Error: declaration expected following attribute, not end of file --- */ diff --git a/tests/dmd/fail_compilation/diag_funclit.d b/tests/dmd/fail_compilation/diag_funclit.d index 0173d4b78e..4009beadcf 100644 --- a/tests/dmd/fail_compilation/diag_funclit.d +++ b/tests/dmd/fail_compilation/diag_funclit.d @@ -1,19 +1,19 @@ /** TEST_OUTPUT: --- -fail_compilation/diag_funclit.d(103): Error: function literal `__lambda_L103_C5(x, y, z)` is not callable using argument types `()` +fail_compilation/diag_funclit.d(103): Error: function literal `(x, y, z) { return 42; }` is not callable using argument types `()` fail_compilation/diag_funclit.d(103): too few arguments, expected 3, got 0 -fail_compilation/diag_funclit.d(106): Error: function literal `__lambda_L106_C5(x, y, z)` is not callable using argument types `(int, string, int, int)` +fail_compilation/diag_funclit.d(106): Error: `(x, y, z) { return 42; }` is not callable using argument types `(int, string, int, int)` fail_compilation/diag_funclit.d(106): too many arguments, expected 3, got 4 -fail_compilation/diag_funclit.d(108): Error: function literal `__lambda_L108_C5(x, y, string z = "Hello")` is not callable using argument types `(int, int, string, string)` +fail_compilation/diag_funclit.d(108): Error: `(x, y, string z = "Hello") { return x; }` is not callable using argument types `(int, int, string, string)` fail_compilation/diag_funclit.d(108): too many arguments, expected 3, got 4 -fail_compilation/diag_funclit.d(110): Error: function literal `__lambda_L110_C5(x, y, string z = "Hello")` is not callable using argument types `(int)` +fail_compilation/diag_funclit.d(110): Error: `(x, y, string z = "Hello") { return x; }` is not callable using argument types `(int)` fail_compilation/diag_funclit.d(110): too few arguments, expected 3, got 1 -fail_compilation/diag_funclit.d(112): Error: function literal `__lambda_L112_C5(x, y, z)` is not callable using argument types `(int)` +fail_compilation/diag_funclit.d(112): Error: `(x, y, z) { return x; }` is not callable using argument types `(int)` fail_compilation/diag_funclit.d(112): too few arguments, expected 3, got 1 -fail_compilation/diag_funclit.d(115): Error: function literal `__lambda_L115_C5(x, y, ...)` is not callable using argument types `(int)` +fail_compilation/diag_funclit.d(115): Error: `(x, y, ...) { return x; }` is not callable using argument types `(int)` fail_compilation/diag_funclit.d(115): too few arguments, expected 2, got 1 -fail_compilation/diag_funclit.d(117): Error: function literal `__lambda_L117_C5(x, y, string z = "Hey", ...)` is not callable using argument types `(int)` +fail_compilation/diag_funclit.d(117): Error: `(x, y, string z = "Hey", ...) { return x; }` is not callable using argument types `(int)` fail_compilation/diag_funclit.d(117): too few arguments, expected 3, got 1 --- */ diff --git a/tests/dmd/fail_compilation/diag_template_alias.d b/tests/dmd/fail_compilation/diag_template_alias.d index 151bb42657..fd17852f6b 100644 --- a/tests/dmd/fail_compilation/diag_template_alias.d +++ b/tests/dmd/fail_compilation/diag_template_alias.d @@ -4,7 +4,7 @@ TEST_OUTPUT: fail_compilation/diag_template_alias.d(1): Error: identifier expected for template `alias` parameter fail_compilation/diag_template_alias.d(1): Error: found `alias` when expecting `(` fail_compilation/diag_template_alias.d(1): Error: semicolon expected following function declaration, not `(` -fail_compilation/diag_template_alias.d(1): Error: declaration expected, not `(` +fail_compilation/diag_template_alias.d(1): Error: declaration expected, not `)` --- */ #line 1 diff --git a/tests/dmd/fail_compilation/diag_template_this.d b/tests/dmd/fail_compilation/diag_template_this.d index 25de03ce19..d4250f77e3 100644 --- a/tests/dmd/fail_compilation/diag_template_this.d +++ b/tests/dmd/fail_compilation/diag_template_this.d @@ -4,7 +4,7 @@ TEST_OUTPUT: fail_compilation/diag_template_this.d(1): Error: identifier expected for template `this` parameter fail_compilation/diag_template_this.d(1): Error: found `this` when expecting `(` fail_compilation/diag_template_this.d(1): Error: semicolon expected following function declaration, not `(` -fail_compilation/diag_template_this.d(1): Error: declaration expected, not `(` +fail_compilation/diag_template_this.d(1): Error: declaration expected, not `)` --- */ #line 1 diff --git a/tests/dmd/fail_compilation/dip25.d b/tests/dmd/fail_compilation/dip25.d index f43a6e9ef8..f17e5e1dfa 100644 --- a/tests/dmd/fail_compilation/dip25.d +++ b/tests/dmd/fail_compilation/dip25.d @@ -2,10 +2,10 @@ REQUIRED_ARGS: TEST_OUTPUT: --- -fail_compilation/dip25.d(17): Error: returning `this.buffer[]` escapes a reference to parameter `this` +fail_compilation/dip25.d(17): Error: escaping a reference to parameter `this` by returning `this.buffer[]` is not allowed in a `@safe` function fail_compilation/dip25.d(15): perhaps annotate the function with `return` fail_compilation/dip25.d(22): Error: returning `identity(x)` escapes a reference to parameter `x` -fail_compilation/dip25.d(23): Error: returning `identity(x)` escapes a reference to parameter `x` +fail_compilation/dip25.d(23): Error: escaping a reference to parameter `x` by returning `identity(x)` is not allowed in a `@safe` function fail_compilation/dip25.d(23): perhaps annotate the parameter with `return` --- */ diff --git a/tests/dmd/fail_compilation/e15876_1.d b/tests/dmd/fail_compilation/e15876_1.d index 0152cdee27..70b79a2449 100644 --- a/tests/dmd/fail_compilation/e15876_1.d +++ b/tests/dmd/fail_compilation/e15876_1.d @@ -7,11 +7,11 @@ fail_compilation/e15876_1.d(18): Error: found `End of File` instead of statement fail_compilation/e15876_1.d(18): Error: matching `}` expected following compound statement, not `End of File` fail_compilation/e15876_1.d(17): unmatched `{` fail_compilation/e15876_1.d(18): Error: found `End of File` when expecting `]` -fail_compilation/e15876_1.d(18): Error: no identifier for declarator `o[() +fail_compilation/e15876_1.d(18): Error: variable name expected after type `o[() { scope(exit) __error__ } -]` +]`, not `End of File` --- */ o[{scope(x diff --git a/tests/dmd/fail_compilation/e15876_2.d b/tests/dmd/fail_compilation/e15876_2.d index 92164a4fd8..c996b95e33 100644 --- a/tests/dmd/fail_compilation/e15876_2.d +++ b/tests/dmd/fail_compilation/e15876_2.d @@ -5,11 +5,11 @@ fail_compilation/e15876_2.d(16): Error: identifier expected following `template` fail_compilation/e15876_2.d(16): Error: matching `}` expected following compound statement, not `End of File` fail_compilation/e15876_2.d(15): unmatched `{` fail_compilation/e15876_2.d(16): Error: found `End of File` when expecting `]` -fail_compilation/e15876_2.d(16): Error: no identifier for declarator `o[() +fail_compilation/e15876_2.d(16): Error: variable name expected after type `o[() { ; } -]` +]`, not `End of File` --- */ o[{template diff --git a/tests/dmd/fail_compilation/e15876_3.d b/tests/dmd/fail_compilation/e15876_3.d index 0482e87a54..7e9d70913d 100644 --- a/tests/dmd/fail_compilation/e15876_3.d +++ b/tests/dmd/fail_compilation/e15876_3.d @@ -13,15 +13,15 @@ fail_compilation/e15876_3.d(29): Error: found `End of File` instead of statement fail_compilation/e15876_3.d(29): Error: matching `}` expected following compound statement, not `End of File` fail_compilation/e15876_3.d(28): unmatched `{` fail_compilation/e15876_3.d(29): Error: found `End of File` when expecting `)` -fail_compilation/e15876_3.d(29): Error: no identifier for declarator `d(_error_ = () +fail_compilation/e15876_3.d(29): Error: variable name expected after type `d(_error_ = () { for (__error__ - 0; 0) + __error; __error) { __error__ } } -)` +)`, not `End of File` fail_compilation/e15876_3.d(29): Error: semicolon expected following function declaration, not `End of File` --- */ diff --git a/tests/dmd/fail_compilation/e15876_4.d b/tests/dmd/fail_compilation/e15876_4.d index e5c3bbf567..7652167164 100644 --- a/tests/dmd/fail_compilation/e15876_4.d +++ b/tests/dmd/fail_compilation/e15876_4.d @@ -12,15 +12,15 @@ fail_compilation/e15876_4.d(27): Error: found `End of File` instead of statement fail_compilation/e15876_4.d(27): Error: matching `}` expected following compound statement, not `End of File` fail_compilation/e15876_4.d(26): unmatched `{` fail_compilation/e15876_4.d(27): Error: found `End of File` when expecting `)` -fail_compilation/e15876_4.d(27): Error: no identifier for declarator `typeof(() +fail_compilation/e15876_4.d(27): Error: variable name expected after type `typeof(() { for (__error__ - 0; 0) + __error; __error) { __error__ } } -)` +)`, not `End of File` --- */ typeof){for diff --git a/tests/dmd/fail_compilation/e15876_5.d b/tests/dmd/fail_compilation/e15876_5.d index 6bebc29fcb..42183b26bd 100644 --- a/tests/dmd/fail_compilation/e15876_5.d +++ b/tests/dmd/fail_compilation/e15876_5.d @@ -6,11 +6,11 @@ fail_compilation/e15876_5.d(17): Error: semicolon expected to close `alias` decl fail_compilation/e15876_5.d(17): Error: matching `}` expected following compound statement, not `End of File` fail_compilation/e15876_5.d(16): unmatched `{` fail_compilation/e15876_5.d(17): Error: found `End of File` when expecting `]` -fail_compilation/e15876_5.d(17): Error: no identifier for declarator `p[() +fail_compilation/e15876_5.d(17): Error: variable name expected after type `p[() { alias ; } -]` +]`, not `End of File` --- */ p[{alias diff --git a/tests/dmd/fail_compilation/fail100.d b/tests/dmd/fail_compilation/fail100.d index a8189ecb0a..0ebb921023 100644 --- a/tests/dmd/fail_compilation/fail100.d +++ b/tests/dmd/fail_compilation/fail100.d @@ -1,7 +1,7 @@ /* TEST_OUTPUT: --- -fail_compilation/fail100.d(24): Error: cannot implicitly convert expression `f` of type `Class[]` to `I[]` +fail_compilation/fail100.d(24): Error: return value `f` of type `Class[]` does not match return type `I[]`, and cannot be implicitly converted --- */ diff --git a/tests/dmd/fail_compilation/fail10534.d b/tests/dmd/fail_compilation/fail10534.d index b5bb67c267..f0e0b855b7 100644 --- a/tests/dmd/fail_compilation/fail10534.d +++ b/tests/dmd/fail_compilation/fail10534.d @@ -1,22 +1,18 @@ /* TEST_OUTPUT: --- -fail_compilation/fail10534.d(28): Error: illegal operator `+` for `a` of type `int delegate()` -fail_compilation/fail10534.d(28): Error: illegal operator `+` for `b` of type `int delegate()` -fail_compilation/fail10534.d(29): Error: illegal operator `-` for `a` of type `int delegate()` -fail_compilation/fail10534.d(29): Error: illegal operator `-` for `b` of type `int delegate()` -fail_compilation/fail10534.d(30): Error: illegal operator `/` for `a` of type `int delegate()` -fail_compilation/fail10534.d(30): Error: illegal operator `/` for `b` of type `int delegate()` -fail_compilation/fail10534.d(31): Error: illegal operator `*` for `a` of type `int delegate()` -fail_compilation/fail10534.d(31): Error: illegal operator `*` for `b` of type `int delegate()` -fail_compilation/fail10534.d(36): Error: illegal operator `+` for `a` of type `int function()` -fail_compilation/fail10534.d(36): Error: illegal operator `+` for `b` of type `int function()` -fail_compilation/fail10534.d(37): Error: illegal operator `-` for `a` of type `int function()` -fail_compilation/fail10534.d(37): Error: illegal operator `-` for `b` of type `int function()` -fail_compilation/fail10534.d(38): Error: illegal operator `/` for `a` of type `int function()` -fail_compilation/fail10534.d(38): Error: illegal operator `/` for `b` of type `int function()` -fail_compilation/fail10534.d(39): Error: illegal operator `*` for `a` of type `int function()` -fail_compilation/fail10534.d(39): Error: illegal operator `*` for `b` of type `int function()` +fail_compilation/fail10534.d(24): Error: illegal operator `+` for `a` of type `int delegate()` +fail_compilation/fail10534.d(24): Error: illegal operator `+` for `b` of type `int delegate()` +fail_compilation/fail10534.d(25): Error: illegal operator `-` for `a` of type `int delegate()` +fail_compilation/fail10534.d(25): Error: illegal operator `-` for `b` of type `int delegate()` +fail_compilation/fail10534.d(26): Error: illegal operator `/` for `a` of type `int delegate()` +fail_compilation/fail10534.d(27): Error: illegal operator `*` for `a` of type `int delegate()` +fail_compilation/fail10534.d(32): Error: illegal operator `+` for `a` of type `int function()` +fail_compilation/fail10534.d(32): Error: illegal operator `+` for `b` of type `int function()` +fail_compilation/fail10534.d(33): Error: illegal operator `-` for `a` of type `int function()` +fail_compilation/fail10534.d(33): Error: illegal operator `-` for `b` of type `int function()` +fail_compilation/fail10534.d(34): Error: illegal operator `/` for `a` of type `int function()` +fail_compilation/fail10534.d(35): Error: illegal operator `*` for `a` of type `int function()` --- */ diff --git a/tests/dmd/fail_compilation/fail10964.d b/tests/dmd/fail_compilation/fail10964.d index 9f38cb0406..e496f87043 100644 --- a/tests/dmd/fail_compilation/fail10964.d +++ b/tests/dmd/fail_compilation/fail10964.d @@ -1,12 +1,12 @@ /* TEST_OUTPUT: --- -fail_compilation/fail10964.d(28): Error: function `fail10964.S.__postblit` is not `nothrow` -fail_compilation/fail10964.d(29): Error: function `fail10964.S.__postblit` is not `nothrow` -fail_compilation/fail10964.d(30): Error: function `fail10964.S.__postblit` is not `nothrow` -fail_compilation/fail10964.d(33): Error: function `fail10964.S.__postblit` is not `nothrow` -fail_compilation/fail10964.d(34): Error: function `fail10964.S.__postblit` is not `nothrow` -fail_compilation/fail10964.d(35): Error: function `fail10964.S.__postblit` is not `nothrow` +fail_compilation/fail10964.d(28): Error: function `fail10964.S.this(this)` is not `nothrow` +fail_compilation/fail10964.d(29): Error: function `fail10964.S.this(this)` is not `nothrow` +fail_compilation/fail10964.d(30): Error: function `fail10964.S.this(this)` is not `nothrow` +fail_compilation/fail10964.d(33): Error: function `fail10964.S.this(this)` is not `nothrow` +fail_compilation/fail10964.d(34): Error: function `fail10964.S.this(this)` is not `nothrow` +fail_compilation/fail10964.d(35): Error: function `fail10964.S.this(this)` is not `nothrow` fail_compilation/fail10964.d(22): Error: function `fail10964.foo` may throw but is marked as `nothrow` --- */ diff --git a/tests/dmd/fail_compilation/fail10968.d b/tests/dmd/fail_compilation/fail10968.d index 3b6c3a0610..dd793e108a 100644 --- a/tests/dmd/fail_compilation/fail10968.d +++ b/tests/dmd/fail_compilation/fail10968.d @@ -1,36 +1,24 @@ /* TEST_OUTPUT: --- -fail_compilation/fail10968.d(43): Error: `pure` function `fail10968.bar` cannot call impure function `fail10968.SA.__postblit` -fail_compilation/fail10968.d(43): Error: `@safe` function `fail10968.bar` cannot call `@system` function `fail10968.SA.__postblit` -fail_compilation/fail10968.d(31): `fail10968.SA.__postblit` is declared here -fail_compilation/fail10968.d(44): Error: `pure` function `fail10968.bar` cannot call impure function `fail10968.SA.__postblit` -fail_compilation/fail10968.d(44): Error: `@safe` function `fail10968.bar` cannot call `@system` function `fail10968.SA.__postblit` -fail_compilation/fail10968.d(31): `fail10968.SA.__postblit` is declared here -fail_compilation/fail10968.d(44): Error: `pure` function `fail10968.bar` cannot call impure function `core.internal.array.arrayassign._d_arraysetassign!(SA[], SA)._d_arraysetassign` -$p:/core/internal/array/arrayassign.d$($n$): which calls `core.lifetime.copyEmplace!(SA, SA).copyEmplace` -$p:/core/lifetime.d$($n$): which calls `fail10968.SA.__postblit` -fail_compilation/fail10968.d(45): Error: `pure` function `fail10968.bar` cannot call impure function `fail10968.SA.__postblit` -fail_compilation/fail10968.d(45): Error: `@safe` function `fail10968.bar` cannot call `@system` function `fail10968.SA.__postblit` -fail_compilation/fail10968.d(31): `fail10968.SA.__postblit` is declared here -fail_compilation/fail10968.d(45): Error: `pure` function `fail10968.bar` cannot call impure function `core.internal.array.arrayassign._d_arrayassign_l!(SA[], SA)._d_arrayassign_l` -$p:/core/internal/array/arrayassign.d$-mixin-$n$($n$): which calls `core.lifetime.copyEmplace!(SA, SA).copyEmplace` -$p:/core/lifetime.d$($n$): which calls `fail10968.SA.__postblit` -fail_compilation/fail10968.d(48): Error: `pure` function `fail10968.bar` cannot call impure function `fail10968.SA.__postblit` -fail_compilation/fail10968.d(48): Error: `@safe` function `fail10968.bar` cannot call `@system` function `fail10968.SA.__postblit` -fail_compilation/fail10968.d(31): `fail10968.SA.__postblit` is declared here -fail_compilation/fail10968.d(49): Error: `pure` function `fail10968.bar` cannot call impure function `fail10968.SA.__postblit` -fail_compilation/fail10968.d(49): Error: `@safe` function `fail10968.bar` cannot call `@system` function `fail10968.SA.__postblit` -fail_compilation/fail10968.d(31): `fail10968.SA.__postblit` is declared here -fail_compilation/fail10968.d(49): Error: `pure` function `fail10968.bar` cannot call impure function `core.internal.array.construction._d_arraysetctor!(SA[], SA)._d_arraysetctor` -$p:/core/internal/array/construction.d$($n$): which calls `core.lifetime.copyEmplace!(SA, SA).copyEmplace` -$p:/core/lifetime.d$($n$): which calls `fail10968.SA.__postblit` -fail_compilation/fail10968.d(50): Error: `pure` function `fail10968.bar` cannot call impure function `fail10968.SA.__postblit` -fail_compilation/fail10968.d(50): Error: `@safe` function `fail10968.bar` cannot call `@system` function `fail10968.SA.__postblit` -fail_compilation/fail10968.d(31): `fail10968.SA.__postblit` is declared here -fail_compilation/fail10968.d(50): Error: `pure` function `fail10968.bar` cannot call impure function `core.internal.array.construction._d_arrayctor!(SA[], SA)._d_arrayctor` -$p:/core/internal/array/construction.d$($n$): which calls `core.lifetime.copyEmplace!(SA, SA).copyEmplace` -$p:/core/lifetime.d$($n$): which calls `fail10968.SA.__postblit` +fail_compilation/fail10968.d(43): Error: `pure` function `fail10968.bar` cannot call impure function `fail10968.SA.this(this)` +fail_compilation/fail10968.d(43): Error: `@safe` function `fail10968.bar` cannot call `@system` function `fail10968.SA.this(this)` +fail_compilation/fail10968.d(31): `fail10968.SA.this(this)` is declared here +fail_compilation/fail10968.d(44): Error: `pure` function `fail10968.bar` cannot call impure function `fail10968.SA.this(this)` +fail_compilation/fail10968.d(44): Error: `@safe` function `fail10968.bar` cannot call `@system` function `fail10968.SA.this(this)` +fail_compilation/fail10968.d(31): `fail10968.SA.this(this)` is declared here +fail_compilation/fail10968.d(45): Error: `pure` function `fail10968.bar` cannot call impure function `fail10968.SA.this(this)` +fail_compilation/fail10968.d(45): Error: `@safe` function `fail10968.bar` cannot call `@system` function `fail10968.SA.this(this)` +fail_compilation/fail10968.d(31): `fail10968.SA.this(this)` is declared here +fail_compilation/fail10968.d(48): Error: `pure` function `fail10968.bar` cannot call impure function `fail10968.SA.this(this)` +fail_compilation/fail10968.d(48): Error: `@safe` function `fail10968.bar` cannot call `@system` function `fail10968.SA.this(this)` +fail_compilation/fail10968.d(31): `fail10968.SA.this(this)` is declared here +fail_compilation/fail10968.d(49): Error: `pure` function `fail10968.bar` cannot call impure function `fail10968.SA.this(this)` +fail_compilation/fail10968.d(49): Error: `@safe` function `fail10968.bar` cannot call `@system` function `fail10968.SA.this(this)` +fail_compilation/fail10968.d(31): `fail10968.SA.this(this)` is declared here +fail_compilation/fail10968.d(50): Error: `pure` function `fail10968.bar` cannot call impure function `fail10968.SA.this(this)` +fail_compilation/fail10968.d(50): Error: `@safe` function `fail10968.bar` cannot call `@system` function `fail10968.SA.this(this)` +fail_compilation/fail10968.d(31): `fail10968.SA.this(this)` is declared here --- */ diff --git a/tests/dmd/fail_compilation/fail11125.d b/tests/dmd/fail_compilation/fail11125.d index 4349755519..457dda83f5 100644 --- a/tests/dmd/fail_compilation/fail11125.d +++ b/tests/dmd/fail_compilation/fail11125.d @@ -2,11 +2,11 @@ TEST_OUTPUT: --- fail_compilation/fail11125.d(26): Error: template instance `fail11125.filter!(function (int a) pure nothrow @nogc @safe => a + 1)` does not match template declaration `filter(alias predfun)` - with `predfun = __lambda_L26_C13` + with `predfun = (int a) => a + 1` must satisfy the following constraint: ` is(ReturnType!predfun == bool)` fail_compilation/fail11125.d(27): Error: template instance `fail11125.filter!(function (int a) pure nothrow @nogc @safe => a + 1)` does not match template declaration `filter(alias predfun)` - with `predfun = __lambda_L27_C17` + with `predfun = (int a) => a + 1` must satisfy the following constraint: ` is(ReturnType!predfun == bool)` --- diff --git a/tests/dmd/fail_compilation/fail11375.d b/tests/dmd/fail_compilation/fail11375.d index cabf87a6ca..26e633c27d 100644 --- a/tests/dmd/fail_compilation/fail11375.d +++ b/tests/dmd/fail_compilation/fail11375.d @@ -2,7 +2,7 @@ TEST_OUTPUT: --- fail_compilation/fail11375.d(18): Error: constructor `fail11375.D!().D.this` is not `nothrow` - which calls `fail11375.B.this` + which calls `this` fail_compilation/fail11375.d(16): Error: function `D main` may throw but is marked as `nothrow` --- */ diff --git a/tests/dmd/fail_compilation/fail11445.d b/tests/dmd/fail_compilation/fail11445.d index 3295b24381..e4105b8d92 100644 --- a/tests/dmd/fail_compilation/fail11445.d +++ b/tests/dmd/fail_compilation/fail11445.d @@ -1,7 +1,7 @@ /* TEST_OUTPUT: --- -fail_compilation/fail11445.d(11): Error: incompatible types for `(a) + (b)`: both operands are of type `double[string]` +fail_compilation/fail11445.d(11): Error: illegal operator `+` for `a` of type `double[string]` --- */ diff --git a/tests/dmd/fail_compilation/fail117.d b/tests/dmd/fail_compilation/fail117.d index b0e1b120c9..0f0c5ad678 100644 --- a/tests/dmd/fail_compilation/fail117.d +++ b/tests/dmd/fail_compilation/fail117.d @@ -1,10 +1,8 @@ /* TEST_OUTPUT: --- -fail_compilation/fail117.d(37): Error: expression `foo.mixin MGettor!(a) geta; -` is `void` and has no value -fail_compilation/fail117.d(38): Error: expression `foo.mixin MGettor!(b) getb; -` is `void` and has no value +fail_compilation/fail117.d(35): Error: expression `foo.mixin MGettor!(a) geta;` is `void` and has no value +fail_compilation/fail117.d(36): Error: expression `foo.mixin MGettor!(b) getb;` is `void` and has no value --- */ diff --git a/tests/dmd/fail_compilation/fail11751.d b/tests/dmd/fail_compilation/fail11751.d index 36d7f9d8e3..b2d774b754 100644 --- a/tests/dmd/fail_compilation/fail11751.d +++ b/tests/dmd/fail_compilation/fail11751.d @@ -3,7 +3,7 @@ TEST_OUTPUT: --- fail_compilation/fail11751.d(10): Error: missing exponent fail_compilation/fail11751.d(10): Error: semicolon expected following auto declaration, not `ABC` -fail_compilation/fail11751.d(10): Error: no identifier for declarator `ABC` +fail_compilation/fail11751.d(10): Error: variable name expected after type `ABC`, not `;` --- */ diff --git a/tests/dmd/fail_compilation/fail12932.d b/tests/dmd/fail_compilation/fail12932.d index fe68fea01c..2ef9e152d0 100644 --- a/tests/dmd/fail_compilation/fail12932.d +++ b/tests/dmd/fail_compilation/fail12932.d @@ -1,8 +1,8 @@ /* TEST_OUTPUT: --- -fail_compilation/fail12932.d(11): Error: array literal in `@nogc` function `fail12932.foo` may cause a GC allocation -fail_compilation/fail12932.d(15): Error: array literal in `@nogc` function `fail12932.foo` may cause a GC allocation +fail_compilation/fail12932.d(11): Error: this array literal causes a GC allocation in `@nogc` function `foo` +fail_compilation/fail12932.d(15): Error: this array literal causes a GC allocation in `@nogc` function `foo` --- */ diff --git a/tests/dmd/fail_compilation/fail13120.d b/tests/dmd/fail_compilation/fail13120.d index 1d1127c4c3..cc73b5f9ba 100644 --- a/tests/dmd/fail_compilation/fail13120.d +++ b/tests/dmd/fail_compilation/fail13120.d @@ -17,7 +17,7 @@ void g1(char[] s) pure @nogc TEST_OUTPUT: --- fail_compilation/fail13120.d(35): Error: `pure` function `fail13120.h2` cannot call impure function `fail13120.g2!().g2` -fail_compilation/fail13120.d(30): which calls `fail13120.f2` +fail_compilation/fail13120.d(30): which calls `f2` fail_compilation/fail13120.d(35): Error: `@safe` function `fail13120.h2` cannot call `@system` function `fail13120.g2!().g2` fail_compilation/fail13120.d(27): `fail13120.g2!().g2` is declared here fail_compilation/fail13120.d(35): Error: `@nogc` function `fail13120.h2` cannot call non-@nogc function `fail13120.g2!().g2` diff --git a/tests/dmd/fail_compilation/fail13123.d b/tests/dmd/fail_compilation/fail13123.d index 7784cba67b..4a3be08308 100644 --- a/tests/dmd/fail_compilation/fail13123.d +++ b/tests/dmd/fail_compilation/fail13123.d @@ -1,9 +1,8 @@ -// REQUIRED_ARGS: -de /* TEST_OUTPUT: --- -fail_compilation/fail13123.d(10): Deprecation: `fail13123.test`: `in` contract may throw but function is marked as `nothrow` -fail_compilation/fail13123.d(10): Deprecation: `fail13123.test`: `out` contract may throw but function is marked as `nothrow` +fail_compilation/fail13123.d(9): Error: `fail13123.test`: `in` contract may throw but function is marked as `nothrow` +fail_compilation/fail13123.d(9): Error: `fail13123.test`: `out` contract may throw but function is marked as `nothrow` --- */ diff --git a/tests/dmd/fail_compilation/fail13424.d b/tests/dmd/fail_compilation/fail13424.d index 1a7f16b55c..5533e24b0e 100644 --- a/tests/dmd/fail_compilation/fail13424.d +++ b/tests/dmd/fail_compilation/fail13424.d @@ -1,9 +1,9 @@ /* TEST_OUTPUT: --- -fail_compilation/fail13424.d(12): Error: delegate `fail13424.S.__lambda_L12_C35` cannot be struct members -fail_compilation/fail13424.d(17): Error: delegate `fail13424.U.__lambda_L17_C35` cannot be union members -fail_compilation/fail13424.d(22): Error: delegate `fail13424.C.__lambda_L22_C35` cannot be class members +fail_compilation/fail13424.d(12): Error: delegate `(dchar) { }` cannot be struct members +fail_compilation/fail13424.d(17): Error: delegate `(dchar) { }` cannot be union members +fail_compilation/fail13424.d(22): Error: delegate `(dchar) { }` cannot be class members --- */ diff --git a/tests/dmd/fail_compilation/fail13498.d b/tests/dmd/fail_compilation/fail13498.d index 27f47b3f7a..e8099f3cd8 100644 --- a/tests/dmd/fail_compilation/fail13498.d +++ b/tests/dmd/fail_compilation/fail13498.d @@ -1,7 +1,7 @@ /* TEST_OUTPUT: --- -fail_compilation/fail13498.d(11): Error: cannot implicitly convert expression `"foo"` of type `string` to `int` +fail_compilation/fail13498.d(11): Error: return value `"foo"` of type `string` does not match return type `int`, and cannot be implicitly converted fail_compilation/fail13498.d(16): Error: template instance `fail13498.foo!()` error instantiating --- */ diff --git a/tests/dmd/fail_compilation/fail13902.d b/tests/dmd/fail_compilation/fail13902.d index 47cb65cde2..a1c1bab986 100644 --- a/tests/dmd/fail_compilation/fail13902.d +++ b/tests/dmd/fail_compilation/fail13902.d @@ -14,7 +14,7 @@ fail_compilation/fail13902.d(33): Error: returning `&s1.v` escapes a reference t fail_compilation/fail13902.d(38): Error: returning `& sa1` escapes a reference to local variable `sa1` fail_compilation/fail13902.d(39): Error: returning `& sa2` escapes a reference to local variable `sa2` fail_compilation/fail13902.d(40): Error: returning `& x` escapes a reference to local variable `x` -fail_compilation/fail13902.d(41): Error: returning `(& x+4)` escapes a reference to local variable `x` +fail_compilation/fail13902.d(41): Error: returning `(& x + 4)` escapes a reference to local variable `x` fail_compilation/fail13902.d(42): Error: returning `& x + cast(long)x * 4L` escapes a reference to local variable `x` fail_compilation/fail13902.d(45): Error: returning `& y` escapes a reference to local variable `y` --- @@ -59,7 +59,7 @@ fail_compilation/fail13902.d(76): Error: returning `&s1.v` escapes a reference t fail_compilation/fail13902.d(81): Error: returning `& sa1` escapes a reference to parameter `sa1` fail_compilation/fail13902.d(82): Error: returning `& sa2` escapes a reference to parameter `sa2` fail_compilation/fail13902.d(83): Error: returning `& x` escapes a reference to parameter `x` -fail_compilation/fail13902.d(84): Error: returning `(& x+4)` escapes a reference to parameter `x` +fail_compilation/fail13902.d(84): Error: returning `(& x + 4)` escapes a reference to parameter `x` fail_compilation/fail13902.d(85): Error: returning `& x + cast(long)x * 4L` escapes a reference to parameter `x` fail_compilation/fail13902.d(88): Error: returning `& y` escapes a reference to parameter `y` --- @@ -323,9 +323,9 @@ int[] testSlice2() { int[3] sa; int n; return sa[n..2][1..2]; } /* TEST_OUTPUT: --- -fail_compilation/fail13902.d(324): Error: returning `vda[0]` escapes a reference to parameter `vda` +fail_compilation/fail13902.d(324): Error: returning `vda[0]` escapes a reference to variadic parameter `vda` +fail_compilation/fail13902.d(325): Error: returning `vda[]` escapes a reference to variadic parameter `vda` --- - */ ref int testDynamicArrayVariadic1(int[] vda...) { return vda[0]; } @safe int[] testDynamicArrayVariadic2(int[] vda...) { return vda[]; } @@ -335,9 +335,28 @@ int[3] testDynamicArrayVariadic3(int[] vda...) { return vda[0..3]; } // no er TEST_OUTPUT: --- fail_compilation/fail13902.d(335): Error: returning `vsa[0]` escapes a reference to parameter `vsa` -fail_compilation/fail13902.d(336): Error: returning `vsa[]` escapes a reference to variadic parameter `vsa` +fail_compilation/fail13902.d(336): Error: returning `vsa[]` escapes a reference to parameter `vsa` --- */ ref int testStaticArrayVariadic1(int[3] vsa...) { return vsa[0]; } int[] testStaticArrayVariadic2(int[3] vsa...) { return vsa[]; } int[3] testStaticArrayVariadic3(int[3] vsa...) { return vsa[0..3]; } // no error + +/* +TEST_OUTPUT: +--- +fail_compilation/fail13902.d(355): Error: returning `match(st)` escapes a reference to local variable `st` +--- +*/ + +// This was reduced from a `static assert(!__traits(compiles, {...}))` test in `std.sumtype` +// which was asserting that matchers couldn't escape sumtype members. +// This should give an error even without `@safe` or `-preview=dip1000` + +int* match(return ref int i) { return &i; } + +int* escape() +{ + int st; + return match(st); +} diff --git a/tests/dmd/fail_compilation/fail14343.d b/tests/dmd/fail_compilation/fail14343.d new file mode 100644 index 0000000000..d644ec9e56 --- /dev/null +++ b/tests/dmd/fail_compilation/fail14343.d @@ -0,0 +1,25 @@ +// https://issues.dlang.org/show_bug.cgi?id=14343 +/* TEST_OUTPUT: +--- +fail_compilation/fail14343.d(21): Error: cannot modify struct instance `s` of type `S14343b` because it contains `const` or `immutable` members +fail_compilation/fail14343.d(23): Error: cannot modify struct instance `s` of type `S14343b` because it contains `const` or `immutable` members +--- +*/ + +struct S14343b +{ + int i; + immutable(Object) o; + + void opAddAssign(int j) { i += j; } + void opAssign(S14343b other) {} +} + +void test14343() +{ + S14343b s; + ++s; + assert(s.i == 1); + s++; + assert(s.i == 2); +} diff --git a/tests/dmd/fail_compilation/fail14486.d b/tests/dmd/fail_compilation/fail14486.d deleted file mode 100644 index 3531245496..0000000000 --- a/tests/dmd/fail_compilation/fail14486.d +++ /dev/null @@ -1,79 +0,0 @@ -// REQUIRED_ARGS: -o- - -/* -TEST_OUTPUT: ---- -fail_compilation/fail14486.d(47): Error: the `delete` keyword is obsolete -fail_compilation/fail14486.d(47): use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead -fail_compilation/fail14486.d(48): Error: the `delete` keyword is obsolete -fail_compilation/fail14486.d(48): use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead -fail_compilation/fail14486.d(53): Error: the `delete` keyword is obsolete -fail_compilation/fail14486.d(53): use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead -fail_compilation/fail14486.d(54): Error: the `delete` keyword is obsolete -fail_compilation/fail14486.d(54): use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead -fail_compilation/fail14486.d(59): Error: the `delete` keyword is obsolete -fail_compilation/fail14486.d(59): use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead -fail_compilation/fail14486.d(60): Error: the `delete` keyword is obsolete -fail_compilation/fail14486.d(60): use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead -fail_compilation/fail14486.d(65): Error: the `delete` keyword is obsolete -fail_compilation/fail14486.d(65): use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead -fail_compilation/fail14486.d(66): Error: the `delete` keyword is obsolete -fail_compilation/fail14486.d(66): use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead -fail_compilation/fail14486.d(71): Error: the `delete` keyword is obsolete -fail_compilation/fail14486.d(71): use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead -fail_compilation/fail14486.d(72): Error: the `delete` keyword is obsolete -fail_compilation/fail14486.d(72): use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead -fail_compilation/fail14486.d(77): Error: the `delete` keyword is obsolete -fail_compilation/fail14486.d(77): use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead -fail_compilation/fail14486.d(78): Error: the `delete` keyword is obsolete -fail_compilation/fail14486.d(78): use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead ---- -*/ - -class C0a { } -class C1a { ~this() {} } - -class C0b { } -class C1b { ~this() {} } - -struct S0a { } -struct S1a { ~this() {} } - -struct S0b { } -struct S1b { ~this() {} } - -void test1a() @nogc pure @safe -{ - C0a c0; delete c0; // error - C1a c1; delete c1; // error -} - -void test1b() nothrow -{ - C0b c0; delete c0; // no error - C1b c1; delete c1; // error -} - -void test2a() @nogc pure @safe -{ - S0a* s0; delete s0; // error - S1a* s1; delete s1; // error -} - -void test2b() nothrow -{ - S0b* s0; delete s0; // no error - S1b* s1; delete s1; // error -} - -void test3a() @nogc pure @safe -{ - S0a[] a0; delete a0; // error - S1a[] a1; delete a1; // error -} - -void test3b() nothrow -{ - S0b[] a0; delete a0; // no error - S1b[] a1; delete a1; // error -} diff --git a/tests/dmd/fail_compilation/fail14554.d b/tests/dmd/fail_compilation/fail14554.d index b71a68e232..26f6f16215 100644 --- a/tests/dmd/fail_compilation/fail14554.d +++ b/tests/dmd/fail_compilation/fail14554.d @@ -3,11 +3,11 @@ /* TEST_OUTPUT: --- -fail_compilation/fail14554.d(28): Error: `fail14554.issue14554_1.foo` called with argument types `(int)` matches both: +fail_compilation/fail14554.d(28): Error: `fail14554.issue14554_1.foo` called with argument types `(int)` matches multiple overloads exactly: fail_compilation/fail14554.d(17): `fail14554.issue14554_1.foo!bool.foo(int j)` and: fail_compilation/fail14554.d(18): `fail14554.issue14554_1.foo!bool.foo(int j)` -fail_compilation/fail14554.d(29): Error: `fail14554.issue14554_2.foo` called with argument types `(int)` matches both: +fail_compilation/fail14554.d(29): Error: `fail14554.issue14554_2.foo` called with argument types `(int)` matches multiple overloads exactly: fail_compilation/fail14554.d(22): `fail14554.issue14554_2.foo!bool.foo(int j)` and: fail_compilation/fail14554.d(23): `fail14554.issue14554_2.foo!bool.foo(int j)` diff --git a/tests/dmd/fail_compilation/fail16.d b/tests/dmd/fail_compilation/fail16.d index f462a13a58..4602d3de3b 100644 --- a/tests/dmd/fail_compilation/fail16.d +++ b/tests/dmd/fail_compilation/fail16.d @@ -3,7 +3,7 @@ TEST_OUTPUT: --- fail_compilation/fail16.d(19): Error: function declaration without return type. (Note that constructors are always named `this`) -fail_compilation/fail16.d(19): Error: no identifier for declarator `bar!(typeof(X))(X)` +fail_compilation/fail16.d(19): Error: variable name expected after type `bar!(typeof(X))(X)`, not `;` --- */ diff --git a/tests/dmd/fail_compilation/fail16575.d b/tests/dmd/fail_compilation/fail16575.d index 7f6672428b..89d63ae279 100644 --- a/tests/dmd/fail_compilation/fail16575.d +++ b/tests/dmd/fail_compilation/fail16575.d @@ -3,29 +3,29 @@ REQUIRED_ARGS: -m64 TEST_OUTPUT: --- -fail_compilation/fail16575.d(10): Error: function `fail16575.immNull` cannot have parameter of type `immutable(typeof(null))*` because its linkage is `extern(C++)` -fail_compilation/fail16575.d(11): Error: function `fail16575.shaNull` cannot have parameter of type `shared(typeof(null))*` because its linkage is `extern(C++)` -fail_compilation/fail16575.d(20): Error: function `fail16575.immNoReturn` cannot have parameter of type `immutable(noreturn)*` because its linkage is `extern(C++)` -fail_compilation/fail16575.d(21): Error: function `fail16575.shaNoReturn` cannot have parameter of type `shared(noreturn)*` because its linkage is `extern(C++)` -fail_compilation/fail16575.d(30): Error: function `fail16575.immBasic` cannot have parameter of type `immutable(int)*` because its linkage is `extern(C++)` -fail_compilation/fail16575.d(31): Error: function `fail16575.shaBasic` cannot have parameter of type `shared(int)*` because its linkage is `extern(C++)` -fail_compilation/fail16575.d(40): Error: function `fail16575.immVector` cannot have parameter of type `immutable(__vector(long[2]))*` because its linkage is `extern(C++)` -fail_compilation/fail16575.d(41): Error: function `fail16575.shaVector` cannot have parameter of type `shared(__vector(long[2]))*` because its linkage is `extern(C++)` -fail_compilation/fail16575.d(50): Error: function `fail16575.immSArray` cannot have parameter of type `immutable(long[2])` because its linkage is `extern(C++)` +fail_compilation/fail16575.d(10): Error: function `immNull` cannot have parameter of type `immutable(typeof(null))*` because its linkage is `extern(C++)` +fail_compilation/fail16575.d(11): Error: function `shaNull` cannot have parameter of type `shared(typeof(null))*` because its linkage is `extern(C++)` +fail_compilation/fail16575.d(20): Error: function `immNoReturn` cannot have parameter of type `immutable(noreturn)*` because its linkage is `extern(C++)` +fail_compilation/fail16575.d(21): Error: function `shaNoReturn` cannot have parameter of type `shared(noreturn)*` because its linkage is `extern(C++)` +fail_compilation/fail16575.d(30): Error: function `immBasic` cannot have parameter of type `immutable(int)*` because its linkage is `extern(C++)` +fail_compilation/fail16575.d(31): Error: function `shaBasic` cannot have parameter of type `shared(int)*` because its linkage is `extern(C++)` +fail_compilation/fail16575.d(40): Error: function `immVector` cannot have parameter of type `immutable(__vector(long[2]))*` because its linkage is `extern(C++)` +fail_compilation/fail16575.d(41): Error: function `shaVector` cannot have parameter of type `shared(__vector(long[2]))*` because its linkage is `extern(C++)` +fail_compilation/fail16575.d(50): Error: function `immSArray` cannot have parameter of type `immutable(long[2])` because its linkage is `extern(C++)` fail_compilation/fail16575.d(50): perhaps use a `long*` type instead -fail_compilation/fail16575.d(51): Error: function `fail16575.shaSArray` cannot have parameter of type `shared(long[2])` because its linkage is `extern(C++)` +fail_compilation/fail16575.d(51): Error: function `shaSArray` cannot have parameter of type `shared(long[2])` because its linkage is `extern(C++)` fail_compilation/fail16575.d(51): perhaps use a `long*` type instead -fail_compilation/fail16575.d(60): Error: function `fail16575.immPointer` cannot have parameter of type `immutable(int*)` because its linkage is `extern(C++)` -fail_compilation/fail16575.d(61): Error: function `fail16575.shaPointer` cannot have parameter of type `shared(int*)` because its linkage is `extern(C++)` -fail_compilation/fail16575.d(71): Error: function `fail16575.immStruct` cannot have parameter of type `immutable(SPP)*` because its linkage is `extern(C++)` -fail_compilation/fail16575.d(72): Error: function `fail16575.shaStruct` cannot have parameter of type `shared(SPP)*` because its linkage is `extern(C++)` -fail_compilation/fail16575.d(81): Error: function `fail16575.immClass` cannot have parameter of type `immutable(CPP)` because its linkage is `extern(C++)` -fail_compilation/fail16575.d(82): Error: function `fail16575.shaClass` cannot have parameter of type `shared(CPP)` because its linkage is `extern(C++)` -fail_compilation/fail16575.d(91): Error: function `fail16575.immEnum` cannot have parameter of type `immutable(EPP)*` because its linkage is `extern(C++)` -fail_compilation/fail16575.d(92): Error: function `fail16575.shaEnum` cannot have parameter of type `shared(EPP)*` because its linkage is `extern(C++)` -fail_compilation/fail16575.d(100): Error: function `fail16575.typeDArray` cannot have parameter of type `int[]` because its linkage is `extern(C++)` -fail_compilation/fail16575.d(101): Error: function `fail16575.typeAArray` cannot have parameter of type `int[int]` because its linkage is `extern(C++)` -fail_compilation/fail16575.d(102): Error: function `fail16575.typeDelegate` cannot have parameter of type `extern (C++) int delegate()` because its linkage is `extern(C++)` +fail_compilation/fail16575.d(60): Error: function `immPointer` cannot have parameter of type `immutable(int*)` because its linkage is `extern(C++)` +fail_compilation/fail16575.d(61): Error: function `shaPointer` cannot have parameter of type `shared(int*)` because its linkage is `extern(C++)` +fail_compilation/fail16575.d(71): Error: function `immStruct` cannot have parameter of type `immutable(SPP)*` because its linkage is `extern(C++)` +fail_compilation/fail16575.d(72): Error: function `shaStruct` cannot have parameter of type `shared(SPP)*` because its linkage is `extern(C++)` +fail_compilation/fail16575.d(81): Error: function `immClass` cannot have parameter of type `immutable(CPP)` because its linkage is `extern(C++)` +fail_compilation/fail16575.d(82): Error: function `shaClass` cannot have parameter of type `shared(CPP)` because its linkage is `extern(C++)` +fail_compilation/fail16575.d(91): Error: function `immEnum` cannot have parameter of type `immutable(EPP)*` because its linkage is `extern(C++)` +fail_compilation/fail16575.d(92): Error: function `shaEnum` cannot have parameter of type `shared(EPP)*` because its linkage is `extern(C++)` +fail_compilation/fail16575.d(100): Error: function `typeDArray` cannot have parameter of type `int[]` because its linkage is `extern(C++)` +fail_compilation/fail16575.d(101): Error: function `typeAArray` cannot have parameter of type `int[int]` because its linkage is `extern(C++)` +fail_compilation/fail16575.d(102): Error: function `typeDelegate` cannot have parameter of type `extern (C++) int delegate()` because its linkage is `extern(C++)` --- */ diff --git a/tests/dmd/fail_compilation/fail16600.d b/tests/dmd/fail_compilation/fail16600.d index 3bd600e507..025d93fbd1 100644 --- a/tests/dmd/fail_compilation/fail16600.d +++ b/tests/dmd/fail_compilation/fail16600.d @@ -1,6 +1,6 @@ /* TEST_OUTPUT: --- -fail_compilation/fail16600.d(22): Error: `fail16600.S.__ctor` called with argument types `(string) const` matches both: +fail_compilation/fail16600.d(22): Error: `fail16600.S.__ctor` called with argument types `(string) const` matches multiple overloads exactly: fail_compilation/fail16600.d(16): `fail16600.S.this(string __param_0)` and: fail_compilation/fail16600.d(17): `fail16600.S.this(string __param_0) immutable` diff --git a/tests/dmd/fail_compilation/fail16689.d b/tests/dmd/fail_compilation/fail16689.d index f8e0bae50d..30a745ec80 100644 --- a/tests/dmd/fail_compilation/fail16689.d +++ b/tests/dmd/fail_compilation/fail16689.d @@ -2,7 +2,7 @@ TEST_OUTPUT: --- fail_compilation/fail16689.d(3): Error: static assert: "false" -fail_compilation/fail16689.d(6): instantiated from here: `Issue16689!()` +fail_compilation/fail16689.d(6): instantiated from here: `mixin Issue16689!();` --- */ #line 1 diff --git a/tests/dmd/fail_compilation/fail16772.d b/tests/dmd/fail_compilation/fail16772.d index 0bc97512c9..333ee6e98b 100644 --- a/tests/dmd/fail_compilation/fail16772.d +++ b/tests/dmd/fail_compilation/fail16772.d @@ -1,7 +1,7 @@ // https://issues.dlang.org/show_bug.cgi?id=16772 /* TEST_OUTPUT: --- -fail_compilation/fail16772.d(8): Error: function `fail16772.ice16772` cannot return type `ubyte[]` because its linkage is `extern(C++)` +fail_compilation/fail16772.d(8): Error: function `ice16772` cannot return type `ubyte[]` because its linkage is `extern(C++)` fail_compilation/fail16772.d(8): slices are specific to D and do not have a counterpart representation in C++ --- */ diff --git a/tests/dmd/fail_compilation/fail17570.d b/tests/dmd/fail_compilation/fail17570.d index 9be7cd4b05..9cb3fa0d44 100644 --- a/tests/dmd/fail_compilation/fail17570.d +++ b/tests/dmd/fail_compilation/fail17570.d @@ -2,7 +2,7 @@ TEST_OUTPUT: --- fail_compilation/fail17570.d(12): Error: cannot use function constraints for non-template functions. Use `static if` instead -fail_compilation/fail17570.d(12): Error: declaration expected, not `if` +fail_compilation/fail17570.d(13): Error: declaration expected, not `{` fail_compilation/fail17570.d(15): Error: `}` expected following members in `struct` declaration fail_compilation/fail17570.d(11): struct `S` starts here --- diff --git a/tests/dmd/fail_compilation/fail17842.d b/tests/dmd/fail_compilation/fail17842.d index 734f8d795e..ff801a1c04 100644 --- a/tests/dmd/fail_compilation/fail17842.d +++ b/tests/dmd/fail_compilation/fail17842.d @@ -1,8 +1,8 @@ /* REQUIRED_ARGS: -preview=dip1000 * TEST_OUTPUT: --- -fail_compilation/fail17842.d(14): Error: scope variable `p` assigned to non-scope `*q` -fail_compilation/fail17842.d(23): Error: scope variable `obj` may not be copied into allocated memory +fail_compilation/fail17842.d(14): Error: assigning scope variable `p` to non-scope `*q` is not allowed in a `@safe` function +fail_compilation/fail17842.d(23): Error: copying scope variable `obj` into allocated memory is not allowed in a `@safe` function --- */ diff --git a/tests/dmd/fail_compilation/fail17906.d b/tests/dmd/fail_compilation/fail17906.d deleted file mode 100644 index 41f7465557..0000000000 --- a/tests/dmd/fail_compilation/fail17906.d +++ /dev/null @@ -1,13 +0,0 @@ -// REQUIRED_ARGS: -de -/* TEST_OUTPUT: ---- -fail_compilation/fail17906.d(12): Error: the `delete` keyword is obsolete -fail_compilation/fail17906.d(12): use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead ---- -*/ -// https://issues.dlang.org/show_bug.cgi?id=18647 -deprecated void main () -{ - Object o = new Object; - delete o; -} diff --git a/tests/dmd/fail_compilation/fail18143.d b/tests/dmd/fail_compilation/fail18143.d index 28df93a199..52c4ae5e6c 100644 --- a/tests/dmd/fail_compilation/fail18143.d +++ b/tests/dmd/fail_compilation/fail18143.d @@ -1,14 +1,14 @@ /* TEST_OUTPUT: --- -fail_compilation/fail18143.d(20): Error: variable `fail18143.S.a` cannot modify parameter `this` in contract -fail_compilation/fail18143.d(21): Error: variable `fail18143.S.a` cannot modify parameter `this` in contract -fail_compilation/fail18143.d(25): Error: variable `fail18143.S.a` cannot modify parameter `this` in contract -fail_compilation/fail18143.d(26): Error: variable `fail18143.S.a` cannot modify parameter `this` in contract -fail_compilation/fail18143.d(35): Error: variable `fail18143.C.a` cannot modify parameter `this` in contract -fail_compilation/fail18143.d(36): Error: variable `fail18143.C.a` cannot modify parameter `this` in contract -fail_compilation/fail18143.d(40): Error: variable `fail18143.C.a` cannot modify parameter `this` in contract -fail_compilation/fail18143.d(41): Error: variable `fail18143.C.a` cannot modify parameter `this` in contract +fail_compilation/fail18143.d(20): Error: cannot modify member variable `fail18143.S.a` in contract +fail_compilation/fail18143.d(21): Error: cannot modify member variable `fail18143.S.a` in contract +fail_compilation/fail18143.d(25): Error: cannot modify member variable `fail18143.S.a` in contract +fail_compilation/fail18143.d(26): Error: cannot modify member variable `fail18143.S.a` in contract +fail_compilation/fail18143.d(35): Error: cannot modify member variable `fail18143.C.a` in contract +fail_compilation/fail18143.d(36): Error: cannot modify member variable `fail18143.C.a` in contract +fail_compilation/fail18143.d(40): Error: cannot modify member variable `fail18143.C.a` in contract +fail_compilation/fail18143.d(41): Error: cannot modify member variable `fail18143.C.a` in contract --- */ diff --git a/tests/dmd/fail_compilation/fail18243.d b/tests/dmd/fail_compilation/fail18243.d index f31319b0d3..016fff9fce 100644 --- a/tests/dmd/fail_compilation/fail18243.d +++ b/tests/dmd/fail_compilation/fail18243.d @@ -1,8 +1,10 @@ /* -EXTRA_FILES: imports/a18243.d +EXTRA_FILES: imports/a18243.d imports/b18243.d TEST_OUTPUT: --- -fail_compilation/fail18243.d(15): Error: none of the overloads of `isNaN` are callable using argument types `!()(float)` +fail_compilation/fail18243.d(17): Error: none of the overloads of `isNaN` are callable using argument types `!()(float)` +fail_compilation/imports/b18243.d(3): Candidates are: `isNaN(T)(T x)` +fail_compilation/imports/a18243.d(5): `imports.a18243.isNaN()` --- */ diff --git a/tests/dmd/fail_compilation/fail18970.d b/tests/dmd/fail_compilation/fail18970.d index 9b1ec1d458..d7f76afc53 100644 --- a/tests/dmd/fail_compilation/fail18970.d +++ b/tests/dmd/fail_compilation/fail18970.d @@ -2,11 +2,11 @@ TEST_OUTPUT: --- fail_compilation/fail18970.d(26): Error: no property `y` for `S()` of type `fail18970.S` -fail_compilation/fail18970.d(26): potentially malformed `opDispatch`. Use an explicit instantiation to get a better error message -fail_compilation/fail18970.d(15): struct `S` defined here +fail_compilation/fail18970.d(19): Error: undefined identifier `x` +fail_compilation/fail18970.d(26): Error: template instance `fail18970.S.opDispatch!"y"` error instantiating fail_compilation/fail18970.d(33): Error: no property `yyy` for `this` of type `fail18970.S2` -fail_compilation/fail18970.d(33): potentially malformed `opDispatch`. Use an explicit instantiation to get a better error message -fail_compilation/fail18970.d(29): struct `S2` defined here +fail_compilation/fail18970.d(38): Error: undefined identifier `x` +fail_compilation/fail18970.d(33): Error: template instance `fail18970.S2.opDispatch!"yyy"` error instantiating --- */ diff --git a/tests/dmd/fail_compilation/fail18985.d b/tests/dmd/fail_compilation/fail18985.d index 830a6792ad..f2899bdb33 100644 --- a/tests/dmd/fail_compilation/fail18985.d +++ b/tests/dmd/fail_compilation/fail18985.d @@ -1,15 +1,19 @@ /* TEST_OUTPUT: --- -fail_compilation/fail18985.d(16): Error: `foo` is not a scalar, it is a `object.Object` -fail_compilation/fail18985.d(17): Error: `bar` is not a scalar, it is a `shared(Object)` +fail_compilation/fail18985.d(20): Error: operator `+=` not supported for `foo` of type `C` +fail_compilation/fail18985.d(13): perhaps implement `auto opOpAssign(string op : "+")(int) {}` +fail_compilation/fail18985.d(21): Error: operator `+=` not supported for `bar` of type `C` +fail_compilation/fail18985.d(13): perhaps implement `auto opOpAssign(string op : "+")(int) {}` --- */ // https://issues.dlang.org/show_bug.cgi?id=18985 -Object foo; -shared Object bar; +class C {} + +C foo; +shared C bar; void main() { diff --git a/tests/dmd/fail_compilation/fail19729.d b/tests/dmd/fail_compilation/fail19729.d index 5943d08687..6fd904529d 100644 --- a/tests/dmd/fail_compilation/fail19729.d +++ b/tests/dmd/fail_compilation/fail19729.d @@ -1,11 +1,11 @@ /* TEST_OUTPUT: --- -fail_compilation/fail19729.d(35): Error: `fail19729.C.__ctor` called with argument types `(string)` matches both: +fail_compilation/fail19729.d(35): Error: `fail19729.C.__ctor` called with argument types `(string)` matches multiple overloads exactly: fail_compilation/fail19729.d(18): `fail19729.C.Templ!string.this(string t)` and: fail_compilation/fail19729.d(18): `fail19729.C.Templ!string.this(string t)` -fail_compilation/fail19729.d(36): Error: `fail19729.D.__ctor` called with argument types `(string)` matches both: +fail_compilation/fail19729.d(36): Error: `fail19729.D.__ctor` called with argument types `(string)` matches multiple overloads after qualifier conversion: fail_compilation/fail19729.d(18): `fail19729.D.Templ!(const(char)[]).this(const(char)[] t)` and: fail_compilation/fail19729.d(18): `fail19729.D.Templ!(const(char)*).this(const(char)* t)` diff --git a/tests/dmd/fail_compilation/fail19759.d b/tests/dmd/fail_compilation/fail19759.d index cdb65ae0c8..2c8d98b513 100644 --- a/tests/dmd/fail_compilation/fail19759.d +++ b/tests/dmd/fail_compilation/fail19759.d @@ -1,7 +1,7 @@ // https://issues.dlang.org/show_bug.cgi?id=19759 /* TEST_OUTPUT: --- -fail_compilation/fail19759.d(8): Error: function `fail19759.fail19759` cannot have parameter of type `float[4]` because its linkage is `extern(C++)` +fail_compilation/fail19759.d(8): Error: function `fail19759` cannot have parameter of type `float[4]` because its linkage is `extern(C++)` fail_compilation/fail19759.d(8): perhaps use a `float*` type instead --- */ diff --git a/tests/dmd/fail_compilation/fail19871.d b/tests/dmd/fail_compilation/fail19871.d deleted file mode 100644 index ad458df200..0000000000 --- a/tests/dmd/fail_compilation/fail19871.d +++ /dev/null @@ -1,20 +0,0 @@ -/* -TEST_OUTPUT: ---- -fail_compilation/fail19871.d(10): Error: `struct Struct` may not define both a rvalue constructor and a copy constructor -fail_compilation/fail19871.d(19): rvalue constructor defined here -fail_compilation/fail19871.d(13): copy constructor defined here ---- -*/ - -struct Struct -{ - @disable this(); - this(ref Struct other) - { - const Struct s = void; - this(s); - } - - this(Struct) {} -} diff --git a/tests/dmd/fail_compilation/fail19881.d b/tests/dmd/fail_compilation/fail19881.d index 62f3dc4310..f059ebf56d 100644 --- a/tests/dmd/fail_compilation/fail19881.d +++ b/tests/dmd/fail_compilation/fail19881.d @@ -1,8 +1,8 @@ /* REQUIRED_ARGS: -preview=dip1000 * TEST_OUTPUT: --- -fail_compilation/fail19881.d(13): Error: address of local variable `local` assigned to return scope `input` -fail_compilation/fail19881.d(13): Error: address of variable `local` assigned to `input` with longer lifetime +fail_compilation/fail19881.d(13): Error: assigning address of local variable `local` to return scope `input` is not allowed in a `@safe` function +fail_compilation/fail19881.d(13): Error: assigning address of variable `local` to `input` with longer lifetime is not allowed in a `@safe` function --- */ diff --git a/tests/dmd/fail_compilation/fail19931.d b/tests/dmd/fail_compilation/fail19931.d deleted file mode 100644 index 940a1faee0..0000000000 --- a/tests/dmd/fail_compilation/fail19931.d +++ /dev/null @@ -1,15 +0,0 @@ -/* -TEST_OUTPUT: ---- -fail_compilation/fail19931.d(10): Error: `struct S` may not define both a rvalue constructor and a copy constructor -fail_compilation/fail19931.d(12): rvalue constructor defined here -fail_compilation/fail19931.d(13): copy constructor defined here ---- -*/ - -struct S -{ - this(S s) {} - this(ref S s) {} - this(this) {} -} diff --git a/tests/dmd/fail_compilation/fail19948.d b/tests/dmd/fail_compilation/fail19948.d index 9c23b78df9..09737e5b25 100644 --- a/tests/dmd/fail_compilation/fail19948.d +++ b/tests/dmd/fail_compilation/fail19948.d @@ -1,5 +1,5 @@ // https://issues.dlang.org/show_bug.cgi?id=19948 -// DISABLED: win32 + /* TEST_OUTPUT: --- @@ -8,7 +8,7 @@ fail_compilation/fail19948.d(16): cannot pass argument `X()` of type `fai fail_compilation/fail19948.d(19): `fail19948.func(const(X))` declared here --- */ -// DISABLED: win32 + struct X {} void main() { diff --git a/tests/dmd/fail_compilation/fail19965.d b/tests/dmd/fail_compilation/fail19965.d index b76a3a08c0..3128796788 100644 --- a/tests/dmd/fail_compilation/fail19965.d +++ b/tests/dmd/fail_compilation/fail19965.d @@ -2,7 +2,7 @@ REQUIRED_ARGS: -preview=dip1000 TEST_OUTPUT: --- -fail_compilation/fail19965.d(36): Error: address of variable `f` assigned to `a` with longer lifetime +fail_compilation/fail19965.d(36): Error: assigning address of variable `f` to `a` with longer lifetime is not allowed in a `@safe` function --- */ diff --git a/tests/dmd/fail_compilation/fail20.d b/tests/dmd/fail_compilation/fail20.d index 4528d365da..dbd4333fc6 100644 --- a/tests/dmd/fail_compilation/fail20.d +++ b/tests/dmd/fail_compilation/fail20.d @@ -1,7 +1,8 @@ /* TEST_OUTPUT: --- -fail_compilation/fail20.d(16): Error: need member function `opCmp()` for struct `FOO` to compare +fail_compilation/fail20.d(17): Error: no operator `<` for type `FOO` +fail_compilation/fail20.d(11): perhaps overload it with `int opCmp(FOO other) const {}` --- */ diff --git a/tests/dmd/fail_compilation/fail20000.d b/tests/dmd/fail_compilation/fail20000.d index a049dacb2e..8ef4e25c02 100644 --- a/tests/dmd/fail_compilation/fail20000.d +++ b/tests/dmd/fail_compilation/fail20000.d @@ -1,29 +1,29 @@ /* TEST_OUTPUT: --- -fail_compilation/fail20000.d(37): Error: cast from `fail20000.DClass` to `fail20000.CppClass` not allowed in safe code +fail_compilation/fail20000.d(37): Error: cast from `fail20000.DClass` to `fail20000.CppClass` is not allowed in a `@safe` function fail_compilation/fail20000.d(37): Source object type is incompatible with target type -fail_compilation/fail20000.d(38): Error: cast from `fail20000.DInterface` to `fail20000.CppClass` not allowed in safe code +fail_compilation/fail20000.d(38): Error: cast from `fail20000.DInterface` to `fail20000.CppClass` is not allowed in a `@safe` function fail_compilation/fail20000.d(38): Source object type is incompatible with target type -fail_compilation/fail20000.d(39): Error: cast from `fail20000.CppClass2` to `fail20000.CppClass` not allowed in safe code +fail_compilation/fail20000.d(39): Error: cast from `fail20000.CppClass2` to `fail20000.CppClass` is not allowed in a `@safe` function fail_compilation/fail20000.d(39): Source object type is incompatible with target type -fail_compilation/fail20000.d(40): Error: cast from `fail20000.CppInterface2` to `fail20000.CppClass` not allowed in safe code +fail_compilation/fail20000.d(40): Error: cast from `fail20000.CppInterface2` to `fail20000.CppClass` is not allowed in a `@safe` function fail_compilation/fail20000.d(40): Source object type is incompatible with target type -fail_compilation/fail20000.d(42): Error: cast from `fail20000.DClass` to `fail20000.CppInterface` not allowed in safe code +fail_compilation/fail20000.d(42): Error: cast from `fail20000.DClass` to `fail20000.CppInterface` is not allowed in a `@safe` function fail_compilation/fail20000.d(42): Source object type is incompatible with target type -fail_compilation/fail20000.d(43): Error: cast from `fail20000.DInterface` to `fail20000.CppInterface` not allowed in safe code +fail_compilation/fail20000.d(43): Error: cast from `fail20000.DInterface` to `fail20000.CppInterface` is not allowed in a `@safe` function fail_compilation/fail20000.d(43): Source object type is incompatible with target type -fail_compilation/fail20000.d(44): Error: cast from `fail20000.CppClass2` to `fail20000.CppInterface` not allowed in safe code +fail_compilation/fail20000.d(44): Error: cast from `fail20000.CppClass2` to `fail20000.CppInterface` is not allowed in a `@safe` function fail_compilation/fail20000.d(44): Source object type is incompatible with target type -fail_compilation/fail20000.d(45): Error: cast from `fail20000.CppInterface2` to `fail20000.CppInterface` not allowed in safe code +fail_compilation/fail20000.d(45): Error: cast from `fail20000.CppInterface2` to `fail20000.CppInterface` is not allowed in a `@safe` function fail_compilation/fail20000.d(45): Source object type is incompatible with target type -fail_compilation/fail20000.d(47): Error: cast from `fail20000.CppClass` to `fail20000.DClass` not allowed in safe code +fail_compilation/fail20000.d(47): Error: cast from `fail20000.CppClass` to `fail20000.DClass` is not allowed in a `@safe` function fail_compilation/fail20000.d(47): Source object type is incompatible with target type -fail_compilation/fail20000.d(48): Error: cast from `fail20000.CppInterface` to `fail20000.DClass` not allowed in safe code +fail_compilation/fail20000.d(48): Error: cast from `fail20000.CppInterface` to `fail20000.DClass` is not allowed in a `@safe` function fail_compilation/fail20000.d(48): Source object type is incompatible with target type -fail_compilation/fail20000.d(50): Error: cast from `fail20000.CppClass` to `fail20000.DInterface` not allowed in safe code +fail_compilation/fail20000.d(50): Error: cast from `fail20000.CppClass` to `fail20000.DInterface` is not allowed in a `@safe` function fail_compilation/fail20000.d(50): Source object type is incompatible with target type -fail_compilation/fail20000.d(51): Error: cast from `fail20000.CppInterface` to `fail20000.DInterface` not allowed in safe code +fail_compilation/fail20000.d(51): Error: cast from `fail20000.CppInterface` to `fail20000.DInterface` is not allowed in a `@safe` function fail_compilation/fail20000.d(51): Source object type is incompatible with target type --- */ diff --git a/tests/dmd/fail_compilation/fail20073.d b/tests/dmd/fail_compilation/fail20073.d index 01a9ede462..5353016d79 100644 --- a/tests/dmd/fail_compilation/fail20073.d +++ b/tests/dmd/fail_compilation/fail20073.d @@ -3,7 +3,7 @@ TEST_OUTPUT: --- fail_compilation/fail20073.d(20): Error: cannot implicitly convert expression `s` of type `S` to `string` -fail_compilation/fail20073.d(21): Error: cannot implicitly convert expression `s` of type `S` to `string` +fail_compilation/fail20073.d(21): Error: return value `s` of type `S` does not match return type `string`, and cannot be implicitly converted --- */ diff --git a/tests/dmd/fail_compilation/fail20084.d b/tests/dmd/fail_compilation/fail20084.d index 22f64337a9..f3f52febd6 100644 --- a/tests/dmd/fail_compilation/fail20084.d +++ b/tests/dmd/fail_compilation/fail20084.d @@ -1,7 +1,7 @@ /* REQUIRED_ARGS: -preview=dip1000 TEST_OUTPUT: --- -fail_compilation/fail20084.d(109): Error: returning `v.front()` escapes a reference to parameter `v` +fail_compilation/fail20084.d(109): Error: escaping a reference to parameter `v` by returning `v.front()` is not allowed in a `@safe` function --- */ diff --git a/tests/dmd/fail_compilation/fail20108.d b/tests/dmd/fail_compilation/fail20108.d index 15845e1d2f..f2130f31ab 100644 --- a/tests/dmd/fail_compilation/fail20108.d +++ b/tests/dmd/fail_compilation/fail20108.d @@ -2,10 +2,10 @@ /* TEST_OUTPUT: --- -fail_compilation/fail20108.d(15): Error: address of variable `y` assigned to `x` with longer lifetime +fail_compilation/fail20108.d(15): Error: assigning address of variable `y` to `x` with longer lifetime is not allowed in a `@safe` function fail_compilation/fail20108.d(16): Error: scope parameter `x` may not be returned -fail_compilation/fail20108.d(23): Error: address of variable `y` assigned to `x` with longer lifetime -fail_compilation/fail20108.d(24): Error: scope variable `x` may not be returned +fail_compilation/fail20108.d(23): Error: assigning address of variable `y` to `x` with longer lifetime is not allowed in a `@safe` function +fail_compilation/fail20108.d(24): Error: returning scope variable `x` is not allowed in a `@safe` function --- */ diff --git a/tests/dmd/fail_compilation/fail20183.d b/tests/dmd/fail_compilation/fail20183.d index f04db02493..b0e4cc7234 100644 --- a/tests/dmd/fail_compilation/fail20183.d +++ b/tests/dmd/fail_compilation/fail20183.d @@ -3,15 +3,13 @@ TEST_OUTPUT: --- fail_compilation/fail20183.d(1016): Error: function `addr` is not callable using argument types `(int)` fail_compilation/fail20183.d(1016): cannot pass rvalue argument `S(0).i` of type `int` to parameter `return ref int b` -fail_compilation/fail20183.d(1005): `fail20183.addr(return ref int b)` declared here -fail_compilation/fail20183.d(1017): Error: address of struct temporary returned by `s()` assigned to longer lived variable `q` +fail_compilation/fail20183.d(1004): `fail20183.addr(return ref int b)` declared here --- */ #line 1000 // https://issues.dlang.org/show_bug.cgi?id=20183 - @safe: int* addr(return ref int b) { return &b; } @@ -19,20 +17,24 @@ int* addr(return ref int b) { return &b; } struct S { int i; + S* addrOf() return => &this; } S s() { return S(); } void test() { - int* p = addr(S().i); // struct literal - int* q = addr(s().i); // struct temporary + scope int* p = addr(S().i); // struct literal + scope int* q = addr(s().i); // struct temporary + scope S* r = S().addrOf(); // struct literal } /* TEST_OUTPUT: --- -fail_compilation/fail20183.d(1107): Error: address of struct temporary returned by `s()` assigned to longer lived variable `this.ptr` +fail_compilation/fail20183.d(1017): Error: assigning address of expression temporary returned by `s()` to `q` with longer lifetime is not allowed in a `@safe` function +fail_compilation/fail20183.d(1018): Error: assigning address of struct literal `S(0)` to `r` with longer lifetime is not allowed in a `@safe` function +fail_compilation/fail20183.d(1107): Error: assigning address of expression temporary returned by `s()` to `this.ptr` with longer lifetime is not allowed in a `@safe` function --- */ #line 1100 diff --git a/tests/dmd/fail_compilation/fail20376.d b/tests/dmd/fail_compilation/fail20376.d index 8410af535e..dbd0061ef8 100644 --- a/tests/dmd/fail_compilation/fail20376.d +++ b/tests/dmd/fail_compilation/fail20376.d @@ -3,7 +3,7 @@ /* TEST_OUTPUT: --- -fail_compilation/fail20376.d(17): Error: cannot implicitly convert expression `Foo()` of type `Foo` to `ubyte` +fail_compilation/fail20376.d(17): Error: return value `Foo()` of type `Foo` does not match return type `ubyte`, and cannot be implicitly converted --- */ diff --git a/tests/dmd/fail_compilation/fail20461.d b/tests/dmd/fail_compilation/fail20461.d index 77c7178462..9fe453c4ef 100644 --- a/tests/dmd/fail_compilation/fail20461.d +++ b/tests/dmd/fail_compilation/fail20461.d @@ -1,7 +1,7 @@ /* REQUIRED_ARGS: -preview=dip1000 TEST_OUTPUT: --- -fail_compilation/fail20461.d(106): Error: reference to local variable `buffer` assigned to non-scope parameter calling `assert()` +fail_compilation/fail20461.d(106): Error: assigningreference to local variable `buffer` to non-scope parameter calling `assert()` is not allowed in a `@safe` function --- */ diff --git a/tests/dmd/fail_compilation/fail20551.d b/tests/dmd/fail_compilation/fail20551.d index 633be0a596..b2b0429c0b 100644 --- a/tests/dmd/fail_compilation/fail20551.d +++ b/tests/dmd/fail_compilation/fail20551.d @@ -1,7 +1,7 @@ /* TEST_OUTPUT: --- -fail_compilation/fail20551.d(15): Error: cannot take address of lazy parameter `e` in `@safe` function `opAssign` +fail_compilation/fail20551.d(15): Error: taking address of lazy parameter `e` is not allowed in a `@safe` function fail_compilation/fail20551.d(26): Error: template instance `fail20551.LazyStore!int.LazyStore.opAssign!int` error instantiating --- */ diff --git a/tests/dmd/fail_compilation/fail20616.d b/tests/dmd/fail_compilation/fail20616.d index 0f76e315ac..3acf84fb8f 100644 --- a/tests/dmd/fail_compilation/fail20616.d +++ b/tests/dmd/fail_compilation/fail20616.d @@ -2,9 +2,9 @@ TEST_OUTPUT: --- fail_compilation/fail20616.d(16): Error: undefined identifier `$` -fail_compilation/fail20616.d(16): Aggregate declaration 'X()' does not define 'opDollar' +fail_compilation/fail20616.d(13): perhaps define `opDollar` for `X` fail_compilation/fail20616.d(18): Error: undefined identifier `$` -fail_compilation/fail20616.d(18): Aggregate declaration 'b' does not define 'opDollar' +fail_compilation/fail20616.d(13): perhaps define `opDollar` for `X` --- */ module fail20616; diff --git a/tests/dmd/fail_compilation/fail20658.d b/tests/dmd/fail_compilation/fail20658.d index fd422aaa16..0f48ba7df6 100644 --- a/tests/dmd/fail_compilation/fail20658.d +++ b/tests/dmd/fail_compilation/fail20658.d @@ -1,7 +1,7 @@ /* TEST_OUTPUT: --- -fail_compilation/fail20658.d(14): Error: field `U.m` cannot modify fields in `@safe` code that overlap fields with other storage classes +fail_compilation/fail20658.d(14): Error: modifying field `U.m` which overlaps with fields with other storage classes is not allowed in a `@safe` function --- */ diff --git a/tests/dmd/fail_compilation/fail20691.d b/tests/dmd/fail_compilation/fail20691.d index 54e36fcfb7..240b2cadcb 100644 --- a/tests/dmd/fail_compilation/fail20691.d +++ b/tests/dmd/fail_compilation/fail20691.d @@ -1,9 +1,9 @@ /* REQUIRED_ARGS: -preview=dip1000 TEST_OUTPUT: --- -fail_compilation/fail20691.d(106): Error: cannot take address of `scope` variable `sa` since `scope` applies to first indirection only -fail_compilation/fail20691.d(107): Error: cannot take address of `scope` variable `sa` since `scope` applies to first indirection only -fail_compilation/fail20691.d(108): Error: cannot take address of `scope` variable `sa` since `scope` applies to first indirection only +fail_compilation/fail20691.d(106): Error: taking address of `scope` variable `sa` with pointers is not allowed in a `@safe` function +fail_compilation/fail20691.d(107): Error: taking address of `scope` variable `sa` with pointers is not allowed in a `@safe` function +fail_compilation/fail20691.d(108): Error: taking address of `scope` variable `sa` with pointers is not allowed in a `@safe` function --- */ diff --git a/tests/dmd/fail_compilation/fail20730b.d b/tests/dmd/fail_compilation/fail20730b.d index 7269f42514..ccab5ad46c 100644 --- a/tests/dmd/fail_compilation/fail20730b.d +++ b/tests/dmd/fail_compilation/fail20730b.d @@ -4,7 +4,7 @@ TEST_OUTPUT: --- (spec:1) fail_compilation/fail20730b.d-mixin-43(43): Error: C style cast illegal, use `cast(int)mod` fail_compilation/fail20730b.d(26): Error: template `atomicOp` is not callable using argument types `!("+=")(shared(uint), int)` -fail_compilation/fail20730b.d(41): Candidate is: `atomicOp(string op, T, V1)(shared ref T val, V1 mod)` +fail_compilation/fail20730b.d(41): Candidate is: `atomicOp(string op, T, V1)(ref shared T val, V1 mod)` with `op = "+=", T = uint, V1 = int` diff --git a/tests/dmd/fail_compilation/fail21045.d b/tests/dmd/fail_compilation/fail21045.d new file mode 100644 index 0000000000..ebc8b4dec7 --- /dev/null +++ b/tests/dmd/fail_compilation/fail21045.d @@ -0,0 +1,10 @@ +/* +TRANSFORM_OUTPUT: remove_lines("^import path") +TEST_OUTPUT: +--- +fail_compilation/fail21045.d(10): Error: unable to read module `__stdin` +fail_compilation/fail21045.d(10): Expected '__stdin.d' or '__stdin/package.d' in one of the following import paths: +--- +*/ + +import __stdin; diff --git a/tests/dmd/fail_compilation/fail21206.d b/tests/dmd/fail_compilation/fail21206.d index b3b42f3429..5dc85ca9be 100644 --- a/tests/dmd/fail_compilation/fail21206.d +++ b/tests/dmd/fail_compilation/fail21206.d @@ -1,7 +1,7 @@ // https://issues.dlang.org/show_bug.cgi?id=21206 /* TEST_OUTPUT: --- -fail_compilation/fail21206.d(10): Error: function `fail21206.Obj.toString` cannot return type `string` because its linkage is `extern(C++)` +fail_compilation/fail21206.d(10): Error: function `toString` cannot return type `string` because its linkage is `extern(C++)` fail_compilation/fail21206.d(10): slices are specific to D and do not have a counterpart representation in C++ --- */ diff --git a/tests/dmd/fail_compilation/fail21849.d b/tests/dmd/fail_compilation/fail21849.d index 75821f69ea..700e424277 100644 --- a/tests/dmd/fail_compilation/fail21849.d +++ b/tests/dmd/fail_compilation/fail21849.d @@ -29,6 +29,10 @@ void fail21849c() string s = " ß-utf"; undefined_identifier; } + +// Test correct context with line directive present +// https://github.com/dlang/dmd/issues/20929 +#line 32 void fail21849d() { string s = " diff --git a/tests/dmd/fail_compilation/fail21868b.d b/tests/dmd/fail_compilation/fail21868b.d index 0df31d7de3..802ceaf283 100644 --- a/tests/dmd/fail_compilation/fail21868b.d +++ b/tests/dmd/fail_compilation/fail21868b.d @@ -1,7 +1,7 @@ /* REQUIRED_ARGS: -preview=dip1000 TEST_OUTPUT: --- -fail_compilation/fail21868b.d(19): Error: returning `&s.x` escapes a reference to parameter `s` +fail_compilation/fail21868b.d(19): Error: escaping a reference to parameter `s` by returning `&s.x` is not allowed in a `@safe` function fail_compilation/fail21868b.d(17): perhaps change the `return scope` into `scope return` --- */ diff --git a/tests/dmd/fail_compilation/fail21928.d b/tests/dmd/fail_compilation/fail21928.d index c9fac133dd..fd6c26ebd3 100644 --- a/tests/dmd/fail_compilation/fail21928.d +++ b/tests/dmd/fail_compilation/fail21928.d @@ -1,7 +1,7 @@ /* TEST_OUTPUT: --- -fail_compilation/fail21928.d(18): Error: array literal in `@nogc` function `D main` may cause a GC allocation +fail_compilation/fail21928.d(18): Error: this array literal causes a GC allocation in `@nogc` function `main` --- */ diff --git a/tests/dmd/fail_compilation/fail21928b.d b/tests/dmd/fail_compilation/fail21928b.d index 3ce93e0a09..2caf737baa 100644 --- a/tests/dmd/fail_compilation/fail21928b.d +++ b/tests/dmd/fail_compilation/fail21928b.d @@ -1,7 +1,7 @@ /* TEST_OUTPUT: --- -fail_compilation/fail21928b.d(18): Error: array literal in `@nogc` function `D main` may cause a GC allocation +fail_compilation/fail21928b.d(18): Error: this array literal causes a GC allocation in `@nogc` function `main` --- */ diff --git a/tests/dmd/fail_compilation/fail22.d b/tests/dmd/fail_compilation/fail22.d index 1de2a13044..47c6018d13 100644 --- a/tests/dmd/fail_compilation/fail22.d +++ b/tests/dmd/fail_compilation/fail22.d @@ -1,7 +1,7 @@ /* TEST_OUTPUT: --- -fail_compilation/fail22.d(13): Error: no identifier for declarator `char` +fail_compilation/fail22.d(13): Error: variable name expected after type `char`, not `;` --- */ diff --git a/tests/dmd/fail_compilation/fail22054.d b/tests/dmd/fail_compilation/fail22054.d index 8b525d85fb..c172f089d9 100644 --- a/tests/dmd/fail_compilation/fail22054.d +++ b/tests/dmd/fail_compilation/fail22054.d @@ -3,12 +3,10 @@ /* TEST_OUTPUT: --- -fail_compilation/fail22054.d(23): Error: no property `what` for type `fail22054.exception` -fail_compilation/fail22054.d(18): `class fail22054.exception` is opaque and has no members. -fail_compilation/fail22054.d(18): class `exception` defined here -fail_compilation/fail22054.d(24): Error: no property `what` for type `fail22054.exception2` -fail_compilation/fail22054.d(19): `struct fail22054.exception2` is opaque and has no members. -fail_compilation/fail22054.d(19): struct `exception2` defined here +fail_compilation/fail22054.d(21): Error: no property `what` for type `fail22054.exception` +fail_compilation/fail22054.d(16): `class fail22054.exception` is opaque and has no members. +fail_compilation/fail22054.d(22): Error: no property `what` for type `fail22054.exception2` +fail_compilation/fail22054.d(17): `struct fail22054.exception2` is opaque and has no members. --- */ diff --git a/tests/dmd/fail_compilation/fail22138.d b/tests/dmd/fail_compilation/fail22138.d index 4fee96f14d..f4ba87947c 100644 --- a/tests/dmd/fail_compilation/fail22138.d +++ b/tests/dmd/fail_compilation/fail22138.d @@ -1,7 +1,7 @@ /* REQUIRED_ARGS: -preview=dip1000 TEST_OUTPUT: --- -fail_compilation/fail22138.d(107): Error: scope variable `e` may not be returned +fail_compilation/fail22138.d(107): Error: returning scope variable `e` is not allowed in a `@safe` function --- */ diff --git a/tests/dmd/fail_compilation/fail22157.d b/tests/dmd/fail_compilation/fail22157.d index d25aaada2c..8a144232bb 100644 --- a/tests/dmd/fail_compilation/fail22157.d +++ b/tests/dmd/fail_compilation/fail22157.d @@ -3,11 +3,11 @@ /* TEST_OUTPUT: --- -fail_compilation/fail22157.d(32): Error: `fail22157.S!true.S.foo` called with argument types `()` matches both: +fail_compilation/fail22157.d(32): Error: `fail22157.S!true.S.foo` called with argument types `()` matches multiple overloads exactly: fail_compilation/fail22157.d(21): `fail22157.S!true.S.foo()` and: fail_compilation/fail22157.d(22): `fail22157.S!true.S.foo()` -fail_compilation/fail22157.d(33): Error: `fail22157.S!false.S.foo` called with argument types `()` matches both: +fail_compilation/fail22157.d(33): Error: `fail22157.S!false.S.foo` called with argument types `()` matches multiple overloads exactly: fail_compilation/fail22157.d(26): `fail22157.S!false.S.foo()` and: fail_compilation/fail22157.d(27): `fail22157.S!false.S.foo()` diff --git a/tests/dmd/fail_compilation/fail22366.d b/tests/dmd/fail_compilation/fail22366.d index 675ba831ef..5be7d3e62c 100644 --- a/tests/dmd/fail_compilation/fail22366.d +++ b/tests/dmd/fail_compilation/fail22366.d @@ -2,13 +2,13 @@ REQUIRED_ARGS: -preview=dip1000 TEST_OUTPUT: --- -fail_compilation/fail22366.d(22): Error: scope variable `s` may not be copied into allocated memory -fail_compilation/fail22366.d(25): Error: scope variable `s` may not be copied into allocated memory -fail_compilation/fail22366.d(26): Error: scope variable `s` may not be copied into allocated memory -fail_compilation/fail22366.d(27): Error: scope variable `s` may not be copied into allocated memory -fail_compilation/fail22366.d(28): Error: scope variable `s` may not be copied into allocated memory -fail_compilation/fail22366.d(31): Error: scope variable `s` may not be copied into allocated memory -fail_compilation/fail22366.d(32): Error: scope variable `s` may not be copied into allocated memory +fail_compilation/fail22366.d(22): Error: copying scope variable `s` into allocated memory is not allowed in a `@safe` function +fail_compilation/fail22366.d(25): Error: copying scope variable `s` into allocated memory is not allowed in a `@safe` function +fail_compilation/fail22366.d(26): Error: copying scope variable `s` into allocated memory is not allowed in a `@safe` function +fail_compilation/fail22366.d(27): Error: copying scope variable `s` into allocated memory is not allowed in a `@safe` function +fail_compilation/fail22366.d(28): Error: copying scope variable `s` into allocated memory is not allowed in a `@safe` function +fail_compilation/fail22366.d(31): Error: copying scope variable `s` into allocated memory is not allowed in a `@safe` function +fail_compilation/fail22366.d(32): Error: copying scope variable `s` into allocated memory is not allowed in a `@safe` function --- */ diff --git a/tests/dmd/fail_compilation/fail23036.d b/tests/dmd/fail_compilation/fail23036.d deleted file mode 100644 index 8920586c67..0000000000 --- a/tests/dmd/fail_compilation/fail23036.d +++ /dev/null @@ -1,22 +0,0 @@ -// https://issues.dlang.org/show_bug.cgi?id=23036 - -/* -TEST_OUTPUT: ---- -fail_compilation/fail23036.d(12): Error: `struct S` may not define both a rvalue constructor and a copy constructor -fail_compilation/fail23036.d(15): rvalue constructor defined here -fail_compilation/fail23036.d(14): copy constructor defined here ---- -*/ - -struct S -{ - this(ref S) {} - this(S, int a = 2) {} -} - -void main() -{ - S a; - S b = a; -} diff --git a/tests/dmd/fail_compilation/fail2361.d b/tests/dmd/fail_compilation/fail2361.d deleted file mode 100644 index ffc12f1631..0000000000 --- a/tests/dmd/fail_compilation/fail2361.d +++ /dev/null @@ -1,15 +0,0 @@ -/* -TEST_OUTPUT: ---- -fail_compilation/fail2361.d(14): Error: the `delete` keyword is obsolete -fail_compilation/fail2361.d(14): use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead ---- -*/ - -class C {} - -void main() -{ - immutable c = new immutable(C); - delete c; -} diff --git a/tests/dmd/fail_compilation/fail23626b.d b/tests/dmd/fail_compilation/fail23626b.d index 758a28b141..55ed42d7b8 100644 --- a/tests/dmd/fail_compilation/fail23626b.d +++ b/tests/dmd/fail_compilation/fail23626b.d @@ -1,6 +1,6 @@ /* TEST_OUTPUT: --- -fail_compilation/fail23626b.d(26): Error: `fail23626b.AmbigOpApply.opApply` called with argument types `(int delegate(int i) pure nothrow @nogc @system)` matches both: +fail_compilation/fail23626b.d(26): Error: `fail23626b.AmbigOpApply.opApply` called with argument types `(int delegate(int i) pure nothrow @nogc @system)` matches multiple overloads after qualifier conversion: fail_compilation/fail23626b.d(12): `fail23626b.AmbigOpApply.opApply(int delegate(int) dg)` and: fail_compilation/fail23626b.d(17): `fail23626b.AmbigOpApply.opApply(int delegate(int) dg)` diff --git a/tests/dmd/fail_compilation/fail24208.d b/tests/dmd/fail_compilation/fail24208.d index 149c1386f5..c39676a285 100644 --- a/tests/dmd/fail_compilation/fail24208.d +++ b/tests/dmd/fail_compilation/fail24208.d @@ -2,7 +2,7 @@ REQUIRED_ARGS: -preview=dip1000 TEST_OUTPUT: --- -fail_compilation/fail24208.d(19): Error: reference to local variable `n` assigned to non-scope parameter `p` calling `escape` +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` --- +/ diff --git a/tests/dmd/fail_compilation/fail24212.d b/tests/dmd/fail_compilation/fail24212.d index 767951dafb..c8d2fc1de2 100644 --- a/tests/dmd/fail_compilation/fail24212.d +++ b/tests/dmd/fail_compilation/fail24212.d @@ -2,7 +2,7 @@ REQUIRED_ARGS: -preview=dip1000 TEST_OUTPUT: --- -fail_compilation/fail24212.d(29): Error: reference to local variable `n` assigned to non-scope parameter `p` calling `fun` +fail_compilation/fail24212.d(29): Error: assigning reference to local variable `n` to non-scope parameter `p` calling `fun` is not allowed in a `@safe` function --- +/ class Base diff --git a/tests/dmd/fail_compilation/fail24213.d b/tests/dmd/fail_compilation/fail24213.d index e2af6fd1b9..a6a75c8273 100644 --- a/tests/dmd/fail_compilation/fail24213.d +++ b/tests/dmd/fail_compilation/fail24213.d @@ -2,7 +2,7 @@ REQUIRED_ARGS: -preview=dip1000 TEST_OUTPUT: --- -fail_compilation/fail24213.d(16): Error: reference to local variable `n` assigned to non-scope parameter `p` +fail_compilation/fail24213.d(16): Error: assigning reference to local variable `n` to non-scope parameter `p` is not allowed in a `@safe` function --- +/ alias Dg = void delegate(int* p) @safe pure nothrow; diff --git a/tests/dmd/fail_compilation/fail2450.d b/tests/dmd/fail_compilation/fail2450.d index 82118d5de4..b61df87b52 100644 --- a/tests/dmd/fail_compilation/fail2450.d +++ b/tests/dmd/fail_compilation/fail2450.d @@ -2,10 +2,8 @@ /* TEST_OUTPUT: --- -fail_compilation/fail2450.d(22): Error: function expected before `()`, not `this.mixin Event!() clicked; -` of type `void` -fail_compilation/fail2450.d(25): Error: function expected before `()`, not `b.mixin Event!() clicked; -` of type `void` +fail_compilation/fail2450.d(20): Error: function expected before `()`, not `this.mixin Event!() clicked;` of type `void` +fail_compilation/fail2450.d(23): Error: function expected before `()`, not `b.mixin Event!() clicked;` of type `void` --- */ diff --git a/tests/dmd/fail_compilation/fail297.d b/tests/dmd/fail_compilation/fail297.d index 5fc3bbf8e5..f74b814c18 100644 --- a/tests/dmd/fail_compilation/fail297.d +++ b/tests/dmd/fail_compilation/fail297.d @@ -1,7 +1,8 @@ /* TEST_OUTPUT: --- -fail_compilation/fail297.d(30): Error: incompatible types for `(Bar()) + (baz())`: `Bar` and `const(Bar)` +fail_compilation/fail297.d(31): Error: operator `+` is not defined for type `Bar` +fail_compilation/fail297.d(25): perhaps overload the operator with `auto opBinary(string op : "+")(const(Bar) rhs) {}` --- */ diff --git a/tests/dmd/fail_compilation/fail3.d b/tests/dmd/fail_compilation/fail3.d index 5c1ea9178b..bcc9614db6 100644 --- a/tests/dmd/fail_compilation/fail3.d +++ b/tests/dmd/fail_compilation/fail3.d @@ -1,7 +1,8 @@ /* TEST_OUTPUT: --- -fail_compilation/fail3.d(41): Error: incompatible types for `(a) + (b)`: both operands are of type `vec2` +fail_compilation/fail3.d(42): Error: operator `+` is not defined for type `vec2` +fail_compilation/fail3.d(13): perhaps overload the operator with `auto opBinary(string op : "+")(vec2 rhs) {}` --- */ diff --git a/tests/dmd/fail_compilation/fail327.d b/tests/dmd/fail_compilation/fail327.d index 71ec1606bf..07edea99bf 100644 --- a/tests/dmd/fail_compilation/fail327.d +++ b/tests/dmd/fail_compilation/fail327.d @@ -1,7 +1,7 @@ /* DISABLED: LDC_not_x86 TEST_OUTPUT: --- -fail_compilation/fail327.d(11): Error: `asm` statement is assumed to be `@system` - mark it with `@trusted` if it is not +fail_compilation/fail327.d(11): Error: executing an `asm` statement without `@trusted` annotation is not allowed in a `@safe` function fail_compilation/fail327.d(12): Deprecation: `asm` statement cannot be marked `@safe`, use `@system` or `@trusted` instead --- */ diff --git a/tests/dmd/fail_compilation/fail347.d b/tests/dmd/fail_compilation/fail347.d index 8d061f96fd..c56acf5642 100644 --- a/tests/dmd/fail_compilation/fail347.d +++ b/tests/dmd/fail_compilation/fail347.d @@ -2,9 +2,13 @@ EXTRA_FILES: imports/fail347a.d TEST_OUTPUT: --- -fail_compilation/fail347.d(22): Error: undefined identifier `bbr`, did you mean variable `bar`? -fail_compilation/fail347.d(23): Error: no property `ofo` for type `S`, did you mean `fail347.S.foo`? -fail_compilation/fail347.d(24): Error: undefined identifier `strlenx`, did you mean function `strlen`? +fail_compilation/fail347.d(26): Error: undefined identifier `bbr`, did you mean variable `bar`? +fail_compilation/fail347.d(27): Error: no property `ofo` for type `S`, did you mean `fail347.S.foo`? +fail_compilation/fail347.d(29): Error: no property `fool` for `sp` of type `fail347.S*` +fail_compilation/fail347.d(20): did you mean variable `foo`? +fail_compilation/fail347.d(30): Error: undefined identifier `strlenx`, did you mean function `strlen`? +fail_compilation/fail347.d(31): Error: no property `strlenx` for `"hello"` of type `string` +fail_compilation/imports/fail347a.d(3): did you mean function `strlen`? --- */ @@ -21,5 +25,8 @@ void main() S bar; bbr.foo = 3; bar.ofo = 4; + auto sp = &bar; + sp.fool = 5; auto s = strlenx("hello"); + auto q = "hello".strlenx(); } diff --git a/tests/dmd/fail_compilation/fail3672.d b/tests/dmd/fail_compilation/fail3672.d index 2f9bdf742b..0062798f31 100644 --- a/tests/dmd/fail_compilation/fail3672.d +++ b/tests/dmd/fail_compilation/fail3672.d @@ -1,9 +1,10 @@ /* TEST_OUTPUT: --- -fail_compilation/fail3672.d(28): Error: read-modify-write operations are not allowed for `shared` variables -fail_compilation/fail3672.d(28): Use `core.atomic.atomicOp!"+="(*p, 1)` instead -fail_compilation/fail3672.d(32): Error: none of the `opOpAssign` overloads of `SF` are callable for `*sfp` of type `shared(SF)` +fail_compilation/fail3672.d(29): Error: read-modify-write operations are not allowed for `shared` variables +fail_compilation/fail3672.d(29): Use `core.atomic.atomicOp!"+="(*p, 1)` instead +fail_compilation/fail3672.d(33): Error: template `opOpAssign` is not callable using argument types `!("+")(int) shared` +fail_compilation/fail3672.d(13): Candidate is: `opOpAssign(string op, T)(T rhs)` --- */ diff --git a/tests/dmd/fail_compilation/fail3673b.d b/tests/dmd/fail_compilation/fail3673b.d index bf745ac8bb..60de5971ac 100644 --- a/tests/dmd/fail_compilation/fail3673b.d +++ b/tests/dmd/fail_compilation/fail3673b.d @@ -4,7 +4,7 @@ TEST_OUTPUT: fail_compilation/fail3673b.d(12): Error: basic type expected, not `if` fail_compilation/fail3673b.d(12): Error: template constraints only allowed for templates fail_compilation/fail3673b.d(12): Error: { } expected following `class` declaration -fail_compilation/fail3673b.d(12): Error: no identifier for declarator `A` +fail_compilation/fail3673b.d(12): Error: variable name expected after type `A`, not `{` fail_compilation/fail3673b.d(12): Error: declaration expected, not `{` --- */ diff --git a/tests/dmd/fail_compilation/fail4269e.d b/tests/dmd/fail_compilation/fail4269e.d index 8a95b34e89..33f9ea1ec3 100644 --- a/tests/dmd/fail_compilation/fail4269e.d +++ b/tests/dmd/fail_compilation/fail4269e.d @@ -2,7 +2,7 @@ TEST_OUTPUT: --- fail_compilation/fail4269e.d(10): Error: semicolon needed to end declaration of `Y` instead of `X5` -fail_compilation/fail4269e.d(10): Error: no identifier for declarator `X5` +fail_compilation/fail4269e.d(10): Error: variable name expected after type `X5`, not `;` --- */ diff --git a/tests/dmd/fail_compilation/fail4421.d b/tests/dmd/fail_compilation/fail4421.d index 3aedfc31f3..e7af18127e 100644 --- a/tests/dmd/fail_compilation/fail4421.d +++ b/tests/dmd/fail_compilation/fail4421.d @@ -1,9 +1,9 @@ /* TEST_OUTPUT: --- -fail_compilation/fail4421.d(16): Error: function `fail4421.U1.__postblit` destructors, postblits and invariants are not allowed in union `U1` +fail_compilation/fail4421.d(16): Error: function `fail4421.U1.this(this)` destructors, postblits and invariants are not allowed in union `U1` fail_compilation/fail4421.d(17): Error: destructor `fail4421.U1.~this` destructors, postblits and invariants are not allowed in union `U1` -fail_compilation/fail4421.d(18): Error: function `fail4421.U1.__invariant1` destructors, postblits and invariants are not allowed in union `U1` +fail_compilation/fail4421.d(18): Error: function `fail4421.U1.invariant` destructors, postblits and invariants are not allowed in union `U1` --- diff --git a/tests/dmd/fail_compilation/fail4544.d b/tests/dmd/fail_compilation/fail4544.d index cf1554da23..1cb0292cc8 100644 --- a/tests/dmd/fail_compilation/fail4544.d +++ b/tests/dmd/fail_compilation/fail4544.d @@ -3,7 +3,7 @@ TEST_OUTPUT: --- fail_compilation/fail4544.d(15): Error: character constant has multiple characters fail_compilation/fail4544.d(16): Error: `0x` isn't a valid integer literal, use `0x0` instead -fail_compilation/fail4544.d(16): Error: no identifier for declarator `int` +fail_compilation/fail4544.d(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 --- diff --git a/tests/dmd/fail_compilation/fail6107.d b/tests/dmd/fail_compilation/fail6107.d index a21fbb78f2..b707a568b3 100644 --- a/tests/dmd/fail_compilation/fail6107.d +++ b/tests/dmd/fail_compilation/fail6107.d @@ -1,8 +1,10 @@ /* TEST_OUTPUT: --- -fail_compilation/fail6107.d(10): Error: variable `fail6107.Foo.__ctor` is not a constructor; identifiers starting with `__` are reserved for the implementation -fail_compilation/fail6107.d(14): Error: variable `fail6107.Bar.__ctor` is not a constructor; identifiers starting with `__` are reserved for the implementation +fail_compilation/fail6107.d(12): Error: variable name `__ctor` is not allowed +fail_compilation/fail6107.d(12): identifiers starting with `__` are reserved for internal use +fail_compilation/fail6107.d(16): Error: variable name `__ctor` is not allowed +fail_compilation/fail6107.d(16): identifiers starting with `__` are reserved for internal use --- */ struct Foo diff --git a/tests/dmd/fail_compilation/fail6334.d b/tests/dmd/fail_compilation/fail6334.d index 7abdb9e26a..4afe654622 100644 --- a/tests/dmd/fail_compilation/fail6334.d +++ b/tests/dmd/fail_compilation/fail6334.d @@ -2,7 +2,7 @@ TEST_OUTPUT: --- fail_compilation/fail6334.d(13): Error: static assert: `0` is false -fail_compilation/fail6334.d(11): instantiated from here: `T2!()` +fail_compilation/fail6334.d(11): instantiated from here: `mixin T2!();` --- */ diff --git a/tests/dmd/fail_compilation/fail6497.d b/tests/dmd/fail_compilation/fail6497.d index 3ac90b5c0c..00c66a6574 100644 --- a/tests/dmd/fail_compilation/fail6497.d +++ b/tests/dmd/fail_compilation/fail6497.d @@ -1,8 +1,8 @@ /* TEST_OUTPUT: --- -fail_compilation/fail6497.d(12): Error: cannot take address of local `n` in `@safe` function `main` -fail_compilation/fail6497.d(12): Error: cannot take address of local `n` in `@safe` function `main` +fail_compilation/fail6497.d(12): Error: taking the address of stack-allocated local variable `n` is not allowed in a `@safe` function +fail_compilation/fail6497.d(12): Error: taking the address of stack-allocated local variable `n` is not allowed in a `@safe` function --- */ diff --git a/tests/dmd/fail_compilation/fail6795.d b/tests/dmd/fail_compilation/fail6795.d index 26d4429649..dbdd5d5666 100644 --- a/tests/dmd/fail_compilation/fail6795.d +++ b/tests/dmd/fail_compilation/fail6795.d @@ -8,9 +8,9 @@ fail_compilation/fail6795.d(22): Error: cannot modify expression `[0][0]` becaus fail_compilation/fail6795.d(23): Error: cannot modify expression `[0:0][0]` because it is not an lvalue fail_compilation/fail6795.d(25): Error: cannot take address of expression `[0][0]` because it is not an lvalue fail_compilation/fail6795.d(26): Error: cannot take address of expression `[0:0][0]` because it is not an lvalue +fail_compilation/fail6795.d(30): Error: cannot modify expression `Some["zz"]` because it is not an lvalue --- */ - void test_wrong_line_num() { enum int[1] sa = [0]; @@ -24,4 +24,8 @@ void test_wrong_line_num() auto ps = &sa[0]; auto pa = &aa[0]; + + // https://issues.dlang.org/show_bug.cgi?id=24845 + enum Maps : int[string] { Some = ["aa" : 12], Other = ["bb" : 24] } + Maps.Some["zz"] = 44; } diff --git a/tests/dmd/fail_compilation/fail7848.d b/tests/dmd/fail_compilation/fail7848.d index 001c7d7544..8237fd1cb5 100644 --- a/tests/dmd/fail_compilation/fail7848.d +++ b/tests/dmd/fail_compilation/fail7848.d @@ -9,12 +9,12 @@ fail_compilation/fail7848.d(21): `fail7848.func` is declared here fail_compilation/fail7848.d(27): Error: `@nogc` function `fail7848.C.__unittest_L25_C30` cannot call non-@nogc function `fail7848.func` fail_compilation/fail7848.d(27): Error: function `fail7848.func` is not `nothrow` fail_compilation/fail7848.d(25): Error: function `fail7848.C.__unittest_L25_C30` may throw but is marked as `nothrow` -fail_compilation/fail7848.d(32): Error: `pure` function `fail7848.C.__invariant0` cannot call impure function `fail7848.func` -fail_compilation/fail7848.d(32): Error: `@safe` function `fail7848.C.__invariant0` cannot call `@system` function `fail7848.func` +fail_compilation/fail7848.d(32): Error: `pure` function `fail7848.C.invariant` cannot call impure function `fail7848.func` +fail_compilation/fail7848.d(32): Error: `@safe` function `fail7848.C.invariant` cannot call `@system` function `fail7848.func` fail_compilation/fail7848.d(21): `fail7848.func` is declared here -fail_compilation/fail7848.d(32): Error: `@nogc` function `fail7848.C.__invariant0` cannot call non-@nogc function `fail7848.func` +fail_compilation/fail7848.d(32): Error: `@nogc` function `fail7848.C.invariant` cannot call non-@nogc function `fail7848.func` fail_compilation/fail7848.d(32): Error: function `fail7848.func` is not `nothrow` -fail_compilation/fail7848.d(30): Error: function `fail7848.C.__invariant0` may throw but is marked as `nothrow` +fail_compilation/fail7848.d(30): Error: function `fail7848.C.invariant` may throw but is marked as `nothrow` --- */ diff --git a/tests/dmd/fail_compilation/fail8313.d b/tests/dmd/fail_compilation/fail8313.d index 2badeff454..9fac4fe20b 100644 --- a/tests/dmd/fail_compilation/fail8313.d +++ b/tests/dmd/fail_compilation/fail8313.d @@ -1,7 +1,7 @@ /* TEST_OUTPUT: --- -fail_compilation/fail8313.d(13): Error: `fail8313.bar` called with argument types `(int)` matches both: +fail_compilation/fail8313.d(13): Error: `fail8313.bar` called with argument types `(int)` matches multiple overloads exactly: fail_compilation/fail8313.d(11): `fail8313.bar!().bar(int x)` and: fail_compilation/fail8313.d(12): `fail8313.bar!().bar(int x)` diff --git a/tests/dmd/fail_compilation/fail8373.d b/tests/dmd/fail_compilation/fail8373.d index 6f6337db2c..2d1d8a65b3 100644 --- a/tests/dmd/fail_compilation/fail8373.d +++ b/tests/dmd/fail_compilation/fail8373.d @@ -1,11 +1,11 @@ /* TEST_OUTPUT: --- -fail_compilation/fail8373.d(21): Error: `fail8373.fun1` called with argument types `(int)` matches both: +fail_compilation/fail8373.d(21): Error: `fail8373.fun1` called with argument types `(int)` matches multiple overloads exactly: fail_compilation/fail8373.d(15): `fail8373.fun1!().fun1!int.fun1(int)` and: fail_compilation/fail8373.d(16): `fail8373.fun1!int.fun1(int)` -fail_compilation/fail8373.d(22): Error: `fail8373.fun2` called with argument types `(int)` matches both: +fail_compilation/fail8373.d(22): Error: `fail8373.fun2` called with argument types `(int)` matches multiple overloads exactly: fail_compilation/fail8373.d(18): `fail8373.fun2!int.fun2(int)` and: fail_compilation/fail8373.d(19): `fail8373.fun2!().fun2!int.fun2(int)` diff --git a/tests/dmd/fail_compilation/fail99.d b/tests/dmd/fail_compilation/fail99.d index a12fa3f4cc..4f119f2aa9 100644 --- a/tests/dmd/fail_compilation/fail99.d +++ b/tests/dmd/fail_compilation/fail99.d @@ -1,7 +1,7 @@ /* TEST_OUTPUT: --- -fail_compilation/fail99.d(13): Error: delegate `dg(int)` is not callable using argument types `()` +fail_compilation/fail99.d(13): Error: delegate `dg` is not callable using argument types `()` fail_compilation/fail99.d(13): too few arguments, expected 1, got 0 --- */ diff --git a/tests/dmd/fail_compilation/failCopyCtor.d b/tests/dmd/fail_compilation/failCopyCtor.d deleted file mode 100644 index f341675f32..0000000000 --- a/tests/dmd/fail_compilation/failCopyCtor.d +++ /dev/null @@ -1,15 +0,0 @@ -/* -TEST_OUTPUT: ---- -fail_compilation/failCopyCtor.d(10): Error: `struct A` may not define both a rvalue constructor and a copy constructor -fail_compilation/failCopyCtor.d(12): rvalue constructor defined here -fail_compilation/failCopyCtor.d(13): copy constructor defined here ---- -*/ - -struct A -{ - this(immutable A a) {} - this(ref shared A a) immutable {} - this(ref A a) {} -} diff --git a/tests/dmd/fail_compilation/failCopyCtor2.d b/tests/dmd/fail_compilation/failCopyCtor2.d deleted file mode 100644 index 5e8f8c40f7..0000000000 --- a/tests/dmd/fail_compilation/failCopyCtor2.d +++ /dev/null @@ -1,19 +0,0 @@ -/* -TEST_OUTPUT: ---- -fail_compilation/failCopyCtor2.d(15): Error: `struct B` may not define a rvalue constructor and have fields with copy constructors -fail_compilation/failCopyCtor2.d(18): rvalue constructor defined here -fail_compilation/failCopyCtor2.d(17): field with copy constructor defined here ---- -*/ - -struct A -{ - this (ref shared A a) immutable {} -} - -struct B -{ - A a; - this(immutable B b) shared {} -} diff --git a/tests/dmd/fail_compilation/fail_arrayexp.d b/tests/dmd/fail_compilation/fail_arrayexp.d index 1a766ff80d..6f9868a48b 100644 --- a/tests/dmd/fail_compilation/fail_arrayexp.d +++ b/tests/dmd/fail_compilation/fail_arrayexp.d @@ -6,6 +6,7 @@ fail_compilation/fail_arrayexp.d(25): Error: cannot use `[]` operator on express fail_compilation/fail_arrayexp.d(26): Error: static array of `const(int)[]` with multiple lengths not allowed fail_compilation/fail_arrayexp.d(27): Error: only one index allowed to index `string` fail_compilation/fail_arrayexp.d(28): Error: no `[]` operator overload for type `U` +fail_compilation/fail_arrayexp.d(16): perhaps define `auto opIndex() {}` for `fail_arrayexp.U` fail_compilation/fail_arrayexp.d(29): Error: only one index allowed to index `(int, string)` --- */ @@ -28,3 +29,24 @@ void test19534() // https://issues.dlang.org/show_bug.cgi?id=19534 auto t = agg[]; auto u = getTuple!(int, string)[1, 2]; } + +/* +TEST_OUTPUT: +--- +fail_compilation/fail_arrayexp.d(49): Error: no `[3.."4"]` operator overload for type `S` +fail_compilation/fail_arrayexp.d(42): perhaps define `auto opSlice(int lower, string upper) {}` for `fail_arrayexp.S` +fail_compilation/fail_arrayexp.d(50): Error: no `[]` operator overload for type `S` +fail_compilation/fail_arrayexp.d(42): perhaps define `auto opIndex(int, string, char) {}` for `fail_arrayexp.S` +--- +*/ + +struct S +{ +} + +void testSlice() +{ + S s; + const b = s[3 .. "4"]; + const c = s[3, "4", 'c']; +} diff --git a/tests/dmd/fail_compilation/fail_arrayop2.d b/tests/dmd/fail_compilation/fail_arrayop2.d index 0db6a45c9a..a8b53f1e95 100644 --- a/tests/dmd/fail_compilation/fail_arrayop2.d +++ b/tests/dmd/fail_compilation/fail_arrayop2.d @@ -209,8 +209,6 @@ fail_compilation/fail_arrayop2.d(269): Error: array operation `"abc"[] + '\x01'` fail_compilation/fail_arrayop2.d(272): Error: array operation `[1] * 6` without destination memory not allowed fail_compilation/fail_arrayop2.d(275): Error: cannot take address of expression `([1] * 6)[0..2]` because it is not an lvalue fail_compilation/fail_arrayop2.d(278): Error: can only `*` a pointer, not a `int[]` -fail_compilation/fail_arrayop2.d(281): Error: the `delete` keyword is obsolete -fail_compilation/fail_arrayop2.d(281): use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead fail_compilation/fail_arrayop2.d(284): Error: array operation `da[] * 6` without destination memory not allowed fail_compilation/fail_arrayop2.d(287): Error: array operation `da[] * 6` without destination memory not allowed fail_compilation/fail_arrayop2.d(290): Error: cannot modify expression `[1] * 6` because it is not an lvalue @@ -235,6 +233,8 @@ fail_compilation/fail_arrayop2.d(321): Error: array operation `[1] * 6` without fail_compilation/fail_arrayop2.d(321): Error: array operation `[1] * 6` without destination memory not allowed --- */ + + // Test all expressions, which can take arrays as their operands but cannot be a part of array operation. void test15407exp() { @@ -277,8 +277,8 @@ void test15407exp() // PtrExp, *([1] * 6).ptr is also invalid -> show better diagnostic { auto r = *([1] * 6); } - // DeleteExp - e1 - delete ([1] * 6); + + // TypeDArray.dotExp, cannot check in ArrayLengthExp.semantic() { auto r = (6 * da[]).length; } diff --git a/tests/dmd/fail_compilation/fail_opover.d b/tests/dmd/fail_compilation/fail_opover.d index 2a62f53bbd..3a0b75fc46 100644 --- a/tests/dmd/fail_compilation/fail_opover.d +++ b/tests/dmd/fail_compilation/fail_opover.d @@ -3,8 +3,34 @@ /* TEST_OUTPUT: --- -fail_compilation/fail_opover.d(13): Error: no `[]` operator overload for type `object.Object` -fail_compilation/fail_opover.d(17): Error: no `[]` operator overload for type `TestS` +fail_compilation/fail_opover.d(39): Error: no `[]` operator overload for type `object.Object` +$p:object.d$($n$): perhaps define `auto opIndex() {}` for `object.Object` +fail_compilation/fail_opover.d(43): Error: no `[]` operator overload for type `TestS` +fail_compilation/fail_opover.d(41): perhaps define `auto opIndex() {}` for `fail_opover.test1.TestS` +fail_compilation/fail_opover.d(55): Error: no `[]` operator overload for type `S` +fail_compilation/fail_opover.d(48): perhaps define `auto opIndex() {}` for `fail_opover.test2.S` +fail_compilation/fail_opover.d(56): Error: no `[]` operator overload for type `S` +fail_compilation/fail_opover.d(48): perhaps define `auto opIndex(int) {}` for `fail_opover.test2.S` +fail_compilation/fail_opover.d(57): Error: no `[1..2]` operator overload for type `S` +fail_compilation/fail_opover.d(48): perhaps define `auto opSlice(int lower, int upper) {}` for `fail_opover.test2.S` +fail_compilation/fail_opover.d(58): Error: no `[]` operator overload for type `S` +fail_compilation/fail_opover.d(48): perhaps define `auto opIndex() {}` for `fail_opover.test2.S` +fail_compilation/fail_opover.d(59): Error: no `[]` operator overload for type `S` +fail_compilation/fail_opover.d(48): perhaps define `auto opIndex(int) {}` for `fail_opover.test2.S` +fail_compilation/fail_opover.d(60): Error: no `[1..2]` operator overload for type `S` +fail_compilation/fail_opover.d(48): perhaps define `auto opSlice(int lower, int upper) {}` for `fail_opover.test2.S` +fail_compilation/fail_opover.d(61): Error: no `[]` operator overload for type `S` +fail_compilation/fail_opover.d(48): perhaps define `auto opIndex() {}` for `fail_opover.test2.S` +fail_compilation/fail_opover.d(62): Error: no `[]` operator overload for type `S` +fail_compilation/fail_opover.d(48): perhaps define `auto opIndex(int) {}` for `fail_opover.test2.S` +fail_compilation/fail_opover.d(63): Error: no `[1..2]` operator overload for type `S` +fail_compilation/fail_opover.d(48): perhaps define `auto opSlice(int lower, int upper) {}` for `fail_opover.test2.S` +fail_compilation/fail_opover.d(64): Error: no `[]` operator overload for type `S` +fail_compilation/fail_opover.d(48): perhaps define `auto opIndex() {}` for `fail_opover.test2.S` +fail_compilation/fail_opover.d(65): Error: no `[]` operator overload for type `S` +fail_compilation/fail_opover.d(48): perhaps define `auto opIndex(int) {}` for `fail_opover.test2.S` +fail_compilation/fail_opover.d(66): Error: no `[1..2]` operator overload for type `S` +fail_compilation/fail_opover.d(48): perhaps define `auto opSlice(int lower, int upper) {}` for `fail_opover.test2.S` --- */ void test1() @@ -17,23 +43,6 @@ void test1() s[] = error; } -/* -TEST_OUTPUT: ---- -fail_compilation/fail_opover.d(46): Error: no `[]` operator overload for type `S` -fail_compilation/fail_opover.d(47): Error: no `[]` operator overload for type `S` -fail_compilation/fail_opover.d(48): Error: no `[]` operator overload for type `S` -fail_compilation/fail_opover.d(49): Error: no `[]` operator overload for type `S` -fail_compilation/fail_opover.d(50): Error: no `[]` operator overload for type `S` -fail_compilation/fail_opover.d(51): Error: no `[]` operator overload for type `S` -fail_compilation/fail_opover.d(52): Error: no `[]` operator overload for type `S` -fail_compilation/fail_opover.d(53): Error: no `[]` operator overload for type `S` -fail_compilation/fail_opover.d(54): Error: no `[]` operator overload for type `S` -fail_compilation/fail_opover.d(55): Error: no `[]` operator overload for type `S` -fail_compilation/fail_opover.d(56): Error: no `[]` operator overload for type `S` -fail_compilation/fail_opover.d(57): Error: no `[]` operator overload for type `S` ---- -*/ void test2() { struct S diff --git a/tests/dmd/fail_compilation/fail_pretty_errors.d b/tests/dmd/fail_compilation/fail_pretty_errors.d index e9533c04c6..79242b163e 100644 --- a/tests/dmd/fail_compilation/fail_pretty_errors.d +++ b/tests/dmd/fail_compilation/fail_pretty_errors.d @@ -1,21 +1,26 @@ -/* +/* REQUIRED_ARGS: -verrors=context TEST_OUTPUT: --- -fail_compilation/fail_pretty_errors.d(24): Error: undefined identifier `a` +fail_compilation/fail_pretty_errors.d(29): Error: undefined identifier `a` a = 1; ^ -fail_compilation/fail_pretty_errors.d-mixin-29(29): Error: undefined identifier `b` -fail_compilation/fail_pretty_errors.d(34): Error: cannot implicitly convert expression `5` of type `int` to `string` +fail_compilation/fail_pretty_errors.d-mixin-34(34): Error: undefined identifier `b` +b = 1; +^ +fail_compilation/fail_pretty_errors.d(39): Error: cannot implicitly convert expression `5` of type `int` to `string` string x = 5; ^ -fail_compilation/fail_pretty_errors.d(39): Error: mixin `fail_pretty_errors.testMixin2.mixinTemplate!()` error instantiating +fail_compilation/fail_pretty_errors.d(44): Error: mixin `fail_pretty_errors.testMixin2.mixinTemplate!()` error instantiating mixin mixinTemplate; ^ -fail_compilation/fail_pretty_errors.d(45): Error: invalid array operation `"" + ""` (possible missing []) +fail_compilation/fail_pretty_errors.d(50): Error: invalid array operation `"" + ""` (possible missing []) auto x = ""+""; ^ -fail_compilation/fail_pretty_errors.d(45): did you mean to concatenate (`"" ~ ""`) instead ? +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` + byte ɑ = 1111; + ^ --- */ @@ -43,4 +48,7 @@ void f() { // check supplemental error doesn't show context auto x = ""+""; + + // Check correct spacing with the presence of unicode characters and tabs + byte ɑ = 1111; } diff --git a/tests/dmd/fail_compilation/fail_scope.d b/tests/dmd/fail_compilation/fail_scope.d index 9851ffc1c7..a7ac298f59 100644 --- a/tests/dmd/fail_compilation/fail_scope.d +++ b/tests/dmd/fail_compilation/fail_scope.d @@ -3,7 +3,7 @@ TEST_OUTPUT: --- fail_compilation/fail_scope.d(43): Error: returning `cast(char[])string` escapes a reference to local variable `string` fail_compilation/fail_scope.d(61): Error: returning `s.bar()` escapes a reference to local variable `s` -fail_compilation/fail_scope.d(72): Error: `fail_scope.foo8` called with argument types `(int)` matches both: +fail_compilation/fail_scope.d(72): Error: `fail_scope.foo8` called with argument types `(int)` matches multiple overloads exactly: fail_compilation/fail_scope.d(66): `fail_scope.foo8(ref int x)` and: fail_compilation/fail_scope.d(67): `fail_scope.foo8(return ref int x)` diff --git a/tests/dmd/fail_compilation/failcontracts.d b/tests/dmd/fail_compilation/failcontracts.d index 51275d0140..b6c3bc906a 100644 --- a/tests/dmd/fail_compilation/failcontracts.d +++ b/tests/dmd/fail_compilation/failcontracts.d @@ -3,7 +3,7 @@ fail_compilation/failcontracts.d(17): Error: missing `{ ... }` for function literal fail_compilation/failcontracts.d(17): Error: semicolon expected following auto declaration, not `bode` fail_compilation/failcontracts.d(18): Error: function declaration without return type. (Note that constructors are always named `this`) -fail_compilation/failcontracts.d(18): Error: no identifier for declarator `test1()` +fail_compilation/failcontracts.d(18): Error: variable name expected after type `test1()`, not `bode` fail_compilation/failcontracts.d(18): Error: semicolon expected following function declaration, not `bode` fail_compilation/failcontracts.d(19): Error: semicolon expected following function declaration, not `bode` fail_compilation/failcontracts.d(21): Error: unexpected `(` in declarator diff --git a/tests/dmd/fail_compilation/failcstuff6.c b/tests/dmd/fail_compilation/failcstuff6.c index 88c541ca64..24ea19c81d 100644 --- a/tests/dmd/fail_compilation/failcstuff6.c +++ b/tests/dmd/fail_compilation/failcstuff6.c @@ -1,7 +1,10 @@ // check dsymbolSemantic analysis of C files /* TEST_OUTPUT: +REQUIRED_ARGS: -verrors=context --- fail_compilation/failcstuff6.c(56): Error: enum member `failcstuff6.test_overflow.boom` initialization with `2147483647+1` causes overflow for type `int` + boom, + ^ --- */ diff --git a/tests/dmd/fail_compilation/faildeleteaa.d b/tests/dmd/fail_compilation/faildeleteaa.d deleted file mode 100644 index ed640fb896..0000000000 --- a/tests/dmd/fail_compilation/faildeleteaa.d +++ /dev/null @@ -1,13 +0,0 @@ -/* -TEST_OUTPUT: ---- -fail_compilation/faildeleteaa.d(12): Error: the `delete` keyword is obsolete -fail_compilation/faildeleteaa.d(12): use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead ---- -*/ - -void main() -{ - int[int] aa = [1 : 2]; - delete aa[1]; -} diff --git a/tests/dmd/fail_compilation/fix22108.d b/tests/dmd/fail_compilation/fix22108.d index 149bebae0e..8fa75cbf6f 100644 --- a/tests/dmd/fail_compilation/fix22108.d +++ b/tests/dmd/fail_compilation/fix22108.d @@ -1,7 +1,7 @@ /* REQUIRED_ARGS: -preview=dip1000 TEST_OUTPUT: --- -fail_compilation/fix22108.d(12): Error: scope variable `p` may not be returned +fail_compilation/fix22108.d(12): Error: returning scope variable `p` is not allowed in a `@safe` function --- */ diff --git a/tests/dmd/fail_compilation/fix5212.d b/tests/dmd/fail_compilation/fix5212.d index 0b804dced9..9e87e2cc85 100644 --- a/tests/dmd/fail_compilation/fix5212.d +++ b/tests/dmd/fail_compilation/fix5212.d @@ -1,7 +1,7 @@ /* REQUIRED_ARGS: -preview=dip1000 TEST_OUTPUT: --- -fail_compilation/fix5212.d(14): Error: scope variable `args_` assigned to non-scope `this.args` +fail_compilation/fix5212.d(14): Error: assigning scope variable `args_` to non-scope `this.args` is not allowed in a `@safe` function --- */ diff --git a/tests/dmd/fail_compilation/fnconstraint.d b/tests/dmd/fail_compilation/fnconstraint.d index 21603f725f..6369bdc5ba 100644 --- a/tests/dmd/fail_compilation/fnconstraint.d +++ b/tests/dmd/fail_compilation/fnconstraint.d @@ -2,9 +2,9 @@ TEST_OUTPUT: --- fail_compilation/fnconstraint.d(14): Error: template constraint must follow parameter lists and attributes -fail_compilation/fnconstraint.d(14): Error: declaration expected, not `if` +fail_compilation/fnconstraint.d(15): Error: declaration expected, not `{` fail_compilation/fnconstraint.d(23): Error: template constraint must follow parameter lists and attributes -fail_compilation/fnconstraint.d(23): Error: declaration expected, not `if` +fail_compilation/fnconstraint.d(23): Error: declaration expected, not `{` fail_compilation/fnconstraint.d(27): Error: `}` expected following members in `struct` declaration fail_compilation/fnconstraint.d(19): struct `S` starts here --- diff --git a/tests/dmd/fail_compilation/ftimetrace.d b/tests/dmd/fail_compilation/ftimetrace.d new file mode 100644 index 0000000000..3ed60ee8d9 --- /dev/null +++ b/tests/dmd/fail_compilation/ftimetrace.d @@ -0,0 +1,7 @@ +/** DISABLED: LDC // different error message +REQUIRED_ARGS: -ftime-trace-granularity=-800 +TEST_OUTPUT: +--- +Error: `-ftime-trace-granularity` requires a positive number of microseconds +--- +*/ diff --git a/tests/dmd/fail_compilation/hexstring.d b/tests/dmd/fail_compilation/hexstring.d index edbb4e67bc..2c6191e7f9 100644 --- a/tests/dmd/fail_compilation/hexstring.d +++ b/tests/dmd/fail_compilation/hexstring.d @@ -1,21 +1,24 @@ /** TEST_OUTPUT: --- -fail_compilation/hexstring.d(29): Error: cannot implicitly convert expression `"123F"` of type `string` to `immutable(ubyte[])` -fail_compilation/hexstring.d(33): Error: hex string length 1 must be a multiple of 2 to cast to `immutable(ushort[])` -fail_compilation/hexstring.d(34): Error: hex string length 3 must be a multiple of 4 to cast to `immutable(uint[])` -fail_compilation/hexstring.d(35): Error: hex string length 5 must be a multiple of 8 to cast to `immutable(ulong[])` -fail_compilation/hexstring.d(36): Error: array cast from `wstring` to `immutable(ulong[])` is not supported at compile time -fail_compilation/hexstring.d(36): perhaps remove postfix `w` from hex string -fail_compilation/hexstring.d(37): Error: array cast from `string` to `immutable(uint[])` is not supported at compile time -fail_compilation/hexstring.d(38): Error: array cast from `string` to `immutable(ushort[])` is not supported at compile time -fail_compilation/hexstring.d(39): Error: array cast from `string` to `immutable(uint[])` is not supported at compile time -fail_compilation/hexstring.d(39): perhaps remove postfix `c` from hex string -fail_compilation/hexstring.d(40): Error: hex string with `dstring` type needs to be multiple of 4 bytes, not 5 -fail_compilation/hexstring.d(41): Error: cannot implicitly convert expression `x"11223344"d` of type `dstring` to `immutable(float[])` -fail_compilation/hexstring.d(42): Error: cannot implicitly convert expression `x"1122"w` of type `wstring` to `immutable(ubyte[])` -fail_compilation/hexstring.d(50): Error: array cast from `string` to `S[]` is not supported at compile time -fail_compilation/hexstring.d(28): Error: cannot implicitly convert expression `x"123F"` of type `string` to `ubyte[]` +fail_compilation/hexstring.d(30): Error: array cast from `string` to `ubyte[3][1]` is not supported at compile time +fail_compilation/hexstring.d(33): Error: cannot implicitly convert expression `"123F"` of type `string` to `immutable(ubyte[])` +fail_compilation/hexstring.d(37): Error: hex string length 1 must be a multiple of 2 to cast to `immutable(ushort[])` +fail_compilation/hexstring.d(38): Error: hex string length 3 must be a multiple of 4 to cast to `immutable(uint[])` +fail_compilation/hexstring.d(39): Error: hex string length 5 must be a multiple of 8 to cast to `immutable(ulong[])` +fail_compilation/hexstring.d(40): Error: array cast from `wstring` to `immutable(ulong[])` is not supported at compile time +fail_compilation/hexstring.d(40): perhaps remove postfix `w` from hex string +fail_compilation/hexstring.d(41): Error: array cast from `string` to `immutable(uint[])` is not supported at compile time +fail_compilation/hexstring.d(42): Error: array cast from `string` to `immutable(ushort[])` is not supported at compile time +fail_compilation/hexstring.d(43): Error: array cast from `string` to `immutable(uint[])` is not supported at compile time +fail_compilation/hexstring.d(43): perhaps remove postfix `c` from hex string +fail_compilation/hexstring.d(44): Error: hex string with `dstring` type needs to be multiple of 4 bytes, not 5 +fail_compilation/hexstring.d(45): Error: cannot implicitly convert expression `x"11223344"d` of type `dstring` to `immutable(float[])` +fail_compilation/hexstring.d(46): Error: cannot implicitly convert expression `x"1122"w` of type `wstring` to `immutable(ubyte[])` +fail_compilation/hexstring.d(47): Error: array cast from `string` to `ubyte[3][1]` is not supported at compile time +fail_compilation/hexstring.d(48): Error: array cast from `string` to `ubyte[3][1][1]` is not supported at compile time +fail_compilation/hexstring.d(56): Error: array cast from `string` to `S[]` is not supported at compile time +fail_compilation/hexstring.d(32): Error: cannot implicitly convert expression `x"123F"` of type `string` to `ubyte[]` --- */ immutable ubyte[] s0 = x"123F"; @@ -24,6 +27,7 @@ static assert(s0[1] == 0x3F); immutable byte[] s1 = x"123F"; enum E(X) = cast(X[]) x"AABBCCDD"; static assert(E!int[0] == 0xAABBCCDD); +immutable ubyte[3] s2 = cast(ubyte[3][1])x"FFAAFF"; ubyte[] f1 = x"123F"; immutable ubyte[] f2 = "123F"; @@ -40,6 +44,8 @@ immutable uint[] f11 = cast(immutable uint[]) x"AABBCCDD"c; immutable uint[] f12 = x"1122334455"d; immutable float[] f13 = x"11223344"d; immutable ubyte[] f14 = x"1122"w; +immutable ubyte[3][1] f16 = cast(ubyte[3][1])x"FFBBFF"; +immutable ubyte[3][1][1] f17 = cast(ubyte[3][1][1])x"FFCCFF"; // https://issues.dlang.org/show_bug.cgi?id=24832 struct S diff --git a/tests/dmd/fail_compilation/ice10624.d b/tests/dmd/fail_compilation/ice10624.d index 3bc3c7d502..8fa2e6b1a8 100644 --- a/tests/dmd/fail_compilation/ice10624.d +++ b/tests/dmd/fail_compilation/ice10624.d @@ -1,9 +1,10 @@ /* TEST_OUTPUT: --- -fail_compilation/ice10624.d(38): Error: need member function `opCmp()` for struct `Tuple!(Msg)` to compare -fail_compilation/ice10624.d(48): Error: template instance `ice10624.Variant.handler!(Tuple!(Msg))` error instantiating -fail_compilation/ice10624.d(21): instantiated from here: `opAssign!(Tuple!(Msg))` +fail_compilation/ice10624.d(39): Error: no operator `<` for type `Tuple` +fail_compilation/ice10624.d(13): perhaps overload it with `int opCmp(Tuple!(Msg) other) const {}` +fail_compilation/ice10624.d(49): Error: template instance `ice10624.Variant.handler!(Tuple!(Msg))` error instantiating +fail_compilation/ice10624.d(22): instantiated from here: `opAssign!(Tuple!(Msg))` --- */ diff --git a/tests/dmd/fail_compilation/ice10922.d b/tests/dmd/fail_compilation/ice10922.d index fc6c593ae0..4f052b2819 100644 --- a/tests/dmd/fail_compilation/ice10922.d +++ b/tests/dmd/fail_compilation/ice10922.d @@ -1,7 +1,7 @@ /* TEST_OUTPUT: --- -fail_compilation/ice10922.d(11): Error: function `__lambda_L10_C12` is not callable using argument types `()` +fail_compilation/ice10922.d(11): Error: function `(in uint n) { enum self = __lambda_L10_C12; return n < 2 ? n : self(n - 1) + ...` is not callable using argument types `()` fail_compilation/ice10922.d(11): too few arguments, expected 1, got 0 fail_compilation/ice10922.d(10): `ice10922.__lambda_L10_C12(in uint n)` declared here --- diff --git a/tests/dmd/fail_compilation/ice10938.d b/tests/dmd/fail_compilation/ice10938.d index 4d107c9e7b..16eaa99b3d 100644 --- a/tests/dmd/fail_compilation/ice10938.d +++ b/tests/dmd/fail_compilation/ice10938.d @@ -2,8 +2,8 @@ TEST_OUTPUT: --- fail_compilation/ice10938.d(14): Error: no property `opts` for `this` of type `ice10938.C` -fail_compilation/ice10938.d(14): potentially malformed `opDispatch`. Use an explicit instantiation to get a better error message -fail_compilation/ice10938.d(10): class `C` defined here +fail_compilation/ice10938.d(19): Error: forward reference to inferred return type of function call `this.opDispatch()` +fail_compilation/ice10938.d(14): Error: template instance `ice10938.C.opDispatch!"opts"` error instantiating --- */ diff --git a/tests/dmd/fail_compilation/ice11153.d b/tests/dmd/fail_compilation/ice11153.d index 6a1b89ba77..826bd8cd7e 100644 --- a/tests/dmd/fail_compilation/ice11153.d +++ b/tests/dmd/fail_compilation/ice11153.d @@ -2,7 +2,7 @@ TEST_OUTPUT: --- fail_compilation/ice11153.d(11): Error: function declaration without return type. (Note that constructors are always named `this`) -fail_compilation/ice11153.d(11): Error: no identifier for declarator `foo()` +fail_compilation/ice11153.d(11): Error: variable name expected after type `foo()`, not `{` --- */ diff --git a/tests/dmd/fail_compilation/ice11963.d b/tests/dmd/fail_compilation/ice11963.d index 39eb120827..165945b6f4 100644 --- a/tests/dmd/fail_compilation/ice11963.d +++ b/tests/dmd/fail_compilation/ice11963.d @@ -3,7 +3,7 @@ TEST_OUTPUT: --- fail_compilation/ice11963.d(10): Error: unexpected `(` in declarator fail_compilation/ice11963.d(10): Error: identifier expected for template type parameter -fail_compilation/ice11963.d(10): Error: no identifier for declarator `A` +fail_compilation/ice11963.d(10): Error: variable name expected after type `A`, not `""` fail_compilation/ice11963.d(10): Error: declaration expected, not `""` --- */ diff --git a/tests/dmd/fail_compilation/ice11965.d b/tests/dmd/fail_compilation/ice11965.d index 9e6da3bbf8..79d15aa5a6 100644 --- a/tests/dmd/fail_compilation/ice11965.d +++ b/tests/dmd/fail_compilation/ice11965.d @@ -1,15 +1,15 @@ /* TEST_OUTPUT: --- -fail_compilation/ice11965.d(16): Error: no identifier for declarator `b*` +fail_compilation/ice11965.d(16): Error: variable name expected after type `b*`, not `End of File` fail_compilation/ice11965.d(16): Error: matching `}` expected following compound statement, not `End of File` fail_compilation/ice11965.d(15): unmatched `{` fail_compilation/ice11965.d(16): Error: found `End of File` when expecting `]` -fail_compilation/ice11965.d(16): Error: no identifier for declarator `u[() +fail_compilation/ice11965.d(16): Error: variable name expected after type `u[() { b* A; } -]` +]`, not `End of File` --- */ u[{b*A, diff --git a/tests/dmd/fail_compilation/ice11968.d b/tests/dmd/fail_compilation/ice11968.d deleted file mode 100644 index 1d50b66e00..0000000000 --- a/tests/dmd/fail_compilation/ice11968.d +++ /dev/null @@ -1,9 +0,0 @@ -/* -TEST_OUTPUT: ----- -fail_compilation/ice11968.d(9): Error: the `delete` keyword is obsolete -fail_compilation/ice11968.d(9): use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead ----- -*/ - -void main() { delete __FILE__ ; } diff --git a/tests/dmd/fail_compilation/ice12902.d b/tests/dmd/fail_compilation/ice12902.d index 03763f7c18..5f27ce45c1 100644 --- a/tests/dmd/fail_compilation/ice12902.d +++ b/tests/dmd/fail_compilation/ice12902.d @@ -1,7 +1,7 @@ /* TEST_OUTPUT: --- -fail_compilation/ice12902.d(20): Error: variable `ice12902.main.__dollar` - type `void` is inferred from initializer `s.opDollar()`, and variables cannot be of type `void` +fail_compilation/ice12902.d(20): Error: variable `ice12902.main.$` - type `void` is inferred from initializer `s.opDollar()`, and variables cannot be of type `void` fail_compilation/ice12902.d(20): Error: expression `s.opDollar()` is `void` and has no value --- */ diff --git a/tests/dmd/fail_compilation/ice14642.d b/tests/dmd/fail_compilation/ice14642.d index 90b9867527..91b1f950ca 100644 --- a/tests/dmd/fail_compilation/ice14642.d +++ b/tests/dmd/fail_compilation/ice14642.d @@ -1,8 +1,9 @@ /* TEST_OUTPUT: --- -fail_compilation/ice14642.d(47): Error: undefined identifier `errorValue` -fail_compilation/ice14642.d(23): Error: template instance `ice14642.X.NA!()` error instantiating +fail_compilation/ice14642.d(48): Error: undefined identifier `errorValue` +fail_compilation/ice14642.d(52): error on member `ice14642.Z.v` +fail_compilation/ice14642.d(24): Error: template instance `ice14642.X.NA!()` error instantiating --- */ diff --git a/tests/dmd/fail_compilation/ice15788.d b/tests/dmd/fail_compilation/ice15788.d index 941b179fb7..66942545d1 100644 --- a/tests/dmd/fail_compilation/ice15788.d +++ b/tests/dmd/fail_compilation/ice15788.d @@ -2,7 +2,9 @@ EXTRA_FILES: imports/range15788.d TEST_OUTPUT: --- -fail_compilation/ice15788.d(18): Error: none of the overloads of `iota` are callable using argument types `!()(S, S)` +fail_compilation/ice15788.d(20): Error: none of the overloads of `iota` are callable using argument types `!()(S, S)` +fail_compilation/imports/range15788.d(3): Candidates are: `iota(B, E, S)(B, E, S)` +fail_compilation/ice15788.d(13): `ice15788.iota()` --- */ diff --git a/tests/dmd/fail_compilation/ice15855.d b/tests/dmd/fail_compilation/ice15855.d index f7ad390068..da630913b2 100644 --- a/tests/dmd/fail_compilation/ice15855.d +++ b/tests/dmd/fail_compilation/ice15855.d @@ -12,15 +12,15 @@ fail_compilation/ice15855.d(28): Error: found `End of File` instead of statement fail_compilation/ice15855.d(28): Error: matching `}` expected following compound statement, not `End of File` fail_compilation/ice15855.d(27): unmatched `{` fail_compilation/ice15855.d(28): Error: found `End of File` when expecting `]` -fail_compilation/ice15855.d(28): Error: no identifier for declarator `a[() +fail_compilation/ice15855.d(28): Error: variable name expected after type `a[() { for (__error__ - 0; 0) + __error; __error) { __error__ } } -]` +]`, not `End of File` --- */ diff --git a/tests/dmd/fail_compilation/ice22377.d b/tests/dmd/fail_compilation/ice22377.d index 686e700cae..b9e1a92781 100644 --- a/tests/dmd/fail_compilation/ice22377.d +++ b/tests/dmd/fail_compilation/ice22377.d @@ -1,7 +1,7 @@ /* TEST_OUTPUT: --- -fail_compilation/ice22377.d(8): Error: function `ice22377.foo` cannot have parameter of type `string` because its linkage is `extern(C++)` +fail_compilation/ice22377.d(8): Error: function `foo` cannot have parameter of type `string` because its linkage is `extern(C++)` --- */ diff --git a/tests/dmd/fail_compilation/ice8309.d b/tests/dmd/fail_compilation/ice8309.d index a9f6f7253a..48e35e2ed0 100644 --- a/tests/dmd/fail_compilation/ice8309.d +++ b/tests/dmd/fail_compilation/ice8309.d @@ -1,7 +1,7 @@ /* TEST_OUTPUT: --- -fail_compilation/ice8309.d(10): Error: incompatible types for `(__lambda_L10_C15) : (__lambda_L10_C24)`: `double function() pure nothrow @nogc @safe` and `int function() pure nothrow @nogc @safe` +fail_compilation/ice8309.d(10): Error: incompatible types for `(() => 1.0) : (() => 1)`: `double function() pure nothrow @nogc @safe` and `int function() pure nothrow @nogc @safe` --- */ diff --git a/tests/dmd/fail_compilation/ice8795.d b/tests/dmd/fail_compilation/ice8795.d index a30a65b200..33aa6e4d54 100644 --- a/tests/dmd/fail_compilation/ice8795.d +++ b/tests/dmd/fail_compilation/ice8795.d @@ -3,7 +3,7 @@ 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 (0` +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 diff --git a/tests/dmd/fail_compilation/ice9406.d b/tests/dmd/fail_compilation/ice9406.d index c1807a0f54..f9032ec0e8 100644 --- a/tests/dmd/fail_compilation/ice9406.d +++ b/tests/dmd/fail_compilation/ice9406.d @@ -1,8 +1,7 @@ /* TEST_OUTPUT: --- -fail_compilation/ice9406.d(22): Error: `s1.mixin Mixin!() t1; -` has no effect +fail_compilation/ice9406.d(21): Error: `s1.mixin Mixin!() t1;` has no effect --- */ diff --git a/tests/dmd/fail_compilation/ice9545.d b/tests/dmd/fail_compilation/ice9545.d index 7360bc534f..b10e63987c 100644 --- a/tests/dmd/fail_compilation/ice9545.d +++ b/tests/dmd/fail_compilation/ice9545.d @@ -2,7 +2,8 @@ /* TEST_OUTPUT: ---- -fail_compilation/ice9545.d(13): Error: type `int` has no value +fail_compilation/ice9545.d(14): Error: type `int` has no value +fail_compilation/ice9545.d(14): perhaps use `int.init` ---- */ diff --git a/tests/dmd/fail_compilation/ice9865.d b/tests/dmd/fail_compilation/ice9865.d index 3d8e997946..ec84263416 100644 --- a/tests/dmd/fail_compilation/ice9865.d +++ b/tests/dmd/fail_compilation/ice9865.d @@ -2,7 +2,8 @@ EXTRA_FILES: imports/ice9865b.d TEST_OUTPUT: --- -fail_compilation/ice9865.d(9): Error: alias `ice9865.Baz` recursive alias declaration +fail_compilation/ice9865.d(10): Error: alias `ice9865.Baz` recursive alias declaration +fail_compilation/ice9865.d(11): error on member `ice9865.Foo.f` --- */ diff --git a/tests/dmd/fail_compilation/imphint.d b/tests/dmd/fail_compilation/imphint.d index 101e7862f3..7ca81f47aa 100644 --- a/tests/dmd/fail_compilation/imphint.d +++ b/tests/dmd/fail_compilation/imphint.d @@ -1,53 +1,60 @@ /* TEST_OUTPUT: --- -fail_compilation/imphint.d(53): Error: `printf` is not defined, perhaps `import core.stdc.stdio;` is needed? -fail_compilation/imphint.d(54): Error: `writeln` is not defined, perhaps `import std.stdio;` is needed? -fail_compilation/imphint.d(55): Error: `sin` is not defined, perhaps `import std.math;` is needed? -fail_compilation/imphint.d(56): Error: `cos` is not defined, perhaps `import std.math;` is needed? -fail_compilation/imphint.d(57): Error: `sqrt` is not defined, perhaps `import std.math;` is needed? -fail_compilation/imphint.d(58): Error: `fabs` is not defined, perhaps `import std.math;` is needed? -fail_compilation/imphint.d(61): Error: `AliasSeq` is not defined, perhaps `import std.meta;` is needed? -fail_compilation/imphint.d(62): Error: `appender` is not defined, perhaps `import std.array;` is needed? -fail_compilation/imphint.d(63): Error: `array` is not defined, perhaps `import std.array;` is needed? -fail_compilation/imphint.d(64): Error: `calloc` is not defined, perhaps `import core.stdc.stdlib;` is needed? -fail_compilation/imphint.d(65): Error: `chdir` is not defined, perhaps `import std.file;` is needed? -fail_compilation/imphint.d(66): Error: `dirEntries` is not defined, perhaps `import std.file;` is needed? -fail_compilation/imphint.d(67): Error: `drop` is not defined, perhaps `import std.range;` is needed? -fail_compilation/imphint.d(68): Error: `each` is not defined, perhaps `import std.algorithm;` is needed? -fail_compilation/imphint.d(69): Error: `empty` is not defined, perhaps `import std.range;` is needed? -fail_compilation/imphint.d(70): Error: `enumerate` is not defined, perhaps `import std.range;` is needed? -fail_compilation/imphint.d(71): Error: `endsWith` is not defined, perhaps `import std.algorithm;` is needed? -fail_compilation/imphint.d(72): Error: `enforce` is not defined, perhaps `import std.exception;` is needed? -fail_compilation/imphint.d(73): Error: `equal` is not defined, perhaps `import std.algorithm;` is needed? -fail_compilation/imphint.d(74): Error: `exists` is not defined, perhaps `import std.file;` is needed? -fail_compilation/imphint.d(75): Error: `filter` is not defined, perhaps `import std.algorithm;` is needed? -fail_compilation/imphint.d(76): Error: `format` is not defined, perhaps `import std.format;` is needed? -fail_compilation/imphint.d(77): Error: `free` is not defined, perhaps `import core.stdc.stdlib;` is needed? -fail_compilation/imphint.d(78): Error: `front` is not defined, perhaps `import std.range;` is needed? -fail_compilation/imphint.d(79): Error: `iota` is not defined, perhaps `import std.range;` is needed? -fail_compilation/imphint.d(80): Error: `isDir` is not defined, perhaps `import std.file;` is needed? -fail_compilation/imphint.d(81): Error: `isFile` is not defined, perhaps `import std.file;` is needed? -fail_compilation/imphint.d(82): Error: `join` is not defined, perhaps `import std.array;` is needed? -fail_compilation/imphint.d(83): Error: `joiner` is not defined, perhaps `import std.algorithm;` is needed? -fail_compilation/imphint.d(84): Error: `malloc` is not defined, perhaps `import core.stdc.stdlib;` is needed? -fail_compilation/imphint.d(85): Error: `map` is not defined, perhaps `import std.algorithm;` is needed? -fail_compilation/imphint.d(86): Error: `max` is not defined, perhaps `import std.algorithm;` is needed? -fail_compilation/imphint.d(87): Error: `min` is not defined, perhaps `import std.algorithm;` is needed? -fail_compilation/imphint.d(88): Error: `mkdir` is not defined, perhaps `import std.file;` is needed? -fail_compilation/imphint.d(89): Error: `popFront` is not defined, perhaps `import std.range;` is needed? -fail_compilation/imphint.d(90): Error: `realloc` is not defined, perhaps `import core.stdc.stdlib;` is needed? -fail_compilation/imphint.d(91): Error: `replace` is not defined, perhaps `import std.array;` is needed? -fail_compilation/imphint.d(92): Error: `rmdir` is not defined, perhaps `import std.file;` is needed? -fail_compilation/imphint.d(93): Error: `sort` is not defined, perhaps `import std.algorithm;` is needed? -fail_compilation/imphint.d(94): Error: `split` is not defined, perhaps `import std.array;` is needed? -fail_compilation/imphint.d(95): Error: `startsWith` is not defined, perhaps `import std.algorithm;` is needed? -fail_compilation/imphint.d(96): Error: `take` is not defined, perhaps `import std.range;` is needed? -fail_compilation/imphint.d(97): Error: `text` is not defined, perhaps `import std.conv;` is needed? -fail_compilation/imphint.d(98): Error: `to` is not defined, perhaps `import std.conv;` is needed? +fail_compilation/imphint.d(60): Error: `printf` is not defined, perhaps `import core.stdc.stdio;` is needed? +fail_compilation/imphint.d(61): Error: `writeln` is not defined, perhaps `import std.stdio;` is needed? +fail_compilation/imphint.d(62): Error: `sin` is not defined, perhaps `import std.math;` is needed? +fail_compilation/imphint.d(63): Error: `cos` is not defined, perhaps `import std.math;` is needed? +fail_compilation/imphint.d(64): Error: `sqrt` is not defined, perhaps `import std.math;` is needed? +fail_compilation/imphint.d(65): Error: `fabs` is not defined, perhaps `import std.math;` is needed? +fail_compilation/imphint.d(68): Error: `AliasSeq` is not defined, perhaps `import std.meta;` is needed? +fail_compilation/imphint.d(69): Error: `appender` is not defined, perhaps `import std.array;` is needed? +fail_compilation/imphint.d(70): Error: `array` is not defined, perhaps `import std.array;` is needed? +fail_compilation/imphint.d(71): Error: `calloc` is not defined, perhaps `import core.stdc.stdlib;` is needed? +fail_compilation/imphint.d(72): Error: `chdir` is not defined, perhaps `import std.file;` is needed? +fail_compilation/imphint.d(73): Error: `dirEntries` is not defined, perhaps `import std.file;` is needed? +fail_compilation/imphint.d(74): Error: `drop` is not defined, perhaps `import std.range;` is needed? +fail_compilation/imphint.d(75): Error: `each` is not defined, perhaps `import std.algorithm;` is needed? +fail_compilation/imphint.d(76): Error: `empty` is not defined, perhaps `import std.range;` is needed? +fail_compilation/imphint.d(77): Error: `enumerate` is not defined, perhaps `import std.range;` is needed? +fail_compilation/imphint.d(78): Error: `endsWith` is not defined, perhaps `import std.algorithm;` is needed? +fail_compilation/imphint.d(79): Error: `enforce` is not defined, perhaps `import std.exception;` is needed? +fail_compilation/imphint.d(80): Error: `equal` is not defined, perhaps `import std.algorithm;` is needed? +fail_compilation/imphint.d(81): Error: `exists` is not defined, perhaps `import std.file;` is needed? +fail_compilation/imphint.d(82): Error: `filter` is not defined, perhaps `import std.algorithm;` is needed? +fail_compilation/imphint.d(83): Error: `format` is not defined, perhaps `import std.format;` is needed? +fail_compilation/imphint.d(84): Error: `free` is not defined, perhaps `import core.stdc.stdlib;` is needed? +fail_compilation/imphint.d(85): Error: `front` is not defined, perhaps `import std.range;` is needed? +fail_compilation/imphint.d(86): Error: `iota` is not defined, perhaps `import std.range;` is needed? +fail_compilation/imphint.d(87): Error: `isDir` is not defined, perhaps `import std.file;` is needed? +fail_compilation/imphint.d(88): Error: `isFile` is not defined, perhaps `import std.file;` is needed? +fail_compilation/imphint.d(89): Error: `join` is not defined, perhaps `import std.array;` is needed? +fail_compilation/imphint.d(90): Error: `joiner` is not defined, perhaps `import std.algorithm;` is needed? +fail_compilation/imphint.d(91): Error: `malloc` is not defined, perhaps `import core.stdc.stdlib;` is needed? +fail_compilation/imphint.d(92): Error: `map` is not defined, perhaps `import std.algorithm;` is needed? +fail_compilation/imphint.d(93): Error: `max` is not defined, perhaps `import std.algorithm;` is needed? +fail_compilation/imphint.d(94): Error: `min` is not defined, perhaps `import std.algorithm;` is needed? +fail_compilation/imphint.d(95): Error: `mkdir` is not defined, perhaps `import std.file;` is needed? +fail_compilation/imphint.d(96): Error: `popFront` is not defined, perhaps `import std.range;` is needed? +fail_compilation/imphint.d(97): Error: `realloc` is not defined, perhaps `import core.stdc.stdlib;` is needed? +fail_compilation/imphint.d(98): Error: `replace` is not defined, perhaps `import std.array;` is needed? +fail_compilation/imphint.d(99): Error: `rmdir` is not defined, perhaps `import std.file;` is needed? +fail_compilation/imphint.d(100): Error: `sort` is not defined, perhaps `import std.algorithm;` is needed? +fail_compilation/imphint.d(101): Error: `split` is not defined, perhaps `import std.array;` is needed? +fail_compilation/imphint.d(102): Error: `startsWith` is not defined, perhaps `import std.algorithm;` is needed? +fail_compilation/imphint.d(103): Error: `take` is not defined, perhaps `import std.range;` is needed? +fail_compilation/imphint.d(104): Error: `text` is not defined, perhaps `import std.conv;` is needed? +fail_compilation/imphint.d(105): Error: `to` is not defined, perhaps `import std.conv;` is needed? +fail_compilation/imphint.d(107): Error: `InterpolationHeader` is not defined, perhaps `import core.interpolation;` ? +fail_compilation/imphint.d(108): Error: template `heresy` is not callable using argument types `!()(InterpolationHeader, InterpolationFooter)` +fail_compilation/imphint.d(107): Candidate is: `heresy(Args...)(InterpolationHeader header, Args args, InterpolationFooter footer)` --- */ + + + + void foo() { printf("hello world\n"); @@ -96,4 +103,7 @@ void foo() take(); text(); to(); + + void heresy(Args...)(InterpolationHeader header, Args args, InterpolationFooter footer) {} + heresy(i""); } diff --git a/tests/dmd/fail_compilation/imports/a18243.d b/tests/dmd/fail_compilation/imports/a18243.d index 73df7511ce..35da731b44 100644 --- a/tests/dmd/fail_compilation/imports/a18243.d +++ b/tests/dmd/fail_compilation/imports/a18243.d @@ -1,5 +1,5 @@ -module a18243; +module imports.a18243; -import std.math : isNaN; +import imports.b18243 : isNaN; public bool isNaN() { return false; } diff --git a/tests/dmd/fail_compilation/imports/b18243.d b/tests/dmd/fail_compilation/imports/b18243.d new file mode 100644 index 0000000000..d59128f1f3 --- /dev/null +++ b/tests/dmd/fail_compilation/imports/b18243.d @@ -0,0 +1,3 @@ +module imports.b18243; + +bool isNaN(T)(T x) { return false; } diff --git a/tests/dmd/fail_compilation/invalid_lib.d b/tests/dmd/fail_compilation/invalid_lib.d index 3aff5744b3..1693160156 100644 --- a/tests/dmd/fail_compilation/invalid_lib.d +++ b/tests/dmd/fail_compilation/invalid_lib.d @@ -9,7 +9,7 @@ Use a regex because the path is really strange on Azure (OMF_32, 64): TEST_OUTPUT: ---- -$r:.*$: Error: corrupt $?:windows=MS Coff|osx=Mach|ELF$ object module $?:windows=fail_compilation\extra-files\fake.lib|fail_compilation/extra-files/fake.a$ $n$ +Error: corrupt $?:windows=MS Coff|osx=Mach|ELF$ object `$r:.*$` module $?:windows=fail_compilation\extra-files\fake.lib|fail_compilation/extra-files/fake.a$ $n$ ---- */ void main() {} diff --git a/tests/dmd/fail_compilation/issue16020.d b/tests/dmd/fail_compilation/issue16020.d index 79eda2ea08..9f1f377638 100644 --- a/tests/dmd/fail_compilation/issue16020.d +++ b/tests/dmd/fail_compilation/issue16020.d @@ -1,10 +1,11 @@ /* TEST_OUTPUT: --- -fail_compilation/issue16020.d(13): Error: user-defined attributes not allowed for `alias` declarations -fail_compilation/issue16020.d(14): Error: semicolon expected to close `alias` declaration, not `(` -fail_compilation/issue16020.d(14): Error: declaration expected, not `(` -fail_compilation/issue16020.d(15): Deprecation: storage class `final` has no effect in type aliases +fail_compilation/issue16020.d(14): Error: user-defined attributes not allowed for `alias` declarations +fail_compilation/issue16020.d(15): Error: semicolon expected to close `alias` declaration, not `(` +fail_compilation/issue16020.d(15): Error: semicolon needed to end declaration of `t` instead of `)` +fail_compilation/issue16020.d(15): Error: declaration expected, not `)` +fail_compilation/issue16020.d(16): Deprecation: storage class `final` has no effect in type aliases --- */ module issue16020; diff --git a/tests/dmd/fail_compilation/lexer23465.d b/tests/dmd/fail_compilation/lexer23465.d index 4ea41a58af..adf12f5b6b 100644 --- a/tests/dmd/fail_compilation/lexer23465.d +++ b/tests/dmd/fail_compilation/lexer23465.d @@ -1,13 +1,16 @@ /* TEST_OUTPUT: --- -fail_compilation/lexer23465.d(19): Error: character 0x1f37a is not allowed as a continue character in an identifier -fail_compilation/lexer23465.d(19): Error: character 0x1f37a is not a valid token -fail_compilation/lexer23465.d(20): Error: character '\' is not a valid token -fail_compilation/lexer23465.d(21): Error: unterminated /+ +/ comment -fail_compilation/lexer23465.d(22): Error: found `End of File` instead of array initializer -fail_compilation/lexer23465.d(22): Error: semicolon needed to end declaration of `arr`, instead of `End of File` -fail_compilation/lexer23465.d(17): `arr` declared here +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 --- */ @@ -18,4 +21,6 @@ int[] arr = [ 0, x🍺, 3\, + 09, + 9999999999999999999999, 5, /+ diff --git a/tests/dmd/fail_compilation/misc1.d b/tests/dmd/fail_compilation/misc1.d index 2a14f2bcfb..957c0809d5 100644 --- a/tests/dmd/fail_compilation/misc1.d +++ b/tests/dmd/fail_compilation/misc1.d @@ -1,13 +1,14 @@ /* TEST_OUTPUT: --- -fail_compilation/misc1.d(108): Error: `5` has no effect -fail_compilation/misc1.d(109): Error: `1 + 2` has no effect -fail_compilation/misc1.d(115): Deprecation: `1 * 1` has no effect -fail_compilation/misc1.d(116): Deprecation: `__lambda_L116_C34` has no effect -fail_compilation/misc1.d(122): Deprecation: `false` has no effect -fail_compilation/misc1.d(125): Deprecation: `*sp++` has no effect -fail_compilation/misc1.d(126): Deprecation: `j` has no effect +fail_compilation/misc1.d(109): Error: `5` has no effect +fail_compilation/misc1.d(110): Error: `1 + 2` has no effect +fail_compilation/misc1.d(111): Error: `x` has no effect +fail_compilation/misc1.d(117): Deprecation: `1 * 1` has no effect +fail_compilation/misc1.d(118): Deprecation: `() { j++; d++; }` has no effect +fail_compilation/misc1.d(124): Deprecation: `false` has no effect +fail_compilation/misc1.d(127): Deprecation: `*sp++` has no effect +fail_compilation/misc1.d(128): Deprecation: `j` has no effect --- */ @@ -20,8 +21,10 @@ void hasSideEffect12490(){} void issue12490() { + int x; 5, hasSideEffect12490(); 1 + 2, hasSideEffect12490(); + x, x++; } void issue23480() diff --git a/tests/dmd/fail_compilation/misc_parser_err_cov1.d b/tests/dmd/fail_compilation/misc_parser_err_cov1.d index a170b77b88..4a8875d6d6 100644 --- a/tests/dmd/fail_compilation/misc_parser_err_cov1.d +++ b/tests/dmd/fail_compilation/misc_parser_err_cov1.d @@ -24,7 +24,7 @@ fail_compilation/misc_parser_err_cov1.d(40): Error: identifier or `new` expected fail_compilation/misc_parser_err_cov1.d(41): Error: identifier or new keyword expected following `(...)`. fail_compilation/misc_parser_err_cov1.d(41): Error: expression expected, not `;` fail_compilation/misc_parser_err_cov1.d(42): Error: found `}` when expecting `;` following expression -fail_compilation/misc_parser_err_cov1.d(41): expression: `(__error) + 0` +fail_compilation/misc_parser_err_cov1.d(41): expression: `(__error) + (__error)` fail_compilation/misc_parser_err_cov1.d(43): Error: matching `}` expected following compound statement, not `End of File` fail_compilation/misc_parser_err_cov1.d(33): unmatched `{` --- diff --git a/tests/dmd/fail_compilation/must_use.d b/tests/dmd/fail_compilation/must_use.d index e3b5fedfd4..f21c4a405d 100644 --- a/tests/dmd/fail_compilation/must_use.d +++ b/tests/dmd/fail_compilation/must_use.d @@ -1,16 +1,19 @@ /+ TEST_OUTPUT: --- -fail_compilation/must_use.d(15): Error: ignored value of `@mustuse` type `must_use.S`; prepend a `cast(void)` if intentional +fail_compilation/must_use.d(17): Error: ignored value of `@mustuse` type `must_use.S`; prepend a `cast(void)` if intentional +fail_compilation/must_use.d(18): Error: ignored value of `@mustuse` type `must_use.S`; prepend a `cast(void)` if intentional --- +/ import core.attribute; @mustuse struct S {} -S fun() { return S(); } +S fun(); void test() { + int x; fun(); + fun(), x++; } diff --git a/tests/dmd/fail_compilation/named_arguments_overload.d b/tests/dmd/fail_compilation/named_arguments_overload.d index a970446460..7d884c4310 100644 --- a/tests/dmd/fail_compilation/named_arguments_overload.d +++ b/tests/dmd/fail_compilation/named_arguments_overload.d @@ -7,7 +7,7 @@ fail_compilation/named_arguments_overload.d(18): `named_a fail_compilation/named_arguments_overload.d(34): Error: none of the overloads of `snoopy` are callable using argument types `(immutable(T), immutable(S))` fail_compilation/named_arguments_overload.d(17): Candidates are: `named_arguments_overload.snoopy(S s, int i = 0, T t = T())` fail_compilation/named_arguments_overload.d(18): `named_arguments_overload.snoopy(T t, int i, S s)` -fail_compilation/named_arguments_overload.d(35): Error: `named_arguments_overload.snoopy` called with argument types `(immutable(S), immutable(T), immutable(int))` matches both: +fail_compilation/named_arguments_overload.d(35): Error: `named_arguments_overload.snoopy` called with argument types `(immutable(S), immutable(T), immutable(int))` matches multiple overloads after qualifier conversion: fail_compilation/named_arguments_overload.d(17): `named_arguments_overload.snoopy(S s, int i = 0, T t = T())` and: fail_compilation/named_arguments_overload.d(18): `named_arguments_overload.snoopy(T t, int i, S s)` diff --git a/tests/dmd/fail_compilation/needspkgmod.d b/tests/dmd/fail_compilation/needspkgmod.d index c8c5d59fc1..79679cd950 100644 --- a/tests/dmd/fail_compilation/needspkgmod.d +++ b/tests/dmd/fail_compilation/needspkgmod.d @@ -4,7 +4,7 @@ // ARG_SETS: -i=,imports.pkgmod313 // ARG_SETS: -i=imports.pkgmod313,-imports.pkgmod313.mod // ARG_SETS: -i=imports.pkgmod313.package,-imports.pkgmod313.mod -// REQUIRED_ARGS: -Icompilable +// REQUIRED_ARGS: -Icompilable -L--no-demangle // LINK: /* TEST_OUTPUT: diff --git a/tests/dmd/fail_compilation/nested_template_constraint.d b/tests/dmd/fail_compilation/nested_template_constraint.d new file mode 100644 index 0000000000..9a89c0ae86 --- /dev/null +++ b/tests/dmd/fail_compilation/nested_template_constraint.d @@ -0,0 +1,18 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/nested_template_constraint.d(17): Error: template `foo` is not callable using argument types `!()(string, int)` +fail_compilation/nested_template_constraint.d(10): Candidate is: `foo(int x = 0)` +fail_compilation/nested_template_constraint.d(11): - Containing: `foo(T, U)(T t, U u)` +--- +*/ + +template foo(int x = 0) { + void foo(T, U)(T t, U u) + if (is(T == int) && is(U == int)) {} +} + +void main() +{ + foo("hello", 4); +} diff --git a/tests/dmd/fail_compilation/nogc1.d b/tests/dmd/fail_compilation/nogc1.d index 859bd40ab0..ba6956e1bd 100644 --- a/tests/dmd/fail_compilation/nogc1.d +++ b/tests/dmd/fail_compilation/nogc1.d @@ -9,13 +9,13 @@ struct S3 { this(int) @nogc; } /* TEST_OUTPUT: --- -fail_compilation/nogc1.d(23): Error: cannot use `new` in `@nogc` function `nogc1.testNew` -fail_compilation/nogc1.d(25): Error: cannot use `new` in `@nogc` function `nogc1.testNew` -fail_compilation/nogc1.d(26): Error: cannot use `new` in `@nogc` function `nogc1.testNew` -fail_compilation/nogc1.d(28): Error: cannot use `new` in `@nogc` function `nogc1.testNew` +fail_compilation/nogc1.d(23): Error: allocating with `new` causes a GC allocation in `@nogc` function `testNew` +fail_compilation/nogc1.d(25): Error: allocating with `new` causes a GC allocation in `@nogc` function `testNew` +fail_compilation/nogc1.d(26): Error: allocating with `new` causes a GC allocation in `@nogc` function `testNew` +fail_compilation/nogc1.d(28): Error: allocating with `new` causes a GC allocation in `@nogc` function `testNew` fail_compilation/nogc1.d(29): Error: `@nogc` function `nogc1.testNew` cannot call non-@nogc constructor `nogc1.S2.this` -fail_compilation/nogc1.d(30): Error: cannot use `new` in `@nogc` function `nogc1.testNew` -fail_compilation/nogc1.d(32): Error: cannot use `new` in `@nogc` function `nogc1.testNew` +fail_compilation/nogc1.d(30): Error: allocating with `new` causes a GC allocation in `@nogc` function `testNew` +fail_compilation/nogc1.d(32): Error: allocating with `new` causes a GC allocation in `@nogc` function `testNew` --- */ @nogc void testNew() @@ -35,12 +35,12 @@ fail_compilation/nogc1.d(32): Error: cannot use `new` in `@nogc` function `nogc1 /* TEST_OUTPUT: --- -fail_compilation/nogc1.d(48): Error: cannot use `new` in `@nogc` function `nogc1.testNewScope` -fail_compilation/nogc1.d(50): Error: cannot use `new` in `@nogc` function `nogc1.testNewScope` -fail_compilation/nogc1.d(51): Error: cannot use `new` in `@nogc` function `nogc1.testNewScope` -fail_compilation/nogc1.d(53): Error: cannot use `new` in `@nogc` function `nogc1.testNewScope` +fail_compilation/nogc1.d(48): Error: allocating with `new` causes a GC allocation in `@nogc` function `testNewScope` +fail_compilation/nogc1.d(50): Error: allocating with `new` causes a GC allocation in `@nogc` function `testNewScope` +fail_compilation/nogc1.d(51): Error: allocating with `new` causes a GC allocation in `@nogc` function `testNewScope` +fail_compilation/nogc1.d(53): Error: allocating with `new` causes a GC allocation in `@nogc` function `testNewScope` fail_compilation/nogc1.d(54): Error: `@nogc` function `nogc1.testNewScope` cannot call non-@nogc constructor `nogc1.S2.this` -fail_compilation/nogc1.d(55): Error: cannot use `new` in `@nogc` function `nogc1.testNewScope` +fail_compilation/nogc1.d(55): Error: allocating with `new` causes a GC allocation in `@nogc` function `testNewScope` --- */ @nogc void testNewScope() @@ -57,23 +57,3 @@ fail_compilation/nogc1.d(55): Error: cannot use `new` in `@nogc` function `nogc1 scope Object o1 = new Object(); // no error scope o2 = new Object(); // no error } - -/***************** DeleteExp *******************/ - -/* -TEST_OUTPUT: ---- -fail_compilation/nogc1.d(76): Error: the `delete` keyword is obsolete -fail_compilation/nogc1.d(76): use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead -fail_compilation/nogc1.d(77): Error: the `delete` keyword is obsolete -fail_compilation/nogc1.d(77): use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead -fail_compilation/nogc1.d(78): Error: the `delete` keyword is obsolete -fail_compilation/nogc1.d(78): use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead ---- -*/ -@nogc void testDelete(int* p, Object o, S1* s) -{ - delete p; - delete o; - delete s; -} diff --git a/tests/dmd/fail_compilation/nogc2.d b/tests/dmd/fail_compilation/nogc2.d index 9016dd9e96..9601c2faa2 100644 --- a/tests/dmd/fail_compilation/nogc2.d +++ b/tests/dmd/fail_compilation/nogc2.d @@ -5,14 +5,14 @@ /* TEST_OUTPUT: --- -fail_compilation/nogc2.d(20): Error: cannot use operator `~` in `@nogc` function `nogc2.testCat` -fail_compilation/nogc2.d(21): Error: cannot use operator `~` in `@nogc` function `nogc2.testCat` -fail_compilation/nogc2.d(22): Error: cannot use operator `~` in `@nogc` function `nogc2.testCat` -fail_compilation/nogc2.d(24): Error: cannot use operator `~` in `@nogc` function `nogc2.testCat` -fail_compilation/nogc2.d(25): Error: cannot use operator `~` in `@nogc` function `nogc2.testCat` -fail_compilation/nogc2.d(26): Error: cannot use operator `~` in `@nogc` function `nogc2.testCat` -fail_compilation/nogc2.d(27): Error: cannot use operator `~` in `@nogc` function `nogc2.testCat` -fail_compilation/nogc2.d(28): Error: cannot use operator `~` in `@nogc` function `nogc2.testCat` +fail_compilation/nogc2.d(20): Error: concatenating with operator `~` causes a GC allocation in `@nogc` function `testCat` +fail_compilation/nogc2.d(21): Error: concatenating with operator `~` causes a GC allocation in `@nogc` function `testCat` +fail_compilation/nogc2.d(22): Error: concatenating with operator `~` causes a GC allocation in `@nogc` function `testCat` +fail_compilation/nogc2.d(24): Error: concatenating with operator `~` causes a GC allocation in `@nogc` function `testCat` +fail_compilation/nogc2.d(25): Error: concatenating with operator `~` causes a GC allocation in `@nogc` function `testCat` +fail_compilation/nogc2.d(26): Error: concatenating with operator `~` causes a GC allocation in `@nogc` function `testCat` +fail_compilation/nogc2.d(27): Error: concatenating with operator `~` causes a GC allocation in `@nogc` function `testCat` +fail_compilation/nogc2.d(28): Error: concatenating with operator `~` causes a GC allocation in `@nogc` function `testCat` --- */ @nogc void testCat(int[] a, string s) @@ -37,9 +37,9 @@ fail_compilation/nogc2.d(28): Error: cannot use operator `~` in `@nogc` function /* TEST_OUTPUT: --- -fail_compilation/nogc2.d(47): Error: cannot use operator `~=` in `@nogc` function `nogc2.testCatAssign` -fail_compilation/nogc2.d(49): Error: cannot use operator `~=` in `@nogc` function `nogc2.testCatAssign` -fail_compilation/nogc2.d(50): Error: cannot use operator `~=` in `@nogc` function `nogc2.testCatAssign` +fail_compilation/nogc2.d(47): Error: appending to this array with operator `~=` causes a GC allocation in `@nogc` function `testCatAssign` +fail_compilation/nogc2.d(49): Error: appending to this array with operator `~=` causes a GC allocation in `@nogc` function `testCatAssign` +fail_compilation/nogc2.d(50): Error: appending to this array with operator `~=` causes a GC allocation in `@nogc` function `testCatAssign` --- */ @nogc void testCatAssign(int[] a, string s) @@ -57,8 +57,8 @@ fail_compilation/nogc2.d(50): Error: cannot use operator `~=` in `@nogc` functio /* TEST_OUTPUT: --- -fail_compilation/nogc2.d(69): Error: array literal in `@nogc` function `nogc2.testArray` may cause a GC allocation -fail_compilation/nogc2.d(70): Error: array literal in `@nogc` function `nogc2.testArray` may cause a GC allocation +fail_compilation/nogc2.d(69): Error: this array literal causes a GC allocation in `@nogc` function `testArray` +fail_compilation/nogc2.d(70): Error: this array literal causes a GC allocation in `@nogc` function `testArray` --- */ @nogc void testArray() @@ -75,8 +75,8 @@ fail_compilation/nogc2.d(70): Error: array literal in `@nogc` function `nogc2.te /* TEST_OUTPUT: --- -fail_compilation/nogc2.d(86): Error: associative array literal in `@nogc` function `nogc2.testAssocArray` may cause a GC allocation -fail_compilation/nogc2.d(87): Error: associative array literal in `@nogc` function `nogc2.testAssocArray` may cause a GC allocation +fail_compilation/nogc2.d(86): Error: this associative array literal causes a GC allocation in `@nogc` function `testAssocArray` +fail_compilation/nogc2.d(87): Error: this associative array literal causes a GC allocation in `@nogc` function `testAssocArray` --- */ @nogc void testAssocArray() @@ -92,7 +92,7 @@ fail_compilation/nogc2.d(87): Error: associative array literal in `@nogc` functi /* TEST_OUTPUT: --- -fail_compilation/nogc2.d(100): Error: assigning an associative array element in `@nogc` function `nogc2.testIndex` may cause a GC allocation +fail_compilation/nogc2.d(100): Error: assigning this associative array element causes a GC allocation in `@nogc` function `testIndex` --- */ @nogc void testIndex(int[int] aa) diff --git a/tests/dmd/fail_compilation/nogc3.d b/tests/dmd/fail_compilation/nogc3.d index b53903ffbc..17daa96358 100644 --- a/tests/dmd/fail_compilation/nogc3.d +++ b/tests/dmd/fail_compilation/nogc3.d @@ -5,9 +5,9 @@ /* TEST_OUTPUT: --- -fail_compilation/nogc3.d(15): Error: setting `length` in `@nogc` function `nogc3.testArrayLength` may cause a GC allocation -fail_compilation/nogc3.d(16): Error: setting `length` in `@nogc` function `nogc3.testArrayLength` may cause a GC allocation -fail_compilation/nogc3.d(17): Error: setting `length` in `@nogc` function `nogc3.testArrayLength` may cause a GC allocation +fail_compilation/nogc3.d(15): Error: setting this array's `length` causes a GC allocation in `@nogc` function `testArrayLength` +fail_compilation/nogc3.d(16): Error: setting this array's `length` causes a GC allocation in `@nogc` function `testArrayLength` +fail_compilation/nogc3.d(17): Error: setting this array's `length` causes a GC allocation in `@nogc` function `testArrayLength` --- */ @nogc void testArrayLength(int[] a) @@ -44,10 +44,10 @@ fail_compilation/nogc3.d(35): Error: `@nogc` function `nogc3.testCall` cannot ca TEST_OUTPUT: --- fail_compilation/nogc3.d(54): Error: function `nogc3.testClosure1` is `@nogc` yet allocates closure for `testClosure1()` with the GC -fail_compilation/nogc3.d(57): function `nogc3.testClosure1.bar` closes over variable `x` +fail_compilation/nogc3.d(57): function `bar` closes over variable `x` fail_compilation/nogc3.d(56): `x` declared here fail_compilation/nogc3.d(66): Error: function `nogc3.testClosure3` is `@nogc` yet allocates closure for `testClosure3()` with the GC -fail_compilation/nogc3.d(69): function `nogc3.testClosure3.bar` closes over variable `x` +fail_compilation/nogc3.d(69): function `bar` closes over variable `x` fail_compilation/nogc3.d(68): `x` declared here --- */ @@ -75,10 +75,10 @@ fail_compilation/nogc3.d(68): `x` declared here /* TEST_OUTPUT: --- -fail_compilation/nogc3.d(87): Error: array literal in `@nogc` function `nogc3.foo13702` may cause a GC allocation -fail_compilation/nogc3.d(88): Error: array literal in `@nogc` function `nogc3.foo13702` may cause a GC allocation -fail_compilation/nogc3.d(94): Error: array literal in `@nogc` function `nogc3.bar13702` may cause a GC allocation -fail_compilation/nogc3.d(93): Error: array literal in `@nogc` function `nogc3.bar13702` may cause a GC allocation +fail_compilation/nogc3.d(87): Error: this array literal causes a GC allocation in `@nogc` function `foo13702` +fail_compilation/nogc3.d(88): Error: this array literal causes a GC allocation in `@nogc` function `foo13702` +fail_compilation/nogc3.d(94): Error: this array literal causes a GC allocation in `@nogc` function `bar13702` +fail_compilation/nogc3.d(93): Error: this array literal causes a GC allocation in `@nogc` function `bar13702` --- */ int[] foo13702(bool b) @nogc diff --git a/tests/dmd/fail_compilation/noreturn2.d b/tests/dmd/fail_compilation/noreturn2.d index 66c1d5287f..ec6bd3ebce 100644 --- a/tests/dmd/fail_compilation/noreturn2.d +++ b/tests/dmd/fail_compilation/noreturn2.d @@ -55,7 +55,7 @@ NR returns() /+ TEST_OUTPUT: --- -fail_compilation/noreturn2.d(64): Error: cannot implicitly convert expression `1` of type `int` to `noreturn` +fail_compilation/noreturn2.d(64): Error: return value `1` of type `int` does not match return type `noreturn`, and cannot be implicitly converted --- +/ diff --git a/tests/dmd/fail_compilation/opapplyscope.d b/tests/dmd/fail_compilation/opapplyscope.d index 3eef0f9293..872a44796f 100644 --- a/tests/dmd/fail_compilation/opapplyscope.d +++ b/tests/dmd/fail_compilation/opapplyscope.d @@ -1,7 +1,7 @@ /* TEST_OUTPUT: --- fail_compilation/opapplyscope.d(113): Error: function `opapplyscope.S.opApply(scope int delegate(scope int* ptr) @safe dg)` is not callable using argument types `(int delegate(int* x) nothrow @nogc @safe)` -fail_compilation/opapplyscope.d(113): cannot pass argument `__foreachbody_L113_C5` of type `int delegate(int* x) nothrow @nogc @safe` to parameter `scope int delegate(scope int* ptr) @safe dg` +fail_compilation/opapplyscope.d(113): cannot pass argument `int(int* x) => 0` of type `int delegate(int* x) nothrow @nogc @safe` to parameter `scope int delegate(scope int* ptr) @safe dg` --- */ diff --git a/tests/dmd/fail_compilation/operator_undefined.d b/tests/dmd/fail_compilation/operator_undefined.d index 3065bdb8e7..1c9803d21e 100644 --- a/tests/dmd/fail_compilation/operator_undefined.d +++ b/tests/dmd/fail_compilation/operator_undefined.d @@ -1,7 +1,8 @@ /* TEST_OUTPUT: --- -fail_compilation/operator_undefined.d(19): Error: operator `-` is not defined for `toJson(2)` of type `Json` +fail_compilation/operator_undefined.d(20): Error: operator `-` is not defined for `Json` +fail_compilation/operator_undefined.d(11): perhaps overload the operator with `auto opUnary(string op : "-")() {}` --- */ diff --git a/tests/dmd/fail_compilation/parse14285.d b/tests/dmd/fail_compilation/parse14285.d index c9aa70aa79..3777cf6a95 100644 --- a/tests/dmd/fail_compilation/parse14285.d +++ b/tests/dmd/fail_compilation/parse14285.d @@ -1,7 +1,7 @@ /* TEST_OUTPUT: --- -fail_compilation/parse14285.d(10): Error: no identifier for declarator `this` +fail_compilation/parse14285.d(10): Error: variable name expected after type `this`, not `;` --- */ diff --git a/tests/dmd/fail_compilation/parseStc2.d b/tests/dmd/fail_compilation/parseStc2.d index 936769a59d..dbc98f91ca 100644 --- a/tests/dmd/fail_compilation/parseStc2.d +++ b/tests/dmd/fail_compilation/parseStc2.d @@ -53,10 +53,10 @@ public private void f10() {} /* TEST_OUTPUT: --- -fail_compilation/parseStc2.d(63): Error: redundant alignment attribute `align` +fail_compilation/parseStc2.d(63): Error: redundant alignment attribute `align(default)` fail_compilation/parseStc2.d(64): Error: redundant alignment attribute `align(1)` fail_compilation/parseStc2.d(65): Error: redundant alignment attribute `align(1)` -fail_compilation/parseStc2.d(66): Error: redundant alignment attribute `align` +fail_compilation/parseStc2.d(66): Error: redundant alignment attribute `align(default)` fail_compilation/parseStc2.d(67): Error: redundant alignment attribute `align(2)` --- */ diff --git a/tests/dmd/fail_compilation/placenew.d b/tests/dmd/fail_compilation/placenew.d new file mode 100644 index 0000000000..033d078e19 --- /dev/null +++ b/tests/dmd/fail_compilation/placenew.d @@ -0,0 +1,80 @@ +/* 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 +--- +*/ + +void test0() +{ + int i; + int* pi = new (i) int; +} + +void test1() +{ + int* pi = new (3) int; +} + +void test2() +{ + int* px = new (x) int; +} + +void test3() +{ + int xxx() + { + int i; + int* pi = new (i) int(1); + return 1; + } + static assert(xxx() == 1); +} + +struct S { int[6] a; } +struct T { int[10] a; } + +void test4() +{ + S s; + new (s) T(); +} + +void test5() +{ + T p; + auto aa = new(p) int[int*]; +} + +/*************************************************/ + +class C6 +{ + int i, j; +} + +int test6() +{ + int k; + C6 c = new(k) C6; + return c.j; +} + +/*************************************************/ + +@safe +void test7() +{ + int i; + int* p = new(i) int; +} + +/*************************************************/ diff --git a/tests/dmd/fail_compilation/previewin.d b/tests/dmd/fail_compilation/previewin.d index c3d11cbfd0..3ae5de0099 100644 --- a/tests/dmd/fail_compilation/previewin.d +++ b/tests/dmd/fail_compilation/previewin.d @@ -3,19 +3,19 @@ REQUIRED_ARGS: -preview=in -preview=dip1000 TEST_OUTPUT: ---- fail_compilation/previewin.d(4): Error: function `takeFunction` is not callable using argument types `(void function(real x) pure nothrow @nogc @safe)` -fail_compilation/previewin.d(4): cannot pass argument `__lambda_L4_C18` of type `void function(real x) pure nothrow @nogc @safe` to parameter `void function(in real) f` +fail_compilation/previewin.d(4): cannot pass argument `(real x) { }` of type `void function(real x) pure nothrow @nogc @safe` to parameter `void function(in real) f` fail_compilation/previewin.d(11): `previewin.takeFunction(void function(in real) f)` declared here fail_compilation/previewin.d(5): Error: function `takeFunction` is not callable using argument types `(void function(scope const(real) x) pure nothrow @nogc @safe)` -fail_compilation/previewin.d(5): cannot pass argument `__lambda_L5_C18` of type `void function(scope const(real) x) pure nothrow @nogc @safe` to parameter `void function(in real) f` +fail_compilation/previewin.d(5): cannot pass argument `(scope const(real) x) { }` of type `void function(scope const(real) x) pure nothrow @nogc @safe` to parameter `void function(in real) f` fail_compilation/previewin.d(11): `previewin.takeFunction(void function(in real) f)` declared here fail_compilation/previewin.d(6): Error: function `takeFunction` is not callable using argument types `(void function(ref scope const(real) x) pure nothrow @nogc @safe)` -fail_compilation/previewin.d(6): cannot pass argument `__lambda_L6_C18` of type `void function(ref scope const(real) x) pure nothrow @nogc @safe` to parameter `void function(in real) f` +fail_compilation/previewin.d(6): cannot pass argument `(ref scope const(real) x) { }` of type `void function(ref scope const(real) x) pure nothrow @nogc @safe` to parameter `void function(in real) f` fail_compilation/previewin.d(11): `previewin.takeFunction(void function(in real) f)` declared here -fail_compilation/previewin.d(15): Error: scope variable `arg` assigned to global variable `myGlobal` -fail_compilation/previewin.d(16): Error: scope variable `arg` assigned to global variable `myGlobal` +fail_compilation/previewin.d(15): Error: assigning scope variable `arg` to global variable `myGlobal` is not allowed in a `@safe` function +fail_compilation/previewin.d(16): Error: assigning scope variable `arg` to global variable `myGlobal` is not allowed in a `@safe` function fail_compilation/previewin.d(17): Error: scope parameter `arg` may not be returned -fail_compilation/previewin.d(18): Error: scope variable `arg` assigned to `ref` variable `escape` with longer lifetime -fail_compilation/previewin.d(22): Error: returning `arg` escapes a reference to parameter `arg` +fail_compilation/previewin.d(18): Error: assigning scope variable `arg` to `ref` variable `escape` with longer lifetime is not allowed in a `@safe` function +fail_compilation/previewin.d(22): Error: escaping a reference to parameter `arg` by returning `arg` is not allowed in a `@safe` function fail_compilation/previewin.d(22): perhaps annotate the parameter with `return` ---- */ diff --git a/tests/dmd/fail_compilation/pull12941.d b/tests/dmd/fail_compilation/pull12941.d index ca78050e6a..4d14993ef5 100644 --- a/tests/dmd/fail_compilation/pull12941.d +++ b/tests/dmd/fail_compilation/pull12941.d @@ -1,6 +1,6 @@ /* TEST_OUTPUT: --- -fail_compilation/pull12941.d(110): Error: `pull12941.foo` called with argument types `(int*)` matches both: +fail_compilation/pull12941.d(110): Error: `pull12941.foo` called with argument types `(int*)` matches multiple overloads exactly: fail_compilation/pull12941.d(101): `pull12941.foo(return ref scope int* p)` and: fail_compilation/pull12941.d(102): `pull12941.foo(return out scope int* p)` diff --git a/tests/dmd/fail_compilation/reserved_version.d b/tests/dmd/fail_compilation/reserved_version.d index 502ae4a5bc..2432d1b30e 100644 --- a/tests/dmd/fail_compilation/reserved_version.d +++ b/tests/dmd/fail_compilation/reserved_version.d @@ -80,49 +80,50 @@ fail_compilation/reserved_version.d(179): Error: version identifier `LoongArch32 fail_compilation/reserved_version.d(180): Error: version identifier `LoongArch64` is reserved and cannot be set fail_compilation/reserved_version.d(181): Error: version identifier `LoongArch_HardFloat` is reserved and cannot be set fail_compilation/reserved_version.d(182): Error: version identifier `LoongArch_SoftFloat` is reserved and cannot be set -fail_compilation/reserved_version.d(183): Error: version identifier `LittleEndian` is reserved and cannot be set -fail_compilation/reserved_version.d(184): Error: version identifier `BigEndian` is reserved and cannot be set -fail_compilation/reserved_version.d(185): Error: version identifier `ELFv1` is reserved and cannot be set -fail_compilation/reserved_version.d(186): Error: version identifier `ELFv2` is reserved and cannot be set -fail_compilation/reserved_version.d(187): Error: version identifier `CRuntime_Bionic` is reserved and cannot be set -fail_compilation/reserved_version.d(188): Error: version identifier `CRuntime_DigitalMars` is reserved and cannot be set -fail_compilation/reserved_version.d(189): Error: version identifier `CRuntime_Glibc` is reserved and cannot be set -fail_compilation/reserved_version.d(190): Error: version identifier `CRuntime_Microsoft` is reserved and cannot be set -fail_compilation/reserved_version.d(191): Error: version identifier `CRuntime_Musl` is reserved and cannot be set -fail_compilation/reserved_version.d(192): Error: version identifier `CRuntime_Newlib` is reserved and cannot be set -fail_compilation/reserved_version.d(193): Error: version identifier `CRuntime_UClibc` is reserved and cannot be set -fail_compilation/reserved_version.d(194): Error: version identifier `CRuntime_WASI` is reserved and cannot be set -fail_compilation/reserved_version.d(195): Error: version identifier `D_Coverage` is reserved and cannot be set -fail_compilation/reserved_version.d(196): Error: version identifier `D_Ddoc` is reserved and cannot be set -fail_compilation/reserved_version.d(197): Error: version identifier `D_InlineAsm_X86` is reserved and cannot be set -fail_compilation/reserved_version.d(198): Error: version identifier `D_InlineAsm_X86_64` is reserved and cannot be set -fail_compilation/reserved_version.d(199): Error: version identifier `D_LP64` is reserved and cannot be set -fail_compilation/reserved_version.d(200): Error: version identifier `D_X32` is reserved and cannot be set -fail_compilation/reserved_version.d(201): Error: version identifier `D_HardFloat` is reserved and cannot be set -fail_compilation/reserved_version.d(202): Error: version identifier `D_SoftFloat` is reserved and cannot be set -fail_compilation/reserved_version.d(203): Error: version identifier `D_PIC` is reserved and cannot be set -fail_compilation/reserved_version.d(204): Error: version identifier `D_SIMD` is reserved and cannot be set -fail_compilation/reserved_version.d(205): Error: version identifier `D_Version2` is reserved and cannot be set -fail_compilation/reserved_version.d(206): Error: version identifier `D_NoBoundsChecks` is reserved and cannot be set -fail_compilation/reserved_version.d(209): Error: version identifier `all` is reserved and cannot be set -fail_compilation/reserved_version.d(210): Error: version identifier `none` is reserved and cannot be set -fail_compilation/reserved_version.d(211): Error: version identifier `AsmJS` is reserved and cannot be set -fail_compilation/reserved_version.d(212): Error: version identifier `Emscripten` is reserved and cannot be set -fail_compilation/reserved_version.d(213): Error: version identifier `WebAssembly` is reserved and cannot be set -fail_compilation/reserved_version.d(214): Error: version identifier `WASI` is reserved and cannot be set -fail_compilation/reserved_version.d(215): Error: version identifier `CppRuntime_LLVM` is reserved and cannot be set -fail_compilation/reserved_version.d(216): Error: version identifier `CppRuntime_DigitalMars` is reserved and cannot be set -fail_compilation/reserved_version.d(217): Error: version identifier `CppRuntime_GNU` is reserved and cannot be set -fail_compilation/reserved_version.d(218): Error: version identifier `CppRuntime_Microsoft` is reserved and cannot be set -fail_compilation/reserved_version.d(219): Error: version identifier `CppRuntime_Sun` is reserved and cannot be set -fail_compilation/reserved_version.d(220): Error: version identifier `D_PIE` is reserved and cannot be set -fail_compilation/reserved_version.d(221): Error: version identifier `AVR` is reserved and cannot be set -fail_compilation/reserved_version.d(222): Error: version identifier `D_PreConditions` is reserved and cannot be set -fail_compilation/reserved_version.d(223): Error: version identifier `D_PostConditions` is reserved and cannot be set -fail_compilation/reserved_version.d(224): Error: version identifier `D_ProfileGC` is reserved and cannot be set -fail_compilation/reserved_version.d(225): Error: version identifier `D_Invariants` is reserved and cannot be set -fail_compilation/reserved_version.d(226): Error: version identifier `D_Optimized` is reserved and cannot be set -fail_compilation/reserved_version.d(227): Error: version identifier `VisionOS` is reserved and cannot be set +fail_compilation/reserved_version.d(183): Error: version identifier `Xtensa` is reserved and cannot be set +fail_compilation/reserved_version.d(184): Error: version identifier `LittleEndian` is reserved and cannot be set +fail_compilation/reserved_version.d(185): Error: version identifier `BigEndian` is reserved and cannot be set +fail_compilation/reserved_version.d(186): Error: version identifier `ELFv1` is reserved and cannot be set +fail_compilation/reserved_version.d(187): Error: version identifier `ELFv2` is reserved and cannot be set +fail_compilation/reserved_version.d(188): Error: version identifier `CRuntime_Bionic` is reserved and cannot be set +fail_compilation/reserved_version.d(189): Error: version identifier `CRuntime_DigitalMars` is reserved and cannot be set +fail_compilation/reserved_version.d(190): Error: version identifier `CRuntime_Glibc` is reserved and cannot be set +fail_compilation/reserved_version.d(191): Error: version identifier `CRuntime_Microsoft` is reserved and cannot be set +fail_compilation/reserved_version.d(192): Error: version identifier `CRuntime_Musl` is reserved and cannot be set +fail_compilation/reserved_version.d(193): Error: version identifier `CRuntime_Newlib` is reserved and cannot be set +fail_compilation/reserved_version.d(194): Error: version identifier `CRuntime_UClibc` is reserved and cannot be set +fail_compilation/reserved_version.d(195): Error: version identifier `CRuntime_WASI` is reserved and cannot be set +fail_compilation/reserved_version.d(196): Error: version identifier `D_Coverage` is reserved and cannot be set +fail_compilation/reserved_version.d(197): Error: version identifier `D_Ddoc` is reserved and cannot be set +fail_compilation/reserved_version.d(198): Error: version identifier `D_InlineAsm_X86` is reserved and cannot be set +fail_compilation/reserved_version.d(199): Error: version identifier `D_InlineAsm_X86_64` is reserved and cannot be set +fail_compilation/reserved_version.d(200): Error: version identifier `D_LP64` is reserved and cannot be set +fail_compilation/reserved_version.d(201): Error: version identifier `D_X32` is reserved and cannot be set +fail_compilation/reserved_version.d(202): Error: version identifier `D_HardFloat` is reserved and cannot be set +fail_compilation/reserved_version.d(203): Error: version identifier `D_SoftFloat` is reserved and cannot be set +fail_compilation/reserved_version.d(204): Error: version identifier `D_PIC` is reserved and cannot be set +fail_compilation/reserved_version.d(205): Error: version identifier `D_SIMD` is reserved and cannot be set +fail_compilation/reserved_version.d(206): Error: version identifier `D_Version2` is reserved and cannot be set +fail_compilation/reserved_version.d(207): Error: version identifier `D_NoBoundsChecks` is reserved and cannot be set +fail_compilation/reserved_version.d(210): Error: version identifier `all` is reserved and cannot be set +fail_compilation/reserved_version.d(211): Error: version identifier `none` is reserved and cannot be set +fail_compilation/reserved_version.d(212): Error: version identifier `AsmJS` is reserved and cannot be set +fail_compilation/reserved_version.d(213): Error: version identifier `Emscripten` is reserved and cannot be set +fail_compilation/reserved_version.d(214): Error: version identifier `WebAssembly` is reserved and cannot be set +fail_compilation/reserved_version.d(215): Error: version identifier `WASI` is reserved and cannot be set +fail_compilation/reserved_version.d(216): Error: version identifier `CppRuntime_LLVM` is reserved and cannot be set +fail_compilation/reserved_version.d(217): Error: version identifier `CppRuntime_DigitalMars` is reserved and cannot be set +fail_compilation/reserved_version.d(218): Error: version identifier `CppRuntime_GNU` is reserved and cannot be set +fail_compilation/reserved_version.d(219): Error: version identifier `CppRuntime_Microsoft` is reserved and cannot be set +fail_compilation/reserved_version.d(220): Error: version identifier `CppRuntime_Sun` is reserved and cannot be set +fail_compilation/reserved_version.d(221): Error: version identifier `D_PIE` is reserved and cannot be set +fail_compilation/reserved_version.d(222): Error: version identifier `AVR` is reserved and cannot be set +fail_compilation/reserved_version.d(223): Error: version identifier `D_PreConditions` is reserved and cannot be set +fail_compilation/reserved_version.d(224): Error: version identifier `D_PostConditions` is reserved and cannot be set +fail_compilation/reserved_version.d(225): Error: version identifier `D_ProfileGC` is reserved and cannot be set +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 --- */ @@ -207,6 +208,7 @@ version = LoongArch32; version = LoongArch64; version = LoongArch_HardFloat; version = LoongArch_SoftFloat; +version = Xtensa; version = LittleEndian; version = BigEndian; version = ELFv1; @@ -327,6 +329,7 @@ debug = LoongArch32; debug = LoongArch64; debug = LoongArch_HardFloat; debug = LoongArch_SoftFloat; +debug = Xtensa; debug = LittleEndian; debug = BigEndian; debug = ELFv1; diff --git a/tests/dmd/fail_compilation/reserved_version_switch.d b/tests/dmd/fail_compilation/reserved_version_switch.d index 2b6bb5e535..f124a3bf80 100644 --- a/tests/dmd/fail_compilation/reserved_version_switch.d +++ b/tests/dmd/fail_compilation/reserved_version_switch.d @@ -74,6 +74,7 @@ // REQUIRED_ARGS: -version=LoongArch64 // REQUIRED_ARGS: -version=LoongArch_HardFloat // REQUIRED_ARGS: -version=LoongArch_SoftFloat +// REQUIRED_ARGS: -version=Xtensa // REQUIRED_ARGS: -version=LittleEndian // REQUIRED_ARGS: -version=BigEndian // REQUIRED_ARGS: -version=ELFv1 @@ -183,6 +184,7 @@ // REQUIRED_ARGS: -debug=LoongArch64 // REQUIRED_ARGS: -debug=LoongArch_HardFloat // REQUIRED_ARGS: -debug=LoongArch_SoftFloat +// REQUIRED_ARGS: -debug=Xtensa // REQUIRED_ARGS: -debug=LittleEndian // REQUIRED_ARGS: -debug=BigEndian // REQUIRED_ARGS: -debug=ELFv1 @@ -298,6 +300,7 @@ Error: version identifier `LoongArch32` is reserved and cannot be set Error: version identifier `LoongArch64` is reserved and cannot be set Error: version identifier `LoongArch_HardFloat` is reserved and cannot be set Error: version identifier `LoongArch_SoftFloat` is reserved and cannot be set +Error: version identifier `Xtensa` is reserved and cannot be set Error: version identifier `LittleEndian` is reserved and cannot be set Error: version identifier `BigEndian` is reserved and cannot be set Error: version identifier `ELFv1` is reserved and cannot be set diff --git a/tests/dmd/fail_compilation/retscope.d b/tests/dmd/fail_compilation/retscope.d index 39a0b4d1c0..39fa8ab57a 100644 --- a/tests/dmd/fail_compilation/retscope.d +++ b/tests/dmd/fail_compilation/retscope.d @@ -3,11 +3,11 @@ REQUIRED_ARGS: -preview=dip1000 TEST_OUTPUT: --- fail_compilation/retscope.d(22): Error: scope parameter `p` may not be returned -fail_compilation/retscope.d(32): Error: returning `b ? nested1(& i) : nested2(& j)` escapes a reference to local variable `j` -fail_compilation/retscope.d(45): Error: scope variable `p` assigned to global variable `q` -fail_compilation/retscope.d(47): Error: address of variable `i` assigned to `q` with longer lifetime -fail_compilation/retscope.d(48): Error: scope variable `a` assigned to global variable `b` -fail_compilation/retscope.d(49): Error: address of struct temporary returned by `(*fp2)()` assigned to longer lived variable `q` +fail_compilation/retscope.d(32): Error: escaping a reference to local variable `j` by returning `b ? nested1(& i) : nested2(& j)` is not allowed in a `@safe` function +fail_compilation/retscope.d(45): Error: assigning scope variable `p` to global variable `q` is not allowed in a `@safe` function +fail_compilation/retscope.d(47): Error: assigning address of variable `i` to `q` with longer lifetime is not allowed in a `@safe` function +fail_compilation/retscope.d(48): Error: assigning scope variable `a` to global variable `b` is not allowed in a `@safe` function +fail_compilation/retscope.d(49): Error: assigning address of expression temporary returned by `(*fp2)()` to `q` with longer lifetime is not allowed in a `@safe` function --- */ @@ -55,7 +55,7 @@ void test2(scope int* p, int[] a ...) @safe TEST_OUTPUT: --- fail_compilation/retscope.d(75): Error: function `retscope.HTTP.Impl.onReceive` is `@nogc` yet allocates closure for `onReceive()` with the GC -fail_compilation/retscope.d(77): delegate `retscope.HTTP.Impl.onReceive.__lambda_L77_C23` closes over variable `this` +fail_compilation/retscope.d(77): delegate `() => this.x` closes over variable `this` --- */ @@ -85,7 +85,6 @@ struct HTTP /* TEST_OUTPUT: --- -fail_compilation/retscope.d(96): Error: reference to local variable `sa` assigned to non-scope parameter `a` calling `bar8` --- */ // https://issues.dlang.org/show_bug.cgi?id=8838 @@ -107,7 +106,8 @@ int[] bar8(int[] a) @safe /* TEST_OUTPUT: --- -fail_compilation/retscope.d(123): Error: returning `foo9(cast(char[])tmp)` escapes a reference to local variable `tmp` +fail_compilation/retscope.d(95): Error: assigning reference to local variable `sa` to non-scope parameter `a` calling `bar8` is not allowed in a `@safe` function +fail_compilation/retscope.d(123): Error: escaping a reference to local variable `tmp` by returning `foo9(cast(char[])tmp)` is not allowed in a `@safe` function --- */ @@ -164,7 +164,7 @@ class C11 /* TEST_OUTPUT: --- -fail_compilation/retscope.d(177): Error: address of variable `i` assigned to `p` with longer lifetime +fail_compilation/retscope.d(177): Error: assigning address of variable `i` to `p` with longer lifetime is not allowed in a `@safe` function --- */ @@ -181,7 +181,7 @@ void foo11() @safe /* TEST_OUTPUT: --- -fail_compilation/retscope.d(197): Error: scope variable `e` may not be returned +fail_compilation/retscope.d(197): Error: returning scope variable `e` is not allowed in a `@safe` function --- */ @@ -201,7 +201,7 @@ void* escapeDg1(scope void* d) @safe /* TEST_OUTPUT: --- -fail_compilation/retscope.d(212): Error: scope variable `p` assigned to non-scope `e.e` +fail_compilation/retscope.d(212): Error: assigning scope variable `p` to non-scope `e.e` is not allowed in a `@safe` function --- */ struct Escaper3 { void* e; } @@ -234,10 +234,10 @@ void* funretscope(scope dg_t ptr) @safe /* TEST_OUTPUT: --- -fail_compilation/retscope.d(248): Error: cannot implicitly convert expression `__lambda_L248_C21` of type `void* delegate() pure nothrow @nogc @safe` to `void* delegate() scope @safe` -fail_compilation/retscope.d(248): Error: cannot implicitly convert expression `__lambda_L248_C21` of type `void* delegate() pure nothrow @nogc @safe` to `void* delegate() scope @safe` -fail_compilation/retscope.d(249): Error: cannot implicitly convert expression `__lambda_L249_C21` of type `void* delegate() pure nothrow @nogc @safe` to `void* delegate() scope @safe` -fail_compilation/retscope.d(249): Error: cannot implicitly convert expression `__lambda_L249_C21` 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(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` --- */ @@ -254,7 +254,7 @@ void escape4() @safe /* TEST_OUTPUT: --- -fail_compilation/retscope.d(266): Error: cannot take address of `scope` variable `p` since `scope` applies to first indirection only +fail_compilation/retscope.d(266): Error: taking address of `scope` variable `p` with pointers is not allowed in a `@safe` function --- */ @@ -271,7 +271,7 @@ void escape5() @safe /* TEST_OUTPUT: --- -fail_compilation/retscope.d(286): Error: returning `foo6(& b)` escapes a reference to local variable `b` +fail_compilation/retscope.d(286): Error: escaping a reference to local variable `b` by returning `foo6(& b)` is not allowed in a `@safe` function --- */ @@ -299,15 +299,31 @@ 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 +--- +*/ + int[3] escape8(scope int[] p) @safe { return p[0 .. 3]; } // should not error char*[3] escape9(scope char*[] p) @safe { return p[0 .. 3]; } +// https://issues.dlang.org/show_bug.cgi?id=24663 + int*[3] escape8b(scope int*[3] p) @safe { return p[]; } +ref int*[3] escape9b( int*[3] p) @safe { return p[]; } + +ref int[3] asStatic(return scope int[] p) @safe { return p[0 .. 3]; } +ref int[3] asStatic2( scope int[] p) @safe { return p[0 .. 3]; } + /***************************************************/ /* TEST_OUTPUT: --- -fail_compilation/retscope.d(319): Error: reference to local variable `i` assigned to non-scope `f` +fail_compilation/retscope.d(335): Error: assigning reference to local variable `i` to non-scope `f` is not allowed in a `@safe` function --- */ @@ -331,7 +347,7 @@ int* bar10( scope int** ptr ) @safe /* TEST_OUTPUT: --- -fail_compilation/retscope.d(342): Error: cannot take address of `scope` variable `aa` since `scope` applies to first indirection only +fail_compilation/retscope.d(358): Error: taking address of `scope` variable `aa` with pointers is not allowed in a `@safe` function --- */ @@ -383,7 +399,7 @@ struct Foo12 /* TEST_OUTPUT: --- -fail_compilation/retscope.d(1103): Error: scope variable `f` may not be returned +fail_compilation/retscope.d(1103): Error: returning scope variable `f` is not allowed in a `@safe` function --- */ @@ -403,7 +419,7 @@ class Foo13 /* TEST_OUTPUT: --- -fail_compilation/retscope.d(1205): Error: scope variable `f14` calling non-scope member function `Foo14.foo()` +fail_compilation/retscope.d(1205): Error: scope variable `f14` calling non-scope member function `Foo14.foo()` is not allowed in a `@safe` function --- */ @@ -426,7 +442,7 @@ struct Foo14 /* TEST_OUTPUT: --- -fail_compilation/retscope.d(1311): Error: scope variable `u2` assigned to `ek` with longer lifetime +fail_compilation/retscope.d(1311): Error: assigning scope variable `u2` to `ek` with longer lifetime is not allowed in a `@safe` function --- */ @@ -454,7 +470,7 @@ fail_compilation/retscope.d(1311): Error: scope variable `u2` assigned to `ek` w /* TEST_OUTPUT: --- -fail_compilation/retscope.d(1405): Error: reference to local variable `buf` assigned to non-scope anonymous parameter calling `myprintf` +fail_compilation/retscope.d(1405): Error: assigning reference to local variable `buf` to non-scope anonymous parameter calling `myprintf` is not allowed in a `@safe` function --- */ @@ -472,7 +488,7 @@ fail_compilation/retscope.d(1405): Error: reference to local variable `buf` assi /* TEST_OUTPUT: --- -fail_compilation/retscope.d(1509): Error: reference to stack allocated value returned by `(*fp15)()` assigned to non-scope anonymous parameter +fail_compilation/retscope.d(1509): Error: assigning reference to stack allocated value returned by `(*fp15)()` to non-scope anonymous parameter is not allowed in a `@safe` function --- */ @@ -662,8 +678,8 @@ int test21() /********************************************* TEST_OUTPUT: --- -fail_compilation/retscope.d(1907): Error: scope variable `x` assigned to `ref` variable `this` with longer lifetime -fail_compilation/retscope.d(1913): Error: scope variable `x` may not be returned +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 --- */ #line 1900 diff --git a/tests/dmd/fail_compilation/retscope2.d b/tests/dmd/fail_compilation/retscope2.d index 2e7940f70f..f663e4f4fb 100644 --- a/tests/dmd/fail_compilation/retscope2.d +++ b/tests/dmd/fail_compilation/retscope2.d @@ -2,8 +2,8 @@ REQUIRED_ARGS: -preview=dip1000 TEST_OUTPUT: --- -fail_compilation/retscope2.d(102): Error: scope variable `s` assigned to `ref` variable `p` with longer lifetime -fail_compilation/retscope2.d(107): Error: address of variable `s` assigned to `p` with longer lifetime +fail_compilation/retscope2.d(102): Error: assigning scope variable `s` to `ref` variable `p` with longer lifetime is not allowed in a `@safe` function +fail_compilation/retscope2.d(107): Error: assigning address of variable `s` to `p` with longer lifetime is not allowed in a `@safe` function --- */ @@ -36,7 +36,7 @@ void test200() /* TEST_OUTPUT: --- -fail_compilation/retscope2.d(302): Error: scope variable `a` assigned to return scope `b` +fail_compilation/retscope2.d(302): Error: assigning scope variable `a` to return scope `b` is not allowed in a `@safe` function --- */ @@ -52,7 +52,7 @@ fail_compilation/retscope2.d(302): Error: scope variable `a` assigned to return /* TEST_OUTPUT: --- -fail_compilation/retscope2.d(403): Error: scope variable `a` assigned to return scope `c` +fail_compilation/retscope2.d(403): Error: assigning scope variable `a` to return scope `c` is not allowed in a `@safe` function --- */ @@ -69,7 +69,7 @@ fail_compilation/retscope2.d(403): Error: scope variable `a` assigned to return /* TEST_OUTPUT: --- -fail_compilation/retscope2.d(504): Error: scope variable `c` may not be returned +fail_compilation/retscope2.d(504): Error: returning scope variable `c` is not allowed in a `@safe` function --- */ @@ -86,8 +86,8 @@ fail_compilation/retscope2.d(504): Error: scope variable `c` may not be returned /* TEST_OUTPUT: --- -fail_compilation/retscope2.d(604): Error: scope variable `__param_0` assigned to non-scope anonymous parameter calling `foo600` -fail_compilation/retscope2.d(604): Error: scope variable `__param_1` assigned to non-scope anonymous parameter calling `foo600` +fail_compilation/retscope2.d(604): Error: assigning scope variable `__param_0` to non-scope anonymous parameter calling `foo600` is not allowed in a `@safe` function +fail_compilation/retscope2.d(604): Error: assigning scope variable `__param_1` to non-scope anonymous parameter calling `foo600` is not allowed in a `@safe` function fail_compilation/retscope2.d(614): Error: template instance `retscope2.test600!(int*, int*)` error instantiating --- */ @@ -150,7 +150,7 @@ S700* escape700(int i) @safe /* TEST_OUTPUT: --- -fail_compilation/retscope2.d(804): Error: scope variable `e` may not be thrown +fail_compilation/retscope2.d(804): Error: throwing scope variable `e` is not allowed in a `@safe` function --- */ @@ -167,7 +167,7 @@ void foo800() @safe /* TEST_OUTPUT: --- -fail_compilation/retscope2.d(907): Error: address of variable `this` assigned to `p17568` with longer lifetime +fail_compilation/retscope2.d(907): Error: assigning address of variable `this` to `p17568` with longer lifetime is not allowed in a `@safe` function --- */ @@ -188,9 +188,9 @@ struct T17568 /* TEST_OUTPUT: --- -fail_compilation/retscope2.d(1005): Error: scope variable `p` assigned to non-scope `this._p` -fail_compilation/retscope2.d(1021): Error: scope variable `p` assigned to non-scope `c._p` -fail_compilation/retscope2.d(1024): Error: scope variable `p` assigned to non-scope `d._p` +fail_compilation/retscope2.d(1005): Error: assigning scope variable `p` to non-scope `this._p` is not allowed in a `@safe` function +fail_compilation/retscope2.d(1021): Error: assigning scope variable `p` to non-scope `c._p` is not allowed in a `@safe` function +fail_compilation/retscope2.d(1024): Error: assigning scope variable `p` to non-scope `d._p` is not allowed in a `@safe` function --- */ @@ -229,7 +229,7 @@ void test17428() @safe /* TEST_OUTPUT: --- -fail_compilation/retscope2.d(1107): Error: scope variable `dg` may not be returned +fail_compilation/retscope2.d(1107): Error: returning scope variable `dg` is not allowed in a `@safe` function --- */ @@ -296,7 +296,7 @@ struct T17388 /* TEST_OUTPUT: --- -fail_compilation/retscope2.d(1306): Error: copying `& i` into allocated memory escapes a reference to local variable `i` +fail_compilation/retscope2.d(1306): Error: escaping a reference to local variable `i` by copying `& i` into allocated memory is not allowed in a `@safe` function --- */ diff --git a/tests/dmd/fail_compilation/retscope3.d b/tests/dmd/fail_compilation/retscope3.d index 951ad599d5..438fdd76e1 100644 --- a/tests/dmd/fail_compilation/retscope3.d +++ b/tests/dmd/fail_compilation/retscope3.d @@ -5,8 +5,8 @@ REQUIRED_ARGS: -preview=dip1000 /* TEST_OUTPUT: --- -fail_compilation/retscope3.d(2008): Error: copying `& i` into allocated memory escapes a reference to local variable `i` -fail_compilation/retscope3.d(2017): Error: copying `S2000(& i)` into allocated memory escapes a reference to local variable `i` +fail_compilation/retscope3.d(2008): Error: escaping a reference to local variable `i` by copying `& i` into allocated memory is not allowed in a `@safe` function +fail_compilation/retscope3.d(2017): Error: escaping a reference to local variable `i` by copying `S2000(& i)` into allocated memory is not allowed in a `@safe` function --- */ @@ -53,9 +53,9 @@ void bar4() /* TEST_OUTPUT: --- -fail_compilation/retscope3.d(4003): Error: copying `u[]` into allocated memory escapes a reference to variadic parameter `u` +fail_compilation/retscope3.d(4003): Error: escaping a reference to parameter `u` by copying `u[]` into allocated memory is not allowed in a `@safe` function fail_compilation/retscope3.d(4016): Error: storing reference to outer local variable `i` into allocated memory causes it to escape -fail_compilation/retscope3.d(4025): Error: storing reference to stack allocated value returned by `makeSA()` into allocated memory causes it to escape +fail_compilation/retscope3.d(4025): Error: escaping reference to stack allocated value returned by `makeSA()` into allocated memory --- */ diff --git a/tests/dmd/fail_compilation/retscope5.d b/tests/dmd/fail_compilation/retscope5.d index a4c8d58ae3..fa7f23f618 100644 --- a/tests/dmd/fail_compilation/retscope5.d +++ b/tests/dmd/fail_compilation/retscope5.d @@ -5,7 +5,7 @@ REQUIRED_ARGS: -preview=dip1000 /* TEST_OUTPUT: --- -fail_compilation/retscope5.d(5010): Error: address of variable `t` assigned to `p` with longer lifetime +fail_compilation/retscope5.d(5010): Error: assigning address of variable `t` to `p` with longer lifetime is not allowed in a `@safe` function --- */ diff --git a/tests/dmd/fail_compilation/retscope6.d b/tests/dmd/fail_compilation/retscope6.d index ddeae81bc2..e3b734f03d 100644 --- a/tests/dmd/fail_compilation/retscope6.d +++ b/tests/dmd/fail_compilation/retscope6.d @@ -5,7 +5,7 @@ REQUIRED_ARGS: -preview=dip1000 /* TEST_OUTPUT: --- -fail_compilation/retscope6.d(6007): Error: copying `& i` into allocated memory escapes a reference to local variable `i` +fail_compilation/retscope6.d(6007): Error: escaping a reference to local variable `i` by copying `& i` into allocated memory is not allowed in a `@safe` function --- */ @@ -23,11 +23,11 @@ int* test() @safe /* TEST_OUTPUT: --- -fail_compilation/retscope6.d(7034): Error: address of variable `i` assigned to `s` with longer lifetime -fail_compilation/retscope6.d(7035): Error: address of variable `i` assigned to `s` with longer lifetime -fail_compilation/retscope6.d(7025): Error: scope variable `__param_2` assigned to `ref` variable `t` with longer lifetime +fail_compilation/retscope6.d(7034): Error: assigning address of variable `i` to `s` with longer lifetime is not allowed in a `@safe` function +fail_compilation/retscope6.d(7035): Error: assigning address of variable `i` to `s` with longer lifetime is not allowed in a `@safe` function +fail_compilation/retscope6.d(7025): Error: assigning scope variable `__param_2` to `ref` variable `t` with longer lifetime is not allowed in a `@safe` function fail_compilation/retscope6.d(7037): Error: template instance `retscope6.S.emplace4!(int*)` error instantiating -fail_compilation/retscope6.d(7037): Error: address of variable `i` assigned to `s` with longer lifetime +fail_compilation/retscope6.d(7037): Error: assigning address of variable `i` to `s` with longer lifetime is not allowed in a `@safe` function --- */ @@ -75,11 +75,14 @@ void foo() @safe /* TEST_OUTPUT: --- -fail_compilation/retscope6.d(8016): Error: address of variable `i` assigned to `p` with longer lifetime -fail_compilation/retscope6.d(8031): Error: reference to local variable `i` assigned to non-scope parameter `p` calling `betty` -fail_compilation/retscope6.d(8031): Error: reference to local variable `j` assigned to non-scope parameter `q` calling `betty` -fail_compilation/retscope6.d(8021): which is assigned to non-scope parameter `p` -fail_compilation/retscope6.d(8048): Error: reference to local variable `j` assigned to non-scope parameter `q` calling `archie` +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(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(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` --- */ @@ -109,8 +112,8 @@ void testfrankly() void betty()(int* p, int* q) { - p = q; - escape(p); + p = q; + escape(p); } void testbetty() @@ -124,9 +127,9 @@ void testbetty() void archie()(int* p, int* q, int* r) { - p = q; - r = p; - escape(q); + p = q; + r = p; + escape(q); } void testarchie() @@ -141,7 +144,7 @@ void testarchie() /* TEST_OUTPUT: --- -fail_compilation/retscope6.d(9023): Error: returning `fred(& i)` escapes a reference to local variable `i` +fail_compilation/retscope6.d(9023): Error: escaping a reference to local variable `i` by returning `fred(& i)` is not allowed in a `@safe` function --- */ @@ -174,7 +177,7 @@ T9 testfred() /* TEST_OUTPUT: --- -fail_compilation/retscope6.d(10003): Error: scope variable `values` assigned to non-scope parameter `values` calling `escape` +fail_compilation/retscope6.d(10003): Error: assigning scope variable `values` to non-scope parameter `values` calling `escape` is not allowed in a `@safe` function --- */ @@ -189,7 +192,7 @@ void escape(int[] values) {} /* TEST_OUTPUT: --- -fail_compilation/retscope6.d(11004): Error: address of variable `buffer` assigned to `secret` with longer lifetime +fail_compilation/retscope6.d(11004): Error: assigning address of variable `buffer` to `secret` with longer lifetime is not allowed in a `@safe` function --- */ @@ -204,8 +207,8 @@ void hmac(scope ubyte[] secret) /* TEST_OUTPUT: --- -fail_compilation/retscope6.d(12011): Error: returning `escape_m_20150(& x)` escapes a reference to local variable `x` -fail_compilation/retscope6.d(12022): Error: returning `escape_c_20150(& x)` escapes a reference to local variable `x` +fail_compilation/retscope6.d(12011): Error: escaping a reference to local variable `x` by returning `escape_m_20150(& x)` is not allowed in a `@safe` function +fail_compilation/retscope6.d(12022): Error: escaping a reference to local variable `x` by returning `escape_c_20150(& x)` is not allowed in a `@safe` function --- */ @@ -237,7 +240,7 @@ const(int)* f_c_20150() @safe nothrow /* TEST_OUTPUT: --- -fail_compilation/retscope6.d(13010): Error: reference to local variable `str` assigned to non-scope parameter `x` calling `f_throw` +fail_compilation/retscope6.d(13010): Error: assigning reference to local variable `str` to non-scope parameter `x` calling `f_throw` is not allowed in a `@safe` function --- */ @@ -257,9 +260,9 @@ void escape_throw_20150() @safe /* TEST_OUTPUT: --- -fail_compilation/retscope6.d(14019): Error: scope variable `scopePtr` assigned to non-scope parameter `x` calling `noInfer23021` +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(14022): Error: scope variable `scopePtr` may not be returned +fail_compilation/retscope6.d(14022): Error: returning scope variable `scopePtr` is not allowed in a `@safe` function --- */ @@ -293,3 +296,26 @@ 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` +--- +*/ + +// https://issues.dlang.org/show_bug.cgi?id=23294 + +@safe: +int g23294; + +auto f23294(int* x, int* y) +{ + x = y; + g23294++; // make sure it's not inferring scope from pure +} + +void escape23294(scope int* z) +{ + f23294(z, z); // passes +} diff --git a/tests/dmd/fail_compilation/safe_gshared.d b/tests/dmd/fail_compilation/safe_gshared.d index ea0775cf9a..c894af8d9b 100644 --- a/tests/dmd/fail_compilation/safe_gshared.d +++ b/tests/dmd/fail_compilation/safe_gshared.d @@ -1,8 +1,8 @@ /* TEST_OUTPUT: --- -fail_compilation/safe_gshared.d(13): Error: `@safe` function `f` cannot access `__gshared` data `x` -fail_compilation/safe_gshared.d(14): Error: `@safe` function `f` cannot access `__gshared` data `x` +fail_compilation/safe_gshared.d(13): Error: accessing `__gshared` data `x` is not allowed in a `@safe` function +fail_compilation/safe_gshared.d(14): Error: accessing `__gshared` data `x` is not allowed in a `@safe` function --- */ diff --git a/tests/dmd/fail_compilation/safe_pointer_index.d b/tests/dmd/fail_compilation/safe_pointer_index.d index 4b107dba81..1bf5c5bd4f 100644 --- a/tests/dmd/fail_compilation/safe_pointer_index.d +++ b/tests/dmd/fail_compilation/safe_pointer_index.d @@ -1,7 +1,7 @@ /* TEST_OUTPUT: --- -fail_compilation/safe_pointer_index.d(11): Error: `@safe` function `f` cannot index pointer `x` +fail_compilation/safe_pointer_index.d(11): Error: indexing pointer `x` is not allowed in a `@safe` function --- */ diff --git a/tests/dmd/fail_compilation/safer.d b/tests/dmd/fail_compilation/safer.d new file mode 100644 index 0000000000..bb814676f9 --- /dev/null +++ b/tests/dmd/fail_compilation/safer.d @@ -0,0 +1,18 @@ +/* REQUIRED_ARGS: -preview=safer +TEST_OUTPUT: +--- +fail_compilation/safer.d(10): Error: `void` initializing a pointer is not allowed in a function with default safety with `-preview=safer` +--- +*/ + +void test1() +{ + int* p = void; +} + +void foo3() { } + +void test2() +{ + foo3(); // should not be an error +} diff --git a/tests/dmd/fail_compilation/sarif_test.d b/tests/dmd/fail_compilation/sarif_test.d new file mode 100644 index 0000000000..a538401562 --- /dev/null +++ b/tests/dmd/fail_compilation/sarif_test.d @@ -0,0 +1,46 @@ +// REQUIRED_ARGS: -verror-style=sarif +/* +TEST_OUTPUT: +--- +{ + "version": "2.1.0", + "$schema": "https://schemastore.azurewebsites.net/schemas/json/sarif-2.1.0.json", + "runs": [{ + "tool": { + "driver": { + "name": "LDC", + "version": "$r:\d+\.\d+\.\d+$", + "informationUri": "https://dlang.org/dmd.html" + } + }, + "invocations": [{ + "executionSuccessful": false + }], + "results": [ + { + "ruleId": "DMD-error", + "message": { + "text": "undefined identifier `x`" + }, + "level": "error", + "locations": [{ + "physicalLocation": { + "artifactLocation": { + "uri": "fail_compilation/sarif_test.d" + }, + "region": { + "startLine": 45, + "startColumn": 5 + } + } + }] + } + ] + }] +} +--- +*/ + +void main() { + x = 5; // Undefined variable to trigger the error +} diff --git a/tests/dmd/fail_compilation/sarifmultiple_test.d b/tests/dmd/fail_compilation/sarifmultiple_test.d new file mode 100644 index 0000000000..b9f4ff2ccd --- /dev/null +++ b/tests/dmd/fail_compilation/sarifmultiple_test.d @@ -0,0 +1,65 @@ +// REQUIRED_ARGS: -verror-style=sarif +/* +TEST_OUTPUT: +--- +{ + "version": "2.1.0", + "$schema": "https://schemastore.azurewebsites.net/schemas/json/sarif-2.1.0.json", + "runs": [{ + "tool": { + "driver": { + "name": "LDC", + "version": "$r:\d+\.\d+\.\d+$", + "informationUri": "https://dlang.org/dmd.html" + } + }, + "invocations": [{ + "executionSuccessful": false + }], + "results": [ + { + "ruleId": "DMD-error", + "message": { + "text": "undefined identifier `x`" + }, + "level": "error", + "locations": [{ + "physicalLocation": { + "artifactLocation": { + "uri": "fail_compilation/sarifmultiple_test.d" + }, + "region": { + "startLine": 63, + "startColumn": 5 + } + } + }] + }, + { + "ruleId": "DMD-error", + "message": { + "text": "undefined identifier `y`" + }, + "level": "error", + "locations": [{ + "physicalLocation": { + "artifactLocation": { + "uri": "fail_compilation/sarifmultiple_test.d" + }, + "region": { + "startLine": 64, + "startColumn": 5 + } + } + }] + } + ] + }] +} +--- +*/ + +void main() { + x = 5; // Undefined variable to trigger the error + y = 5; // Undefined variable to trigger the error +} diff --git a/tests/dmd/fail_compilation/shared.d b/tests/dmd/fail_compilation/shared.d index 13b24c81cf..7ab1d7cd17 100644 --- a/tests/dmd/fail_compilation/shared.d +++ b/tests/dmd/fail_compilation/shared.d @@ -96,7 +96,7 @@ fail_compilation/shared.d(2216): return value `getSharedObject()` is not fail_compilation/shared.d(2222): Error: direct access to shared `a` is not allowed, see `core.atomic` fail_compilation/shared.d(2220): Error: function `shared.test_inference_4` function returns `shared` but cannot be inferred `ref` fail_compilation/shared.d(2222): cannot implicitly convert `a` of type `shared(const(Object))` to `object.Object` -fail_compilation/shared.d(2222): Error: cannot implicitly convert expression `a` of type `shared(const(Object))` to `object.Object` +fail_compilation/shared.d(2222): Error: return value `a` of type `shared(const(Object))` does not match return type `object.Object`, and cannot be implicitly converted --- */ @@ -240,13 +240,13 @@ struct BitRange /* TEST_OUTPUT: --- -fail_compilation/shared.d(3004): Error: cast from `void*` to `shared(int*)` not allowed in safe code +fail_compilation/shared.d(3004): Error: cast from `void*` to `shared(int*)` is not allowed in a `@safe` function fail_compilation/shared.d(3004): `void` data may contain pointers and target element type is mutable -fail_compilation/shared.d(3005): Error: cast from `void*` to `shared(const(int*))` not allowed in safe code +fail_compilation/shared.d(3005): Error: cast from `void*` to `shared(const(int*))` is not allowed in a `@safe` function fail_compilation/shared.d(3005): Source type is incompatible with target type containing a pointer -fail_compilation/shared.d(3008): Error: cast from `shared(void*)` to `int*` not allowed in safe code +fail_compilation/shared.d(3008): Error: cast from `shared(void*)` to `int*` is not allowed in a `@safe` function fail_compilation/shared.d(3008): `void` data may contain pointers and target element type is mutable -fail_compilation/shared.d(3009): Error: cast from `shared(void*)` to `shared(const(int*))` not allowed in safe code +fail_compilation/shared.d(3009): Error: cast from `shared(void*)` to `shared(const(int*))` is not allowed in a `@safe` function fail_compilation/shared.d(3009): Source type is incompatible with target type containing a pointer --- */ diff --git a/tests/dmd/fail_compilation/short_fn.d b/tests/dmd/fail_compilation/short_fn.d new file mode 100644 index 0000000000..04f3e3fffb --- /dev/null +++ b/tests/dmd/fail_compilation/short_fn.d @@ -0,0 +1,15 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/short_fn.d(13): Error: can only return void expression, `this` call or `super` call from constructor +fail_compilation/short_fn.d(14): Error: can only return void expression, `this` call or `super` call from constructor +--- +*/ + +struct Number +{ + int x; + + this(int x) => this.x = x; + this(byte x) => Number(); +} diff --git a/tests/dmd/fail_compilation/staticassert_sema1.d b/tests/dmd/fail_compilation/staticassert_sema1.d new file mode 100644 index 0000000000..e64c437de3 --- /dev/null +++ b/tests/dmd/fail_compilation/staticassert_sema1.d @@ -0,0 +1,39 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/staticassert_sema1.d(17): Error: static assert: "unsupported OS" +--- +*/ + +// https://issues.dlang.org/show_bug.cgi?id=24645 +// Test that a static assert(0) is not drowned out by subsequent import errors. + +version(_NONEXISTENT_OS) +{ + +} +else +{ + static assert(0, msg); +} + +import object: _NONEXISTENT; +import object: _NONEXISTENT; +import object: _NONEXISTENT; +import object: _NONEXISTENT; +import object: _NONEXISTENT; +import object: _NONEXISTENT; +import object: _NONEXISTENT; +import object: _NONEXISTENT; +import object: _NONEXISTENT; +import object: _NONEXISTENT; +import object: _NONEXISTENT; +import object: _NONEXISTENT; +import object: _NONEXISTENT; +import object: _NONEXISTENT; +import object: _NONEXISTENT; +import object: _NONEXISTENT; +import object: _NONEXISTENT; +import object: _NONEXISTENT; + +enum msg = "unsupported OS"; diff --git a/tests/dmd/fail_compilation/system_ptr_cast.d b/tests/dmd/fail_compilation/system_ptr_cast.d index fc10b2a780..ce6541b97b 100644 --- a/tests/dmd/fail_compilation/system_ptr_cast.d +++ b/tests/dmd/fail_compilation/system_ptr_cast.d @@ -2,9 +2,9 @@ REQUIRED_ARGS: -preview=dip1000 -de TEST_OUTPUT: --- -fail_compilation/system_ptr_cast.d(20): Deprecation: cast from `S*` to `int*` not allowed in safe code +fail_compilation/system_ptr_cast.d(20): Deprecation: cast from `S*` to `int*` will become `@system` in a future release fail_compilation/system_ptr_cast.d(20): Source element type has unsafe bit patterns and target element type is mutable -fail_compilation/system_ptr_cast.d(24): Deprecation: cast from `int*` to `S*` not allowed in safe code +fail_compilation/system_ptr_cast.d(24): Deprecation: cast from `int*` to `S*` will become `@system` in a future release fail_compilation/system_ptr_cast.d(24): Target element type has unsafe bit patterns --- */ diff --git a/tests/dmd/fail_compilation/systemvariables.d b/tests/dmd/fail_compilation/systemvariables.d index 796eda6093..098b810efc 100644 --- a/tests/dmd/fail_compilation/systemvariables.d +++ b/tests/dmd/fail_compilation/systemvariables.d @@ -2,23 +2,23 @@ REQUIRED_ARGS: -preview=systemVariables TEST_OUTPUT: --- -fail_compilation/systemvariables.d(39): Error: cannot access `@system` variable `gInt` in @safe code +fail_compilation/systemvariables.d(39): Error: access `@system` variable `gInt` is not allowed in a `@safe` function fail_compilation/systemvariables.d(29): `gInt` is declared here -fail_compilation/systemvariables.d(40): Error: cannot access `@system` variable `gInt` in @safe code +fail_compilation/systemvariables.d(40): Error: access `@system` variable `gInt` is not allowed in a `@safe` function fail_compilation/systemvariables.d(29): `gInt` is declared here -fail_compilation/systemvariables.d(41): Error: cannot access `@system` variable `gArr` in @safe code +fail_compilation/systemvariables.d(41): Error: access `@system` variable `gArr` is not allowed in a `@safe` function fail_compilation/systemvariables.d(31): `gArr` is declared here -fail_compilation/systemvariables.d(42): Error: cannot access `@system` variable `gArr` in @safe code +fail_compilation/systemvariables.d(42): Error: access `@system` variable `gArr` is not allowed in a `@safe` function fail_compilation/systemvariables.d(31): `gArr` is declared here -fail_compilation/systemvariables.d(43): Error: cannot access `@system` variable `gInt` in @safe code +fail_compilation/systemvariables.d(43): Error: access `@system` variable `gInt` is not allowed in a `@safe` function fail_compilation/systemvariables.d(29): `gInt` is declared here -fail_compilation/systemvariables.d(46): Error: cannot access `@system` variable `lSys` in @safe code +fail_compilation/systemvariables.d(46): Error: access `@system` variable `lSys` is not allowed in a `@safe` function fail_compilation/systemvariables.d(45): `lSys` is declared here -fail_compilation/systemvariables.d(47): Error: cannot access `@system` variable `lSys` in @safe code +fail_compilation/systemvariables.d(47): Error: access `@system` variable `lSys` is not allowed in a `@safe` function fail_compilation/systemvariables.d(45): `lSys` is declared here -fail_compilation/systemvariables.d(48): Error: cannot access `@system` variable `lSys` in @safe code +fail_compilation/systemvariables.d(48): Error: access `@system` variable `lSys` is not allowed in a `@safe` function fail_compilation/systemvariables.d(45): `lSys` is declared here -fail_compilation/systemvariables.d(50): Error: cannot access `@system` variable `eInt` in @safe code +fail_compilation/systemvariables.d(50): Error: access `@system` variable `eInt` is not allowed in a `@safe` function fail_compilation/systemvariables.d(30): `eInt` is declared here --- */ diff --git a/tests/dmd/fail_compilation/systemvariables_bool_union.d b/tests/dmd/fail_compilation/systemvariables_bool_union.d index ca6e620a51..2ca61e4f7e 100644 --- a/tests/dmd/fail_compilation/systemvariables_bool_union.d +++ b/tests/dmd/fail_compilation/systemvariables_bool_union.d @@ -2,7 +2,7 @@ REQUIRED_ARGS: -de TEST_OUTPUT: --- -fail_compilation/systemvariables_bool_union.d(21): Deprecation: cannot access overlapped field `Box.b` with unsafe bit patterns in `@safe` code +fail_compilation/systemvariables_bool_union.d(21): Deprecation: accessing overlapped field `Box.b` with unsafe bit patterns will become `@system` in a future release --- */ diff --git a/tests/dmd/fail_compilation/systemvariables_deprecation.d b/tests/dmd/fail_compilation/systemvariables_deprecation.d index b5115351ef..9dd0716884 100644 --- a/tests/dmd/fail_compilation/systemvariables_deprecation.d +++ b/tests/dmd/fail_compilation/systemvariables_deprecation.d @@ -2,10 +2,9 @@ REQUIRED_ARGS: -de TEST_OUTPUT: --- -fail_compilation/systemvariables_deprecation.d(16): Deprecation: `@safe` function `main` calling `middle` -fail_compilation/systemvariables_deprecation.d(21): which calls `systemvariables_deprecation.inferred` -fail_compilation/systemvariables_deprecation.d(27): which wouldn't be `@safe` because of: -fail_compilation/systemvariables_deprecation.d(27): cannot access `@system` variable `x0` in @safe code +fail_compilation/systemvariables_deprecation.d(15): Deprecation: `@safe` function `main` calling `middle` +fail_compilation/systemvariables_deprecation.d(20): which calls `inferred` +fail_compilation/systemvariables_deprecation.d(26): and access `@system` variable `x0` makes it fail to infer `@safe` --- */ diff --git a/tests/dmd/fail_compilation/systemvariables_struct.d b/tests/dmd/fail_compilation/systemvariables_struct.d index 7d8bfabeb7..2fe6816888 100644 --- a/tests/dmd/fail_compilation/systemvariables_struct.d +++ b/tests/dmd/fail_compilation/systemvariables_struct.d @@ -2,16 +2,16 @@ REQUIRED_ARGS: -preview=systemVariables TEST_OUTPUT: --- -fail_compilation/systemvariables_struct.d(31): Error: cannot access `@system` field `S.syst` in `@safe` code -fail_compilation/systemvariables_struct.d(32): Error: cannot access `@system` field `S.syst` in `@safe` code -fail_compilation/systemvariables_struct.d(33): Error: cannot access `@system` field `S.syst` in `@safe` code -fail_compilation/systemvariables_struct.d(36): Error: cannot access `@system` field `S.syst` in `@safe` code -fail_compilation/systemvariables_struct.d(37): Error: cannot access `@system` field `S.syst` in `@safe` code -fail_compilation/systemvariables_struct.d(38): Error: cannot access `@system` field `S.syst` in `@safe` code -fail_compilation/systemvariables_struct.d(54): Error: cannot access `@system` field `S2.syst` in `@safe` code -fail_compilation/systemvariables_struct.d(55): Error: cannot access `@system` field `S2.syst` in `@safe` code -fail_compilation/systemvariables_struct.d(56): Error: cannot access `@system` field `S.syst` in `@safe` code -fail_compilation/systemvariables_struct.d(57): Error: cannot access `@system` field `S.syst` in `@safe` code +fail_compilation/systemvariables_struct.d(31): Error: accessing `@system` field `S.syst` is not allowed in a `@safe` function +fail_compilation/systemvariables_struct.d(32): Error: accessing `@system` field `S.syst` is not allowed in a `@safe` function +fail_compilation/systemvariables_struct.d(33): Error: accessing `@system` field `S.syst` is not allowed in a `@safe` function +fail_compilation/systemvariables_struct.d(36): Error: accessing `@system` field `S.syst` is not allowed in a `@safe` function +fail_compilation/systemvariables_struct.d(37): Error: accessing `@system` field `S.syst` is not allowed in a `@safe` function +fail_compilation/systemvariables_struct.d(38): Error: accessing `@system` field `S.syst` is not allowed in a `@safe` function +fail_compilation/systemvariables_struct.d(54): Error: accessing `@system` field `S2.syst` is not allowed in a `@safe` function +fail_compilation/systemvariables_struct.d(55): Error: accessing `@system` field `S2.syst` is not allowed in a `@safe` function +fail_compilation/systemvariables_struct.d(56): Error: accessing `@system` field `S.syst` is not allowed in a `@safe` function +fail_compilation/systemvariables_struct.d(57): Error: accessing `@system` field `S.syst` is not allowed in a `@safe` function --- */ diff --git a/tests/dmd/fail_compilation/systemvariables_var_init.d b/tests/dmd/fail_compilation/systemvariables_var_init.d index 221bd54f44..2affc2fbd3 100644 --- a/tests/dmd/fail_compilation/systemvariables_var_init.d +++ b/tests/dmd/fail_compilation/systemvariables_var_init.d @@ -2,11 +2,11 @@ REQUIRED_ARGS: -preview=systemVariables TEST_OUTPUT: --- -fail_compilation/systemvariables_var_init.d(24): Error: cannot access `@system` variable `ptrEnum` in @safe code +fail_compilation/systemvariables_var_init.d(24): Error: access `@system` variable `ptrEnum` is not allowed in a `@safe` function fail_compilation/systemvariables_var_init.d(16): `ptrEnum` is inferred to be `@system` from its initializer here -fail_compilation/systemvariables_var_init.d(25): Error: cannot access `@system` variable `ptrConst` in @safe code +fail_compilation/systemvariables_var_init.d(25): Error: access `@system` variable `ptrConst` is not allowed in a `@safe` function fail_compilation/systemvariables_var_init.d(17): `ptrConst` is inferred to be `@system` from its initializer here -fail_compilation/systemvariables_var_init.d(27): Error: cannot access `@system` variable `ptrVar` in @safe code +fail_compilation/systemvariables_var_init.d(27): Error: access `@system` variable `ptrVar` is not allowed in a `@safe` function fail_compilation/systemvariables_var_init.d(19): `ptrVar` is inferred to be `@system` from its initializer here --- */ diff --git a/tests/dmd/fail_compilation/systemvariables_void_init.d b/tests/dmd/fail_compilation/systemvariables_void_init.d index ea0e55daff..43478e782f 100644 --- a/tests/dmd/fail_compilation/systemvariables_void_init.d +++ b/tests/dmd/fail_compilation/systemvariables_void_init.d @@ -2,13 +2,13 @@ REQUIRED_ARGS: -preview=systemVariables TEST_OUTPUT: --- -fail_compilation/systemvariables_void_init.d(48): Error: `void` initializers for types with unsafe bit patterns are not allowed in safe functions -fail_compilation/systemvariables_void_init.d(49): Error: `void` initializers for types with unsafe bit patterns are not allowed in safe functions -fail_compilation/systemvariables_void_init.d(50): Error: `void` initializers for types with unsafe bit patterns are not allowed in safe functions -fail_compilation/systemvariables_void_init.d(51): Error: a `bool` must be 0 or 1, so void intializing it is not allowed in safe functions -fail_compilation/systemvariables_void_init.d(52): Error: a `bool` must be 0 or 1, so void intializing it is not allowed in safe functions -fail_compilation/systemvariables_void_init.d(53): Error: `void` initializers for types with unsafe bit patterns are not allowed in safe functions -fail_compilation/systemvariables_void_init.d(54): Error: `void` initializers for types with unsafe bit patterns are not allowed in safe functions +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(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 --- */ diff --git a/tests/dmd/fail_compilation/template_decl.d b/tests/dmd/fail_compilation/template_decl.d index d986dd6506..a28284b9e4 100644 --- a/tests/dmd/fail_compilation/template_decl.d +++ b/tests/dmd/fail_compilation/template_decl.d @@ -2,7 +2,7 @@ TEST_OUTPUT: --- fail_compilation/template_decl.d(8): Error: `{` expected after template parameter list, not `(` -fail_compilation/template_decl.d(8): Error: declaration expected, not `(` +fail_compilation/template_decl.d(8): Error: declaration expected, not `)` --- */ template b(alias d)() { diff --git a/tests/dmd/fail_compilation/template_enum_param.d b/tests/dmd/fail_compilation/template_enum_param.d new file mode 100644 index 0000000000..79fb5e4d8f --- /dev/null +++ b/tests/dmd/fail_compilation/template_enum_param.d @@ -0,0 +1,17 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/template_enum_param.d(15): Error: static assert: `false` is false +fail_compilation/template_enum_param.d(17): instantiated from here: `X!(E.a)` +--- +*/ + +enum E +{ + a,b,c +} +template X(E e) +{ + static assert(false); +} +alias Y = X!(E.a); diff --git a/tests/dmd/fail_compilation/test1.d b/tests/dmd/fail_compilation/test1.d index aeceb52cad..0f5bd707b8 100644 --- a/tests/dmd/fail_compilation/test1.d +++ b/tests/dmd/fail_compilation/test1.d @@ -1,7 +1,7 @@ /* TEST_OUTPUT: --- -fail_compilation/test1.d(8): Error: no identifier for declarator `fail` +fail_compilation/test1.d(8): Error: variable name expected after type `fail`, not `End of File` --- */ fail diff --git a/tests/dmd/fail_compilation/test11006.d b/tests/dmd/fail_compilation/test11006.d index e7257b7c2e..6bcfb929fd 100644 --- a/tests/dmd/fail_compilation/test11006.d +++ b/tests/dmd/fail_compilation/test11006.d @@ -1,11 +1,13 @@ -/* REQUIRED_ARGS: -main -de - * TEST_OUTPUT: +/* TEST_OUTPUT: --- -fail_compilation/test11006.d(10): Deprecation: cannot subtract pointers to different types: `void*` and `int*`. -fail_compilation/test11006.d(10): while evaluating: `static assert(2L == 2L)` -fail_compilation/test11006.d(11): Deprecation: cannot subtract pointers to different types: `int*` and `void*`. -fail_compilation/test11006.d(11): while evaluating: `static assert(8L == 8L)` +fail_compilation/test11006.d(11): Error: cannot subtract pointers to different types: `void*` and `int*`. +fail_compilation/test11006.d(11): while evaluating: `static assert(cast(void*)8 - cast(int*)0 == 2L)` +fail_compilation/test11006.d(12): Error: cannot subtract pointers to different types: `int*` and `void*`. +fail_compilation/test11006.d(12): while evaluating: `static assert(cast(int*)8 - cast(void*)0 == 8L)` +fail_compilation/test11006.d(13): Error: cannot subtract pointers to different types: `ushort*` and `ubyte*`. +fail_compilation/test11006.d(13): while evaluating: `static assert(null - null == 0)` --- */ static assert(cast(void*)8 - cast(int*) 0 == 2L); static assert(cast(int*) 8 - cast(void*)0 == 8L); +static assert((ushort*).init - (ubyte*).init == 0); diff --git a/tests/dmd/fail_compilation/test11176.d b/tests/dmd/fail_compilation/test11176.d index 5ef7324ce8..66a7afe3df 100644 --- a/tests/dmd/fail_compilation/test11176.d +++ b/tests/dmd/fail_compilation/test11176.d @@ -1,8 +1,8 @@ /* TEST_OUTPUT: --- -fail_compilation/test11176.d(12): Error: `b.ptr` cannot be used in `@safe` code, use `&b[0]` instead -fail_compilation/test11176.d(16): Error: `b.ptr` cannot be used in `@safe` code, use `&b[0]` instead +fail_compilation/test11176.d(12): Error: using `b.ptr` (instead of `&b[0])` is not allowed in a `@safe` function +fail_compilation/test11176.d(16): Error: using `b.ptr` (instead of `&b[0])` is not allowed in a `@safe` function --- */ diff --git a/tests/dmd/fail_compilation/test12822.d b/tests/dmd/fail_compilation/test12822.d index 510d9e2a9f..bf606aaac3 100644 --- a/tests/dmd/fail_compilation/test12822.d +++ b/tests/dmd/fail_compilation/test12822.d @@ -1,8 +1,8 @@ /* TEST_OUTPUT: --- -fail_compilation/test12822.d(13): Error: cannot modify delegate pointer in `@safe` code `dg.ptr` -fail_compilation/test12822.d(14): Error: `dg.funcptr` cannot be used in `@safe` code +fail_compilation/test12822.d(13): Error: modifying delegate pointer `dg.ptr` is not allowed in a `@safe` function +fail_compilation/test12822.d(14): Error: using `dg.funcptr` is not allowed in a `@safe` function --- */ diff --git a/tests/dmd/fail_compilation/test13536.d b/tests/dmd/fail_compilation/test13536.d index eff807ab5a..a602ab42d2 100644 --- a/tests/dmd/fail_compilation/test13536.d +++ b/tests/dmd/fail_compilation/test13536.d @@ -1,8 +1,8 @@ /* TEST_OUTPUT: --- -fail_compilation/test13536.d(22): Error: field `U.sysDg` cannot access pointers in `@safe` code that overlap other fields -fail_compilation/test13536.d(23): Error: field `U.safeDg` cannot access pointers in `@safe` code that overlap other fields +fail_compilation/test13536.d(22): Error: accessing overlapped field `U.sysDg` with pointers is not allowed in a `@safe` function +fail_compilation/test13536.d(23): Error: accessing overlapped field `U.safeDg` with pointers is not allowed in a `@safe` function --- */ diff --git a/tests/dmd/fail_compilation/test13537.d b/tests/dmd/fail_compilation/test13537.d index 8e9811c577..5421557ee0 100644 --- a/tests/dmd/fail_compilation/test13537.d +++ b/tests/dmd/fail_compilation/test13537.d @@ -1,10 +1,10 @@ /* TEST_OUTPUT: --- -fail_compilation/test13537.d(31): Error: field `U.y` cannot modify fields in `@safe` code that overlap fields with other storage classes -fail_compilation/test13537.d(32): Error: field `U.y` cannot modify fields in `@safe` code that overlap fields with other storage classes -fail_compilation/test13537.d(33): Error: field `U.z` cannot access pointers in `@safe` code that overlap other fields -fail_compilation/test13537.d(34): Error: field `U.y` cannot modify fields in `@safe` code that overlap fields with other storage classes +fail_compilation/test13537.d(31): Error: modifying field `U.y` which overlaps with fields with other storage classes is not allowed in a `@safe` function +fail_compilation/test13537.d(32): Error: modifying field `U.y` which overlaps with fields with other storage classes is not allowed in a `@safe` function +fail_compilation/test13537.d(33): Error: accessing overlapped field `U.z` with pointers is not allowed in a `@safe` function +fail_compilation/test13537.d(34): Error: modifying field `U.y` which overlaps with fields with other storage classes is not allowed in a `@safe` function --- */ diff --git a/tests/dmd/fail_compilation/test13786.d b/tests/dmd/fail_compilation/test13786.d index 73ec588c77..5a873bf9c9 100644 --- a/tests/dmd/fail_compilation/test13786.d +++ b/tests/dmd/fail_compilation/test13786.d @@ -1,21 +1,15 @@ /* TEST_OUTPUT: --- -fail_compilation/test13786.d(16): Deprecation: `debug = ` is deprecated, use debug identifiers instead -fail_compilation/test13786.d(18): Deprecation: `version = ` is deprecated, use version identifiers instead -fail_compilation/test13786.d(16): Error: debug `123` level declaration must be at module level -fail_compilation/test13786.d(17): Error: debug `abc` declaration must be at module level -fail_compilation/test13786.d(18): Error: version `123` level declaration must be at module level -fail_compilation/test13786.d(19): Error: version `abc` declaration must be at module level -fail_compilation/test13786.d(22): Error: template instance `test13786.T!()` error instantiating +fail_compilation/test13786.d(12): Error: debug `abc` declaration must be at module level +fail_compilation/test13786.d(13): Error: version `abc` declaration must be at module level +fail_compilation/test13786.d(16): Error: template instance `test13786.T!()` error instantiating --- */ template T() { - debug = 123; debug = abc; - version = 123; version = abc; } diff --git a/tests/dmd/fail_compilation/test14238.d b/tests/dmd/fail_compilation/test14238.d index a0e4d69b36..4fcea9d775 100644 --- a/tests/dmd/fail_compilation/test14238.d +++ b/tests/dmd/fail_compilation/test14238.d @@ -1,17 +1,19 @@ /* REQUIRED_ARGS: -preview=dip1000 TEST_OUTPUT: --- -fail_compilation/test14238.d(20): Error: scope parameter `fn` may not be returned -fail_compilation/test14238.d(28): Error: escaping reference to stack allocated value returned by `&baz` +fail_compilation/test14238.d(22): Error: scope parameter `fn` may not be returned +fail_compilation/test14238.d(25): Error: function `test14238.bar` is `@nogc` yet allocates closure for `bar()` with the GC +fail_compilation/test14238.d(27): function `baz` closes over variable `x` +fail_compilation/test14238.d(26): `x` declared here --- */ // https://issues.dlang.org/show_bug.cgi?id=14238 @safe: -alias Fn = ref int delegate() return; +alias Fn = ref int delegate() return @nogc; -ref int foo(return scope Fn fn) +ref int call(return scope Fn fn) @nogc { return fn(); // Ok } @@ -20,10 +22,14 @@ ref int foo2(scope Fn fn) { return fn(); // Error } -ref int bar() { +ref int bar() @nogc { int x; ref int baz() { return x; } - return foo(&baz); + + if (x == 0) + return call(&baz); + else + return (&baz)(); } diff --git a/tests/dmd/fail_compilation/test14496.d b/tests/dmd/fail_compilation/test14496.d index 94d5d0ffc5..e588629f29 100644 --- a/tests/dmd/fail_compilation/test14496.d +++ b/tests/dmd/fail_compilation/test14496.d @@ -1,12 +1,12 @@ /* TEST_OUTPUT: --- -fail_compilation/test14496.d(21): Error: `void` initializers for pointers not allowed in safe functions -fail_compilation/test14496.d(24): Error: `void` initializers for pointers not allowed in safe functions -fail_compilation/test14496.d(28): Error: `void` initializers for pointers not allowed in safe functions -fail_compilation/test14496.d(48): Error: `void` initializers for pointers not allowed in safe functions -fail_compilation/test14496.d(49): Error: `void` initializers for pointers not allowed in safe functions -fail_compilation/test14496.d(50): Error: `void` initializers for pointers not allowed in safe functions +fail_compilation/test14496.d(21): Error: `void` initializing a pointer is not allowed in a `@safe` function +fail_compilation/test14496.d(24): Error: `void` initializing a pointer is not allowed in a `@safe` function +fail_compilation/test14496.d(28): Error: `void` initializing a pointer is not allowed in a `@safe` function +fail_compilation/test14496.d(48): Error: `void` initializers for pointers is not allowed in a `@safe` function +fail_compilation/test14496.d(49): Error: `void` initializers for pointers is not allowed in a `@safe` function +fail_compilation/test14496.d(50): Error: `void` initializers for pointers is not allowed in a `@safe` function --- */ // https://issues.dlang.org/show_bug.cgi?id=14496 diff --git a/tests/dmd/fail_compilation/test14538.d b/tests/dmd/fail_compilation/test14538.d index 1ad2126fd4..c912d165a2 100644 --- a/tests/dmd/fail_compilation/test14538.d +++ b/tests/dmd/fail_compilation/test14538.d @@ -2,7 +2,7 @@ /* TEST_OUTPUT: --- -fail_compilation/test14538.d(18): Error: cannot implicitly convert expression `x ? cast(uint)this.fCells[x].code : 32u` of type `uint` to `Cell` +fail_compilation/test14538.d(18): Error: return value `x ? cast(uint)this.fCells[x].code : 32u` of type `uint` does not match return type `Cell`, and cannot be implicitly converted --- */ diff --git a/tests/dmd/fail_compilation/test15191.d b/tests/dmd/fail_compilation/test15191.d index fbbc1c0fa8..dcd6f4dd23 100644 --- a/tests/dmd/fail_compilation/test15191.d +++ b/tests/dmd/fail_compilation/test15191.d @@ -4,8 +4,8 @@ REQUIRED_ARGS: -preview=dip1000 fail_compilation/test15191.d(34): Error: returning `&identity(x)` escapes a reference to local variable `x` fail_compilation/test15191.d(40): Error: returning `&identityPtr(x)` escapes a reference to local variable `x` fail_compilation/test15191.d(46): Error: returning `&identityPtr(x)` escapes a reference to local variable `x` -fail_compilation/test15191.d(67): Error: cannot take address of `scope` variable `x` since `scope` applies to first indirection only -fail_compilation/test15191.d(69): Error: cannot take address of `scope` variable `x` since `scope` applies to first indirection only +fail_compilation/test15191.d(67): Error: taking address of `scope` variable `x` with pointers is not allowed in a `@safe` function +fail_compilation/test15191.d(69): Error: taking address of `scope` variable `x` with pointers is not allowed in a `@safe` function --- */ diff --git a/tests/dmd/fail_compilation/test15399.d b/tests/dmd/fail_compilation/test15399.d index fe9934a864..e017d196d9 100644 --- a/tests/dmd/fail_compilation/test15399.d +++ b/tests/dmd/fail_compilation/test15399.d @@ -1,14 +1,14 @@ /* https://issues.dlang.org/show_bug.cgi?id=15399 TEST_OUTPUT: --- -fail_compilation/test15399.d(32): Error: field `S1.ptr` cannot modify misaligned pointers in `@safe` code -fail_compilation/test15399.d(33): Error: field `S2.ptr` cannot modify misaligned pointers in `@safe` code -fail_compilation/test15399.d(34): Error: field `S1.ptr` cannot modify misaligned pointers in `@safe` code -fail_compilation/test15399.d(35): Error: field `S2.ptr` cannot modify misaligned pointers in `@safe` code -fail_compilation/test15399.d(36): Error: field `S1.ptr` cannot modify misaligned pointers in `@safe` code -fail_compilation/test15399.d(37): Error: field `S2.ptr` cannot modify misaligned pointers in `@safe` code -fail_compilation/test15399.d(38): Error: field `S1.ptr` cannot modify misaligned pointers in `@safe` code -fail_compilation/test15399.d(39): Error: field `S2.ptr` cannot modify misaligned pointers in `@safe` code +fail_compilation/test15399.d(32): Error: modifying misaligned pointers through field `S1.ptr` is not allowed in a `@safe` function +fail_compilation/test15399.d(33): Error: modifying misaligned pointers through field `S2.ptr` is not allowed in a `@safe` function +fail_compilation/test15399.d(34): Error: modifying misaligned pointers through field `S1.ptr` is not allowed in a `@safe` function +fail_compilation/test15399.d(35): Error: modifying misaligned pointers through field `S2.ptr` is not allowed in a `@safe` function +fail_compilation/test15399.d(36): Error: modifying misaligned pointers through field `S1.ptr` is not allowed in a `@safe` function +fail_compilation/test15399.d(37): Error: modifying misaligned pointers through field `S2.ptr` is not allowed in a `@safe` function +fail_compilation/test15399.d(38): Error: modifying misaligned pointers through field `S1.ptr` is not allowed in a `@safe` function +fail_compilation/test15399.d(39): Error: modifying misaligned pointers through field `S2.ptr` is not allowed in a `@safe` function --- */ diff --git a/tests/dmd/fail_compilation/test15544.d b/tests/dmd/fail_compilation/test15544.d index 91ac675c95..f1a99c6d3d 100644 --- a/tests/dmd/fail_compilation/test15544.d +++ b/tests/dmd/fail_compilation/test15544.d @@ -2,8 +2,8 @@ REQUIRED_ARGS: -preview=dip1000 TEST_OUTPUT: --- -fail_compilation/test15544.d(20): Error: reference to local `this` assigned to non-scope `_del` in @safe code -fail_compilation/test15544.d(22): Error: reference to local `this` assigned to non-scope `_del` in @safe code +fail_compilation/test15544.d(20): Error: assigning reference to local `this` to non-scope `_del` is not allowed in a `@safe` function +fail_compilation/test15544.d(22): Error: assigning reference to local `this` to non-scope `_del` is not allowed in a `@safe` function --- */ @@ -26,7 +26,7 @@ struct S { /* TEST_OUTPUT: --- -fail_compilation/test15544.d(46): Error: reference to local `y` assigned to non-scope `dg` in @safe code +fail_compilation/test15544.d(46): Error: assigning reference to local `y` to non-scope `dg` is not allowed in a `@safe` function --- */ diff --git a/tests/dmd/fail_compilation/test15672.d b/tests/dmd/fail_compilation/test15672.d index c3d14db210..22c1bee6fa 100644 --- a/tests/dmd/fail_compilation/test15672.d +++ b/tests/dmd/fail_compilation/test15672.d @@ -1,9 +1,9 @@ /* * TEST_OUTPUT: --- -fail_compilation/test15672.d(17): Error: cast from `void[]` to `byte[]` not allowed in safe code +fail_compilation/test15672.d(17): Error: cast from `void[]` to `byte[]` is not allowed in a `@safe` function fail_compilation/test15672.d(17): `void` data may contain pointers and target element type is mutable -fail_compilation/test15672.d(27): Error: cast from `void*` to `byte*` not allowed in safe code +fail_compilation/test15672.d(27): Error: cast from `void*` to `byte*` is not allowed in a `@safe` function fail_compilation/test15672.d(27): `void` data may contain pointers and target element type is mutable --- */ diff --git a/tests/dmd/fail_compilation/test15703.d b/tests/dmd/fail_compilation/test15703.d index 4e47c3d794..0452ceef65 100644 --- a/tests/dmd/fail_compilation/test15703.d +++ b/tests/dmd/fail_compilation/test15703.d @@ -2,15 +2,15 @@ REQUIRED_ARGS: -m32 TEST_OUTPUT: --- -fail_compilation/test15703.d(23): Error: cast from `Object[]` to `uint[]` not allowed in safe code +fail_compilation/test15703.d(23): Error: cast from `Object[]` to `uint[]` is not allowed in a `@safe` function fail_compilation/test15703.d(23): Target element type is mutable and source element type contains a pointer -fail_compilation/test15703.d(25): Error: cast from `object.Object` to `const(uint)*` not allowed in safe code +fail_compilation/test15703.d(25): Error: cast from `object.Object` to `const(uint)*` is not allowed in a `@safe` function fail_compilation/test15703.d(25): Source type is incompatible with target type containing a pointer -fail_compilation/test15703.d(28): Error: cast from `uint[]` to `Object[]` not allowed in safe code +fail_compilation/test15703.d(28): Error: cast from `uint[]` to `Object[]` is not allowed in a `@safe` function fail_compilation/test15703.d(28): Target element type contains a pointer -fail_compilation/test15703.d(44): Error: cast from `int[]` to `S[]` not allowed in safe code +fail_compilation/test15703.d(44): Error: cast from `int[]` to `S[]` is not allowed in a `@safe` function fail_compilation/test15703.d(44): Target element type is opaque -fail_compilation/test15703.d(45): Error: cast from `S[]` to `int[]` not allowed in safe code +fail_compilation/test15703.d(45): Error: cast from `S[]` to `int[]` is not allowed in a `@safe` function fail_compilation/test15703.d(45): Source element type is opaque --- */ diff --git a/tests/dmd/fail_compilation/test15704.d b/tests/dmd/fail_compilation/test15704.d index 04d4be4aa9..ce9e5bb188 100644 --- a/tests/dmd/fail_compilation/test15704.d +++ b/tests/dmd/fail_compilation/test15704.d @@ -1,9 +1,9 @@ /* * TEST_OUTPUT: --- -fail_compilation/test15704.d(17): Error: cannot copy `void[]` to `void[]` in `@safe` code -fail_compilation/test15704.d(18): Error: cannot copy `const(void)[]` to `void[]` in `@safe` code -fail_compilation/test15704.d(19): Deprecation: cannot copy `int[]` to `void[]` in `@safe` code +fail_compilation/test15704.d(17): Error: copying `void[]` to `void[]` is not allowed in a `@safe` function +fail_compilation/test15704.d(18): Error: copying `const(void)[]` to `void[]` is not allowed in a `@safe` function +fail_compilation/test15704.d(19): Deprecation: copying `int[]` to `void[]` will become `@system` in a future release --- */ diff --git a/tests/dmd/fail_compilation/test16188.d b/tests/dmd/fail_compilation/test16188.d index 0bd052ca6d..e3fedf34bd 100644 --- a/tests/dmd/fail_compilation/test16188.d +++ b/tests/dmd/fail_compilation/test16188.d @@ -2,8 +2,8 @@ * TEST_OUTPUT: --- fail_compilation/test16188.d(101): Error: no property `name` for `Where()` of type `test16188.Where` -fail_compilation/test16188.d(101): potentially malformed `opDispatch`. Use an explicit instantiation to get a better error message -fail_compilation/test16188.d(103): struct `Where` defined here +fail_compilation/test16188.d(107): Error: undefined identifier `getMember` +fail_compilation/test16188.d(101): Error: template instance `test16188.Where.opDispatch!"name"` error instantiating --- */ diff --git a/tests/dmd/fail_compilation/test16193.d b/tests/dmd/fail_compilation/test16193.d index 84dc7d1c6d..76a2fb6676 100644 --- a/tests/dmd/fail_compilation/test16193.d +++ b/tests/dmd/fail_compilation/test16193.d @@ -3,7 +3,7 @@ REQUIRED_ARGS: -preview=dip1000 TEST_OUTPUT: --- fail_compilation/test16193.d(39): Error: function `test16193.abc` is `@nogc` yet allocates closure for `abc()` with the GC -fail_compilation/test16193.d(41): delegate `test16193.abc.__foreachbody_L41_C5` closes over variable `x` +fail_compilation/test16193.d(41): delegate `int(int i) => 0` closes over variable `x` fail_compilation/test16193.d(40): `x` declared here --- */ diff --git a/tests/dmd/fail_compilation/test16195.d b/tests/dmd/fail_compilation/test16195.d deleted file mode 100644 index 018ab0d8cd..0000000000 --- a/tests/dmd/fail_compilation/test16195.d +++ /dev/null @@ -1,15 +0,0 @@ -/* - * TEST_OUTPUT: ---- -fail_compilation/test16195.d(14): Error: the `delete` keyword is obsolete -fail_compilation/test16195.d(14): use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead ---- - */ - - -// https://issues.dlang.org/show_bug.cgi?id=16195 - -@safe pure nothrow @nogc void test(int* p) -{ - delete p; -} diff --git a/tests/dmd/fail_compilation/test16365.d b/tests/dmd/fail_compilation/test16365.d index 5bfa5f8dba..fd16247428 100644 --- a/tests/dmd/fail_compilation/test16365.d +++ b/tests/dmd/fail_compilation/test16365.d @@ -1,9 +1,9 @@ /* TEST_OUTPUT: --- -fail_compilation/test16365.d(20): Error: `this` reference necessary to take address of member `f1` in `@safe` function `main` +fail_compilation/test16365.d(20): Error: taking address of member `f1` without `this` reference is not allowed in a `@safe` function fail_compilation/test16365.d(22): Error: cannot implicitly convert expression `&f2` of type `void delegate() pure nothrow @nogc @safe` to `void function() @safe` -fail_compilation/test16365.d(27): Error: `dg.funcptr` cannot be used in `@safe` code +fail_compilation/test16365.d(27): Error: using `dg.funcptr` is not allowed in a `@safe` function --- */ diff --git a/tests/dmd/fail_compilation/test16443.d b/tests/dmd/fail_compilation/test16443.d new file mode 100644 index 0000000000..a83dc53bbe --- /dev/null +++ b/tests/dmd/fail_compilation/test16443.d @@ -0,0 +1,12 @@ +/* TEST_OUTPUT: +--- +fail_compilation/test16443.d(10): Error: incompatible types for `(null) + (null)`: both operands are of type `typeof(null)` +fail_compilation/test16443.d(11): Error: incompatible types for `(null) - (null)`: both operands are of type `typeof(null)` +--- +*/ + +void foo() +{ + auto a = null + null; + auto b = null - null; +} diff --git a/tests/dmd/fail_compilation/test16589.d b/tests/dmd/fail_compilation/test16589.d index dc4c5931a4..229028df99 100644 --- a/tests/dmd/fail_compilation/test16589.d +++ b/tests/dmd/fail_compilation/test16589.d @@ -2,13 +2,13 @@ REQUIRED_ARGS: -preview=dip1000 TEST_OUTPUT: --- -fail_compilation/test16589.d(26): Error: returning `&this.data` escapes a reference to parameter `this` +fail_compilation/test16589.d(26): Error: escaping a reference to parameter `this` by returning `&this.data` is not allowed in a `@safe` function fail_compilation/test16589.d(24): perhaps annotate the function with `return` -fail_compilation/test16589.d(31): Error: returning `&this` escapes a reference to parameter `this` +fail_compilation/test16589.d(31): Error: escaping a reference to parameter `this` by returning `&this` is not allowed in a `@safe` function fail_compilation/test16589.d(29): perhaps annotate the function with `return` -fail_compilation/test16589.d(37): Error: returning `&s.data` escapes a reference to parameter `s` +fail_compilation/test16589.d(37): Error: escaping a reference to parameter `s` by returning `&s.data` is not allowed in a `@safe` function fail_compilation/test16589.d(35): perhaps annotate the parameter with `return` -fail_compilation/test16589.d(42): Error: returning `&s` escapes a reference to parameter `s` +fail_compilation/test16589.d(42): Error: escaping a reference to parameter `s` by returning `&s` is not allowed in a `@safe` function fail_compilation/test16589.d(40): perhaps annotate the parameter with `return` fail_compilation/test16589.d(47): Error: returning `&s.data` escapes a reference to parameter `s` fail_compilation/test16589.d(52): Error: returning `& s` escapes a reference to parameter `s` diff --git a/tests/dmd/fail_compilation/test17284.d b/tests/dmd/fail_compilation/test17284.d index b7fd9796d8..a68a0b32d2 100644 --- a/tests/dmd/fail_compilation/test17284.d +++ b/tests/dmd/fail_compilation/test17284.d @@ -1,7 +1,7 @@ /* TEST_OUTPUT: --- -fail_compilation/test17284.d(17): Error: field `U.c` cannot access pointers in `@safe` code that overlap other fields +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 diff --git a/tests/dmd/fail_compilation/test17422.d b/tests/dmd/fail_compilation/test17422.d index 80f8fbe261..e5577ddb54 100644 --- a/tests/dmd/fail_compilation/test17422.d +++ b/tests/dmd/fail_compilation/test17422.d @@ -2,7 +2,7 @@ REQUIRED_ARGS: -preview=dip1000 TEST_OUTPUT: --- -fail_compilation/test17422.d(23): Error: scope variable `p` may not be returned +fail_compilation/test17422.d(23): Error: returning scope variable `p` is not allowed in a `@safe` function --- */ struct RC diff --git a/tests/dmd/fail_compilation/test17423.d b/tests/dmd/fail_compilation/test17423.d index faa98063e6..785bbe7fc4 100644 --- a/tests/dmd/fail_compilation/test17423.d +++ b/tests/dmd/fail_compilation/test17423.d @@ -1,7 +1,7 @@ /* REQUIRED_ARGS: -preview=dip1000 TEST_OUTPUT: --- -fail_compilation/test17423.d(27): Error: reference to local `this` assigned to non-scope parameter `dlg` calling `opApply` +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` --- */ diff --git a/tests/dmd/fail_compilation/test17450.d b/tests/dmd/fail_compilation/test17450.d index ddf3f46fb1..6258dbe0b1 100644 --- a/tests/dmd/fail_compilation/test17450.d +++ b/tests/dmd/fail_compilation/test17450.d @@ -2,9 +2,9 @@ REQUIRED_ARGS: -preview=dip1000 TEST_OUTPUT: --- -fail_compilation/test17450.d(17): Error: returning `&s.bar` escapes a reference to parameter `s` +fail_compilation/test17450.d(17): Error: escaping a reference to parameter `s` by returning `&s.bar` is not allowed in a `@safe` function fail_compilation/test17450.d(16): perhaps annotate the parameter with `return` -fail_compilation/test17450.d(20): Error: returning `&this.bar` escapes a reference to parameter `this` +fail_compilation/test17450.d(20): Error: escaping a reference to parameter `this` by returning `&this.bar` is not allowed in a `@safe` function fail_compilation/test17450.d(19): perhaps annotate the function with `return` --- */ diff --git a/tests/dmd/fail_compilation/test17764.d b/tests/dmd/fail_compilation/test17764.d index befcdb19fe..2f12348e94 100644 --- a/tests/dmd/fail_compilation/test17764.d +++ b/tests/dmd/fail_compilation/test17764.d @@ -1,7 +1,7 @@ /* REQUIRED_ARGS: -preview=dip1000 * TEST_OUTPUT: --- -fail_compilation/test17764.d(109): Error: scope variable `c` assigned to global variable `global` +fail_compilation/test17764.d(109): Error: assigning scope variable `c` to global variable `global` is not allowed in a `@safe` function --- */ diff --git a/tests/dmd/fail_compilation/test17959.d b/tests/dmd/fail_compilation/test17959.d index cd2216ff04..201511d39b 100644 --- a/tests/dmd/fail_compilation/test17959.d +++ b/tests/dmd/fail_compilation/test17959.d @@ -1,8 +1,8 @@ /* REQUIRED_ARGS: -preview=dip1000 TEST_OUTPUT: --- -fail_compilation/test17959.d(18): Error: scope variable `this` assigned to non-scope `this.escape` -fail_compilation/test17959.d(19): Error: scope variable `this` assigned to non-scope `this.f` +fail_compilation/test17959.d(18): Error: assigning scope variable `this` to non-scope `this.escape` is not allowed in a `@safe` function +fail_compilation/test17959.d(19): Error: assigning scope variable `this` to non-scope `this.f` is not allowed in a `@safe` function --- */ diff --git a/tests/dmd/fail_compilation/test17977.d b/tests/dmd/fail_compilation/test17977.d index ff6bc1c44f..b79d36c71f 100644 --- a/tests/dmd/fail_compilation/test17977.d +++ b/tests/dmd/fail_compilation/test17977.d @@ -3,7 +3,7 @@ https://issues.dlang.org/show_bug.cgi?id=15399 REQUIRED_ARGS: -preview=dip1000 TEST_OUTPUT: --- -fail_compilation/test17977.d(19): Error: address of variable `__slList3` assigned to `elem` with longer lifetime +fail_compilation/test17977.d(19): Error: assigning address of variable `__slList3` to `elem` with longer lifetime is not allowed in a `@safe` function --- */ diff --git a/tests/dmd/fail_compilation/test18282.d b/tests/dmd/fail_compilation/test18282.d index 580fe1bde7..b5db07fd93 100644 --- a/tests/dmd/fail_compilation/test18282.d +++ b/tests/dmd/fail_compilation/test18282.d @@ -1,13 +1,13 @@ /* REQUIRED_ARGS: -preview=dip1000 TEST_OUTPUT: --- -fail_compilation/test18282.d(25): Error: scope variable `aa` may not be returned -fail_compilation/test18282.d(34): Error: copying `& i` into allocated memory escapes a reference to local variable `i` -fail_compilation/test18282.d(35): Error: copying `& i` into allocated memory escapes a reference to local variable `i` -fail_compilation/test18282.d(36): Error: scope variable `staa` may not be returned -fail_compilation/test18282.d(44): Error: copying `S2000(& i)` into allocated memory escapes a reference to local variable `i` -fail_compilation/test18282.d(53): Error: copying `& i` into allocated memory escapes a reference to local variable `i` -fail_compilation/test18282.d(53): Error: copying `& c` into allocated memory escapes a reference to local variable `c` +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(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 --- */ @@ -57,10 +57,10 @@ void bar3() /****************************** TEST_OUTPUT: --- -fail_compilation/test18282.d(1007): Error: copying `& foo` into allocated memory escapes a reference to local variable `foo` -fail_compilation/test18282.d(1008): Error: copying `& foo` into allocated memory escapes a reference to local variable `foo` -fail_compilation/test18282.d(1009): Error: copying `& foo` into allocated memory escapes a reference to local variable `foo` -fail_compilation/test18282.d(1016): Error: copying `&this` into allocated memory escapes a reference to parameter `this` +fail_compilation/test18282.d(1007): Error: escaping a reference to local variable `foo` by copying `& foo` into allocated memory is not allowed in a `@safe` function +fail_compilation/test18282.d(1008): Error: escaping a reference to local variable `foo` by copying `& foo` into allocated memory is not allowed in a `@safe` function +fail_compilation/test18282.d(1009): Error: escaping a reference to local variable `foo` by copying `& foo` into allocated memory is not allowed in a `@safe` function +fail_compilation/test18282.d(1016): Error: escaping a reference to parameter `this` by copying `&this` into allocated memory is not allowed in a `@safe` function --- */ diff --git a/tests/dmd/fail_compilation/test18385b.d b/tests/dmd/fail_compilation/test18385b.d index f0b9d014f8..8442226f6e 100644 --- a/tests/dmd/fail_compilation/test18385b.d +++ b/tests/dmd/fail_compilation/test18385b.d @@ -4,11 +4,11 @@ because they ignored the actual function name TEST_OUTPUT: --- -fail_compilation/test18385b.d(13): Error: `test18385b.S.foo` called with argument types `(int)` matches both: +fail_compilation/test18385b.d(13): Error: `test18385b.S.foo` called with argument types `(int)` matches multiple overloads exactly: fail_compilation/test18385b.d(8): `test18385b.S.foo(int s)` and: fail_compilation/test18385b.d(3): `test18385b.foo(int s)` -fail_compilation/test18385b.d(102): Error: `test18385b.bar` called with argument types `(int)` matches both: +fail_compilation/test18385b.d(102): Error: `test18385b.bar` called with argument types `(int)` matches multiple overloads exactly: fail_compilation/test18385b.d(2): `test18385b.bar(int s)` and: fail_compilation/test18385b.d(3): `test18385b.foo(int s)` diff --git a/tests/dmd/fail_compilation/test18597.d b/tests/dmd/fail_compilation/test18597.d index 66cde58e04..38c1f02cf8 100644 --- a/tests/dmd/fail_compilation/test18597.d +++ b/tests/dmd/fail_compilation/test18597.d @@ -1,8 +1,8 @@ /* TEST_OUTPUT: --- -fail_compilation/test18597.d(24): Error: field `Unaligned.p` cannot modify misaligned pointers in `@safe` code -fail_compilation/test18597.d(25): Error: field `Unaligned.p` cannot assign to misaligned pointers in `@safe` code -fail_compilation/test18597.d(26): Error: field `Unaligned.p` cannot assign to misaligned pointers in `@safe` code +fail_compilation/test18597.d(24): Error: modifying misaligned pointers through field `Unaligned.p` is not allowed in a `@safe` function +fail_compilation/test18597.d(25): Error: field `Unaligned.p` assigning to misaligned pointers is not allowed in a `@safe` function +fail_compilation/test18597.d(26): Error: field `Unaligned.p` assigning to misaligned pointers is not allowed in a `@safe` function --- */ diff --git a/tests/dmd/fail_compilation/test18644.d b/tests/dmd/fail_compilation/test18644.d index 430967ee4a..da7c53610e 100644 --- a/tests/dmd/fail_compilation/test18644.d +++ b/tests/dmd/fail_compilation/test18644.d @@ -1,9 +1,9 @@ /* REQUIRED_ARGS: -preview=dip1000 TEST_OUTPUT: --- -fail_compilation/test18644.d(15): Error: storing reference to stack allocated value returned by `foo()` into allocated memory causes it to escape -fail_compilation/test18644.d(16): Error: escaping reference to stack allocated value returned by `foo()` -fail_compilation/test18644.d(22): Error: escaping reference to stack allocated value returned by `foo()` +fail_compilation/test18644.d(15): Error: escaping a `scope` value returned from nested function `foo` into allocated memory is not allowed in a `@safe` function +fail_compilation/test18644.d(16): Error: escaping local variable through nested function `foo` is not allowed in a `@safe` function +fail_compilation/test18644.d(22): Error: escaping local variable through nested function `foo` is not allowed in a `@safe` function --- */ diff --git a/tests/dmd/fail_compilation/test19097.d b/tests/dmd/fail_compilation/test19097.d index 980931e3da..f415dd9207 100644 --- a/tests/dmd/fail_compilation/test19097.d +++ b/tests/dmd/fail_compilation/test19097.d @@ -1,14 +1,14 @@ /* REQUIRED_ARGS: -preview=dip1000 * TEST_OUTPUT: --- -fail_compilation/test19097.d(44): Error: scope variable `s` may not be returned -fail_compilation/test19097.d(48): Error: scope variable `s1` may not be returned -fail_compilation/test19097.d(77): Error: scope variable `z` assigned to `ref` variable `refPtr` with longer lifetime -fail_compilation/test19097.d(108): Error: scope variable `s4` may not be returned -fail_compilation/test19097.d(126): Error: scope variable `s5c` may not be returned -fail_compilation/test19097.d(130): Error: scope variable `s5m` may not be returned -fail_compilation/test19097.d(147): Error: scope variable `s6c` may not be returned -fail_compilation/test19097.d(151): Error: scope variable `s6m` may not be returned +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 --- */ diff --git a/tests/dmd/fail_compilation/test19107.d b/tests/dmd/fail_compilation/test19107.d index 8bbfa82717..e30dcf723d 100644 --- a/tests/dmd/fail_compilation/test19107.d +++ b/tests/dmd/fail_compilation/test19107.d @@ -4,7 +4,7 @@ TEST_OUTPUT: --- fail_compilation/test19107.d(24): Error: template `all` is not callable using argument types `!((c) => c)(string[])` fail_compilation/test19107.d(18): Candidate is: `all(alias pred, T)(T t)` - with `pred = __lambda_L24_C15, + with `pred = (c) => c, T = string[]` must satisfy the following constraint: ` is(typeof(I!pred(t)))` diff --git a/tests/dmd/fail_compilation/test19473.d b/tests/dmd/fail_compilation/test19473.d index ba6024b000..67a66c5720 100644 --- a/tests/dmd/fail_compilation/test19473.d +++ b/tests/dmd/fail_compilation/test19473.d @@ -1,6 +1,7 @@ /* TEST_OUTPUT: --- -fail_compilation/test19473.d(14): Error: union `test19473.P` no size because of forward reference +fail_compilation/test19473.d(15): Error: union `test19473.P` no size because of forward reference +fail_compilation/test19473.d(31): error on member `test19473.P.p` --- */ diff --git a/tests/dmd/fail_compilation/test19646.d b/tests/dmd/fail_compilation/test19646.d index 766b387a55..c564ffe1aa 100644 --- a/tests/dmd/fail_compilation/test19646.d +++ b/tests/dmd/fail_compilation/test19646.d @@ -1,6 +1,6 @@ /* TEST_OUTPUT: --- -fail_compilation/test19646.d(12): Error: cast from `const(int)*` to `int*` not allowed in safe code +fail_compilation/test19646.d(12): Error: cast from `const(int)*` to `int*` can't initialize `@safe` variable `y` fail_compilation/test19646.d(12): Source type is incompatible with target type containing a pointer fail_compilation/test19646.d(18): Error: `@safe` variable `z` cannot be initialized by calling `@system` function `f` --- diff --git a/tests/dmd/fail_compilation/test19971.d b/tests/dmd/fail_compilation/test19971.d index b99afddbdd..1db134f039 100644 --- a/tests/dmd/fail_compilation/test19971.d +++ b/tests/dmd/fail_compilation/test19971.d @@ -3,7 +3,7 @@ fail_compilation/test19971.d(16): Error: function `f` is not callable using argument types `(string)` fail_compilation/test19971.d(16): cannot pass argument `"%s"` of type `string` to parameter `int x` fail_compilation/test19971.d(13): `test19971.f(int x)` declared here -fail_compilation/test19971.d(17): Error: function literal `__lambda_L17_C5(int x)` is not callable using argument types `(string)` +fail_compilation/test19971.d(17): Error: function literal `(int x) { }` is not callable using argument types `(string)` fail_compilation/test19971.d(17): cannot pass argument `"%s"` of type `string` to parameter `int x` --- */ diff --git a/tests/dmd/fail_compilation/test20023.d b/tests/dmd/fail_compilation/test20023.d index 909e699d3b..ab908e86df 100644 --- a/tests/dmd/fail_compilation/test20023.d +++ b/tests/dmd/fail_compilation/test20023.d @@ -3,7 +3,7 @@ /* TEST_OUTPUT: --- -fail_compilation/imports/test20023b.d(8): Error: scope variable `e` may not be returned +fail_compilation/imports/test20023b.d(8): Error: returning scope variable `e` is not allowed in a `@safe` function fail_compilation/test20023.d(15): Error: template instance `imports.test20023b.threw!()` error instantiating --- */ diff --git a/tests/dmd/fail_compilation/test20245.d b/tests/dmd/fail_compilation/test20245.d index a6bbba2912..5484786d64 100644 --- a/tests/dmd/fail_compilation/test20245.d +++ b/tests/dmd/fail_compilation/test20245.d @@ -2,15 +2,15 @@ REQUIRED_ARGS: -preview=dip1000 TEST_OUTPUT: --- -fail_compilation/test20245.d(21): Error: reference to local variable `x` assigned to non-scope parameter `ptr` calling `escape` -fail_compilation/test20245.d(22): Error: copying `&x` into allocated memory escapes a reference to parameter `x` -fail_compilation/test20245.d(23): Error: scope variable `a` may not be returned -fail_compilation/test20245.d(27): Error: cannot take address of `scope` variable `x` since `scope` applies to first indirection only -fail_compilation/test20245.d(33): Error: reference to local variable `x` assigned to non-scope parameter `ptr` calling `escape` -fail_compilation/test20245.d(34): Error: copying `&x` into allocated memory escapes a reference to parameter `x` -fail_compilation/test20245.d(50): Error: reference to local variable `price` assigned to non-scope `this.minPrice` -fail_compilation/test20245.d(69): Error: reference to local variable `this.content[]` calling non-scope member function `Exception.this()` -fail_compilation/test20245.d(89): Error: reference to local variable `this` assigned to non-scope parameter `content` calling `listUp` +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` --- */ diff --git a/tests/dmd/fail_compilation/test20569.d b/tests/dmd/fail_compilation/test20569.d index 7ad50dc130..85a88c8834 100644 --- a/tests/dmd/fail_compilation/test20569.d +++ b/tests/dmd/fail_compilation/test20569.d @@ -1,8 +1,8 @@ /* REQUIRED_ARGS: -preview=dip1000 TEST_OUTPUT: --- -fail_compilation/test20569.d(19): Error: cannot take address of `scope` variable `s1` since `scope` applies to first indirection only -fail_compilation/test20569.d(23): Error: cannot take address of `scope` variable `s2` since `scope` applies to first indirection only +fail_compilation/test20569.d(19): Error: taking address of `scope` variable `s1` with pointers is not allowed in a `@safe` function +fail_compilation/test20569.d(23): Error: taking address of `scope` variable `s2` with pointers is not allowed in a `@safe` function --- */ diff --git a/tests/dmd/fail_compilation/test20655.d b/tests/dmd/fail_compilation/test20655.d index c3bb70af49..25471d242f 100644 --- a/tests/dmd/fail_compilation/test20655.d +++ b/tests/dmd/fail_compilation/test20655.d @@ -2,15 +2,12 @@ REQUIRED_ARGS: -de TEST_OUTPUT: --- -fail_compilation/test20655.d(29): Deprecation: `@safe` function `g` calling `f1` -fail_compilation/test20655.d(24): which wouldn't be `@safe` because of: -fail_compilation/test20655.d(24): field `U.s` cannot access pointers in `@safe` code that overlap other fields -fail_compilation/test20655.d(30): Deprecation: `@safe` function `g` calling `f2` -fail_compilation/test20655.d(25): which wouldn't be `@safe` because of: -fail_compilation/test20655.d(25): field `U.s` cannot access pointers in `@safe` code that overlap other fields -fail_compilation/test20655.d(31): Deprecation: `@safe` function `g` calling `f3` -fail_compilation/test20655.d(28): which wouldn't be `@safe` because of: -fail_compilation/test20655.d(28): field `U.s` cannot access pointers in `@safe` code that overlap other fields +fail_compilation/test20655.d(26): Deprecation: `@safe` function `g` calling `f1` +fail_compilation/test20655.d(21): and accessing overlapped field `U.s` with pointers makes it fail to infer `@safe` +fail_compilation/test20655.d(27): Deprecation: `@safe` function `g` calling `f2` +fail_compilation/test20655.d(22): and accessing overlapped field `U.s` with pointers makes it fail to infer `@safe` +fail_compilation/test20655.d(28): Deprecation: `@safe` function `g` calling `f3` +fail_compilation/test20655.d(25): and accessing overlapped field `U.s` with pointers makes it fail to infer `@safe` --- */ diff --git a/tests/dmd/fail_compilation/test20719.d b/tests/dmd/fail_compilation/test20719.d index 4db59d62ae..56b80bbb1a 100644 --- a/tests/dmd/fail_compilation/test20719.d +++ b/tests/dmd/fail_compilation/test20719.d @@ -1,8 +1,9 @@ /* TEST_OUTPUT: --- -fail_compilation/test20719.d(13): Error: struct `test20719.SumType` no size because of forward reference -fail_compilation/test20719.d(32): Error: variable `test20719.isCopyable!(SumType).__lambda_L32_C22.foo` - size of type `SumType` is invalid -fail_compilation/test20719.d(18): Error: template instance `test20719.isCopyable!(SumType)` error instantiating +fail_compilation/test20719.d(14): Error: struct `test20719.SumType` no size because of forward reference +fail_compilation/test20719.d(17): error on member `test20719.SumType.storage` +fail_compilation/test20719.d(33): Error: variable `test20719.isCopyable!(SumType).__lambda_L33_C22.foo` - size of type `SumType` is invalid +fail_compilation/test20719.d(19): Error: template instance `test20719.isCopyable!(SumType)` error instantiating --- */ struct SumType diff --git a/tests/dmd/fail_compilation/test20763.d b/tests/dmd/fail_compilation/test20763.d new file mode 100644 index 0000000000..786f8d895a --- /dev/null +++ b/tests/dmd/fail_compilation/test20763.d @@ -0,0 +1,31 @@ +/* +REQUIRED_ARGS: -de + +TEST_OUTPUT: +--- +fail_compilation/test20763.d(25): Deprecation: type `ulong` has no value +fail_compilation/test20763.d(25): perhaps use `ulong.init` +fail_compilation/test20763.d(26): Deprecation: type `ulong` has no value +fail_compilation/test20763.d(26): perhaps use `ulong.init` +fail_compilation/test20763.d(27): Error: type `ulong` has no value +fail_compilation/test20763.d(27): perhaps use `ulong.init` +fail_compilation/test20763.d(28): Error: type `ulong` has no value +fail_compilation/test20763.d(28): perhaps use `ulong.init` +fail_compilation/test20763.d(29): Error: type `ulong` has no value +fail_compilation/test20763.d(29): perhaps use `ulong.init` +fail_compilation/test20763.d(30): Error: type `ulong` has no value +fail_compilation/test20763.d(30): perhaps use `ulong.init` +--- +*/ + +// https://github.com/dlang/dmd/issues/20763 +void test() +{ + alias I = ulong; + alias U0 = typeof(I + 1u); + alias U1 = typeof(1 - I); + alias U2 = typeof(+I); + alias U3 = typeof(I * 1); + alias U4 = typeof(I << 1); + alias U5 = typeof(I | 1); +} diff --git a/tests/dmd/fail_compilation/test20809.d b/tests/dmd/fail_compilation/test20809.d index 0b45277424..ea95d6ceec 100644 --- a/tests/dmd/fail_compilation/test20809.d +++ b/tests/dmd/fail_compilation/test20809.d @@ -2,7 +2,7 @@ REQUIRED_ARGS: TEST_OUTPUT: --- -fail_compilation/test20809.d(114): Error: returning `this.a` escapes a reference to parameter `this` +fail_compilation/test20809.d(114): Error: escaping a reference to parameter `this` by returning `this.a` is not allowed in a `@safe` function fail_compilation/test20809.d(112): perhaps annotate the function with `return` --- */ diff --git a/tests/dmd/fail_compilation/test20881.d b/tests/dmd/fail_compilation/test20881.d index d4c5f07bea..add4d738ce 100644 --- a/tests/dmd/fail_compilation/test20881.d +++ b/tests/dmd/fail_compilation/test20881.d @@ -2,10 +2,10 @@ REQUIRED_ARGS: -preview=dip1000 TEST_OUTPUT: --- -fail_compilation/test20881.d(20): Error: scope variable `this` may not be returned -fail_compilation/test20881.d(27): Error: address of variable `s` assigned to `global` with longer lifetime -fail_compilation/test20881.d(28): Error: address of variable `s` assigned to `global` with longer lifetime -fail_compilation/test20881.d(29): Error: address of variable `s` assigned to `global` with longer lifetime +fail_compilation/test20881.d(20): Error: returning scope variable `this` is not allowed in a `@safe` function +fail_compilation/test20881.d(27): Error: assigning address of variable `s` to `global` with longer lifetime is not allowed in a `@safe` function +fail_compilation/test20881.d(28): Error: assigning address of variable `s` to `global` with longer lifetime is not allowed in a `@safe` function +fail_compilation/test20881.d(29): Error: assigning address of variable `s` to `global` with longer lifetime is not allowed in a `@safe` function --- */ @safe: diff --git a/tests/dmd/fail_compilation/test20998.d b/tests/dmd/fail_compilation/test20998.d index 2e137ab763..00ff25f076 100644 --- a/tests/dmd/fail_compilation/test20998.d +++ b/tests/dmd/fail_compilation/test20998.d @@ -30,7 +30,7 @@ X3 x3 = { ptr: null, "a", ptr: 2, 444 }; fail_compilation/test20998.d(90): Error: too many initializers for `X3` with 3 fields X3 x3 = { ptr: null, "a", ptr: 2, 444 }; ^ -fail_compilation/test20998.d(98): Error: field `X4.ptr` cannot assign to misaligned pointers in `@safe` code +fail_compilation/test20998.d(98): Error: field `X4.ptr` assigning to misaligned pointers is not allowed in a `@safe` function X4 x4 = { ptr: null, "a", 444, ptr: 2, true }; ^ fail_compilation/test20998.d(98): Error: cannot implicitly convert expression `"a"` of type `string` to `int` diff --git a/tests/dmd/fail_compilation/test21062.d b/tests/dmd/fail_compilation/test21062.d index 5ab5307373..18839cc3e2 100644 --- a/tests/dmd/fail_compilation/test21062.d +++ b/tests/dmd/fail_compilation/test21062.d @@ -1,11 +1,11 @@ /* TEST_OUTPUT: --- -fail_compilation/test21062.d(16): Error: no identifier for declarator `bool` +fail_compilation/test21062.d(16): Error: variable name expected after type `bool`, not `synchronized` fail_compilation/test21062.d(16): `synchronized` is a keyword, perhaps append `_` to make it an identifier -fail_compilation/test21062.d(17): Error: no identifier for declarator `ubyte*` +fail_compilation/test21062.d(17): Error: variable name expected after type `ubyte*`, not `out` fail_compilation/test21062.d(17): `out` is a keyword, perhaps append `_` to make it an identifier -fail_compilation/test21062.d(21): Error: no identifier for declarator `uint` +fail_compilation/test21062.d(21): Error: variable name expected after type `uint`, not `in` fail_compilation/test21062.d(21): `in` is a keyword, perhaps append `_` to make it an identifier --- */ diff --git a/tests/dmd/fail_compilation/test21096.d b/tests/dmd/fail_compilation/test21096.d index 302eb3da31..169a9d1a5c 100644 --- a/tests/dmd/fail_compilation/test21096.d +++ b/tests/dmd/fail_compilation/test21096.d @@ -4,7 +4,7 @@ TEST_OUTPUT: --- fail_compilation/test21096.d(11): Error: identifier or new keyword expected following `(...)`. -fail_compilation/test21096.d(11): Error: no identifier for declarator `char[(__error)]` +fail_compilation/test21096.d(11): Error: variable name expected after type `char[(__error)]`, not `;` --- */ diff --git a/tests/dmd/fail_compilation/test21665.d b/tests/dmd/fail_compilation/test21665.d index b4c28116e3..eb99509d48 100644 --- a/tests/dmd/fail_compilation/test21665.d +++ b/tests/dmd/fail_compilation/test21665.d @@ -1,7 +1,7 @@ /* TEST_OUTPUT: --- -fail_compilation/test21665.d(18): Error: `void` initializers for structs with invariants are not allowed in safe functions -fail_compilation/test21665.d(30): Error: field `U.s` cannot access structs with invariants in `@safe` code that overlap other fields +fail_compilation/test21665.d(18): Error: `void` initializing a struct with an invariant is not allowed in a `@safe` function +fail_compilation/test21665.d(30): Error: accessing overlapped field `U.s` with a structs invariant is not allowed in a `@safe` function --- */ diff --git a/tests/dmd/fail_compilation/test21912.d b/tests/dmd/fail_compilation/test21912.d index 7e236c8a33..b34f60b68e 100644 --- a/tests/dmd/fail_compilation/test21912.d +++ b/tests/dmd/fail_compilation/test21912.d @@ -3,16 +3,16 @@ PERMUTE_ARGS: -preview=dip1000 TEST_OUTPUT: --- fail_compilation/test21912.d(28): Error: function `test21912.escapeParam` is `@nogc` yet allocates closure for `escapeParam()` with the GC -fail_compilation/test21912.d(30): delegate `test21912.escapeParam.__lambda_L30_C21` closes over variable `i` +fail_compilation/test21912.d(30): delegate `() => i` closes over variable `i` fail_compilation/test21912.d(28): `i` declared here fail_compilation/test21912.d(33): Error: function `test21912.escapeAssign` is `@nogc` yet allocates closure for `escapeAssign()` with the GC -fail_compilation/test21912.d(35): delegate `test21912.escapeAssign.__lambda_L35_C10` closes over variable `i` +fail_compilation/test21912.d(35): delegate `() => i` closes over variable `i` fail_compilation/test21912.d(33): `i` declared here fail_compilation/test21912.d(44): Error: function `test21912.escapeAssignRef` is `@nogc` yet allocates closure for `escapeAssignRef()` with the GC -fail_compilation/test21912.d(46): delegate `test21912.escapeAssignRef.__lambda_L46_C10` closes over variable `i` +fail_compilation/test21912.d(46): delegate `() => i` closes over variable `i` fail_compilation/test21912.d(44): `i` declared here fail_compilation/test21912.d(55): Error: function `test21912.escapeParamInferred` is `@nogc` yet allocates closure for `escapeParamInferred()` with the GC -fail_compilation/test21912.d(57): delegate `test21912.escapeParamInferred.__lambda_L57_C29` closes over variable `i` +fail_compilation/test21912.d(57): delegate `() => i` closes over variable `i` fail_compilation/test21912.d(55): `i` declared here --- */ diff --git a/tests/dmd/fail_compilation/test21995.d b/tests/dmd/fail_compilation/test21995.d new file mode 100644 index 0000000000..a1e0b83191 --- /dev/null +++ b/tests/dmd/fail_compilation/test21995.d @@ -0,0 +1,11 @@ +/* TEST_OUTPUT: +--- +fail_compilation/test21995.d(10): Error: max object size 4294967295 exceeded from adding field size 3 + alignment adjustment 1 + field offset 4294967292 when placing field in aggregate +--- +*/ +struct S +{ + ubyte[0x7ffffffe] a; + ubyte[0x7ffffffe] b; + ubyte[3] c; +} diff --git a/tests/dmd/fail_compilation/test22023.d b/tests/dmd/fail_compilation/test22023.d index a0f553ba5b..95586c58ad 100644 --- a/tests/dmd/fail_compilation/test22023.d +++ b/tests/dmd/fail_compilation/test22023.d @@ -1,7 +1,6 @@ /* TEST_OUTPUT: --- fail_compilation/test22023.d(102): Error: typesafe variadic function parameter `a` of type `int[]` cannot be marked `return` -fail_compilation/test22023.d(107): Error: typesafe variadic function parameter `c` of type `test22023.C` cannot be marked `return` --- */ @@ -14,13 +13,3 @@ ref int f(return int[] a ...) { return a[2]; } - -ref int g(return C c ...) -{ - return c.x; -} - -class C -{ - int x; -} diff --git a/tests/dmd/fail_compilation/test22145.d b/tests/dmd/fail_compilation/test22145.d index 55e7c639dd..ffba4d509c 100644 --- a/tests/dmd/fail_compilation/test22145.d +++ b/tests/dmd/fail_compilation/test22145.d @@ -1,7 +1,7 @@ /* TEST_OUTPUT: REQUIRED_ARGS: -preview=dip1000 --- -fail_compilation/test22145.d(115): Error: scope variable `x` assigned to global variable `global` +fail_compilation/test22145.d(115): Error: assigning scope variable `x` to global variable `global` is not allowed in a `@safe` function --- */ diff --git a/tests/dmd/fail_compilation/test22227.d b/tests/dmd/fail_compilation/test22227.d index ecffb692be..32cac84901 100644 --- a/tests/dmd/fail_compilation/test22227.d +++ b/tests/dmd/fail_compilation/test22227.d @@ -1,8 +1,8 @@ /* REQUIRED_ARGS: -preview=dip1000 TEST_OUTPUT: --- -fail_compilation/test22227.d(12): Error: scope variable `foo` may not be returned -fail_compilation/test22227.d(14): Error: scope variable `foo` may not be returned +fail_compilation/test22227.d(12): Error: returning scope variable `foo` is not allowed in a `@safe` function +fail_compilation/test22227.d(14): Error: returning scope variable `foo` is not allowed in a `@safe` function --- */ diff --git a/tests/dmd/fail_compilation/test22298.d b/tests/dmd/fail_compilation/test22298.d index cdb1a3eb50..339727e2ae 100644 --- a/tests/dmd/fail_compilation/test22298.d +++ b/tests/dmd/fail_compilation/test22298.d @@ -2,8 +2,8 @@ REQUIRED_ARGS: -preview=dip1000 TEST_OUTPUT: --- -fail_compilation/test22298.d(18): Error: scope variable `i` assigned to `p` with longer lifetime -fail_compilation/test22298.d(29): Error: scope variable `y` assigned to `x` with longer lifetime +fail_compilation/test22298.d(18): Error: assigning scope variable `i` to `p` with longer lifetime is not allowed in a `@safe` function +fail_compilation/test22298.d(29): Error: assigning scope variable `y` to `x` with longer lifetime is not allowed in a `@safe` function --- */ diff --git a/tests/dmd/fail_compilation/test22329.d b/tests/dmd/fail_compilation/test22329.d index 25c83f5142..a6e152ce1d 100644 --- a/tests/dmd/fail_compilation/test22329.d +++ b/tests/dmd/fail_compilation/test22329.d @@ -4,9 +4,10 @@ TEST_OUTPUT: --- fail_compilation/imports/imp22329.d(3): Error: no property `values` for type `test22329.Foo` -fail_compilation/test22329.d(13): struct `Foo` defined here -fail_compilation/imports/imp22329.d(3): Error: incompatible types for `(arg) + (1)`: `Foo` and `int` -fail_compilation/test22329.d(21): Error: template instance `imp22329.func!(Foo)` error instantiating +fail_compilation/test22329.d(14): struct `Foo` defined here +fail_compilation/imports/imp22329.d(3): Error: operator `+` is not defined for type `Foo` +fail_compilation/test22329.d(14): perhaps overload the operator with `auto opBinary(string op : "+")(int rhs) {}` +fail_compilation/test22329.d(22): Error: template instance `imp22329.func!(Foo)` error instantiating --- */ diff --git a/tests/dmd/fail_compilation/test22541.d b/tests/dmd/fail_compilation/test22541.d index 6152f44fae..4be00182b1 100644 --- a/tests/dmd/fail_compilation/test22541.d +++ b/tests/dmd/fail_compilation/test22541.d @@ -1,7 +1,7 @@ /* REQUIRED_ARGS: -preview=dip1000 TEST_OUTPUT: --- -fail_compilation/test22541.d(104): Error: returning `i` escapes a reference to parameter `i` +fail_compilation/test22541.d(104): Error: escaping a reference to parameter `i` by returning `i` is not allowed in a `@safe` function fail_compilation/test22541.d(102): perhaps annotate the parameter with `return` --- */ diff --git a/tests/dmd/fail_compilation/test22680.d b/tests/dmd/fail_compilation/test22680.d index 85e653e91e..0c86ec87ec 100644 --- a/tests/dmd/fail_compilation/test22680.d +++ b/tests/dmd/fail_compilation/test22680.d @@ -1,7 +1,7 @@ /* REQUIRED_ARGS: -preview=dip1000 TEST_OUTPUT: --- -fail_compilation/test22680.d(104): Error: scope variable `this` assigned to global variable `c` +fail_compilation/test22680.d(104): Error: assigning scope variable `this` to global variable `c` is not allowed in a `@safe` function --- */ diff --git a/tests/dmd/fail_compilation/test22709.d b/tests/dmd/fail_compilation/test22709.d index dd5258e0b3..17932acf5c 100644 --- a/tests/dmd/fail_compilation/test22709.d +++ b/tests/dmd/fail_compilation/test22709.d @@ -2,8 +2,8 @@ REQUIRED_ARGS: -preview=dip1000 TEST_OUTPUT: --- -fail_compilation/test22709.d(15): Error: address of variable `local` assigned to `arr` with longer lifetime -fail_compilation/test22709.d(20): Error: address of variable `local` assigned to `arr` with longer lifetime +fail_compilation/test22709.d(15): Error: assigning address of variable `local` to `arr` with longer lifetime is not allowed in a `@safe` function +fail_compilation/test22709.d(20): Error: assigning address of variable `local` to `arr` with longer lifetime is not allowed in a `@safe` function --- */ diff --git a/tests/dmd/fail_compilation/test22818.d b/tests/dmd/fail_compilation/test22818.d index 5759415ead..dcb745f900 100644 --- a/tests/dmd/fail_compilation/test22818.d +++ b/tests/dmd/fail_compilation/test22818.d @@ -1,6 +1,7 @@ /* REQUIRED_ARGS: -preview=dip1000 * TEST_OUTPUT: --- +fail_compilation/test22818.d(102): Deprecation: typesafe variadic parameters with a `class` type (`C c...`) are deprecated fail_compilation/test22818.d(104): Error: scope parameter `c` may not be returned --- */ diff --git a/tests/dmd/fail_compilation/test22910.d b/tests/dmd/fail_compilation/test22910.d index 581c693ccb..b4089b5339 100644 --- a/tests/dmd/fail_compilation/test22910.d +++ b/tests/dmd/fail_compilation/test22910.d @@ -1,7 +1,7 @@ /* REQUIRED_ARGS: -preview=dip1000 TEST_OUTPUT: --- -fail_compilation/test22910.d(17): Error: returning `&this.val` escapes a reference to parameter `this` +fail_compilation/test22910.d(17): Error: escaping a reference to parameter `this` by returning `&this.val` is not allowed in a `@safe` function fail_compilation/test22910.d(15): perhaps change the `return scope` into `scope return` --- */ diff --git a/tests/dmd/fail_compilation/test22977.d b/tests/dmd/fail_compilation/test22977.d index 87bb19cc71..569b722e28 100644 --- a/tests/dmd/fail_compilation/test22977.d +++ b/tests/dmd/fail_compilation/test22977.d @@ -2,8 +2,8 @@ REQUIRED_ARGS: -preview=dip1000 TEST_OUTPUT: --- -fail_compilation/test22977.d(16): Error: escaping local variable through nested function `scfunc` -fail_compilation/test22977.d(22): Error: escaping reference to stack allocated value returned by `scfunc2()` +fail_compilation/test22977.d(16): Error: escaping local variable through nested function `scfunc` is not allowed in a `@safe` function +fail_compilation/test22977.d(22): Error: escaping local variable through nested function `scfunc2` is not allowed in a `@safe` function --- */ diff --git a/tests/dmd/fail_compilation/test22999.d b/tests/dmd/fail_compilation/test22999.d index 99dfe70378..921ab6075c 100644 --- a/tests/dmd/fail_compilation/test22999.d +++ b/tests/dmd/fail_compilation/test22999.d @@ -1,9 +1,8 @@ /* -REQUIRED_ARGS: -de TEST_OUTPUT: --- -fail_compilation/test22999.d(18): Deprecation: switch case fallthrough - use 'goto default;' if intended -fail_compilation/test22999.d(25): Deprecation: switch case fallthrough - use 'goto case;' if intended +fail_compilation/test22999.d(17): Error: switch case fallthrough - use 'goto default;' if intended +fail_compilation/test22999.d(24): Error: switch case fallthrough - use 'goto case;' if intended --- */ diff --git a/tests/dmd/fail_compilation/test23022.d b/tests/dmd/fail_compilation/test23022.d index 8c4eca9c56..db1f4a712c 100644 --- a/tests/dmd/fail_compilation/test23022.d +++ b/tests/dmd/fail_compilation/test23022.d @@ -2,7 +2,7 @@ REQUIRED_ARGS: -preview=dip1000 TEST_OUTPUT: --- -fail_compilation/test23022.d(14): Error: scope parameter `p` may not be returned +fail_compilation/test23022.d(14): Error: returning `p` escapes a reference to variadic parameter `p` --- */ diff --git a/tests/dmd/fail_compilation/test23073.d b/tests/dmd/fail_compilation/test23073.d index 39106ba572..e47569aacb 100644 --- a/tests/dmd/fail_compilation/test23073.d +++ b/tests/dmd/fail_compilation/test23073.d @@ -2,7 +2,7 @@ REQUIRED_ARGS: -preview=dip1000 TEST_OUTPUT: --- -fail_compilation/test23073.d(28): Error: scope variable `c` assigned to non-scope parameter `c` calling `assignNext` +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` --- */ diff --git a/tests/dmd/fail_compilation/test23112.d b/tests/dmd/fail_compilation/test23112.d index 79c0b059e9..e6202cdbdd 100644 --- a/tests/dmd/fail_compilation/test23112.d +++ b/tests/dmd/fail_compilation/test23112.d @@ -3,7 +3,7 @@ DISABLED: LDC // FIXME: don't know how to fix this; should probably be handled p TEST_OUTPUT: --- fail_compilation/test23112.d(106): Error: function `test23112.bar` is `@nogc` yet allocates closure for `bar()` with the GC -fail_compilation/test23112.d(108): function `test23112.bar.f` closes over variable `a` +fail_compilation/test23112.d(108): function `f` closes over variable `a` fail_compilation/test23112.d(106): `a` declared here --- */ diff --git a/tests/dmd/fail_compilation/test23145.d b/tests/dmd/fail_compilation/test23145.d index 4767bedaae..0d69c8b884 100644 --- a/tests/dmd/fail_compilation/test23145.d +++ b/tests/dmd/fail_compilation/test23145.d @@ -1,13 +1,12 @@ /* REQUIRED_ARGS: -preview=dip1000 TEST_OUTPUT: --- -fail_compilation/test23145.d(117): Error: `scope` allocation of `c` requires that constructor be annotated with `scope` +fail_compilation/test23145.d(117): Error: `scope` allocation of `c` with a non-`scope` constructor is not allowed in a `@safe` function fail_compilation/test23145.d(111): is the location of the constructor -fail_compilation/test23145.d(124): Error: `scope` allocation of `c` requires that constructor be annotated with `scope` +fail_compilation/test23145.d(124): Error: `scope` allocation of `c` with a non-`scope` constructor is not allowed in a `@safe` function fail_compilation/test23145.d(111): is the location of the constructor fail_compilation/test23145.d(125): Error: `@safe` function `test23145.bax` cannot call `@system` function `test23145.inferred` -fail_compilation/test23145.d(131): which wasn't inferred `@safe` because of: -fail_compilation/test23145.d(131): `scope` allocation of `c` requires that constructor be annotated with `scope` +fail_compilation/test23145.d(131): and `scope` allocation of `c` with a non-`scope` constructor makes it fail to infer `@safe` fail_compilation/test23145.d(129): `test23145.inferred` is declared here --- */ diff --git a/tests/dmd/fail_compilation/test23170.d b/tests/dmd/fail_compilation/test23170.d index fedf31b5fc..f447128da0 100644 --- a/tests/dmd/fail_compilation/test23170.d +++ b/tests/dmd/fail_compilation/test23170.d @@ -1,7 +1,7 @@ /* TEST_OUTPUT: --- -fail_compilation/test23170.d(10): Error: array literal in `@nogc` delegate `test23170.__lambda_L10_C15` may cause a GC allocation +fail_compilation/test23170.d(10): Error: this array literal causes a GC allocation in `@nogc` delegate `__lambda_L10_C15` --- */ // https://issues.dlang.org/show_bug.cgi?id=23170 diff --git a/tests/dmd/fail_compilation/test23491.d b/tests/dmd/fail_compilation/test23491.d index b66d8a860d..124297f43d 100644 --- a/tests/dmd/fail_compilation/test23491.d +++ b/tests/dmd/fail_compilation/test23491.d @@ -2,9 +2,9 @@ REQUIRED_ARGS: -preview=dip1000 TEST_OUTPUT: --- -fail_compilation/test23491.d(16): Error: reference to local variable `buffer` assigned to non-scope anonymous parameter -fail_compilation/test23491.d(17): Error: reference to local variable `buffer` assigned to non-scope anonymous parameter calling `sinkF` -fail_compilation/test23491.d(18): Error: reference to local variable `buffer` assigned to non-scope parameter `buf` +fail_compilation/test23491.d(16): Error: reference to local variable `buffer` assigned to non-scope anonymous parameter is not allowed in a `@safe` function +fail_compilation/test23491.d(17): Error: assigning reference to local variable `buffer` to non-scope anonymous parameter calling `sinkF` is not allowed in a `@safe` function +fail_compilation/test23491.d(18): Error: assigning reference to local variable `buffer` to non-scope parameter `buf` is not allowed in a `@safe` function --- */ diff --git a/tests/dmd/fail_compilation/test23873.d b/tests/dmd/fail_compilation/test23873.d index bb6a71dcc2..948df73603 100644 --- a/tests/dmd/fail_compilation/test23873.d +++ b/tests/dmd/fail_compilation/test23873.d @@ -5,7 +5,7 @@ TEST_OUTPUT: --- fail_compilation/imports/import23873.d(1): Error: (expression) expected following `static if` fail_compilation/imports/import23873.d(1): Error: declaration expected following attribute, not `;` -fail_compilation/imports/import23873.d(3): Error: no identifier for declarator `x` +fail_compilation/imports/import23873.d(3): Error: variable name expected after type `x`, not `End of File` --- */ struct Foo diff --git a/tests/dmd/fail_compilation/test23982.d b/tests/dmd/fail_compilation/test23982.d index f8eee238ed..dac70fd9fb 100644 --- a/tests/dmd/fail_compilation/test23982.d +++ b/tests/dmd/fail_compilation/test23982.d @@ -2,7 +2,7 @@ REQUIRED_ARGS: -preview=dip1000 TEST_OUTPUT: --- -fail_compilation/test23982.d(35): Error: scope variable `a` assigned to non-scope parameter `a` calling `foo2` +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` --- */ diff --git a/tests/dmd/fail_compilation/test24015.d b/tests/dmd/fail_compilation/test24015.d index c9bc42e055..837619de8d 100644 --- a/tests/dmd/fail_compilation/test24015.d +++ b/tests/dmd/fail_compilation/test24015.d @@ -1,7 +1,7 @@ /* REQUIRED_ARGS: -preview=dip1000 * TEST_OUTPUT: --- -fail_compilation/test24015.d(19): Error: scope variable `v` assigned to non-scope parameter `...` calling `jer` +fail_compilation/test24015.d(19): Error: assigning scope variable `v` to non-scope parameter `...` calling `jer` is not allowed in a `@safe` function --- */ diff --git a/tests/dmd/fail_compilation/test24353.d b/tests/dmd/fail_compilation/test24353.d new file mode 100644 index 0000000000..76174aee62 --- /dev/null +++ b/tests/dmd/fail_compilation/test24353.d @@ -0,0 +1,24 @@ +// https://issues.dlang.org/show_bug.cgi?id=24353 + +/** +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 +--- +*/ + + +struct S +{ + int opApply(int delegate(int) dg) + { + return 0; + } +} + +void example() +{ + const S s; + foreach (e; s) {} // Error expected here +} diff --git a/tests/dmd/fail_compilation/test24680.d b/tests/dmd/fail_compilation/test24680.d new file mode 100644 index 0000000000..3833e0e408 --- /dev/null +++ b/tests/dmd/fail_compilation/test24680.d @@ -0,0 +1,20 @@ +/** +REQUIRED_ARGS: -preview=dip1000 +TEST_OUTPUT: +--- +fail_compilation/test24680.d(19): Error: escaping a reference to local variable `buf` by returning `c.peek(buf[])` is not allowed in a `@safe` function +--- +*/ + +// https://issues.dlang.org/show_bug.cgi?id=24680 + +class C +{ + final auto peek(ubyte[] buf) { return buf; } +} + +@safe escape(C c) +{ + ubyte[5] buf; + return c.peek(buf[]); +} diff --git a/tests/dmd/fail_compilation/test24694.d b/tests/dmd/fail_compilation/test24694.d new file mode 100644 index 0000000000..7b58fbbf89 --- /dev/null +++ b/tests/dmd/fail_compilation/test24694.d @@ -0,0 +1,27 @@ +/** +REQUIRED_ARGS: -preview=dip1000 +TEST_OUTPUT: +--- +fail_compilation/test24694.d(25): Error: assigning reference to local variable `x` to non-scope `b.c.p` is not allowed in a `@safe` function +--- +*/ + +// https://issues.dlang.org/show_bug.cgi?id=24694 + +class C +{ + int* p; +} + +struct S +{ + C c; +} + +int* escape() @safe +{ + int x = 0; + S b = S(new C()); + b.c.p = &x; + return b.c.p; +} diff --git a/tests/dmd/fail_compilation/test24745.d b/tests/dmd/fail_compilation/test24745.d new file mode 100644 index 0000000000..6d9c335bb4 --- /dev/null +++ b/tests/dmd/fail_compilation/test24745.d @@ -0,0 +1,13 @@ +// https://issues.dlang.org/show_bug.cgi?id=24745 + +/* +TEST_OUTPUT: +--- +fail_compilation/test24745.d(12): Error: incorrect syntax for associative array, expected `[]`, found `{}` +--- +*/ + +void main() +{ + int[int] f = {1: 1, 2: 2}; +} diff --git a/tests/dmd/fail_compilation/testInference.d b/tests/dmd/fail_compilation/testInference.d index b3a8a561cf..fbbde87c06 100644 --- a/tests/dmd/fail_compilation/testInference.d +++ b/tests/dmd/fail_compilation/testInference.d @@ -1,7 +1,7 @@ /* TEST_OUTPUT: --- -fail_compilation/testInference.d(24): Error: cannot implicitly convert expression `this.a` of type `inout(A8998)` to `immutable(A8998)` +fail_compilation/testInference.d(24): Error: return value `this.a` of type `inout(A8998)` does not match return type `immutable(A8998)`, and cannot be implicitly converted --- */ @@ -28,10 +28,10 @@ class C8998 /* TEST_OUTPUT: --- -fail_compilation/testInference.d(39): Error: cannot implicitly convert expression `s` of type `const(char[])` to `string` -fail_compilation/testInference.d(44): Error: cannot implicitly convert expression `a` of type `int[]` to `immutable(int[])` -fail_compilation/testInference.d(49): Error: cannot implicitly convert expression `a` of type `int[]` to `immutable(int[])` -fail_compilation/testInference.d(54): Error: cannot implicitly convert expression `a` of type `int[]` to `immutable(int[])` +fail_compilation/testInference.d(39): Error: return value `s` of type `const(char[])` does not match return type `string`, and cannot be implicitly converted +fail_compilation/testInference.d(44): Error: return value `a` of type `int[]` does not match return type `immutable(int[])`, and cannot be implicitly converted +fail_compilation/testInference.d(49): Error: return value `a` of type `int[]` does not match return type `immutable(int[])`, and cannot be implicitly converted +fail_compilation/testInference.d(54): Error: return value `a` of type `int[]` does not match return type `immutable(int[])`, and cannot be implicitly converted --- */ string foo(in char[] s) pure @@ -58,18 +58,18 @@ immutable(int[]) x3(immutable(int[]) org) /*pure*/ /* TEST_OUTPUT: --- -fail_compilation/testInference.d(94): Error: cannot implicitly convert expression `c` of type `testInference.C1` to `immutable(C1)` -fail_compilation/testInference.d(95): Error: cannot implicitly convert expression `c` of type `testInference.C1` to `immutable(C1)` -fail_compilation/testInference.d(96): Error: cannot implicitly convert expression `c` of type `testInference.C3` to `immutable(C3)` -fail_compilation/testInference.d(97): Error: cannot implicitly convert expression `c` of type `testInference.C3` to `immutable(C3)` +fail_compilation/testInference.d(94): Error: return value `c` of type `testInference.C1` does not match return type `immutable(C1)`, and cannot be implicitly converted +fail_compilation/testInference.d(95): Error: return value `c` of type `testInference.C1` does not match return type `immutable(C1)`, and cannot be implicitly converted +fail_compilation/testInference.d(96): Error: return value `c` of type `testInference.C3` does not match return type `immutable(C3)`, and cannot be implicitly converted +fail_compilation/testInference.d(97): Error: return value `c` of type `testInference.C3` does not match return type `immutable(C3)`, and cannot be implicitly converted fail_compilation/testInference.d(100): Error: undefined identifier `X1`, did you mean function `x1`? -fail_compilation/testInference.d(106): Error: cannot implicitly convert expression `s` of type `S1` to `immutable(S1)` -fail_compilation/testInference.d(109): Error: cannot implicitly convert expression `a` of type `int*[]` to `immutable(int*[])` -fail_compilation/testInference.d(110): Error: cannot implicitly convert expression `a` of type `const(int)*[]` to `immutable(int*[])` -fail_compilation/testInference.d(114): Error: cannot implicitly convert expression `s` of type `S2` to `immutable(S2)` -fail_compilation/testInference.d(115): Error: cannot implicitly convert expression `s` of type `S2` to `immutable(S2)` -fail_compilation/testInference.d(116): Error: cannot implicitly convert expression `s` of type `S2` to `immutable(S2)` -fail_compilation/testInference.d(118): Error: cannot implicitly convert expression `a` of type `const(int)*[]` to `immutable(int*[])` +fail_compilation/testInference.d(106): Error: return value `s` of type `S1` does not match return type `immutable(S1)`, and cannot be implicitly converted +fail_compilation/testInference.d(109): Error: return value `a` of type `int*[]` does not match return type `immutable(int*[])`, and cannot be implicitly converted +fail_compilation/testInference.d(110): Error: return value `a` of type `const(int)*[]` does not match return type `immutable(int*[])`, and cannot be implicitly converted +fail_compilation/testInference.d(114): Error: return value `s` of type `S2` does not match return type `immutable(S2)`, and cannot be implicitly converted +fail_compilation/testInference.d(115): Error: return value `s` of type `S2` does not match return type `immutable(S2)`, and cannot be implicitly converted +fail_compilation/testInference.d(116): Error: return value `s` of type `S2` does not match return type `immutable(S2)`, and cannot be implicitly converted +fail_compilation/testInference.d(118): Error: return value `a` of type `const(int)*[]` does not match return type `immutable(int*[])`, and cannot be implicitly converted --- */ immutable(Object) get(inout int*) pure @@ -122,7 +122,7 @@ immutable(int*[]) bar2c( S2 prm) pure { immutable(int)*[] a; return /* TEST_OUTPUT: --- -fail_compilation/testInference.d(134): Error: cannot implicitly convert expression `f10063(cast(inout(void*))p)` of type `inout(void)*` to `immutable(void)*` +fail_compilation/testInference.d(134): Error: return value `f10063(cast(inout(void*))p)` of type `inout(void)*` does not match return type `immutable(void)*`, and cannot be implicitly converted --- */ inout(void)* f10063(inout void* p) pure @@ -138,10 +138,9 @@ immutable(void)* g10063(inout int* p) pure TEST_OUTPUT: --- fail_compilation/testInference.d(154): Error: `pure` function `testInference.bar14049` cannot call impure function `testInference.foo14049!int.foo14049` -fail_compilation/testInference.d(149): which calls `testInference.foo14049!int.foo14049.__lambda_L147_C14` -fail_compilation/testInference.d(148): which calls `testInference.impure14049` -fail_compilation/testInference.d(143): which wasn't inferred `pure` because of: -fail_compilation/testInference.d(143): `pure` function `testInference.impure14049` cannot access mutable static data `i` +fail_compilation/testInference.d(149): which calls `() => impure14049()` +fail_compilation/testInference.d(148): which calls `impure14049` +fail_compilation/testInference.d(143): and accessing mutable static data `i` makes it fail to infer `pure` --- */ #line 143 @@ -175,7 +174,7 @@ int* f14160() pure TEST_OUTPUT: --- fail_compilation/testInference.d(180): Error: `pure` function `testInference.test12422` cannot call impure function `testInference.test12422.bar12422!().bar12422` -fail_compilation/testInference.d(179): which calls `testInference.foo12422` +fail_compilation/testInference.d(179): which calls `foo12422` --- */ #line 175 @@ -191,11 +190,9 @@ void test12422() pure TEST_OUTPUT: --- fail_compilation/testInference.d(198): Error: `pure` function `testInference.test13729a` cannot call impure function `testInference.test13729a.foo` -fail_compilation/testInference.d(196): which wasn't inferred `pure` because of: -fail_compilation/testInference.d(196): `pure` function `testInference.test13729a.foo` cannot access mutable static data `g13729` +fail_compilation/testInference.d(196): and accessing mutable static data `g13729` makes it fail to infer `pure` fail_compilation/testInference.d(206): Error: `pure` function `testInference.test13729b` cannot call impure function `testInference.test13729b.foo!().foo` -fail_compilation/testInference.d(204): which wasn't inferred `pure` because of: -fail_compilation/testInference.d(204): `pure` function `testInference.test13729b.foo!().foo` cannot access mutable static data `g13729` +fail_compilation/testInference.d(204): and accessing mutable static data `g13729` makes it fail to infer `pure` --- */ @@ -222,7 +219,7 @@ void test13729b() pure /* TEST_OUTPUT: --- -fail_compilation/testInference.d(225): Error: `testInference.test17086` called with argument types `(bool)` matches both: +fail_compilation/testInference.d(225): Error: `testInference.test17086` called with argument types `(bool)` matches multiple overloads exactly: fail_compilation/testInference.d(219): `testInference.test17086!(bool, false).test17086(bool x)` and: fail_compilation/testInference.d(220): `testInference.test17086!(bool, false).test17086(bool y)` @@ -242,7 +239,7 @@ void test17086_call () TEST_OUTPUT: --- fail_compilation/testInference.d(238): Error: `pure` function `testInference.test20047_pure_function` cannot call impure function `testInference.test20047_pure_function.bug` -fail_compilation/testInference.d(237): which calls `testInference.test20047_impure_function` +fail_compilation/testInference.d(237): which calls `test20047_impure_function` --- */ #line 234 diff --git a/tests/dmd/fail_compilation/testOpApply.d b/tests/dmd/fail_compilation/testOpApply.d index 8d6c736e5a..8aea55fa98 100644 --- a/tests/dmd/fail_compilation/testOpApply.d +++ b/tests/dmd/fail_compilation/testOpApply.d @@ -1,7 +1,7 @@ /+ TEST_OUTPUT: --- -fail_compilation/testOpApply.d(27): Error: `testOpApply.SameAttr.opApply` called with argument types `(int delegate(int i) pure nothrow @nogc @safe)` matches both: +fail_compilation/testOpApply.d(27): Error: `testOpApply.SameAttr.opApply` called with argument types `(int delegate(int i) pure nothrow @nogc @safe)` matches multiple overloads after qualifier conversion: fail_compilation/testOpApply.d(13): `testOpApply.SameAttr.opApply(int delegate(int) @system dg)` and: fail_compilation/testOpApply.d(18): `testOpApply.SameAttr.opApply(int delegate(int) @system dg)` @@ -30,7 +30,7 @@ void testSameAttr() @safe /+ TEST_OUTPUT: --- -fail_compilation/testOpApply.d(104): Error: `testOpApply.SameAttr.opApply` called with argument types `(int delegate(int i) pure nothrow @nogc @system)` matches both: +fail_compilation/testOpApply.d(104): Error: `testOpApply.SameAttr.opApply` called with argument types `(int delegate(int i) pure nothrow @nogc @system)` matches multiple overloads after qualifier conversion: fail_compilation/testOpApply.d(13): `testOpApply.SameAttr.opApply(int delegate(int) @system dg)` and: fail_compilation/testOpApply.d(18): `testOpApply.SameAttr.opApply(int delegate(int) @system dg)` diff --git a/tests/dmd/fail_compilation/testsemi.d b/tests/dmd/fail_compilation/testsemi.d index 77601a5e44..71f2cd1a78 100644 --- a/tests/dmd/fail_compilation/testsemi.d +++ b/tests/dmd/fail_compilation/testsemi.d @@ -1,11 +1,11 @@ /* TEST_OUTPUT: --- fail_compilation/testsemi.d(102): Error: found `int` when expecting `;` following static assert -fail_compilation/testsemi.d(102): Error: no identifier for declarator `x` +fail_compilation/testsemi.d(102): Error: variable name expected after type `x`, not `;` fail_compilation/testsemi.d(109): Error: found `alias` when expecting `;` following alias reassignment fail_compilation/testsemi.d(112): Error: found `}` when expecting `;` following invariant fail_compilation/testsemi.d(117): Error: found `int` when expecting `;` following `alias Identifier this` -fail_compilation/testsemi.d(117): Error: no identifier for declarator `x` +fail_compilation/testsemi.d(117): Error: variable name expected after type `x`, not `;` fail_compilation/testsemi.d(123): Error: found `int` when expecting `;` following mixin fail_compilation/testsemi.d(129): Error: found `int` when expecting `;` following `import` Expression fail_compilation/testsemi.d(131): Error: `}` expected following members in `class` declaration diff --git a/tests/dmd/fail_compilation/trait_loc_err.d b/tests/dmd/fail_compilation/trait_loc_err.d index c5d0579f4a..2916214411 100644 --- a/tests/dmd/fail_compilation/trait_loc_err.d +++ b/tests/dmd/fail_compilation/trait_loc_err.d @@ -1,8 +1,9 @@ /* TEST_OUTPUT: --- -fail_compilation/trait_loc_err.d(13): Error: can only get the location of a symbol, not `trait_loc_err` -fail_compilation/trait_loc_err.d(14): Error: can only get the location of a symbol, not `stdc` +fail_compilation/trait_loc_err.d(14): Error: can only get the location of a symbol, not `trait_loc_err` +fail_compilation/trait_loc_err.d(15): Error: can only get the location of a symbol, not `core.stdc` +fail_compilation/trait_loc_err.d(16): Error: can only get the location of a symbol, not `core.stdc.stdio` --- */ module trait_loc_err; @@ -12,4 +13,5 @@ void main() { __traits(getLocation, __traits(parent, main)); __traits(getLocation, __traits(parent, core.stdc.stdio)); + __traits(getLocation, core.stdc.stdio); } diff --git a/tests/dmd/fail_compilation/traits_alone.d b/tests/dmd/fail_compilation/traits_alone.d index 8f6f145e6b..eb0b396af4 100644 --- a/tests/dmd/fail_compilation/traits_alone.d +++ b/tests/dmd/fail_compilation/traits_alone.d @@ -3,7 +3,7 @@ TEST_OUTPUT: --- fail_compilation/traits_alone.d(11): Error: found `End of File` when expecting `(` fail_compilation/traits_alone.d(11): Error: `__traits(identifier, args...)` expected -fail_compilation/traits_alone.d(11): Error: no identifier for declarator `_error_` +fail_compilation/traits_alone.d(11): Error: variable name expected after type `$r:_?_error_?$`, not `End of File` --- */ //used to segfault diff --git a/tests/dmd/fail_compilation/typeerrors.d b/tests/dmd/fail_compilation/typeerrors.d index 4c8576c88d..ea929bcc45 100644 --- a/tests/dmd/fail_compilation/typeerrors.d +++ b/tests/dmd/fail_compilation/typeerrors.d @@ -17,14 +17,14 @@ fail_compilation/typeerrors.d(52): Error: cannot have associative array of `void fail_compilation/typeerrors.d(54): Error: cannot have parameter of type `void` fail_compilation/typeerrors.d(56): Error: slice `[1..5]` is out of range of [0..4] fail_compilation/typeerrors.d(57): Error: slice `[2..1]` is out of range of [0..4] +fail_compilation/typeerrors.d(59): Error: variable `typeerrors.foo.globalC` globals, statics, fields, manifest constants, ref and out parameters cannot be `scope` +fail_compilation/typeerrors.d(59): Error: variable `typeerrors.foo.globalC` reference to `scope class` must be `scope` +fail_compilation/typeerrors.d(60): Error: variable `typeerrors.foo.manifestC` globals, statics, fields, manifest constants, ref and out parameters cannot be `scope` +fail_compilation/typeerrors.d(60): Error: variable `typeerrors.foo.manifestC` reference to `scope class` must be `scope` --- */ - - - - template tuple(T...) { alias T tuple; } void bar(); @@ -55,4 +55,7 @@ void foo() alias T2 = T[1 .. 5]; alias T3 = T[2 .. 1]; + + static C globalC; + enum C manifestC = new C(); } diff --git a/tests/dmd/fail_compilation/udaparams.d b/tests/dmd/fail_compilation/udaparams.d index 76df55fcfd..06f4bd8b67 100644 --- a/tests/dmd/fail_compilation/udaparams.d +++ b/tests/dmd/fail_compilation/udaparams.d @@ -15,19 +15,19 @@ fail_compilation/udaparams.d(45): Error: `@nogc` attribute for function paramete fail_compilation/udaparams.d(51): Error: cannot put a storage-class in an `alias` declaration. fail_compilation/udaparams.d(52): Error: cannot put a storage-class in an `alias` declaration. fail_compilation/udaparams.d(53): Error: semicolon expected to close `alias` declaration, not `=>` -fail_compilation/udaparams.d(53): Error: declaration expected, not `=>` +fail_compilation/udaparams.d(53): Error: declaration expected, not `1` fail_compilation/udaparams.d(54): Error: semicolon expected to close `alias` declaration, not `=>` -fail_compilation/udaparams.d(54): Error: declaration expected, not `=>` +fail_compilation/udaparams.d(54): Error: declaration expected, not `1` fail_compilation/udaparams.d(57): Error: basic type expected, not `@` fail_compilation/udaparams.d(57): Error: identifier expected for template value parameter fail_compilation/udaparams.d(57): Error: found `@` when expecting `)` fail_compilation/udaparams.d(57): Error: basic type expected, not `3` fail_compilation/udaparams.d(57): Error: found `3` when expecting `)` fail_compilation/udaparams.d(57): Error: semicolon expected following function declaration, not `)` +fail_compilation/udaparams.d(57): Error: variable name expected after type `T`, not `)` fail_compilation/udaparams.d(57): Error: declaration expected, not `)` --- */ - void vararg1(int a, @(10) ...); extern(C) void vararg2(int a, @(10) ...); diff --git a/tests/dmd/fail_compilation/ufcs.d b/tests/dmd/fail_compilation/ufcs.d index 3a92a691e2..87efbcf65c 100644 --- a/tests/dmd/fail_compilation/ufcs.d +++ b/tests/dmd/fail_compilation/ufcs.d @@ -1,23 +1,28 @@ /* TEST_OUTPUT: --- -fail_compilation/ufcs.d(26): Error: no property `regularF` for `s` of type `S` -fail_compilation/ufcs.d(26): the following error occured while looking for a UFCS match -fail_compilation/ufcs.d(26): Error: function `regularF` is not callable using argument types `(S)` -fail_compilation/ufcs.d(26): expected 0 argument(s), not 1 -fail_compilation/ufcs.d(31): `ufcs.regularF()` declared here -fail_compilation/ufcs.d(27): Error: no property `templateF` for `s` of type `S` -fail_compilation/ufcs.d(27): the following error occured while looking for a UFCS match -fail_compilation/ufcs.d(27): Error: template `templateF` is not callable using argument types `!()(S)` -fail_compilation/ufcs.d(32): Candidate is: `templateF()()` -fail_compilation/ufcs.d(28): Error: no property `templateO` for `s` of type `S` -fail_compilation/ufcs.d(28): the following error occured while looking for a UFCS match -fail_compilation/ufcs.d(28): Error: none of the overloads of template `ufcs.templateO` are callable using argument types `!()(S)` -fail_compilation/ufcs.d(34): Candidates are: `templateO()(int x)` -fail_compilation/ufcs.d(35): `templateO()(float y)` +fail_compilation/ufcs.d(31): Error: no property `regularF` for `s` of type `S` +fail_compilation/ufcs.d(31): the following error occured while looking for a UFCS match +fail_compilation/ufcs.d(31): Error: function `regularF` is not callable using argument types `(S)` +fail_compilation/ufcs.d(31): expected 0 argument(s), not 1 +fail_compilation/ufcs.d(39): `ufcs.regularF()` declared here +fail_compilation/ufcs.d(32): Error: no property `templateF` for `s` of type `S` +fail_compilation/ufcs.d(32): the following error occured while looking for a UFCS match +fail_compilation/ufcs.d(32): Error: template `templateF` is not callable using argument types `!()(S)` +fail_compilation/ufcs.d(40): Candidate is: `templateF()()` +fail_compilation/ufcs.d(33): Error: no property `templateO` for `s` of type `S` +fail_compilation/ufcs.d(33): the following error occured while looking for a UFCS match +fail_compilation/ufcs.d(33): Error: none of the overloads of template `ufcs.templateO` are callable using argument types `!()(S)` +fail_compilation/ufcs.d(42): Candidates are: `templateO()(int x)` +fail_compilation/ufcs.d(43): `templateO()(float y)` +fail_compilation/ufcs.d(36): Error: no property `local` for `s` of type `ufcs.S` +fail_compilation/ufcs.d(35): cannot call function `local` with UFCS because it is not declared at module scope +fail_compilation/ufcs.d(26): struct `S` defined here --- */ + + struct S { } void f() @@ -26,6 +31,9 @@ void f() s.regularF(); s.templateF(); s.templateO(); + + void local(S) {} + s.local(); } void regularF(); diff --git a/tests/dmd/fail_compilation/union_initialization.d b/tests/dmd/fail_compilation/union_initialization.d index 36fc63a6f3..5c2a1d463c 100644 --- a/tests/dmd/fail_compilation/union_initialization.d +++ b/tests/dmd/fail_compilation/union_initialization.d @@ -3,8 +3,8 @@ https://issues.dlang.org/show_bug.cgi?id=20068 TEST_OUTPUT: --- -fail_compilation/union_initialization.d(19): Error: field `B.p` cannot access pointers in `@safe` code that overlap other fields -fail_compilation/union_initialization.d(25): Error: field `B.p` cannot access pointers in `@safe` code that overlap other fields +fail_compilation/union_initialization.d(19): Error: accessing overlapped field `B.p` with pointers is not allowed in a `@safe` function +fail_compilation/union_initialization.d(25): Error: accessing overlapped field `B.p` with pointers is not allowed in a `@safe` function --- */ diff --git a/tests/dmd/fail_compilation/var_func_attr.d b/tests/dmd/fail_compilation/var_func_attr.d index 0753b13f31..514a86a284 100644 --- a/tests/dmd/fail_compilation/var_func_attr.d +++ b/tests/dmd/fail_compilation/var_func_attr.d @@ -1,7 +1,7 @@ /* TEST_OUTPUT: --- -fail_compilation/var_func_attr.d(19): Error: cannot implicitly convert expression `__lambda_L19_C27` of type `void function() nothrow @nogc @safe` to `void function() pure` +fail_compilation/var_func_attr.d(19): Error: cannot implicitly convert expression `() { static int g; g++; }` of type `void function() nothrow @nogc @safe` to `void function() pure` --- */ diff --git a/tests/dmd/fail_compilation/varargsstc.d b/tests/dmd/fail_compilation/varargsstc.d index e040574d5b..2345a66764 100644 --- a/tests/dmd/fail_compilation/varargsstc.d +++ b/tests/dmd/fail_compilation/varargsstc.d @@ -1,6 +1,7 @@ /* TEST_OUTPUT: --- -fail_compilation/varargsstc.d(102): Error: variadic parameter cannot have attributes `out ref` +fail_compilation/varargsstc.d(102): Error: variadic parameter cannot have attributes `out` +fail_compilation/varargsstc.d(103): Error: variadic parameter cannot have attributes `ref` --- */ @@ -8,3 +9,4 @@ fail_compilation/varargsstc.d(102): Error: variadic parameter cannot have attrib int printf(const(char)*, const scope shared return ...); int printf(const(char)*, ref out scope immutable shared return ...); +int printf(const(char)*, ref scope immutable shared return ...); diff --git a/tests/dmd/fail_compilation/verrors5.d b/tests/dmd/fail_compilation/verrors5.d index 424d4f7622..74068610ad 100644 --- a/tests/dmd/fail_compilation/verrors5.d +++ b/tests/dmd/fail_compilation/verrors5.d @@ -36,5 +36,6 @@ fail_compilation/verrors5.d(6): Error: undefined identifier `T` fail_compilation/verrors5.d(7): Error: undefined identifier `T` fail_compilation/verrors5.d(8): Error: undefined identifier `T` fail_compilation/verrors5.d(9): Error: undefined identifier `T` +error limit (5) reached, use `-verrors=0` to show all --- */ diff --git a/tests/dmd/run.d b/tests/dmd/run.d index f2fc0be3f0..aa75cea2b5 100755 --- a/tests/dmd/run.d +++ b/tests/dmd/run.d @@ -211,7 +211,7 @@ Options: { const string name = target.filename ? target.normalizedTestName - : "`unit` tests"; + : "`unit` tests: " ~ (cast(string)unitTestRunnerCommand) ~ " " ~ join(target.args, " "); writeln(">>> TARGET FAILED: ", name); synchronized failedTargets ~= name; diff --git a/tests/dmd/runnable/aliasassign.d b/tests/dmd/runnable/aliasassign.d index 986cccc4db..4f61e3787e 100644 --- a/tests/dmd/runnable/aliasassign.d +++ b/tests/dmd/runnable/aliasassign.d @@ -15,7 +15,7 @@ template Qual(alias T) alias Qual = T; } -void test() +void test1() { int x = 3; int y = 4; @@ -25,7 +25,33 @@ void test() assert(XY[1] == 4); } -void main() +/**********************************************/ + +struct T +{ + int k,i = 2; +} + +struct S +{ + int x; + T t; + alias ti = t.i; +} + +void test2() +{ + T t = T(1, 2); + S s; + assert(s.ti == 2); +} + +/**********************************************/ + +int main() { - test(); + test1(); + test2(); + + return 0; } diff --git a/tests/dmd/runnable/declaration.d b/tests/dmd/runnable/declaration.d index 5be76fac6d..991ae7bcc6 100644 --- a/tests/dmd/runnable/declaration.d +++ b/tests/dmd/runnable/declaration.d @@ -406,6 +406,65 @@ void test13950() /***************************************************/ + +void testlocalref() +{ + int x = 4; + ref int rx = x; + rx = 5; + assert(x == 5); + ref int r2 = rx; + r2 = 6; + assert(x == 6); +} + +/***************************************************/ + +int global; + +ref int tgr() { return global; } + +void testglobalref() +{ + auto i = tgr(); + i = 1; + assert(global == 0); +} + +/***************************************************/ + +void testAutoRef() +{ + auto ref int x = 4; + auto ref int y = x; + auto ref z = x + 0; + auto ref char w; + auto ref float v = x; // type conversion + + static assert(!__traits(isRef, x)); + static assert( __traits(isRef, y)); + static assert(!__traits(isRef, z)); + static assert(!__traits(isRef, w)); + static assert(!__traits(isRef, v)); + + assert(&y == &x); + assert(&z != &x); + assert(w == char.init); + assert(v == 4.0); + + if (auto ref int a = 3) + static assert(!__traits(isRef, a)); + + while (auto ref a = x) + { + static assert(is(typeof(a) == int)); + static assert(__traits(isRef, a)); + break; + } +} + +/***************************************************/ + int main() { test6475(); @@ -419,6 +478,9 @@ int main() test10142(); test11421(); test13950(); + testlocalref(); + testglobalref(); + testAutoRef(); printf("Success\n"); return 0; diff --git a/tests/dmd/runnable/exe1.c b/tests/dmd/runnable/exe1.c new file mode 100644 index 0000000000..da797e2e87 --- /dev/null +++ b/tests/dmd/runnable/exe1.c @@ -0,0 +1,2080 @@ +// DISABLED: LDC // FIXME: corner cases for C codegen + +/*_ exe1.c Mon Nov 20 1989 Modified by: Walter Bright */ +/* Copyright (c) 1985-1995 by Symantec */ +/* All Rights Reserved */ +/* Written by Walter Bright */ +/* Check out integer arithmetic. */ + +#include +#include +#include +#include + +/*******************************************/ + +int testcppcomment() +{ +#define XYZ(a,b) a+b + int p; + + p = XYZ(0, /* This is OK ... */ + 0) ; + p = XYZ(0, // ... but this isn't + 0) ; +#undef XYZ + return 0 ; +} + +/*******************************************/ + +void elemi() +{ + int i; + + i = 47; + i = i && 0; + assert(i == 0); + i = 47; + i = i && 1; + assert(i == 1); + i = 47; + i = 0 && i; + assert(i == 0); + i = 47; + i = 1 && i; + assert(i == 1); + + i = 0; + i = i && 0; + assert(i == 0); + i = i && 1; + assert(i == 0); + i = 0 && i; + assert(i == 0); + i = 1 && i; + assert(i == 0); + + i = 47; + i = i || 0; + assert(i == 1); + i = 47; + i = i || 1; + assert(i == 1); + i = 47; + i = 0 || i; + assert(i == 1); + i = 47; + i = 1 || i; + assert(i == 1); + + i = 0; + i = i || 0; + assert(i == 0); + i = 0; + i = i || 1; + assert(i == 1); + i = 0; + i = 0 || i; + assert(i == 0); + i = 0; + i = 1 || i; + assert(i == 1); + i = ((i = 1),(!(i & 5))) && (i == 6); + assert(i == 0); +} + +void elems() +{ + short i; + + i = 47; + i = i && 0; + assert(i == 0); + i = 47; + i = i && 1; + assert(i == 1); + i = 47; + i = 0 && i; + assert(i == 0); + i = 47; + i = 1 && i; + assert(i == 1); + + i = 0; + i = i && 0; + assert(i == 0); + i = i && 1; + assert(i == 0); + i = 0 && i; + assert(i == 0); + i = 1 && i; + assert(i == 0); + + i = 47; + i = i || 0; + assert(i == 1); + i = 47; + i = i || 1; + assert(i == 1); + i = 47; + i = 0 || i; + assert(i == 1); + i = 47; + i = 1 || i; + assert(i == 1); + + i = 0; + i = i || 0; + assert(i == 0); + i = 0; + i = i || 1; + assert(i == 1); + i = 0; + i = 0 || i; + assert(i == 0); + i = 0; + i = 1 || i; + assert(i == 1); + i = ((i = 1),(!(i & 5))) && (i == 6); + assert(i == 0); +} + +void eleml() +{ long l; + + l = 47; + l = l && 0; + assert(l == 0); + l = 47; + l = l && 1; + assert(l == 1); + l = 47; + l = 0 && l; + assert(l == 0); + l = 47; + l = 1 && l; + assert(l == 1); + + l = 0; + l = l && 0; + assert(l == 0); + l = l && 1; + assert(l == 0); + l = 0 && l; + assert(l == 0); + l = 1 && l; + assert(l == 0); + + l = 47; + l = l || 0; + assert(l == 1); + l = 47; + l = l || 1; + assert(l == 1); + l = 47; + l = 0 || l; + assert(l == 1); + l = 47; + l = 1 || l; + assert(l == 1); + + l = 0; + l = l || 0; + assert(l == 0); + l = 0; + l = l || 1; + assert(l == 1); + l = 0; + l = 0 || l; + assert(l == 0); + l = 0; + l = 1 || l; + assert(l == 1); + l = ((l = 1),(!(l & 5))) && (l == 6); + assert(l == 0); +} + +void elemc() +{ char c; + + c = 47; + c = c && 0; + assert(c == 0); + c = 47; + c = c && 1; + assert(c == 1); + c = 47; + c = 0 && c; + assert(c == 0); + c = 47; + c = 1 && c; + assert(c == 1); + + c = 0; + c = c && 0; + assert(c == 0); + c = c && 1; + assert(c == 0); + c = 0 && c; + assert(c == 0); + c = 1 && c; + assert(c == 0); + + c = 47; + c = c || 0; + assert(c == 1); + c = 47; + c = c || 1; + assert(c == 1); + c = 47; + c = 0 || c; + assert(c == 1); + c = 47; + c = 1 || c; + assert(c == 1); + + c = 0; + c = c || 0; + assert(c == 0); + c = 0; + c = c || 1; + assert(c == 1); + c = 0; + c = 0 || c; + assert(c == 0); + c = 0; + c = 1 || c; + assert(c == 1); + c = ((c = 1),(!(c & 5))) && (c == 6); + assert(c == 0); +} + +void align() /* test alignment */ +{ static char a[3][5] = {"1234","5678","abcd"}; + char *p; + int i; + + i = 2; + p = a[i]; + assert(*p == 'a'); +} + +void bitwise() +{ int i; + unsigned u; + long l; + unsigned long ul; + + i = 0x1234; + u = i & 0xFF; + assert(u == 0x34); + u = i & 0xFF00; + assert(u == 0x1200); + u = i | 0xFF; + assert(u == 0x12FF); + u = i | 0xFF00; + assert(u == 0xFF34); + u = i ^ 0xFF; + assert(u == (0x1234 ^ 0xFF)); + u = i ^ 0xFF00; + assert(u == (0x1234 ^ 0xFF00)); + u = i ^ 0xFFFF; + assert(u == (0x1234 ^ 0xFFFF)); + + u = i; + u &= 0xFF; + assert(u == 0x34); + u = i; + u &= 0xFF00; + assert(u == 0x1200); + u = i; + u |= 0xFF; + assert(u == 0x12FF); + u = i; + u |= 0xFF00; + assert(u == 0xFF34); + u = i; + u ^= 0xFF; + assert(u == (0x1234 ^ 0xFF)); + u = i; + u ^= 0xFF00; + assert(u == (0x1234 ^ 0xFF00)); + u = i; + u ^= 0xFFFF; + assert(u == (0x1234 ^ 0xFFFF)); + + l = 0x56781234; + ul = l & 0xFF; + assert(ul == 0x34); + ul = l & 0xFF00; + assert(ul == 0x1200); + ul = l & 0xFFFF; + assert(ul == 0x1234); + ul = l & 0xFFFF0000; + assert(ul == 0x56780000); + ul = l | 0xFF; + assert(ul == 0x567812FF); + ul = l | 0xFF00; + assert(ul == 0x5678FF34); + ul = l | 0xFFFF; + assert(ul == 0x5678FFFF); + ul = l | 0xFFFF0000; + assert(ul == 0xFFFF1234); + ul = l ^ 0xFF; + assert(ul == (0x56781234 ^ 0xFF)); + ul = l ^ 0xFF00; + assert(ul == (0x56781234 ^ 0xFF00)); + ul = l ^ 0xFFFF; + assert(ul == (0x56781234 ^ 0xFFFF)); + ul = l ^ 0xFFFF0000; + assert(ul == (0x56781234 ^ 0xFFFF0000)); + + ul = l; + ul &= 0xFF; + assert(ul == 0x34); + ul = l; + ul &= 0xFF00; + assert(ul == 0x1200); + ul = l; + ul &= 0xFFFF; + assert(ul == 0x1234); + ul = l; + ul &= 0xFFFF0000; + assert(ul == 0x56780000); + ul = l; + ul |= 0xFF; + assert(ul == 0x567812FF); + ul = l; + ul |= 0xFF00; + assert(ul == 0x5678FF34); + ul = l; + ul |= 0xFFFF; + assert(ul == 0x5678FFFF); + ul = l; + ul |= 0xFFFF0000; + assert(ul == 0xFFFF1234); + ul = l; + ul ^= 0xFF; + assert(ul == (0x56781234 ^ 0xFF)); + ul = l; + ul ^= 0xFF00; + assert(ul == (0x56781234 ^ 0xFF00)); + ul = l; + ul ^= 0xFFFF; + assert(ul == (0x56781234 ^ 0xFFFF)); + ul = l; + ul ^= 0xFFFF0000; + assert(ul == (0x56781234 ^ 0xFFFF0000)); +} + +void bitwiseshort() +{ short i; + unsigned short u; + + i = 0x1234; + u = i & 0xFF; + assert(u == 0x34); + u = i & 0xFF00; + assert(u == 0x1200); + u = i | 0xFF; + assert(u == 0x12FF); + u = i | 0xFF00; + assert(u == 0xFF34); + u = i ^ 0xFF; + assert(u == (0x1234 ^ 0xFF)); + u = i ^ 0xFF00; + assert(u == (0x1234 ^ 0xFF00)); + u = i ^ 0xFFFF; + assert(u == (0x1234 ^ 0xFFFF)); + + u = i; + u &= 0xFF; + assert(u == 0x34); + u = i; + u &= 0xFF00; + assert(u == 0x1200); + u = i; + u |= 0xFF; + assert(u == 0x12FF); + u = i; + u |= 0xFF00; + assert(u == 0xFF34); + u = i; + u ^= 0xFF; + assert(u == (0x1234 ^ 0xFF)); + u = i; + u ^= 0xFF00; + assert(u == (0x1234 ^ 0xFF00)); + u = i; + u ^= 0xFFFF; + assert(u == (0x1234 ^ 0xFFFF)); +} + +void carith() +{ int i,*p; + unsigned char u8; + unsigned char u8a,u8b; + signed char i8; + unsigned u; + + u8 = 0x87; + if ((i = u8 & 0xFF) == 0) + assert(0); + assert(i == 0x87); + i = u8 & 7; + assert(i == 7); + + i8 = 0x87; + if ((i = i8 & 0xFF) == 0) + assert(0); + assert(i == 0x87); + i = i8 & 7; + assert(i == 7); + + u8 = i8 = 0x80; + i = i8 & 0x101; + assert(i == 0x100); + i = u8 & 0x101; + assert(i == 0); + i8 = 0x85; + i = i8 & (~0x7F | 1); + assert(i == 0xFFFFFF81); + + i *= 47; /* set AH != 0 */ + u8 = 0x58; + assert((u8 & 0x53) == 0x50); + + u8 = 0; + assert(u8 >= 0); + assert(u8 <= 0); + if (u8 < 0 || u8 > 0) + assert(0); + assert(u8 == 0); + if (u8 != 0) + assert(0); + u8 = 1; + assert(u8 > 0); + assert(u8 >= 0); + if (u8 < 0 || u8 <= 0) + assert(0); + assert(u8 != 0); + if (u8 == 0) + assert(0); + + u8 = -1; + assert(u8 > 0); + assert(u8 == 255); + + i8 = -1; + assert(i8 < 0); + assert(i8 == -1); + assert(i8 != u8); + + i8 = 10; + i8 = (0 > i8/2) ? 0 : i8/2; + assert(i8 == 5); + i8 = 47; + i8 %= 15; + assert(i8 == 2); + i8 *= 15; + assert(i8 == 30); + i8 += 2; + assert(i8 == 32); + i8 -= 7; + assert(i8 == 25); + i8 /= 6; + assert(i8 == 4); + i8 |= 3; + assert(i8 == 7); + i8 ^= 2; + assert(i8 == 5); + i8 &= 6; + assert(i8 == 4); + i8 <<= 3; + assert(i8 == 32); + i8 >>= 2; + assert(i8 == 8); + i8 = 17; + i8 %= -3; + assert(i8 == 2); + i8 = -17; + i8 %= -3; + assert(i8 == -2); + i8 = -17; + i8 %= -3; + assert(i8 == -2); + + i8 = 43; + i8 = -i8; + assert(i8 == -43); + i8 = ~i8; + assert(i8 == 42); + i8 = !!i8; + assert(i8 == 1); + i8 = !i8; + assert(i8 == 0); + assert(~i8); + i8 = ~i8; + assert(!~i8); + + u8a = 0x81; + u8b = 0xF0; + i = (unsigned) u8a < (unsigned) u8b; + assert(i == 1); + + assert(sizeof(i8 << 2L) == sizeof(int)); +} + +void sarith() +{ short i,*p; + char c; + unsigned short u; + + c = 47; + i = 63; + assert(c - i == -16); + assert(c * i == 2961); + i = 4; + assert((c << i) == 752); + u = 0; + assert(u >= 0); + assert(u <= 0); + if (u < 0 || u > 0) + assert(0); + assert(u == 0); + if (u != 0) + assert(0); + u = 1; + assert(u > 0); + assert(u >= 0); + if (u < 0 || u <= 0) + assert(0); + assert(u != 0); + if (u == 0) + assert(0); + + i = 10; + i = (0 > i/2) ? 0 : i/2; + assert(i == 5); + p = &i; + p += 1; + assert((unsigned char *)p - (unsigned char *)&i == sizeof(i)); + i = 47; + i %= 15; + assert(i == 2); + i *= 15; + assert(i == 30); + i += 2; + assert(i == 32); + i -= 7; + assert(i == 25); + i /= 6; + assert(i == 4); + i |= 3; + assert(i == 7); + i ^= 2; + assert(i == 5); + i &= 6; + assert(i == 4); + i <<= 3; + assert(i == 32); + i >>= 2; + assert(i == 8); + i = 17; + i %= -3; + assert(i == 2); + i = -17; + i %= -3; + assert(i == -2); + i = -17; + i %= -3; + assert(i == -2); + + if (i == 0) strlen(""); /* break up basic blocks */ + + /* special code is generated for (int % 2) */ + i = i % 2; + assert(i == 0); + i = -3; + i = i % 2; + assert(i == -1); + i = 3; + i = i % 2; + assert(i == 1); + i = 2; + i = i % 2; + assert(i == 0); + + i = 43; + i = -i; + assert(i == -43); + i = ~i; + assert(i == 42); + i = !!i; + assert(i == 1); + i = !i; + assert(i == 0); +} + +void iarith() +{ int i,*p; + char c; + unsigned u; + + c = 47; + i = 63; + assert(c - i == -16); + assert(c * i == 2961); + i = 4; + assert((c << i) == 752); + u = 0; + assert(u >= 0); + assert(u <= 0); + if (u < 0 || u > 0) + assert(0); + assert(u == 0); + if (u != 0) + assert(0); + u = 1; + assert(u > 0); + assert(u >= 0); + if (u < 0 || u <= 0) + assert(0); + assert(u != 0); + if (u == 0) + assert(0); + + i = 10; + i = (0 > i/2) ? 0 : i/2; + assert(i == 5); + p = &i; + p += 1; + assert((unsigned char *)p - (unsigned char *)&i == sizeof(i)); + i = 47; + i %= 15; + assert(i == 2); + i *= 15; + assert(i == 30); + i += 2; + assert(i == 32); + i -= 7; + assert(i == 25); + i /= 6; + assert(i == 4); + i |= 3; + assert(i == 7); + i ^= 2; + assert(i == 5); + i &= 6; + assert(i == 4); + i <<= 3; + assert(i == 32); + i >>= 2; + assert(i == 8); + i = 17; + i %= -3; + assert(i == 2); + i = -17; + i %= -3; + assert(i == -2); + i = -17; + i %= -3; + assert(i == -2); + + if (i == 0) strlen(""); /* break up basic blocks */ + + /* special code is generated for (int % 2) */ + i = i % 2; + assert(i == 0); + i = -3; + i = i % 2; + assert(i == -1); + i = 3; + i = i % 2; + assert(i == 1); + i = 2; + i = i % 2; + assert(i == 0); + + i = 43; + i = -i; + assert(i == -43); + i = ~i; + assert(i == 42); + i = !!i; + assert(i == 1); + i = !i; + assert(i == 0); +} + +void larith() +{ long i,b,*p; + unsigned long u; + int j; + + (int) i; /* this was a bug in 1.11 */ + u = 0; + assert(u >= 0); + assert(u <= 0); + if (u < 0 || u > 0) + assert(0); + assert(u == 0); + if (u != 0) + assert(0); + u = 1; + assert(u > 0); + assert(u >= 0); + if (u < 0 || u <= 0) + assert(0); + assert(u != 0); + if (u == 0) + assert(0); + + i = 10; + i = (0 > i/2) ? 0 : i/2; + assert(i == 5); + p = &i; + p += 1; + assert((unsigned char *)p - (unsigned char *)&i == sizeof(i)); + i = 47; + i %= 15; + assert(i == 2); + i *= 15; + assert(i == 30); + i += 2; + assert(i == 32); + i -= 7; + assert(i == 25); + i /= 6; + assert(i == 4); + i |= 3; + assert(i == 7); + i ^= 2; + assert(i == 5); + i &= 6; + assert(i == 4); + i <<= 3; + assert(i == 32); + i >>= 2; + assert(i == 8); + i = 17; + i %= -3; + assert(i == 2); + i = -17; + i %= -3; + assert(i == -2); + i = -17; + i %= -3; + assert(i == -2); + i = 1; + b = 2; + i <<= 4; + b <<= 4; + assert(i == 0x10); + assert(b == 0x20); + + i = 43; + i = -i; + assert(i == -43); + i = ~i; + assert(i == 42); + i = !!i; + assert(i == 1); + i = !i; + assert(i == 0); + + i = 4; + i <<= 8; + assert(i == 1024); + j = 8; + i <<= j; + assert(i == 262144); + i = 4; + i = i << 7; + assert(i == 512); + j = 7; + i = i << j; + assert(i == 65536); + + i = 40000L; + i *= 20000; + assert(i == 800000000); + + u = 0x12348756; + j = (u & 0x0000FF00) >> 8L; + assert(j == 0x87); + + u = 4294967295u; + assert(u == 0xFFFFFFFF); +} + +/****************************************************/ + +void cdnot() +{ int a,b,c,d; + int testbool(int,int); + + if (!strlen) + assert(0); + assert(strlen); + a = 5; + // !a && assert(0); // gcc errors on this and the next one + // !!a || assert(0); + b = !a; + assert(b == 0); + b = !!a; + assert(b == 1); + assert((b = !a) + 1 == 1); + assert((b = !!a) == 1); + c = 7; + d = 8; + a = c * d + 3; + if ((b = !!a) != 0) + assert(c * d + 3 == 59); + else + assert(0); + + testbool(-1,0); + testbool(0,1); + testbool(5,1); + testbool(10,1); + testbool(11,0); +} + +#define inrange(a,x,b) ((a <= x) && (x <= b)) + +void testbool(int val, int expect) +{ + int x, y; + int dec; + + /*printf("val = %d, expect = %d\n",val,expect);*/ + + /* combination */ + dec = (x = (!(y = inrange(0, val, 10))) | !(inrange(0, val, 10))) ? 0 : 1; + /*printf("\nx = %d, y = %d, dec = %d\n", x, y, dec);*/ + assert(x == (expect^1) && y == expect && dec == expect); + + /* NOT operator and logical | */ + dec = (x = (!(y = inrange(0, val, 10))) || !(inrange(0, val, 10))) ? 0 : 1; + /*printf("\nx = %d, y = %d, dec = %d\n", x, y, dec);*/ + assert(x == (expect^1) && y == expect && dec == expect); + + /* bitwise | -- no NOT operator */ + dec = (x = ((y = inrange(0, val, 10))) | (inrange(0, val, 10))) ? 1 : 0; + /*printf("\nx = %d, y = %d, dec = %d\n", x, y, dec);*/ + assert(x == expect && y == expect && dec == expect); +} + +/************************************************************/ + +int E1() { return 2; } +int E0() { return 0; } +int EN() { assert(0); } +#define e1 E1() +#define e0 E0() +#define en EN() + +void testelloglog() +{ + int e; + int x; + + /* This doesn't optimize as well as it should */ + + /* e1 || 1 => e1 , 1 */ + x = 0; + e = e1 || (++x,1); + assert(e == 1 && x == 0); + e = e0 || (++x,1); + assert(e == 1 && x == 1); + + /* e1 || 0 => bool e1 */ + x = 0; + e = e1 || (++x,0); + assert(e == 1 && x == 0); + e = e0 || (++x,0); + assert(e == 0 && x == 1); + + /* (x,1) || e2 => (x,1),1 */ + x = 0; + e = (++x,1) || en; + assert(e == 1 && x == 1); + + /* (x,0) || e2 => (x,0),(bool e2) */ + x = 0; + e = (++x,0) || e1; + assert(e == 1 && x == 1); + e = (++x,0) || e0; + assert(e == 0 && x == 2); + + /* e1 && (x,1) => e1 ? ((x,1),1) : 0 */ + x = 0; + e = e1 && (++x,5); + assert(e == 1 && x == 1); + e = e0 && (++x,1); + assert(e == 0 && x == 1); + + /* e1 && (x,0) => e1 , (x,0) */ + x = 0; + e = e1 && (++x,0); + assert(e == 0 && x == 1); + e = e0 && (++x,1); + assert(e == 0 && x == 1); + + /* (x,1) && e2 => (x,1),bool e2 */ + x = 0; + e = (++x,5) && e1; + assert(e == 1 && x == 1); + e = (++x,4) && e0; + assert(e == 0 && x == 2); + + /* (x,0) && e2 => (x,0),0 */ + x = 0; + e = (++x,0) && en; + assert(e == 0 && x == 1); +} + +#undef e1 +#undef e0 +#undef en + +/*************************************/ + +void ptrarith() +{ + char *pc; + typedef char odd[3]; + odd a[10],*p,*q; + typedef char even[4]; + even b[10],*r,*s; + static int cc[10] = {1,2,3,4,5,6,7,8,9,10}; + static int (*bb)[] = &cc; /* should generate */ + + pc = (char *) -1U; + pc = (char *) -1; + + p = a + 5; + q = a; + assert(q - p == -5); + assert(p - q == 5); + r = b + 7; + s = b; + assert(s - r == -7); + assert(r - s == 7); + + assert((*bb)[5] == 6); +} + +/* strange results with D and P models */ + +char thing[] = "abcdefg"; + +void ptrs2a(p,n) +char *p; +int n; +{ + assert(n == 4); + assert(p == thing); +} + +void ptrs2() +{ + char *p; + long x; + + p = thing+4; + ptrs2a(thing,p-thing); + x = (long) thing; + p = thing; + assert(x == (long) p); +} + +void cdeq() +{ unsigned char c; /* unsigned 8 bits */ + signed char s; /* signed 8 bits */ + short i; /* signed 16 */ + unsigned short u; /* unsigned 16 */ + long l; + unsigned long ul; + + c = -1; + i = c; /* c should not become signed */ + assert(i == 255); + assert((c = (unsigned char)0x100) == 0); + assert((c = (unsigned char)0x100) + 1 == 1); + c = (unsigned char)0x100; + assert(c == 0); + assert((s = (signed char)0x200) == 0); + s = (signed char)0x200; + assert(s == 0); + assert((c = (signed char)0x280) == 0x80); + c = (unsigned char)0x280; + assert(c == 0x80); + assert((s = (signed char)0x280) == ~0x7F); + s = (signed char)0x280; + assert(s == ~0x7F); + assert(c == 0x80); + assert(s == ~0x7F); + + assert((u = (unsigned short)0x10000L) == 0L); + assert((u = (unsigned short)0x10000L) + 1L == 1); + u = (unsigned short)0x10000; + assert(u == 0); + assert((i = (short)0x20000) == 0L); + i = (short)0x20000; + assert(i == 0); + assert((u = (unsigned short)0x28000) == 0x8000L); + u = (unsigned short)0x28000; + assert(u == 0x8000L); + assert((i = (short)0x28000) == 0xFFFF8000); + i = (short)0x28000; + assert(i == 0xFFFF8000); + assert(u == 0x8000); + assert(i == (short) 0x8000); +} + +void cdopeq() +{ unsigned char c; /* unsigned 8 bits */ + signed char s; /* signed 8 bits */ + short i,*pi; /* signed 16 */ + unsigned short u; /* unsigned 16 */ + long l; + unsigned long ul; + + l = 500; + i = (l /= 50) != 0; + assert(l == 10 && i == 1); + l = 0x10000; + i = 10; + i /= l; + assert(i == 0); + l += 15; + i = 30; + i /= l; + assert(i == 0); + pi = &i; + i = 30; + *pi++ /= 0x10000 + 15; + assert(i == 0); + assert(pi == &i + 1); + + s = 0x83; + s >>= 1; + assert(s == (0xC1 | ~0xFF)); + i = (*(unsigned char*)&s >>= 9); + assert(s == 0); + assert(i == s); + c = 5; + if (!(c >>= 1)) + assert(0); + i = 0x8300; + i >>= 1; + assert(i == (0xC180 | ~0xFFFF)); + l = (i >>= (int) 17L); + assert(i == -1); + assert(l == i); + u = 5; + if (!(u >>= 1L)) + assert(0); + u = 3; + i = 7; + i <<= u; + assert(i == 56); + + { char a[5],*p; + + l = 543647; + strcpy(a,"wxyz"); + p = a; + p[1] = 's'; + l /= 10; + i = strcmp(p,"wsyz"); + assert(i == 0); + p[0] = 'a'; p[1] = 'b'; p[2] = 'c'; + i = strcmp(p,"abcz"); + assert(i == 0); + assert(l == 54364); + } +} + +char buffer[10] = {0,1,2,3}; +char *flags = (char *)(buffer); +char fname[] = "abc.c"; +char abc[] = "abc"; + +void cdcmp() +{ int i,level; + unsigned short u; + long long l; + unsigned long long ul; + char c,*p; + + l = 0xFFFFFFFFFFFF8002; + u = 0x8001; + assert(l < u); + assert(l > (short) u); + ul = l - 2; + assert(ul > u); + assert(ul < (short) u); + + i = 5; + level = i - 4; + c = 7; + i = c < i; + assert(i == 0); + i = 1; + if (level != 1) + assert(0); + for (i = strlen(fname); i;) + { --i; + if (fname[i] == '.') + fname[i] = 0; + } + assert(strcmp(fname,abc) == 0); + p = "\0\1\2\3"; + assert(memcmp(flags,p,4) == 0); +} + +/***********************/ + +static int cdpost_f1(char *); + +void cdpost() +{ + { + long l = 10; + + for ( ; l--; ) + ; + assert(l == -1); + } + { + struct test { char *ptr; } *t,s; + + t = &s; + t->ptr++; + cdpost_f1(t->ptr); + } +} + +static int cdpost_f1(p) +char *p; +{ + return 0; +} + +/***********************/ + +void question() +{ + int d,x,y; + + y = -3; + d = atoi("-25"); + x = y > 0 ? ((d > 0) ? d : 0) : 0; + assert(x == 0); + + { static char array1[4] = "abcd"; + static int array2[2]; + char c; + + c = array1[array2 ? 3 : 1]; + assert(c == 'd'); + c = array1["hello" ? 1 : 3]; + assert(c == 'b'); + } +} + +typedef struct BLCB { int a; int nextfree; } BLCBtype; + +void scodelem() +{ BLCBtype *b; + int putblb(int,BLCBtype *); + + /* make sure b is in another segment */ + b = (BLCBtype *) malloc(sizeof(BLCBtype)); + b->nextfree = 5; + putblb((unsigned char)'c',b); + assert(b->nextfree == (5 + 1 + 0x1234)); + free(b); +} + +void putblb(byt, cbptr) +int byt; +register BLCBtype *cbptr; +{ + register char *cp; + + char str[9] = "12345678"; + cp = str; + *(cp + cbptr->nextfree++) = byt; + cbptr->nextfree += 0x1234; + assert(cp[5] == 'c'); +} + +void cdind() +{ int i; + char c,*p; + /* make sure pindex and abc are in different segments in D model */ + struct ABC + { short pnum; + char *fptr; + char pfname[10]; + char title[12]; + } *pindex[5]; + static struct ABC abc = {1,0,"abc","def"}; + + i = 3; + pindex[3] = &abc; + c = *(pindex[i]->pfname); + assert(c == 'a'); + p = pindex[i]->pfname; + assert(p[1] == 'b'); + + { char byt; + struct { int a; char *readblock; int nextread; } cb,*cbptr; + + cbptr = &cb; + cb.readblock = "0123456"; + cb.nextread = 3; + byt = *(cbptr->readblock + cbptr->nextread++); + assert(byt == '3'); + assert(cbptr->nextread == 4); + } + +#if 0 // TODO ImportC + { struct ABC { char a,b,*l_text; } abc; + static struct { int w_doto; struct ABC *w_dotp; } curw,*curwp; + + abc.l_text = "012345"; + curwp = &curw; + curw.w_dotp = &abc; + curw.w_doto = 2; + c = curwp->w_dotp->l_text[curwp->w_doto]&0xFF; + assert(c == '2'); + } +#endif +} + +void logexp() +{ +#define t1(a,b,c) ((b<=a) ? ((c=a)) + int x,y,z,i; + static int cases[6][4] = + { 1,2,3, 0, + 1,3,2, 0, + 2,1,3, 0, + 2,3,1, 1, + 3,1,2, 1, + 3,2,1, 0 + }; + + for (i = 0; i < sizeof(cases)/sizeof(cases[0]); i++) + { if (!t1(cases[i][0],cases[i][1],cases[i][2])) + assert(cases[i][3] == 1); + else + assert(cases[i][3] == 0); + } +} + +/***************************** + * Test parameter passing. + */ + +int arrayparam[10]; +int *ptrparam; + +void param() +{ void paramtest(int,int,double,double); + int paramtest2(int *); + char c1,c2,c3,*pc; + int i,*pi; + float f,*pf; + double d,*pd; + for (i = 0; i < 10; i++) + arrayparam[i] = i; + ptrparam = arrayparam; + paramtest2(arrayparam); + + paramtest(47,10000,32,64); + c1 = 1; + c2 = 47; + c3 = 48; + i = 10000; + f = 32; + d = 64; + paramtest(c2,i,f,d); + pc = &c2; + pi = &i; + pf = &f; + pd = &d; + paramtest(*pc++,*pi++,*pf++,*pd++); + paramtest(*--pc,*--pi,*--pf,*--pd); +} + +void paramtest2(pa) +int *pa; +{ int i; + + assert(pa == ptrparam); + assert(pa == arrayparam); + for (i = 0; i < 10; i++) + { assert(pa[i] == i); + assert(ptrparam[i] == i); + } +} + +void paramtest(c,i,f,d) +char c; +int i; +float f; +double d; +{ + assert(c == 47); + assert(i == 10000); +#if 0 // TODO ImportC + assert(f == 32); + assert(d == 64); +#endif +} + +/* This used to give dlc2 bugs */ +int *function (p, t) +int *p, t; +{ + return (p - (t? 0: 1)); +} + +/* So did this */ +void dlc2bugs() +{ + typedef struct { unsigned x; } THING; + THING *p; + long l; + + l /= (long) p->x; + l = (long) p->x; + l /= (long) (p->x + 1); +} + +/* And this */ +void dlc2bugs_2(w) +char *w; +{ + int i,k,t; + + t = w[i]; + k = t/10; + w[i] = t - k*10; +} + +/* And this */ +void lidata_size( hFrom ) +char** hFrom; +{ unsigned uSize; + + (*hFrom) += 4; + uSize = *(*hFrom); + (*hFrom) += 1; +} + + +/***** Test math in register variables *****/ + +void regmath() +{ + short int j,k; + long i; + +/* time_0();*/ + + for(i=0; i<4L; ++i){ + j = 240; k = 15; + +/* test byte-byte combinations */ + j = (k * ( j/k) ); + j = (k * ( j/k) ); + j = ( k+k+k+k+ k+k+k+k+ k+k+k+k+ k+k+k+k ); + k = ( j -k-k-k-k -k-k-k-k -k-k-k-k -k-k-k); + +/* test byte_word combinations */ + j = ( k << 4); k = ( k << 4); + j = ( k * (j / k) ); + j = ( k+k+k+k+ k+k+k+k+ k+k+k+k+ k+k+k+k ); + k = ( j -k-k-k-k -k-k-k-k -k-k-k-k -k-k-k); + + +/* test word - word combinations */ + j = ( k << 4); k = ( k << 4); + j = (k * ( j/k) ); + j = (k * ( j/k) ); + j = ( k+k+k+k+ k+k+k+k+ k+k+k+k+ k+k+k+k ); + k = ( j -k-k-k-k -k-k-k-k -k-k-k-k -k-k-k); + } +/* time_n();*/ + assert(j == -4096 && k == 3840); +} + +void regmath_386() +{ + long long int j,k; + long long i; + +/* time_0();*/ + + for(i=0; i<4L; ++i){ + j = 240; k = 15; + +/* test byte-byte combinations */ + j = (k * ( j/k) ); + j = (k * ( j/k) ); + j = ( k+k+k+k+ k+k+k+k+ k+k+k+k+ k+k+k+k ); + k = ( j -k-k-k-k -k-k-k-k -k-k-k-k -k-k-k); + +/* test byte_word combinations */ + j = ( k << 4); k = ( k << 4); + j = ( k * (j / k) ); + j = ( k+k+k+k+ k+k+k+k+ k+k+k+k+ k+k+k+k ); + k = ( j -k-k-k-k -k-k-k-k -k-k-k-k -k-k-k); + + +/* test word - word combinations */ + j = ( k << 4); k = ( k << 4); + j = (k * ( j/k) ); + j = (k * ( j/k) ); + j = ( k+k+k+k+ k+k+k+k+ k+k+k+k+ k+k+k+k ); + k = ( j -k-k-k-k -k-k-k-k -k-k-k-k -k-k-k); + } +/* time_n();*/ + assert(j == 61440 && k == 3840); +} + +/****************************** + * Test for fixed bug in getlvalue(). + */ + +struct { + int start; + int end; + double *value; + int fstart, fend; + unsigned forms; + } getls[5]; + +double getlfunc(c, r) int c, r; +{ + return getls[c].value[r+1-getls[c].start]; +} + +void getlvalue() +{ + double value[3]; + + value[2] = 7.6; + getls[3].value = value; + getls[3].start = 16; + assert(getlfunc(3,17) == 7.6); +} + +/* Test fixups */ + +extern int fix1; +int *pfix1 = &fix1; +int fix1 = 57; + +extern int fix2; +int fix2 = 23; +int *pfix2 = &fix2; + +extern int fix3; +int *pfix3 = &fix3; +int fix3; + +void testfixups() +{ + assert(pfix1 == &fix1); + assert(*pfix1 == 57); + assert(pfix2 == &fix2); + assert(*pfix2 == 23); + assert(pfix3 == &fix3); + assert(*pfix3 == 0); +} + +/*****************************************************/ +/* Compile with -o+space -ml get "ZTC bug 9542" */ +int cc; + +void seed_player_in_list(int *a,int *b) { } + +int seed6() { return 0; } + +void seed_user_interface (int **list1, int **list3) +{ + int index1, index2, index3, *index; + int double_flag; + + double_flag = seed6 (); + index1 = index2 = index3 = 0; + index = &index1; + do + { + if (index == &index1) + { + seed_player_in_list (list1 [index1], + double_flag? list1 [index1 + 1]: 0); + } else + if (index == &index2) + { + seed_player_in_list (list3 [index3], + double_flag? list3 [index3 + 1]: 0); + } + } while (cc != 27); +} + +/***********************************************/ + +void testdiv1() +{ + static int y; + int i; + int rem, quo; + + y = 10; + + for (i = -10000; i < (int)0x7FFF; i++) + { + assert(i / 10 == i / y); + } + + for (i = -10000; i < (int)0x7FFF; i++) + { + assert(i % 10 == i % y); + } + + for (i = -10000; i < (int)0x7FFF; i++) + { + rem = i % 10; + quo = i / 10; + assert(rem == i % y); + assert(quo == i / y); + } + + for (i = -10000; i < (int)0x7FFF; i++) + { + rem = i % y; + quo = i / y; + assert(rem == i % 10); + assert(quo == i / 10); + } +} + +void testdiv2() +{ + static int y; + int i; + int rem, quo; + + y = 8; + + for (i = -10000; i < (int)0x7FFF; i++) + { + assert(i / 8 == i / y); + } + + for (i = -10000; i < (int)0x7FFF; i++) + { + assert(i % 8 == i % y); + } + + for (i = -10000; i < (int)0x7FFF; i++) + { + rem = i % 8; + quo = i / 8; + assert(rem == i % y); + assert(quo == i / y); + } + + for (i = -10000; i < (int)0x7FFF; i++) + { + rem = i % y; + quo = i / y; + assert(rem == i % 8); + assert(quo == i / 8); + } +} + +/***********************************************/ + +void test1() +{ + int res = 2; + int vl = 0; + double vr = 2.0; + int i = 0; + + if( res == (vl += vr)) + { + i |= 1; + } + assert(i == 1); + vl = 0; + if( res == (vl += 2.f)) + { + i |= 2; + } + assert(i == 3); +} + + +void test2() +{ +#if 0 // TODO ImportC +#ifdef __cplusplus + bool res = 2; + bool vl = 0; +#else + _Bool res = 2; + _Bool vl = 0; +#endif + double vr = 2.0; + + int i = 0; + + if( res == (vl += vr)) + { + i |= 1; + } + assert(i == 1); + i = 0; + vl = 0; + if( res == (vl += 2.f)) + { + i |= 2; + } + assert(i == 2); +#endif +} + + +/***********************************************/ + + +struct HH +{ + long h; + int mode; +}; + + +double foohh(struct HH *h, unsigned long ul) +{ + if (!h->h) + { + h->mode = -1; + return 0.0; + } + h->mode = 2; + return ul; +} + +/***********************************************/ + +void test3() +{ +#if !__cplusplus +#if 0 + int res = 2; + int vl; + double vr = 2.0; +#elif 1 + _Bool res = 1; + struct mixed { + unsigned char : 5; + unsigned char b : 3; + } mixed; +#define vl mixed.b + double vr = 2.0; +#else + _Bool res = 1; + _Bool vl; + double vr = 2.0; +#endif + + vl = 0; + if( res == (vl += vr) ){ } + if( res == (vl += 2.f) ){ } + if( res == (vl += 2.0) ){ } + printf("vl = %d\n", vl); +#if 0 // TODO ImportC + assert(vl == 6); +#endif +#undef vl +#endif +} + + +/***********************************************/ + +struct Foo4 { int quot; int rem; }; + +struct Foo4 foo4 = { .quot = 2, .rem = -1 }; +struct Foo4 bar4 = { .rem = 3, .quot = 7 }; +struct Foo4 abc4 = { .rem = 3, .quot = 9 }; +struct Foo4 def4 = { .quot = 2, 6 }; + +void test4() +{ + assert(foo4.quot == 2); + assert(foo4.rem == -1); + + assert(bar4.quot == 7); + assert(bar4.rem == 3); + + assert(abc4.quot == 9); + assert(abc4.rem == 3); + + assert(def4.quot == 2); + assert(def4.rem == 6); +} + +/***********************************************/ + +union Jack5 { char c; double d; } jack = { .d = 53.42 }; + +int a5[100] = {1,3,5,7,9, [100-5] = 8, 6,5,2,4}; +int b5[8] = {1,3,5,7,9, [3] = 8, 6,5,2,4}; +int c5[8] = {1,3,5,7,9, [3] = 8, 6,5,2,}; +int d5[8] = {1,3,5,7,9, [3] = 8, 6,5,2}; +int e5[] = {1,3,5,7,9, [3] = 8, 6,5,2}; + +//struct Bar5 { int a[3], b; } w5[2] = { [0].a = {1}, [1].a[0] = 2 }; // TODO ImportC + +void test5() +{ +#if 0 // TODO ImportC + int i; + + assert(jack.d == 53.42); + + assert(a5[0] == 1); + assert(a5[1] == 3); + assert(a5[2] == 5); + assert(a5[3] == 7); + assert(a5[4] == 9); + + for (i = 5; i < 95; i++) + assert(a5[i] == 0); + + assert(a5[95] == 8); + assert(a5[96] == 6); + assert(a5[97] == 5); + assert(a5[98] == 2); + assert(a5[99] == 4); + + assert(b5[0] == 1); + assert(b5[1] == 3); + assert(b5[2] == 5); + assert(b5[3] == 8); + assert(b5[4] == 6); + assert(b5[5] == 5); + assert(b5[6] == 2); + assert(b5[7] == 4); + + assert(c5[0] == 1); + assert(c5[1] == 3); + assert(c5[2] == 5); + assert(c5[3] == 8); + assert(c5[4] == 6); + assert(c5[5] == 5); + assert(c5[6] == 2); + assert(c5[7] == 0); + + assert(d5[0] == 1); + assert(d5[1] == 3); + assert(d5[2] == 5); + assert(d5[3] == 8); + assert(d5[4] == 6); + assert(d5[5] == 5); + assert(d5[6] == 2); + assert(d5[7] == 0); + + printf("e dim = %d\n", (int)(sizeof(e5) / sizeof(e5[0]))); + assert(sizeof(e5) / sizeof(e5[0]) == 7); + assert(e5[0] == 1); + assert(e5[1] == 3); + assert(e5[2] == 5); + assert(e5[3] == 8); + assert(e5[4] == 6); + assert(e5[5] == 5); + assert(e5[6] == 2); + + assert(w5[0].a[0] == 1); + assert(w5[1].a[0] == 2); +#endif +} + +/***********************************************/ + +int foo6a(int a) +{ + return (a > 5) && (a < 100); +} + +int foo6b(int a) +{ + return (a >= 5) && (a <= 100); +} + +void test6() +{ + assert(foo6a(5) == 0); + assert(foo6a(6) == 1); + assert(foo6a(99) == 1); + assert(foo6a(100) == 0); + + assert(foo6b(4) == 0); + assert(foo6b(5) == 1); + assert(foo6b(100) == 1); + assert(foo6b(101) == 0); +} + +/***********************************************/ + +int foo7(long c, long d) +{ + return ((c & 0x80000000) ^ (d & 0x80000000)) == 0; +} + +void test7() +{ + int i = foo7(0x80000000, 0x7FFFFFFF); + assert(i == 0); + i = foo7(0x80000000, 0x8FFFFFFF); + assert(i == 1); +} + +/***********************************************/ + +#ifdef __cplusplus + typedef bool bool_t; +#else + typedef _Bool bool_t; +#endif + +bool_t foo8a(signed char a) { return a % 2; } +bool_t foo8b(signed char a) { return a & 1; } +bool_t foo8c(signed char a, signed char b) { return a % b; } + +bool_t foo8d(short a) { return a % 2; } +bool_t foo8e(short a) { return a & 1; } +bool_t foo8f(short a, short b) { return a % b; } + +bool_t foo8g(long a) { return a % 2; } +bool_t foo8h(long a) { return a & 1; } +bool_t foo8i(long a, long b) { return a % b; } + +#if __INTSIZE__ == 4 +bool_t foo8j(long long a) { return a % 2; } +bool_t foo8k(long long a) { return a & 1; } +bool_t foo8l(long long a, long long b) { return a % b; } +#endif + +void test8() +{ + int i; + for (i = -5; i <= 5; i++) + { + printf("%d %d %d\n", foo8a(i), foo8b(i), foo8c(i,2)); + assert(foo8a(i) == foo8b(i)); + assert(foo8b(i) == foo8c(i, 2)); + + assert(foo8d(i) == foo8e(i)); + assert(foo8e(i) == foo8f(i, 2)); + + assert(foo8g(i) == foo8h(i)); + assert(foo8h(i) == foo8i(i, 2)); + +#if __INTSIZE__ == 4 + assert(foo8j(i) == foo8k(i)); + assert(foo8k(i) == foo8l(i, 2)); +#endif + } +} + +/***********************************************/ + +unsigned long foo1(unsigned char *data) +{ + return + ((unsigned long)data[0]<< 0) | + ((unsigned long)data[1]<< 8) | + ((unsigned long)data[2]<< 16) | + ((unsigned long)data[3]<< 24); +} + + +unsigned long foo2(unsigned char *data) +{ + return + ((unsigned long)data[0]<< 24) | + ((unsigned long)data[1]<< 16) | + ((unsigned long)data[2]<< 8 ) | + ((unsigned long)data[3]<< 0 ); +} + +void test9() +{ + unsigned long x1 = 0x01234567; + x1 = (unsigned long)foo1((unsigned char *)&x1); + assert(x1 == 0x01234567); + x1 = (unsigned long)foo2((unsigned char *)&x1); + assert(x1 == 0x67452301); +} + +/***********************************************/ + +int noreturnx() { assert(0); } +#pragma noreturn(noreturnx) + +void returnx() { } + +int test10a(unsigned i) +{ + assert(i < 10); + return i < 10; +} + +int test10b(unsigned i) +{ + (i < 10) && noreturnx(); + return i < 10; +} + +int test10c(unsigned i) +{ + (i < 10) ? returnx() : noreturnx(); + return i < 10; +} + +int test10d(unsigned i) +{ + (i < 10) ? noreturnx() : returnx(); + return i < 10; +} + +void test10() +{ + int i = test10a(8); + assert(i); + i = test10b(10); + assert(!i); + i = test10c(8); + assert(i); + i = test10d(10); + assert(!i); +} + +/***********************************************/ + +int test11a(int i) +{ + return i < 3 ? noreturnx() : 1; +} + +int test11b(int i) +{ + return i < 3 ? 1 : noreturnx(); +} + +void test11() +{ + int i = test11a(8); + assert(i); + i = test11b(2); + assert(i); +} + +/***********************************************/ + +/* No divide-by-zero constant folding errors + * https://issues.dlang.org/show_bug.cgi?id=20906 + */ + +int test12() +{ + int x = 0; + int a = x && 1 / x; + int b = !x || 1 / x; + int c = x ? 1 / x : 1; + int d = !x ? 1 : 1 / x; + return a | b | c; +} + +/***********************************************/ + + +int main() +{ + register unsigned char rc0; + register signed char rs0; + register int ri0; + register long rl0; + register unsigned char rC0; + register unsigned int rI0; + register unsigned long rL0,rL1; + register float rf0; + register double rd0; + + printf("Test file %s\n",__FILE__); + align(); + rL0 = 4294967295UL; + rc0 = 127; + rL0 = rL0 / rc0++; + assert(rL0 == 33818640L); + testcppcomment(); + cdind(); + elemi(); + elems(); + eleml(); + elemc(); + bitwise(); + bitwiseshort(); + carith(); + sarith(); + iarith(); + larith(); + ptrarith(); + ptrs2(); + cdnot(); + testelloglog(); + cdeq(); + cdopeq(); + cdcmp(); + cdpost(); + question(); + scodelem(); + logexp(); + param(); + regmath(); + regmath_386(); + getlvalue(); + testfixups(); + testdiv2(); + test1(); + test2(); + test3(); + test4(); + test5(); + test6(); + test7(); + test8(); + test9(); + test10(); + test11(); + test12(); + + printf("SUCCESS\n"); + return 0; +} diff --git a/tests/dmd/runnable/funclit.d b/tests/dmd/runnable/funclit.d index 253df8feec..bce4e9aacb 100644 --- a/tests/dmd/runnable/funclit.d +++ b/tests/dmd/runnable/funclit.d @@ -383,16 +383,6 @@ void test6714() assert(bar6714y((a, b){ return a; }) == 2); } -/***************************************************/ -// https://issues.dlang.org/show_bug.cgi?id=7193 - -void test7193() -{ - static assert(!__traits(compiles, { - delete a => a; - })); -} - /***************************************************/ // https://issues.dlang.org/show_bug.cgi?id=7207 // on CastExp @@ -1331,7 +1321,6 @@ int main() test11(); test3235(); test6714(); - test7193(); test7202(); test7288(); test7499(); diff --git a/tests/dmd/runnable/future.d b/tests/dmd/runnable/future.d index e0ef466395..9d09f6b2a8 100644 --- a/tests/dmd/runnable/future.d +++ b/tests/dmd/runnable/future.d @@ -1,8 +1,9 @@ /* PERMUTE_ARGS: +REQUIRED_ARGS: -verrors=simple TEST_OUTPUT: --- -runnable/future.d(16): Deprecation: method `future.B.msg` implicitly overrides `@__future` base class method; rename the former -runnable/future.d(11): base method `future.A.msg` defined here +runnable/future.d(17): Deprecation: method `future.B.msg` implicitly overrides `@__future` base class method; rename the former +runnable/future.d(12): base method `future.A.msg` defined here --- */ diff --git a/tests/dmd/runnable/implicit.d b/tests/dmd/runnable/implicit.d index 75b992c5c1..802e382c52 100644 --- a/tests/dmd/runnable/implicit.d +++ b/tests/dmd/runnable/implicit.d @@ -1,8 +1,9 @@ /* +REQUIRED_ARGS: -verrors=simple TEST_OUTPUT: --- -runnable/implicit.d(162): Deprecation: slice of static array temporary returned by `pureMaker3c()` assigned to longer lived variable `z1` -runnable/implicit.d(163): Deprecation: slice of static array temporary returned by `pureMaker3c()` assigned to longer lived variable `z2` +runnable/implicit.d(163): Deprecation: slice of static array temporary returned by `pureMaker3c()` assigned to longer lived variable `z1` +runnable/implicit.d(164): Deprecation: slice of static array temporary returned by `pureMaker3c()` assigned to longer lived variable `z2` --- RUN_OUTPUT: diff --git a/tests/dmd/runnable/lexer.d b/tests/dmd/runnable/lexer.d index c6ca550011..0e1068a428 100644 --- a/tests/dmd/runnable/lexer.d +++ b/tests/dmd/runnable/lexer.d @@ -1,14 +1,3 @@ -// REQUIRED_ARGS: -/* -TEST_OUTPUT: ---- -runnable/lexer.d(86): Deprecation: `version( )` is deprecated, use version identifiers instead -runnable/lexer.d(87): Deprecation: `debug( )` is deprecated, use debug identifiers instead ---- -*/ - -/*********************************************************/ - void test6() { string s = q"(foo(xxx))"; @@ -82,12 +71,6 @@ void test8() /*********************************************************/ -// https://issues.dlang.org/show_bug.cgi?id=6584 -version(9223372036854775807){} -debug(9223372036854775807){} - -/*********************************************************/ - enum e13102=184467440737095516153.6L; /*********************************************************/ diff --git a/tests/dmd/runnable/link11069a.d b/tests/dmd/runnable/link11069a.d index e31cfa0bcf..429ae8da7e 100644 --- a/tests/dmd/runnable/link11069a.d +++ b/tests/dmd/runnable/link11069a.d @@ -1,5 +1,5 @@ // EXTRA_FILES: imports/std11069array.d imports/std11069container.d imports/std11069range.d imports/std11069typecons.d -// REQUIRED_ARGS: -noboundscheck +// REQUIRED_ARGS: -boundscheck=off // <-- To remove necessity of _D7imports13std11069array7__arrayZ class Bar diff --git a/tests/dmd/runnable/mars1.d b/tests/dmd/runnable/mars1.d index 34af4ee8c8..629e7d3392 100644 --- a/tests/dmd/runnable/mars1.d +++ b/tests/dmd/runnable/mars1.d @@ -2509,6 +2509,62 @@ void testDoWhileContinue() while(--i > 0); } +//////////////////////////////////////////////////////////////////////// +// https://github.com/dlang/dmd/issues/20574 + +int test20574x(int i, int y) +{ + return i ? y : y; +} + +void test20574() +{ + assert(test20574x(1, 2) == 2); + assert(test20574x(0, 2) == 2); +} + +//////////////////////////////////////////////////////////////////////// + +struct S8 +{ + int x,y,z; +} + +int test8x(S8 s) +{ + s = s; + return s.y; +} + +void test8() +{ + S8 s; + s.y = 2; + assert(test8x(s) == 2); +} + +//////////////////////////////////////////////////////////////////////// + +struct S9 +{ + int a,b; + ~this() { } +} + + +S9 test9x(ref S9 arg) +{ + return arg; +} + +void test9() +{ + S9 s; + s.b = 3; + S9 t = test9x(s); + assert(t.b == 3); +} + //////////////////////////////////////////////////////////////////////// int main() @@ -2610,6 +2666,9 @@ int main() test21816(); test21835(); testDoWhileContinue(); + test20574(); + test8(); + test9(); printf("Success\n"); return 0; diff --git a/tests/dmd/runnable/mixin1.d b/tests/dmd/runnable/mixin1.d index d36d3f1ec0..2cded33a54 100644 --- a/tests/dmd/runnable/mixin1.d +++ b/tests/dmd/runnable/mixin1.d @@ -100,6 +100,7 @@ template Foo2(T) mixin Foo2!(uint) B2; mixin Foo2!(long) C2; +mixin D2 = Foo2!(wchar); mixin Foo2!(int); void test2() @@ -107,6 +108,7 @@ void test2() B2.x2 = 3; assert(B2.x2 == 3); assert(C2.x2 == long.sizeof); + assert(D2.x2 == 2); // assert(x2 == int.sizeof); } @@ -284,6 +286,9 @@ void test11() int y = 8; mixin Foo11!(y) B; assert(B.abc() == 8); + + mixin C = Foo11!2; + assert(C.abc() == 2); } /*******************************************/ diff --git a/tests/dmd/runnable/newdel.d b/tests/dmd/runnable/newdel.d index 8ba7a0c44c..8888c751ca 100644 --- a/tests/dmd/runnable/newdel.d +++ b/tests/dmd/runnable/newdel.d @@ -41,11 +41,30 @@ void test1() assert(Foo.flags == 1); } +/*********************************************/ +// delete is no longer a keyword and can be used as an identifier + +enum E +{ + add, delete +} + +E delete() +{ + return E.delete; +} + +void test2() +{ + assert(delete() == E.delete); +} + /*********************************************/ int main() { test1(); + test2(); printf("Success\n"); return 0; diff --git a/tests/dmd/runnable/nogc.d b/tests/dmd/runnable/nogc.d index c9a58b7e74..c71866fc19 100644 --- a/tests/dmd/runnable/nogc.d +++ b/tests/dmd/runnable/nogc.d @@ -68,12 +68,37 @@ void test12936() @nogc /***********************/ +version(none) // Pending future enhancements: +void testIndexedArrayLiteral() @nogc +{ + int i = 2; + int x = [10, 20, 30, 40][i]; + assert(x == 30); + + enum arr = [100, 200, 300, 400][1 .. $]; + assert(arr[i] == 400); +} + +void testArrayLiteralLvalue() +{ + // https://github.com/dlang/dmd/pull/16784 + // Test that this array literal is *not* put on the stack because + // it gets its address taken + static int* getPtr(int i) => &[1, 2, 3][i]; + int* x = getPtr(1); + int* y = getPtr(1); + assert(x != y); +} + +/***********************/ + int main() { test1(); test3032(); test12642(); test12936(); + testArrayLiteralLvalue(); printf("Success\n"); return 0; diff --git a/tests/dmd/runnable/noreturn2.d b/tests/dmd/runnable/noreturn2.d index 997168cba0..79ed74e846 100644 --- a/tests/dmd/runnable/noreturn2.d +++ b/tests/dmd/runnable/noreturn2.d @@ -212,10 +212,26 @@ void testFuncCall() assert(WithDtor.destroyed == 1); } +// https://issues.dlang.org/show_bug.cgi?id=24701 +void testCast() +{ + noreturn foo; + try + auto a = cast(int)foo; + catch (Throwable e) + { + assert(e.msg == "Accessed expression of type `noreturn`"); + return; + } + + assert(0); +} + int main() { testDtors(); testAccess(); testFuncCall(); + testCast(); return 0; } diff --git a/tests/dmd/runnable/nrvo.d b/tests/dmd/runnable/nrvo.d index 5c653fe45a..bceba100e0 100644 --- a/tests/dmd/runnable/nrvo.d +++ b/tests/dmd/runnable/nrvo.d @@ -22,9 +22,35 @@ void test1() assert(&r == s1ptr); } +/***************************************************/ +// https://github.com/dlang/dmd/issues/20567 + +struct S2 +{ + int x; + this(ref S2 s) { x = s.x; } +} + +S2 returnRval(ref S2 arg1, ref S2 arg2, int i) +{ + return i ? arg1 : arg2; +} + +void test2() +{ + S2 s1, s2; + s1.x = 3; + s2.x = 4; + S2 s = returnRval(s1, s2, 0); + assert(s.x == 4); + s = returnRval(s1, s2, 1); + assert(s.x == 3); +} + /***************************************************/ void main() { test1(); + test2(); } diff --git a/tests/dmd/runnable/objc_autoselector.d b/tests/dmd/runnable/objc_autoselector.d new file mode 100644 index 0000000000..014aee62ac --- /dev/null +++ b/tests/dmd/runnable/objc_autoselector.d @@ -0,0 +1,73 @@ +// EXTRA_OBJC_SOURCES: +// REQUIRED_ARGS: -L-framework -LFoundation + +extern(Objective-C) +extern class NSObject +{ + static NSObject alloc(); + NSObject init(); + + @property NSString className() const; +} + +extern(Objective-C) +extern class NSString : NSObject +{ + override static NSString alloc(); + override NSString init(); + + @property const(char)* UTF8String() const; +} + +extern(Objective-C) +class MyClass : NSObject +{ + int x; + + override static MyClass alloc(); + override MyClass init() { x = 42; return this; } + + @property bool isFourtyTwo() => x == 42; + @property void isFourtyTwo(bool value) { x = value ? 42 : 0; } + + void myFunction(int a, int b, int c) + { + x = a + b + c; + } +} + +extern(C) void* object_getClass(NSObject obj); +extern(C) void* class_getInstanceMethod(void* cls, void* sel); +extern(C) void* method_getName(void* m); +extern(C) void* sel_registerName(const(char)* str); +extern(C) bool sel_isEqual(void* lhs, void* rhs); + +bool validateMethod(NSObject obj, const(char)* selName) +{ + auto sel = sel_registerName(selName); + + auto cls = object_getClass(obj); + if (auto mth = class_getInstanceMethod(cls, sel)) { + return sel_isEqual(sel, method_getName(mth)); + } + return false; +} + +void main() +{ + // Basic alloc & init + auto obj = NSObject.alloc.init; + assert(obj !is null); + + // Basic property + auto cname = obj.className(); + assert(cname !is null); + assert(cname.UTF8String()); + + // Properties + obj = MyClass.alloc().init(); + assert(obj !is null); + assert(validateMethod(obj, "isFourtyTwo")); // Case: isXYZ + assert(validateMethod(obj, "setFourtyTwo:")); // Case: isXYZ + assert(validateMethod(obj, "myFunction:b:c:")); // Case: Auto-gen function selector. +} diff --git a/tests/dmd/runnable/opover.d b/tests/dmd/runnable/opover.d index 253a7a54ce..4e34e52223 100644 --- a/tests/dmd/runnable/opover.d +++ b/tests/dmd/runnable/opover.d @@ -2,29 +2,13 @@ RUN_OUTPUT: --- i = 1 -Writer.opShl(char[]) -BinaryWriter.opShl(int) -a + 1 = 2 -1 + a = 2 -a + b = 3 -b + a = 3 i = 64 12 534 -A::opShl(int 4) -4A::opShl(char[]) - A::opShl(int 12) -12A::opShl(char[]) - -B::opShl_r(A) Success --- */ -// Test operator overloading -// Ignore deprecation warnings for D1 style operator overloading -// TRANSFORM_OUTPUT: remove_lines("Deprecation: `op") - import core.stdc.stdio; /**************************************/ @@ -996,20 +980,11 @@ struct S14343b void test14343() { - { - S14343a s, t; + S14343a s, t; - t = s; // OK - ++s; // OK - s++; // OK <- Error: cannot modify struct s S with immutable members - } - { - S14343b s; - ++s; - assert(s.i == 1); - s++; - assert(s.i == 2); - } + t = s; // OK + ++s; // OK + s++; // OK <- Error: cannot modify struct s S with immutable members } /**************************************/ @@ -1081,20 +1056,13 @@ void test20475() /**************************************/ int main() { - test1(); - test2(); - test3(); test4(); test5(); test6(); test7(); - test8(); test9(); - test10(); test11(); test12(); - test13(); - test14(); test15(); test1547(); test4953a(); diff --git a/tests/dmd/runnable/placenew.d b/tests/dmd/runnable/placenew.d new file mode 100644 index 0000000000..d90d10a5e7 --- /dev/null +++ b/tests/dmd/runnable/placenew.d @@ -0,0 +1,106 @@ +import core.stdc.stdio; +import core.stdc.stdlib; + +/*************************************************/ + +struct S +{ + float d; + int i; + char c; +} + +void test1() +{ + S s; + S* p = new (s) S(); + assert(p.i == 0 && p.c == 0xFF); +} + +void test2() +{ + S s; + S* p = new (s) S(i:3); + assert(p.i == 3 && p.c == 0xFF); +} + +/*************************************************/ + +struct S3 +{ + int i; + this(int i) { this.i = i + 3; } +} + +void test3() +{ + S3 s; + s.i = 20; + S3* p = new (s) S3(4); + assert(p.i == 7); +} + + +/*************************************************/ + +void test4() +{ + int i = 3; + int* p = new(i) int; + *p = 4; + assert(i == 4); + + p = new(i) int(7); + assert(i == 7); +} + +/*************************************************/ + +class C5 +{ + int i, j = 4; +} + +int test5() +{ + int[10] k; + C5 c = new(k) C5; + //printf("c.j: %d\n", c.j); + assert(c.j == 4); + assert(cast(void*)c == cast(void*)k.ptr); + return c.j; +} + +/*************************************************/ + +struct S6 +{ + int i = 1, j = 4, k = 9; +} + +ref void[T.sizeof] mallocate(T)() +{ + return *(cast(void[T.sizeof]*) malloc(T.sizeof)); +} + +void test6() +{ + S6* ps = new(mallocate!S6()) S6; + assert(ps.i == 1); + assert(ps.j == 4); + assert(ps.k == 9); +} + +/*************************************************/ + +int main() +{ + test1(); + test2(); + test3(); + test4(); + test5(); + test6(); + + return 0; +} diff --git a/tests/dmd/runnable/profilegc_stdout.d b/tests/dmd/runnable/profilegc_stdout.d index b22f9d9015..ecfcc01685 100644 --- a/tests/dmd/runnable/profilegc_stdout.d +++ b/tests/dmd/runnable/profilegc_stdout.d @@ -4,7 +4,7 @@ DISABLED: LDC // -profile=gc not supported RUN_OUTPUT: --- bytes allocated, allocations, type, function, file:line - 96 1 ubyte D main runnable/profilegc_stdout.d:17 + 96 1 ubyte profilegc_stdout.main runnable/profilegc_stdout.d:17 --- */ diff --git a/tests/dmd/runnable/rvalue1.d b/tests/dmd/runnable/rvalue1.d new file mode 100644 index 0000000000..2592f2a9e9 --- /dev/null +++ b/tests/dmd/runnable/rvalue1.d @@ -0,0 +1,260 @@ +/* PERMUTE_ARGS: -preview=rvaluerefparam +/* testing __rvalue */ + +import core.stdc.stdio; + +/********************************/ + +int foo(int) { printf("foo(int)\n"); return 1; } +int foo(ref int) { printf("foo(ref int)\n"); return 2; } + +void test1() +{ + int s; + assert(foo(s) == 2); + assert(foo(__rvalue(s)) == 1); +} + +/********************************/ + +struct S +{ + nothrow: + ~this() { printf("~this() %p\n", &this); } + this(ref S) { printf("this(ref S)\n"); } + void opAssign(S) { printf("opAssign(S)\n"); } +} + +void test2() +{ + S s; + S t; + + t = __rvalue(s); +} + +/********************************/ + +struct S3 +{ + int a, b, c; + + this(S3) {} + this(ref S3) {} +} + +void test3() +{ + S3 s; + S3 x = s; // this line causes the compiler to crash +} + +/********************************/ + +struct S4 +{ + void* p; + + this(ref S4) { } + + this(S4 s) + { + assert(&s is &x); // confirm the rvalue reference + } +} + +__gshared S4 x; + +void test4() +{ + S4 t = __rvalue(x); +} + +/********************************/ + +struct S5 +{ + this(S5 s) { printf("this(S5 s)\n"); } + this(ref inout S5 s) inout { printf("this(ref inout S5 s) inout\n"); } +} + +void test5() +{ + S5 t; + S5 t1 = t; + S5 t2 = __rvalue(t); +} + +/********************************/ + +int moveCtor, copyCtor, moveAss, copyAss; + +struct S6 +{ + this(S6 s) { ++moveCtor; } + this(ref S6 s) { ++copyCtor; } + void opAssign(S6 s) { ++moveAss; } + void opAssign(ref S6 s) { ++copyAss; } +} + +void test6() +{ + S6 x; + S6 s = x; +// printf("S6 s = x; moveCtor %d copyCtor %d moveAss %d copyAss %d\n", moveCtor, copyCtor, moveAss, copyAss); + assert(copyCtor == 1); + S6 s2 = __rvalue(x); +// printf("S6 s2 = __rvalue(x); moveCtor %d copyCtor %d moveAss %d copyAss %d\n", moveCtor, copyCtor, moveAss, copyAss); + assert(moveCtor == 1); + s2 = s; +// printf("s2 = s; moveCtor %d copyCtor %d moveAss %d copyAss %d\n", moveCtor, copyCtor, moveAss, copyAss); + assert(copyAss == 1); + s2 = __rvalue(s); +// printf("s2 = __rvalue(s); moveCtor %d copyCtor %d moveAss %d copyAss %d\n", moveCtor, copyCtor, moveAss, copyAss); + assert(moveAss == 1); + assert(copyCtor == 1 && moveCtor == 1 && copyAss == 1 && moveAss == 1); +} + +/********************************/ +// https://github.com/dlang/dmd/pull/17050#issuecomment-2543370370 + +struct MutableString(size_t E) {} + +struct StringTest +{ + alias toString this; + const(char)[] toString() const pure + => null; + + this(StringTest rhs) {} + this(ref inout StringTest rhs) inout pure {} + + this(typeof(null)) inout pure {} + this(size_t Embed)(MutableString!Embed str) inout {} + + ~this() {} +} + + +void test7() +{ + StringTest s = StringTest(null); +} + +/********************************/ +// https://github.com/dlang/dmd/issues/20562 + +struct S8 +{ + int a,b; + this(S8) { printf("this(S)\n"); b = 4; } + this(ref S8) { printf("this(ref S)\n"); assert(0); } +} + + +S8 returnRval(ref S8 arg) +{ +static if (0) +{ + /* __copytmp2 = 0 ; + _D7rvalue51S6__ctorMFNcKSQxQrZQg call (arg param #__copytmp2); + * __HID1 streq 1 __copytmp2; + __HID1; + */ + return arg; +} +else static if (1) +{ + /* * __HID1 streq 1 * arg; + __HID1; + */ + return __rvalue(arg); // should move-construct the NRVO value +} +else +{ + /* * t = 0 ; + t; + _TMP0 = t; + _D7rvalue51S6__ctorMFNcSQwQqZQg call (arg param _TMP0); + t; + */ + S8 t = __rvalue(arg); + return t; +} +} + + +void test8() +{ + S8 s; + S8 t = returnRval(s); + printf("t.b: %d\n", t.b); + assert(t.b == 4); +} + +/********************************/ + +struct T9 +{ + int i; + inout this(ref inout T9 t) { this.i = t.i - 1; printf("this(ref T9)\n"); } + inout this(inout T9 t) { this.i = t.i + 1; printf("this(T9)\n"); } +} + +struct S9 +{ + T9 t; + //inout this(return ref scope inout S9 t);// { this.i = t.i - 1; printf("this(ref T9)\n"); } + //@system inout this(return scope inout S9 t);// { this.i = t.i + 1; printf("this(T9)\n"); } +} + +void test9() +{ + S9 s; + s.t.i = 3; + S9 u = s; + printf("u.t.i = %d\n", u.t.i); + assert(u.t.i == 2); + + S9 v = __rvalue(u); + printf("v.t.i = %d\n", v.t.i); + assert(v.t.i == 3); +} + +/********************************/ +// https://github.com/s-ludwig/taggedalgebraic/issues/75 + +struct T10 +{ + string s; + this(T10) {} + this(string v) { s = v; } +} + +struct S10 +{ + T10 p; +} + +void test10() +{ + S10 s = S10(T10("hello")); + assert(s.p.s == "hello"); +} + +/********************************/ + +int main() +{ + test1(); + test2(); + test3(); + test4(); + test5(); + test6(); + test7(); + test8(); + test9(); + + return 0; +} diff --git a/tests/dmd/runnable/sdtor.d b/tests/dmd/runnable/sdtor.d index be15cb244a..507c1143fe 100644 --- a/tests/dmd/runnable/sdtor.d +++ b/tests/dmd/runnable/sdtor.d @@ -4330,28 +4330,28 @@ void test14696(int len = 2) check({ foo(len > 2 ? null : (len != 2 ? null : makeS(1).get() ) ); }, "makeS(1).get(1).foo.dtor(1)."); // temporary in condition and throwing callee - // check({ fooThrow(len == 2 ? makeS(1).get() : null); }, "makeS(1).get(1).fooThrow.dtor(1)."); - // check({ fooThrow(len == 2 ? null : makeS(1).get() ); }, "fooThrow."); - // check({ fooThrow(len != 2 ? makeS(1).get() : null); }, "fooThrow."); - // check({ fooThrow(len != 2 ? null : makeS(1).get() ); }, "makeS(1).get(1).fooThrow.dtor(1)."); + check({ fooThrow(len == 2 ? makeS(1).get() : null); }, "makeS(1).get(1).fooThrow.dtor(1)."); + check({ fooThrow(len == 2 ? null : makeS(1).get() ); }, "fooThrow."); + check({ fooThrow(len != 2 ? makeS(1).get() : null); }, "fooThrow."); + check({ fooThrow(len != 2 ? null : makeS(1).get() ); }, "makeS(1).get(1).fooThrow.dtor(1)."); // temporary in nesting condititions and throwing callee - // check({ fooThrow(len >= 2 ? (len == 2 ? makeS(1).get() : null) : null); }, "makeS(1).get(1).fooThrow.dtor(1)."); - // check({ fooThrow(len >= 2 ? (len == 2 ? null : makeS(1).get() ) : null); }, "fooThrow."); - // check({ fooThrow(len >= 2 ? (len != 2 ? makeS(1).get() : null) : null); }, "fooThrow."); - // check({ fooThrow(len >= 2 ? (len != 2 ? null : makeS(1).get() ) : null); }, "makeS(1).get(1).fooThrow.dtor(1)."); - // check({ fooThrow(len >= 2 ? null : (len == 2 ? makeS(1).get() : null) ); }, "fooThrow."); - // check({ fooThrow(len >= 2 ? null : (len == 2 ? null : makeS(1).get() ) ); }, "fooThrow."); - // check({ fooThrow(len >= 2 ? null : (len != 2 ? makeS(1).get() : null) ); }, "fooThrow."); - // check({ fooThrow(len >= 2 ? null : (len != 2 ? null : makeS(1).get() ) ); }, "fooThrow."); - // check({ fooThrow(len > 2 ? (len == 2 ? makeS(1).get() : null) : null); }, "fooThrow."); - // check({ fooThrow(len > 2 ? (len == 2 ? null : makeS(1).get() ) : null); }, "fooThrow."); - // check({ fooThrow(len > 2 ? (len != 2 ? makeS(1).get() : null) : null); }, "fooThrow."); - // check({ fooThrow(len > 2 ? (len != 2 ? null : makeS(1).get() ) : null); }, "fooThrow."); - // check({ fooThrow(len > 2 ? null : (len == 2 ? makeS(1).get() : null) ); }, "makeS(1).get(1).fooThrow.dtor(1)."); - // check({ fooThrow(len > 2 ? null : (len == 2 ? null : makeS(1).get() ) ); }, "fooThrow."); - // check({ fooThrow(len > 2 ? null : (len != 2 ? makeS(1).get() : null) ); }, "fooThrow."); - // check({ fooThrow(len > 2 ? null : (len != 2 ? null : makeS(1).get() ) ); }, "makeS(1).get(1).fooThrow.dtor(1)."); + check({ fooThrow(len >= 2 ? (len == 2 ? makeS(1).get() : null) : null); }, "makeS(1).get(1).fooThrow.dtor(1)."); + check({ fooThrow(len >= 2 ? (len == 2 ? null : makeS(1).get() ) : null); }, "fooThrow."); + check({ fooThrow(len >= 2 ? (len != 2 ? makeS(1).get() : null) : null); }, "fooThrow."); + check({ fooThrow(len >= 2 ? (len != 2 ? null : makeS(1).get() ) : null); }, "makeS(1).get(1).fooThrow.dtor(1)."); + check({ fooThrow(len >= 2 ? null : (len == 2 ? makeS(1).get() : null) ); }, "fooThrow."); + check({ fooThrow(len >= 2 ? null : (len == 2 ? null : makeS(1).get() ) ); }, "fooThrow."); + check({ fooThrow(len >= 2 ? null : (len != 2 ? makeS(1).get() : null) ); }, "fooThrow."); + check({ fooThrow(len >= 2 ? null : (len != 2 ? null : makeS(1).get() ) ); }, "fooThrow."); + check({ fooThrow(len > 2 ? (len == 2 ? makeS(1).get() : null) : null); }, "fooThrow."); + check({ fooThrow(len > 2 ? (len == 2 ? null : makeS(1).get() ) : null); }, "fooThrow."); + check({ fooThrow(len > 2 ? (len != 2 ? makeS(1).get() : null) : null); }, "fooThrow."); + check({ fooThrow(len > 2 ? (len != 2 ? null : makeS(1).get() ) : null); }, "fooThrow."); + check({ fooThrow(len > 2 ? null : (len == 2 ? makeS(1).get() : null) ); }, "makeS(1).get(1).fooThrow.dtor(1)."); + check({ fooThrow(len > 2 ? null : (len == 2 ? null : makeS(1).get() ) ); }, "fooThrow."); + check({ fooThrow(len > 2 ? null : (len != 2 ? makeS(1).get() : null) ); }, "fooThrow."); + check({ fooThrow(len > 2 ? null : (len != 2 ? null : makeS(1).get() ) ); }, "makeS(1).get(1).fooThrow.dtor(1)."); // temporaries in each conditions check({ foo(len == 2 ? makeS(1).get() : null, len == 2 ? makeS(2).get() : null); }, "makeS(1).get(1).makeS(2).get(2).foo.dtor(2).dtor(1)."); diff --git a/tests/dmd/runnable/sroa13220.d b/tests/dmd/runnable/sroa13220.d index 2b7f5d7b85..e2744e19d4 100644 --- a/tests/dmd/runnable/sroa13220.d +++ b/tests/dmd/runnable/sroa13220.d @@ -1,4 +1,4 @@ -/* REQUIRED_ARGS: -O -inline -noboundscheck +/* REQUIRED_ARGS: -O -inline -boundscheck=off */ // https://github.com/dlang/pull/13220 diff --git a/tests/dmd/runnable/template10.d b/tests/dmd/runnable/template10.d index a0e51c2365..719d84440b 100644 --- a/tests/dmd/runnable/template10.d +++ b/tests/dmd/runnable/template10.d @@ -1,6 +1,6 @@ // DISABLED: LDC // PERMUTE_ARGS: -inline -// REQUIRED_ARGS: -verrors=0 +// REQUIRED_ARGS: -verrors=simple -verrors=0 /* TEST_OUTPUT: --- runnable/template10.d(89): Deprecation: function `template10.test1b.f0.f!(a).f` function requires a dual-context, which is deprecated diff --git a/tests/dmd/runnable/test17559.d b/tests/dmd/runnable/test17559.d index 22e512166c..ac9ccc4ff6 100644 --- a/tests/dmd/runnable/test17559.d +++ b/tests/dmd/runnable/test17559.d @@ -1,7 +1,5 @@ // REQUIRED_ARGS: -g // REQUIRED_ARGS(linux freebsd openbsd dragonflybsd): -L-export-dynamic -// LDC (required for Win32 and -O): REQUIRED_ARGS(windows32): -link-defaultlib-debug -// LDC (FreeBSD's libexecinfo apparently doesn't like elided frame pointers): REQUIRED_ARGS(freebsd): -link-defaultlib-debug -frame-pointer=all // PERMUTE_ARGS: // DISABLED: osx diff --git a/tests/dmd/runnable/test19086.d b/tests/dmd/runnable/test19086.d index 6ac2e657fa..b2e58fb4a1 100644 --- a/tests/dmd/runnable/test19086.d +++ b/tests/dmd/runnable/test19086.d @@ -1,6 +1,5 @@ // REQUIRED_ARGS: -g // REQUIRED_ARGS(linux freebsd openbsd dragonflybsd): -L-export-dynamic -// LDC (FreeBSD's libexecinfo apparently doesn't like elided frame pointers): REQUIRED_ARGS(freebsd): -link-defaultlib-debug -frame-pointer=all // DISABLED: LDC_win32 // no file/line info for the `run19086` frame (and only that frame), even without -O // PERMUTE_ARGS: // DISABLED: osx diff --git a/tests/dmd/runnable/test20831.c b/tests/dmd/runnable/test20831.c new file mode 100644 index 0000000000..29a91a9f87 --- /dev/null +++ b/tests/dmd/runnable/test20831.c @@ -0,0 +1,15 @@ +// https://github.com/dlang/dmd/issues/20831 +#include "assert.h" + +int* p; + +#define IMPL() \ +f()\ +{\ + assert(p);\ + assert(0);\ +} + +void IMPL() + +void main(void) {} diff --git a/tests/dmd/runnable/test21020.d b/tests/dmd/runnable/test21020.d new file mode 100644 index 0000000000..484db30a56 --- /dev/null +++ b/tests/dmd/runnable/test21020.d @@ -0,0 +1,11 @@ +// https://github.com/dlang/dmd/issues/21020 + +shared struct Queue { + int[int] map; +} + +void main() { + auto queue = Queue(); + (cast(int[int]) queue.map)[1] = 2; + assert(queue.map[1] == 2); +} diff --git a/tests/dmd/runnable/test23036.d b/tests/dmd/runnable/test23036.d new file mode 100644 index 0000000000..efb6fef395 --- /dev/null +++ b/tests/dmd/runnable/test23036.d @@ -0,0 +1,13 @@ +// https://issues.dlang.org/show_bug.cgi?id=23036 + +struct S +{ + this(ref S) {} + this(S, int a = 2) {} +} + +void main() +{ + S a; + S b = a; +} diff --git a/tests/dmd/runnable/test8.d b/tests/dmd/runnable/test8.d index f10076223d..0c08571fc2 100644 --- a/tests/dmd/runnable/test8.d +++ b/tests/dmd/runnable/test8.d @@ -1,7 +1,8 @@ /* +REQUIRED_ARGS: -verrors=simple TEST_OUTPUT: --- -runnable/test8.d(261): Deprecation: identity comparison of static arrays implicitly coerces them to slices, which are compared by reference +runnable/test8.d(262): Deprecation: identity comparison of static arrays implicitly coerces them to slices, which are compared by reference --- */ diff --git a/tests/dmd/runnable/test_switches.sh b/tests/dmd/runnable/test_switches.sh index 465dcea3ab..f0fce9e2fd 100755 --- a/tests/dmd/runnable/test_switches.sh +++ b/tests/dmd/runnable/test_switches.sh @@ -14,6 +14,7 @@ # -Xf= src_file=${OUTPUT_BASE}/src.d +src_file_in_pkg=${OUTPUT_BASE}/pkg/src.d clean() { @@ -26,6 +27,8 @@ prepare() mkdir -p ${OUTPUT_BASE} echo "module mymod;" > ${OUTPUT_BASE}/mymod.d echo "module src; import mymod;" > ${src_file} + mkdir ${OUTPUT_BASE}/pkg + echo "module pkg.src;" > ${src_file_in_pkg} } die() @@ -59,7 +62,9 @@ $DMD -o- -od=${OUTPUT_BASE} -D -Df=${OUTPUT_BASE}/src.html -Hf=${OUTPUT_BASE}/sr checkFiles; prepare; -$DMD -o- -od=${OUTPUT_BASE} -D -Dd=${OUTPUT_BASE} -Hd=${OUTPUT_BASE} -I=${OUTPUT_BASE} -L=-v -Xf=${OUTPUT_BASE}/json.json ${src_file} +$DMD -o- -oq -od=${OUTPUT_BASE} -D -Dd=${OUTPUT_BASE} -Hd=${OUTPUT_BASE} -I=${OUTPUT_BASE} -L=-v -Xf=${OUTPUT_BASE}/json.json ${src_file} ${src_file_in_pkg} checkFiles; +checkFile ${OUTPUT_BASE}/pkg.src.di +checkFile ${OUTPUT_BASE}/pkg.src.html clean; diff --git a/tests/dmd/runnable/testaa2.d b/tests/dmd/runnable/testaa2.d index ba8ceecc55..e1abcb4418 100644 --- a/tests/dmd/runnable/testaa2.d +++ b/tests/dmd/runnable/testaa2.d @@ -286,6 +286,27 @@ struct PropertyTable10106 CodepointSet10106[string] table; } +/************************************************/ +// strip inout in key and value types +void testinout() +{ + inout(long) func1(inout(long[][int]) aa) + { + return aa[0][0]; + } + long[][int] a = [ 0 : [42] ]; + long b = func1(a); + assert(b == 42); +} + +/************************************************/ +// type info generated for enum creation in InExp? +void testinenum() +{ + enum string[string] aa = [ "one" : "un", "two" : "deux" ]; + assert("one" in aa); +} + /************************************************/ int main() @@ -295,6 +316,8 @@ int main() test4523(); test3825(); test3825x(); + testinout(); + testinenum(); printf("Success\n"); return 0; diff --git a/tests/dmd/runnable/testsafe.d b/tests/dmd/runnable/testsafe.d index e5a99c8f71..bb715794ba 100644 --- a/tests/dmd/runnable/testsafe.d +++ b/tests/dmd/runnable/testsafe.d @@ -24,9 +24,9 @@ void pointercast2() int b; Object c; - static assert(!__traits(compiles, cast(void*)a)); - static assert(!__traits(compiles, cast(void*)b)); - static assert(!__traits(compiles, cast(void*)c)); + static assert(__traits(compiles, cast(void*)a)); + static assert(__traits(compiles, cast(void*)b)); + static assert(__traits(compiles, cast(void*)c)); } @safe @@ -247,11 +247,11 @@ void multablecast() static assert( __traits(compiles, cast(const(void)*)mp)); static assert( __traits(compiles, cast(const(void)*)ip)); - static assert(!__traits(compiles, cast(immutable(void)*)mp)); - static assert(!__traits(compiles, cast(immutable(void)*)cp)); + static assert(__traits(compiles, cast(immutable(void)*)mp)); + static assert(__traits(compiles, cast(immutable(void)*)cp)); - static assert(!__traits(compiles, cast(void*)cp)); - static assert(!__traits(compiles, cast(void*)ip)); + static assert(__traits(compiles, cast(void*)cp)); + static assert(__traits(compiles, cast(void*)ip)); } @safe diff --git a/tests/dmd/runnable/testv.d b/tests/dmd/runnable/testv.d index d146fd49db..51a95a08a6 100644 --- a/tests/dmd/runnable/testv.d +++ b/tests/dmd/runnable/testv.d @@ -83,39 +83,6 @@ void test3() printf("%d\n", i); } -/*********************************************************/ - -class Foo4 -{ - int a; - float f; - double d; - - this(int a, float f, double d) - { - this.a = a; - this.f = f; - this.d = d; - } -} - -int sum4(Foo4 f ...) -{ - return cast(int)(f.a + f.f + f.d); -} - -void test4() -{ - int i; - Foo4 foo = new Foo4(1, 2f, 3.0); - - i = sum4(foo); - assert(i == 1+2+3); - i = sum4(4, 5f, 6.0); - assert(i == 4+5+6); - - printf("%d\n", i); -} /*********************************************************/ @@ -139,7 +106,6 @@ int main() test1(); test2(); test3(); - test4(); test5(); printf("Success\n"); diff --git a/tests/dmd/runnable/xtest46.d b/tests/dmd/runnable/xtest46.d index cbcbd1a97f..5893560a62 100644 --- a/tests/dmd/runnable/xtest46.d +++ b/tests/dmd/runnable/xtest46.d @@ -4091,30 +4091,30 @@ void test4258() { struct Foo4258 { // binary ++/-- - int opPostInc()() if (false) { return 0; } + int opUnary(string op)() if (false) { return 0; } // binary 1st - int opAdd(R)(R rhs) if (false) { return 0; } - int opAdd_r(R)(R rhs) if (false) { return 0; } + int opBinary(string op, R)(R rhs) if (false) { return 0; } + int opBinaryRight(string op, R)(R rhs) if (false) { return 0; } // compare - int opCmp(R)(R rhs) if (false) { return 0; } + int opCmp(R)(const(R) rhs) const if (false) { return 0; } // binary-op assign - int opAddAssign(R)(R rhs) if (false) { return 0; } + int opOpAssign(string op, R)(R rhs) if (false) { return 0; } } struct Bar4258 { // binary commutive 1 - int opAdd_r(R)(R rhs) if (false) { return 0; } + int opBinary(string op, R)(R rhs) if (false) { return 0; } // binary-op assign int opOpAssign(string op, R)(R rhs) if (false) { return 0; } } struct Baz4258 { // binary commutive 2 - int opAdd(R)(R rhs) if (false) { return 0; } + int opBinaryRight(string op, R)(R rhs) if (false) { return 0; } } -static assert(!is(typeof(Foo4258.init++))); +static assert(!is(typeof(++Foo4258.init))); static assert(!is(typeof(Foo4258.init + 1))); static assert(!is(typeof(1 + Foo4258.init))); static assert(!is(typeof(Foo4258.init < Foo4258.init))); diff --git a/tests/dmd/runnable_cxx/extra-files/testbitfields_cpp.cpp b/tests/dmd/runnable_cxx/extra-files/testbitfields_cpp.cpp index 7302ca6df9..896f2f8f98 100644 --- a/tests/dmd/runnable_cxx/extra-files/testbitfields_cpp.cpp +++ b/tests/dmd/runnable_cxx/extra-files/testbitfields_cpp.cpp @@ -82,3 +82,5 @@ BEGIN_STRUCT(Issue24592a) FIELD(a) FIELD(b) FIELD(c) END_STRUCT BEGIN_STRUCT(Issue24592b) FIELD(x) FIELD(a) FIELD(b) FIELD(c) END_STRUCT BEGIN_STRUCT(Issue24592c) FIELD(a) FIELD(b) FIELD(c) FIELD(d) FIELD(e) FIELD(f) END_STRUCT BEGIN_STRUCT(Issue24592d) FIELD(a) FIELD(b) FIELD(c) FIELD(d) FIELD(e) FIELD(f) END_STRUCT +BEGIN_STRUCT(Issue24651a) FIELD(a) FIELD(b) END_STRUCT +BEGIN_STRUCT(Issue24651b) FIELD(a) FIELD(b) END_STRUCT diff --git a/tests/dmd/runnable_cxx/extra-files/testbitfields_importc.c b/tests/dmd/runnable_cxx/extra-files/testbitfields_importc.c index 2bc7569067..162c7c687a 100644 --- a/tests/dmd/runnable_cxx/extra-files/testbitfields_importc.c +++ b/tests/dmd/runnable_cxx/extra-files/testbitfields_importc.c @@ -50,3 +50,5 @@ struct Issue24592a { unsigned long long a:20, b:20, c:24; }; struct Issue24592b { unsigned int x; unsigned long long a:20, b:20, c:24; }; struct Issue24592c { unsigned long long a:20, b:32, c:32, d:32, e:32, f:32; }; struct Issue24592d { unsigned long long a:10, b:16, c:16, d:16, e:16, f:16; }; +struct Issue24651a { unsigned long long a:48, b:17; }; +struct Issue24651b { unsigned long long a:48, b:48; }; diff --git a/tests/dmd/tools/d_do_test.d b/tests/dmd/tools/d_do_test.d index 4de96f38b3..b212a14cb1 100755 --- a/tests/dmd/tools/d_do_test.d +++ b/tests/dmd/tools/d_do_test.d @@ -33,7 +33,7 @@ import std.format; import std.meta : AliasSeq; import std.process; import std.random; -import std.range : chain; +import std.range : chain, choose, roundRobin, takeOne; import std.regex; import std.path; import std.stdio; @@ -736,7 +736,7 @@ bool gatherTestParameters(ref TestArgs testArgs, string input_dir, string input_ // tests can override -verrors by using REQUIRED_ARGS if (testArgs.mode == TestMode.FAIL_COMPILE) - testArgs.requiredArgs = "-verrors=0 " ~ testArgs.requiredArgs; + testArgs.requiredArgs = "-verrors=simple -verrors=0 " ~ testArgs.requiredArgs; version (LDC) { @@ -1197,6 +1197,7 @@ Applies custom transformations defined in transformOutput to testOutput. Currently the following actions are supported: * "sanitize_json" = replace compiler/plattform specific data from generated JSON + * "sanitize_timetrace" = parse -ftime-trace profiler output, and only extract event names * "remove_lines()" = remove all lines matching a regex Params: @@ -1255,6 +1256,11 @@ void applyOutputTransformations(ref string testOutput, string transformOutput) break; } + case "sanitize_timetrace": + import sanitize_timetrace; + sanitizeTimeTrace(testOutput); + break; + default: throw new Exception(format(`Unknown transformation: "%s"!`, step)); } @@ -1608,6 +1614,7 @@ class CompareException : Exception string expected; /// expected output string actual; /// actual output bool fromRun; /// Compared execution instead of compilation output + string diff; /// diff between expected and actual output this(string expected, string actual, string diff, bool fromRun = false) { string msg = "\nexpected:\n----\n" ~ expected ~ @@ -1617,6 +1624,7 @@ class CompareException : Exception this.expected = expected; this.actual = actual; this.fromRun = fromRun; + this.diff = diff; } } @@ -2041,7 +2049,19 @@ int tryMain(string[] args) } else { - writefln("\nWARNING: %s has multiple `%s_OUTPUT` blocks and can't be auto-updated", input_file, type); + try + { + string diffUpdatedText = replaceFromDiff(existingText, ce.diff); + std.file.write(input_file, diffUpdatedText); + writefln("\n==> `%s_OUTPUT` of %s has been updated by applying a diff", type, input_file); + return Result.returnRerun; + } + catch (Exception e) + { + writefln("\nERROR: Couldn't update `%s_OUTPUT` blocks of %s through the diff: \"%s\" + Please update the file manually to make the tests pass.", type, input_file, e.msg); + } + return Result.return0; } } @@ -2109,6 +2129,169 @@ int tryMain(string[] args) return 0; } +// Replace consecutive ---+++ diff lines with intertwined lines -+-+-+, which helps putting +// additions in the right TEST_OUTPUT block. Otherwise, sometimes all but the last TEST_OUTPUT blocks +// are emptied and the last TEST_OUTPUT block will be filled will all updated output. +string intertwineDiff(string diff) +{ + static if (__VERSION__ < 2097) + { + // `splitWhen` didn't exist, but the bootstrap compiler test doesn't need AUTO_UPDATE + return diff; + } + else + { + // First, split diff lines into groups of deletions (-), additions (+), or other (@, ' ') + auto editGroups = diff.splitter('\n').chunkBy!((a, b) => a.takeOne.equal(b.takeOne)).map!array; + // Then split before every deletion (-) group + auto deletionGroups = editGroups.splitWhen!((a, b) => b.front.startsWith("-")).map!array; + + // Then, if we have a deletion group followed by an addition group, roundRobin the first two editGroups, and append the rest + // Otherwise, just join all editGroups to keep the original order + return deletionGroups.map!(g => choose( + g.length > 1 && g[0].front.startsWith("-") && g[1].front.startsWith("+"), + g.length > 1 ? chain(roundRobin(g[0], g[1]), g[2 .. $].join).array : g.join.array, + g.join + )).join.join("\n"); + } +} + +unittest +{ + string input = "@@@ +-A0 +-B1 ++E2 ++F3 ++H4 +-32 ++33 ++34 + 35 + 36 +-C5"; + + string expected = "@@@ +-A0 ++E2 +-B1 ++F3 ++H4 +-32 ++33 ++34 + 35 + 36 +-C5"; + + assert(intertwineDiff("") == ""); + static if (__VERSION__ >= 2097) + assert(intertwineDiff(input) == expected); +} + +/// Given test file with contents `input` and diff file with the diff of actual TEST_OUTPUT vs expected TEST_OUTPUT, +/// return new contents of the test file with updated TEST_OUTPUT blocks. Throws an Exception if the diff couldn't be +/// matched against the input. +string replaceFromDiff(string input, string diff) +{ + const string[] lines = input.splitLines; + string result = ""; + size_t i = 0; + foreach (diffLine; intertwineDiff(diff).splitLines) + { + const bool deletion = diffLine.skipOver("-"); + const bool seek = deletion || diffLine.skipOver(" "); + + if (seek) + { + while (i < lines.length && lines[i] != diffLine) + { + result ~= lines[i] ~ "\n"; + i++; + } + if (i >= lines.length) + throw new Exception("Can't find diff line \"" ~ diffLine ~ "\" in the text to update"); + + if (!deletion) + result ~= lines[i] ~ "\n"; + + i++; + } + else if (diffLine.skipOver("+")) + { + result ~= diffLine ~ "\n"; + continue; + } + else if (diffLine.skipOver("@")) + { + continue; + } + else + { + throw new Exception("Unrecognized first character in diff line: \"" ~ diffLine ~ "\""); + } + } + while (i < lines.length) + { + result ~= lines[i] ~ "\n"; + i++; + } + return result; +} + +unittest +{ + string input = " +TEST_OUTPUT: +--- +Error: dummy +--- + +TEST_OUTPUT: +--- +Error: something else +Deprecation: dummy +--- +"; + + string diff = +"-Error: dummy +-Error: something else ++Error: dummies +@@@ ... + Deprecation: dummy ++Deprecation: another +"; + + string expected = " +TEST_OUTPUT: +--- +Error: dummies +--- + +TEST_OUTPUT: +--- +Deprecation: dummy +Deprecation: another +--- +"; + + string result = replaceFromDiff(input, diff); + + static if (__VERSION__ >= 2097) + assert(result == expected); + + try + { + replaceFromDiff(input, "-Nonexistend line"); + assert(0); + } + catch (Exception e) + { + assert(e.msg == `Can't find diff line "Nonexistend line" in the text to update`); + } +} + /** * Executes a bash script (deprecated in favour of `dshell` tests). * diff --git a/tests/dmd/tools/sanitize_timetrace.d b/tests/dmd/tools/sanitize_timetrace.d new file mode 100644 index 0000000000..5cdb8484f2 --- /dev/null +++ b/tests/dmd/tools/sanitize_timetrace.d @@ -0,0 +1,28 @@ +/** +The output of -ftime-trace is not deterministic because it contains timer data, +and it's also full of implementation details (such as the order of semantic analysis). +In order to test the output, this program extracts only 'name' and 'details' strings of events, +and sorts them, so the output can be tested. +*/ +module sanitize_timetrace; + +import std.algorithm; +import std.array; +import std.conv; +import std.json; +import std.range; +import std.string; + +void sanitizeTimeTrace(ref string testOutput) +{ + parseJSON(testOutput); + auto json = parseJSON(testOutput); + string result = json["traceEvents"].array + .filter!(x => x["ph"].str == "X") + .map!(x => strip(x["name"].str ~ ", " ~ x["args"]["detail"].str)) + .array + .sort + .uniq + .joiner("\n").text; + testOutput = result; +} diff --git a/tests/dmd/unit/frontend.d b/tests/dmd/unit/frontend.d index c970e5aa8f..e554f7ce5d 100644 --- a/tests/dmd/unit/frontend.d +++ b/tests/dmd/unit/frontend.d @@ -33,7 +33,7 @@ unittest bool created = false; - extern (D) this(const ref Loc loc, Identifier id, BaseClasses* baseclasses) + extern (D) this(Loc loc, Identifier id, BaseClasses* baseclasses) { super(loc, id, baseclasses, null, false); created = true; @@ -115,7 +115,7 @@ unittest string[] diagnosticMessages; - nothrow bool diagnosticHandler(const ref Loc loc, Color headerColor, const(char)* header, + nothrow bool diagnosticHandler(const ref SourceLoc loc, Color headerColor, const(char)* header, const(char)* format, va_list ap, const(char)* p1, const(char)* p2) { OutBuffer tmp; @@ -251,7 +251,7 @@ unittest string[] diagnosticMessages; - nothrow bool diagnosticHandler(const ref Loc loc, Color headerColor, const(char)* header, + nothrow bool diagnosticHandler(const ref SourceLoc loc, Color headerColor, const(char)* header, const(char)* format, va_list ap, const(char)* p1, const(char)* p2) { OutBuffer tmp; @@ -271,7 +271,7 @@ unittest } }); - assert(endsWith(diagnosticMessages[0], "no identifier for declarator `temp`")); + assert(endsWith(diagnosticMessages[0], "variable name expected after type `temp`, not `==`")); assert(endsWith(diagnosticMessages[1], "found `==` instead of statement")); } @@ -368,7 +368,7 @@ unittest string[] diagnosticMessages; - nothrow bool diagnosticHandler(const ref Loc loc, Color headerColor, const(char)* header, + nothrow bool diagnosticHandler(const ref SourceLoc loc, Color headerColor, const(char)* header, const(char)* format, va_list ap, const(char)* p1, const(char)* p2) { OutBuffer tmp; diff --git a/tests/dmd/unit/interfaces/check_implementations_20861.d b/tests/dmd/unit/interfaces/check_implementations_20861.d index 269fab36a9..2292938512 100644 --- a/tests/dmd/unit/interfaces/check_implementations_20861.d +++ b/tests/dmd/unit/interfaces/check_implementations_20861.d @@ -45,7 +45,7 @@ import support : afterEach, beforeEach, compiles, stripDelimited, Diagnostic; }.stripDelimited; enum message = "Error: class test.Bar interface function void foo() is not implemented"; - const expected = Diagnostic(Loc(filename, 6, 1), message); + const expected = Diagnostic(SourceLoc(filename, 6, 1), message); const diagnostics = compiles(code, filename); assert(diagnostics == [expected], "\n" ~ diagnostics.toString); @@ -67,7 +67,7 @@ import support : afterEach, beforeEach, compiles, stripDelimited, Diagnostic; }.stripDelimited; enum message = "Error: class test.Bar interface function void foo() is not implemented"; - const expected = Diagnostic(Loc(filename, 6, 1), message); + const expected = Diagnostic(SourceLoc(filename, 6, 1), message); const diagnostics = compiles(code, filename); assert(diagnostics == [expected], "\n" ~ diagnostics.toString); @@ -94,7 +94,7 @@ import support : afterEach, beforeEach, compiles, stripDelimited, Diagnostic; }.stripDelimited; enum message = "Error: class test.Bar interface function void foo() is not implemented"; - const expected = Diagnostic(Loc(filename, 11, 1), message); + const expected = Diagnostic(SourceLoc(filename, 11, 1), message); const diagnostics = compiles(code, filename); assert(diagnostics == [expected], "\n" ~ diagnostics.toString); @@ -123,7 +123,7 @@ import support : afterEach, beforeEach, compiles, stripDelimited, Diagnostic; }.stripDelimited; enum message = "Error: class test.B interface function void foo() is not implemented"; - const expected = Diagnostic(Loc(filename, 11, 1), message); + const expected = Diagnostic(SourceLoc(filename, 11, 1), message); const diagnostics = compiles(code, filename); assert(diagnostics == [expected], "\n" ~ diagnostics.toString); diff --git a/tests/dmd/unit/lexer/diagnostic_reporter.d b/tests/dmd/unit/lexer/diagnostic_reporter.d index 00ac59af6e..df4fed8bd7 100644 --- a/tests/dmd/unit/lexer/diagnostic_reporter.d +++ b/tests/dmd/unit/lexer/diagnostic_reporter.d @@ -23,7 +23,7 @@ unittest { int errorCount; - override bool error(const ref Loc, const(char)*, va_list, const(char)*, const(char)*) + override bool error(const ref SourceLoc, const(char)*, va_list, const(char)*, const(char)*) { errorCount++; return true; diff --git a/tests/dmd/unit/lexer/lexer_dmdlib.d b/tests/dmd/unit/lexer/lexer_dmdlib.d index ea7ebb4ab8..41dd490e12 100644 --- a/tests/dmd/unit/lexer/lexer_dmdlib.d +++ b/tests/dmd/unit/lexer/lexer_dmdlib.d @@ -245,7 +245,7 @@ unittest import dmd.errors; const(char)[][2][] diagnosticMessages; - nothrow bool diagnosticHandler(const ref Loc loc, Color headerColor, const(char)* header, + nothrow bool diagnosticHandler(const ref SourceLoc loc, Color headerColor, const(char)* header, const(char)* format, va_list ap, const(char)* p1, const(char)* p2) { OutBuffer tmp; diff --git a/tests/dmd/unit/lexer/location_offset.d b/tests/dmd/unit/lexer/location_offset.d index 21266276d2..fb7edfd5eb 100644 --- a/tests/dmd/unit/lexer/location_offset.d +++ b/tests/dmd/unit/lexer/location_offset.d @@ -399,6 +399,7 @@ enum Test[string] tests = [ "auto_" : Test("auto"), "package_" : Test("package"), "immutable_" : Test("immutable"), + "rvalue" : Test("__rvalue"), "if_" : Test("if"), "else_" : Test("else"), diff --git a/tests/dmd/unit/objc/protocols/diagnostic_messages.d b/tests/dmd/unit/objc/protocols/diagnostic_messages.d index ce673c9311..691b25f695 100644 --- a/tests/dmd/unit/objc/protocols/diagnostic_messages.d +++ b/tests/dmd/unit/objc/protocols/diagnostic_messages.d @@ -41,7 +41,7 @@ unittest }.stripDelimited; enum message = "Error: class test.Bar interface function extern (Objective-C) static void foo() is not implemented"; - auto expected = Diagnostic(Loc(filename, 8, 1), message); + auto expected = Diagnostic(SourceLoc(filename, 8, 1), message); const diagnostics = compiles(code, filename); assert(diagnostics == [expected], "\n" ~ diagnostics.toString); @@ -65,7 +65,7 @@ unittest }.stripDelimited; enum message = "Error: function test.Foo.foo function body only allowed in final functions in interface Foo"; - auto expected = Diagnostic(Loc(filename, 4, 17), message); + auto expected = Diagnostic(SourceLoc(filename, 4, 17), message); const diagnostics = compiles(code, filename); assert(diagnostics == [expected], "\n" ~ diagnostics.toString); diff --git a/tests/dmd/unit/objc/protocols/optional_methods.d b/tests/dmd/unit/objc/protocols/optional_methods.d index 4a87f85cac..3a55bbcf7e 100644 --- a/tests/dmd/unit/objc/protocols/optional_methods.d +++ b/tests/dmd/unit/objc/protocols/optional_methods.d @@ -129,7 +129,7 @@ unittest } }.stripDelimited; - Loc loc = Loc(filename, 5, 20); + SourceLoc loc = SourceLoc(filename, 5, 20); enum message = "Error: function test.Foo.foo only functions with Objective-C linkage can be declared as optional"; enum supplemental = "function is declared with D linkage"; auto expected = [Diagnostic(loc, message), Diagnostic(loc, supplemental)]; @@ -154,7 +154,7 @@ unittest } }.stripDelimited; - Loc loc = Loc(filename, 6, 30); + SourceLoc loc = SourceLoc(filename, 6, 30); enum message = "Error: function test.Foo.foo can only declare a function as optional once"; auto expected = Diagnostic(loc, message); @@ -184,15 +184,13 @@ unittest } }.stripDelimited; - Loc loc = Loc(filename, 6, 20); - auto expected = [ Diagnostic( - Loc(filename, 6, 20), + SourceLoc(filename, 6, 20), "Error: function test.Foo.foo!().foo template cannot be optional" ), Diagnostic( - Loc(filename, 12, 10), + SourceLoc(filename, 12, 10), "Error: template instance test.Foo.foo!() error instantiating" ) ]; @@ -223,7 +221,7 @@ unittest } }.stripDelimited; - Loc loc = Loc(filename, 6, 20); + SourceLoc loc = SourceLoc(filename, 6, 20); enum message = "Error: function test.Foo.foo only functions declared inside interfaces can be optional"; enum supplemental = "function is declared inside class"; auto expected = [Diagnostic(loc, message), Diagnostic(loc, supplemental)]; diff --git a/tests/dmd/unit/parser/diagnostic_reporter.d b/tests/dmd/unit/parser/diagnostic_reporter.d index 388b5e41b7..77681a10e5 100644 --- a/tests/dmd/unit/parser/diagnostic_reporter.d +++ b/tests/dmd/unit/parser/diagnostic_reporter.d @@ -29,7 +29,7 @@ unittest { int errorCount; - override bool error(const ref Loc, const(char)*, va_list, const(char)*, const(char)*) + override bool error(const ref SourceLoc, const(char)*, va_list, const(char)*, const(char)*) { errorCount++; return true; @@ -52,7 +52,7 @@ unittest { int supplementalCount; - override bool errorSupplemental(const ref Loc, const(char)*, va_list, const(char)*, const(char)*) + override bool errorSupplemental(const ref SourceLoc, const(char)*, va_list, const(char)*, const(char)*) { supplementalCount++; return true; @@ -79,14 +79,14 @@ unittest { int warningCount; - override bool warning(const ref Loc, const(char)*, va_list, const(char)*, const(char)*) + override bool warning(const ref SourceLoc, const(char)*, va_list, const(char)*, const(char)*) { warningCount++; return true; } } - global.params.warnings = DiagnosticReporting.inform; + global.params.useWarnings = DiagnosticReporting.inform; scope reporter = new WarningCountingDiagnosticReporter; parseModule("test.d", q{ diff --git a/tests/dmd/unit/parser/linkage_location.d b/tests/dmd/unit/parser/linkage_location.d index 2d1cab8813..c167a6bec5 100644 --- a/tests/dmd/unit/parser/linkage_location.d +++ b/tests/dmd/unit/parser/linkage_location.d @@ -1,273 +1,66 @@ module parser.linkage_location; -import dmd.frontend : parseModule; -import support : afterEach, beforeEach; +import dmd.frontend : parseModule, initDMD, deinitializeDMD; import dmd.attrib : CPPMangleDeclaration, CPPNamespaceDeclaration, LinkDeclaration; import dmd.location; import dmd.visitor : SemanticTimeTransitiveVisitor; -@beforeEach initializeFrontend() -{ - import dmd.frontend : initDMD; - - initDMD(); -} - -@afterEach deinitializeFrontend() -{ - import dmd.frontend : deinitializeDMD; - deinitializeDMD(); -} - -extern (C++) static class Visitor : SemanticTimeTransitiveVisitor +extern (C++) static class LinkVisitor : SemanticTimeTransitiveVisitor { alias visit = typeof(super).visit; - Loc l; + Loc[] locs; override void visit(CPPMangleDeclaration md) { - l = md.loc; + locs ~= md.loc; } override void visit(CPPNamespaceDeclaration nd) { - l = nd.loc; - } -} - -// Testing cpp mangling - -@("extern (C++, struct) class") -unittest -{ - auto t = parseModule("test.d", "extern (C++, struct) class C {}"); - - scope visitor = new Visitor; - t.module_.accept(visitor); - - assert(visitor.l.linnum == 1); - assert(visitor.l.charnum == 1); -} - -@("extern (C++, struct) struct") -unittest -{ - auto t = parseModule("test.d", "extern (C++, struct) struct S {}"); - - scope visitor = new Visitor; - t.module_.accept(visitor); - - assert(visitor.l.linnum == 1); - assert(visitor.l.charnum == 1); -} - -@("extern (C++, class) class") -unittest -{ - auto t = parseModule("test.d", "extern (C++, class) class C {}"); - - scope visitor = new Visitor; - t.module_.accept(visitor); - - assert(visitor.l.linnum == 1); - assert(visitor.l.charnum == 1); -} + locs ~= nd.loc; -@("extern (C++, class) struct") -unittest -{ - auto t = parseModule("test.d", "extern (C++, class) struct C {}"); - - scope visitor = new Visitor; - t.module_.accept(visitor); - - assert(visitor.l.linnum == 1); - assert(visitor.l.charnum == 1); -} - -@("extern (C++, struct) {}") -unittest -{ - auto t = parseModule("test.d", "extern (C++, struct) {}"); - - scope visitor = new Visitor; - t.module_.accept(visitor); - - assert(visitor.l.linnum == 1); - assert(visitor.l.charnum == 1); -} - -@("extern (C++, class) {}") -unittest -{ - auto t = parseModule("test.d", "extern (C++, class) {}"); - - scope visitor = new Visitor; - t.module_.accept(visitor); - - assert(visitor.l.linnum == 1); - assert(visitor.l.charnum == 1); -} - -// Testing namespaces - -@(`"namespace"`) -unittest -{ - auto t = parseModule("test.d", `extern (C++, "namespace") {}`); - - scope visitor = new Visitor; - t.module_.accept(visitor); - - assert(visitor.l.linnum == 1); - assert(visitor.l.charnum == 1); -} - -@(`"name"~"space"`) -unittest -{ - auto t = parseModule("test.d", `extern (C++, "name"~"space") {}`); - - scope visitor = new Visitor; - t.module_.accept(visitor); - - assert(visitor.l.linnum == 1); - assert(visitor.l.charnum == 1); -} - -extern (C++) static class NamespaceVisitor : SemanticTimeTransitiveVisitor -{ - alias visit = typeof(super).visit; - Loc[] l; - - override void visit(CPPNamespaceDeclaration nd) - { - l ~= nd.loc; - if (nd.decl.length) { + if (nd.decl.length) + { CPPNamespaceDeclaration next = cast(CPPNamespaceDeclaration)nd.decl.pop(); next.accept(this); } } -} - -@("multiple namespaces - comma separated") -unittest -{ - auto t = parseModule("test.d", `extern (C++, "ns1", "ns2", "ns3") {}`); - - scope visitor = new NamespaceVisitor; - t.module_.accept(visitor); - - assert(visitor.l.length == 3); - foreach (Loc l; visitor.l) { - assert(l.linnum == 1); - assert(l.charnum == 1); - } -} - -@("multiple namespaces - same line") -unittest -{ - auto t = parseModule("test.d", `extern (C++, "ns1") extern(C++, "ns2") {}`); - - scope visitor = new NamespaceVisitor; - t.module_.accept(visitor); - int charnum = 1; - - assert(visitor.l.length == 2); - foreach (Loc l; visitor.l) { - assert(l.linnum == 1); - assert(l.charnum == charnum); - charnum += 20; - } -} - -@("multiple namespaces - different lines") -unittest -{ - auto t = parseModule("test.d", "extern (C++, 'ns1')\nextern(C++, 'ns2') {}"); - - scope visitor = new NamespaceVisitor; - t.module_.accept(visitor); - int linnum = 1; - - assert(visitor.l.length == 2); - foreach (Loc l; visitor.l) { - assert(l.linnum == linnum); - assert(l.charnum == 1); - linnum++; - } -} - -// Testing linkage - -extern (C++) static class LinkVisitor : SemanticTimeTransitiveVisitor -{ - alias visit = typeof(super).visit; - Loc l; override void visit(LinkDeclaration ld) { - l = ld.loc; + locs ~= ld.loc; } } -@("extern(C++) variable") -unittest +void testLocs(string src, int[2][] lineCols...) { - auto t = parseModule("test.d", " extern(C++) int a;"); - - scope visitor = new LinkVisitor; - t.module_.accept(visitor); - - assert(visitor.l.linnum == 1); - assert(visitor.l.charnum == 13); -} - -@("extern(C++) function") -unittest -{ - auto t = parseModule("test.d", " extern(C++) void f();"); - - scope visitor = new LinkVisitor; - t.module_.accept(visitor); - - assert(visitor.l.linnum == 1); - assert(visitor.l.charnum == 13); -} + initDMD(); -@("extern(C++) struct") -unittest -{ - auto t = parseModule("test.d", " extern(C++) struct C {}"); + auto t = parseModule("test.d", src); scope visitor = new LinkVisitor; t.module_.accept(visitor); - assert(visitor.l.linnum == 1); - assert(visitor.l.charnum == 13); -} - -@("extern(C++) class") -unittest -{ - auto t = parseModule("test.d", " extern(C++) class C {}"); + // import std; writeln(visitor.locs.map!SourceLoc); - scope visitor = new LinkVisitor; - t.module_.accept(visitor); + assert(visitor.locs.length == lineCols.length); + foreach (i, lineCol; lineCols) + { + assert(visitor.locs[i].linnum == lineCol[0]); + assert(visitor.locs[i].charnum == lineCol[1]); + } - assert(visitor.l.linnum == 1); - assert(visitor.l.charnum == 13); + deinitializeDMD(); } -@("alias t = extern(C++) void f();") unittest { - auto t = parseModule("test.d", "alias t = extern(C++) void f();"); + testLocs("extern (C++, class) struct C {}", [1, 1]); + testLocs(`extern (C++, "name"~"space") {}`, [1, 1]); - scope visitor = new LinkVisitor; - t.module_.accept(visitor); + testLocs(" extern(C++) int a;", [1, 13]); + testLocs("alias t = extern(C++) void f();", [1, 11]); - assert(visitor.l.linnum == 1); - assert(visitor.l.charnum == 11); + testLocs(`extern (C++, "ns1", "ns2", "ns3") {}`, [1, 1], [1, 1], [1, 1]); + testLocs("extern (C++, 'ns1')\n extern(C++, 'ns2') {}", [1, 1], [2, 3]); } diff --git a/tests/dmd/unit/semantic/covariance.d b/tests/dmd/unit/semantic/covariance.d index dc2123146d..52787be66a 100644 --- a/tests/dmd/unit/semantic/covariance.d +++ b/tests/dmd/unit/semantic/covariance.d @@ -857,7 +857,7 @@ void testCovariant(Type base, Type target, in Result expected, const size_t line assert(base); assert(target); - StorageClass actualStc; + STC actualStc; const actual = base.covariant(target, &actualStc); enforce(!global.errors, line, "`covariant` raised an error!"); diff --git a/tests/dmd/unit/support.d b/tests/dmd/unit/support.d index e5e6d5187d..65e7e8e471 100644 --- a/tests/dmd/unit/support.d +++ b/tests/dmd/unit/support.d @@ -115,19 +115,19 @@ class NoopDiagnosticReporter : DiagnosticReporter override int errorCount() { return 0; } override int warningCount() { return 0; } override int deprecationCount() { return 0; } - override bool error(const ref Loc loc, const(char)* format, va_list, const(char)* p1, const(char)* p2) { return true; } - override bool errorSupplemental(const ref Loc loc, const(char)* format, va_list, const(char)* p1, const(char)* p2) { return true; } - override bool warning(const ref Loc loc, const(char)* format, va_list, const(char)* p1, const(char)* p2) { return true; } - override bool warningSupplemental(const ref Loc loc, const(char)* format, va_list, const(char)* p1, const(char)* p2) { return true; } - override bool deprecation(const ref Loc loc, const(char)* format, va_list, const(char)* p1, const(char)* p2) { return true; } - override bool deprecationSupplemental(const ref Loc loc, const(char)* format, va_list, const(char)* p1, const(char)* p2) { return true; } + override bool error(const ref SourceLoc loc, const(char)* format, va_list, const(char)* p1, const(char)* p2) { return true; } + override bool errorSupplemental(const ref SourceLoc loc, const(char)* format, va_list, const(char)* p1, const(char)* p2) { return true; } + override bool warning(const ref SourceLoc loc, const(char)* format, va_list, const(char)* p1, const(char)* p2) { return true; } + override bool warningSupplemental(const ref SourceLoc loc, const(char)* format, va_list, const(char)* p1, const(char)* p2) { return true; } + override bool deprecation(const ref SourceLoc loc, const(char)* format, va_list, const(char)* p1, const(char)* p2) { return true; } + override bool deprecationSupplemental(const ref SourceLoc loc, const(char)* format, va_list, const(char)* p1, const(char)* p2) { return true; } } /// A single diagnostic. const struct Diagnostic { /// The location of the diagnostic. - Loc location; + SourceLoc location; /// The diagnostic message. string message; @@ -157,7 +157,7 @@ struct DiagnosticCollector /// Handles a diagnostic. bool handleDiagnostic ( - const ref Loc location, + const ref SourceLoc location, Color headerColor, const(char)* header, const(char)* messageFormat, diff --git a/tests/driver/ftime-trace-ctfe.d b/tests/driver/ftime-trace-ctfe.d index 5c9eb1ef39..9c74d1413b 100644 --- a/tests/driver/ftime-trace-ctfe.d +++ b/tests/driver/ftime-trace-ctfe.d @@ -4,10 +4,9 @@ // CHECK: traceEvents -// CHECK-DAG: CTFE func: mulmul -// CHECK-DAG: CTFE start: Thing -// CHECK-DAG: CTFE func: muloddmul -// CHECK-DAG: ExecuteCompiler +// CHECK-DAG: Ctfe: call mulmul +// CHECK-DAG: Ctfe: Thing +// CHECK-DAG: Ctfe: call muloddmul module ftimetrace; diff --git a/tests/driver/ftime-trace.d b/tests/driver/ftime-trace.d index e8c7ab2cfa..0fbb740676 100644 --- a/tests/driver/ftime-trace.d +++ b/tests/driver/ftime-trace.d @@ -37,5 +37,4 @@ void main() foo(); } -// ALL-DAG: ExecuteCompiler -// FINE-DAG: Linking executable +// FINE-DAG: Linking diff --git a/tests/fail_compilation/global_var_collision.d b/tests/fail_compilation/global_var_collision.d index 89be7c930b..c51992cc5d 100644 --- a/tests/fail_compilation/global_var_collision.d +++ b/tests/fail_compilation/global_var_collision.d @@ -3,7 +3,7 @@ extern(C) extern int myGlobal; // CHECK: global_var_collision.d(9): Error: Global variable type does not match previous declaration with same mangled name: `myGlobal` -// CHECK-NEXT: Previous IR type: i32, mutable, thread-local +// CHECK: Previous IR type: i32, mutable, thread-local // CHECK-NEXT: New IR type: i64, const, non-thread-local pragma(mangle, myGlobal.mangleof) extern(C) __gshared const long myGlobal2 = 123; diff --git a/tests/sanitizers/fuzz_asan.d b/tests/sanitizers/fuzz_asan.d index 3a1711b153..b8be47e412 100644 --- a/tests/sanitizers/fuzz_asan.d +++ b/tests/sanitizers/fuzz_asan.d @@ -2,9 +2,8 @@ // REQUIRES: Fuzzer, ASan -// See https://github.com/ldc-developers/ldc/issues/2222 for -frame-pointer=all // See https://github.com/ldc-developers/ldc/pull/4328 for -fsanitize-address-use-after-return=never -// RUN: %ldc -g -fsanitize=address,fuzzer -fsanitize-address-use-after-return=never -frame-pointer=all %s -of=%t%exe +// RUN: %ldc -g -fsanitize=address,fuzzer -fsanitize-address-use-after-return=never %s -of=%t%exe // RUN: not %t%exe 2>&1 | FileCheck %s bool FuzzMe(ubyte* data, size_t dataSize)