diff --git a/.circleci/config.yml b/.circleci/config.yml index afdb8092017..f0ea4449621 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -18,27 +18,21 @@ commonSteps: &commonSteps fi apt-get -q update apt-get -yq install \ - git-core cmake ninja-build $gcc_pkg \ + git-core $gcc_pkg \ zlib1g-dev $libcurl_pkg curl gdb python3 python3-pip tzdata unzip zip \ $EXTRA_APT_PACKAGES - else # Download & extract CMake - curl -fL --retry 3 --max-time 300 -o cmake.tar.gz https://github.com/Kitware/CMake/releases/download/v3.20.3/cmake-3.20.3-macos-universal.tar.gz + curl -fL --retry 3 --max-time 300 -o cmake.tar.gz https://github.com/Kitware/CMake/releases/download/v3.27.1/cmake-3.27.1-linux-x86_64.tar.gz mkdir cmake - tar -xf cmake.tar.gz --strip 3 -C cmake + tar -xf cmake.tar.gz --strip 1 -C cmake rm cmake.tar.gz # Download & extract Ninja - curl -fL --retry 3 --max-time 60 -O https://github.com/ninja-build/ninja/releases/download/v1.10.2/ninja-mac.zip + curl -fL --retry 3 --max-time 60 -O https://github.com/symmetryinvestments/ninja/releases/download/v1.11.1-sym1/ninja-linux.zip mkdir ninja - tar -xf ninja-mac.zip -C ninja - rm ninja-mac.zip - # Download & extract LDC-flavoured LLVM with enabled assertions - curl -fL --retry 3 --max-time 300 -o llvm.tar.xz https://github.com/ldc-developers/llvm-project/releases/download/ldc-v$LLVM_VERSION/llvm-$LLVM_VERSION-osx-x86_64-withAsserts.tar.xz - mkdir llvm - tar -xf llvm.tar.xz --strip 1 -C llvm - rm llvm.tar.xz - # Add CMake, Ninja and LLVM to PATH for future steps - echo "export PATH=$PWD/cmake/bin:$PWD/ninja:$PWD/llvm/bin:$PATH" >> $BASH_ENV + unzip ninja-linux.zip -d ninja + rm ninja-linux.zip + # Add CMake and Ninja to PATH for future steps + echo "export PATH=$PWD/cmake/bin:$PWD/ninja:$PATH" >> $BASH_ENV fi # Install lit python3 --version @@ -68,12 +62,7 @@ commonSteps: &commonSteps -DD_COMPILER=$PWD/../host-ldc/bin/ldmd2 \ -DLDC_LINK_MANUALLY=OFF \ $EXTRA_CMAKE_FLAGS - # Work around out-of-memory errors - retry once with parallelization and one last time serially - targets='all ldc2-unittest all-test-runners' - for i in {1..2}; do - ninja -j$PARALLELISM -k0 $targets && break || true - done - ninja -j1 $targets + ninja -j$PARALLELISM obj/ldc2.o all ldc2-unittest all-test-runners bin/ldc2 -version - run: name: Run LDC D unittests @@ -104,44 +93,24 @@ jobs: <<: *commonSteps docker: - image: ubuntu:20.04 + resource_class: large environment: - - PARALLELISM: 2 + - PARALLELISM: 4 - CI_OS: linux - - EXTRA_APT_PACKAGES: llvm-dev libclang-common-10-dev + - EXTRA_APT_PACKAGES: llvm-11-dev libclang-common-11-dev - HOST_LDC_VERSION: 1.24.0 - EXTRA_CMAKE_FLAGS: "-DMULTILIB=ON -DRT_SUPPORT_SANITIZERS=ON -DBUILD_LTO_LIBS=ON" Ubuntu-20.04-sharedLibsOnly-gdmd: <<: *commonSteps docker: - image: ubuntu:20.04 + resource_class: large environment: - - PARALLELISM: 2 + - PARALLELISM: 4 - CI_OS: linux - - EXTRA_APT_PACKAGES: gdmd llvm-dev libclang-common-10-dev + - EXTRA_APT_PACKAGES: gdmd llvm-11-dev libclang-common-11-dev - HOST_LDC_VERSION: 1.24.0 - EXTRA_CMAKE_FLAGS: "-DBUILD_SHARED_LIBS=ON -DBUILD_LTO_LIBS=ON -DD_COMPILER=gdmd -DLDC_LINK_MANUALLY=ON" - macOS-x64: - <<: *commonSteps - macos: - xcode: "13.2.1" - environment: - - PARALLELISM: 4 - - CI_OS: osx - - LLVM_VERSION: 15.0.7 - - HOST_LDC_VERSION: 1.24.0 - - EXTRA_CMAKE_FLAGS: "-DD_COMPILER_FLAGS=-gcc=/usr/bin/c++ -DBUILD_LTO_LIBS=ON" - - MACOSX_DEPLOYMENT_TARGET: 11.0 # silence `ld: warning: object file (…/libphobos2-ldc.a(adler32.c.o)) was built for newer macOS version (11.6) than being linked (11.0)` - macOS-x64-sharedLibsOnly: - <<: *commonSteps - macos: - xcode: "13.2.1" - environment: - - PARALLELISM: 4 - - CI_OS: osx - - LLVM_VERSION: 15.0.7 - - HOST_LDC_VERSION: 1.24.0 - - EXTRA_CMAKE_FLAGS: "-DD_COMPILER_FLAGS=-gcc=/usr/bin/c++ -DBUILD_SHARED_LIBS=ON -DBUILD_LTO_LIBS=ON" - - MACOSX_DEPLOYMENT_TARGET: 11.0 workflows: version: 2 @@ -149,5 +118,3 @@ workflows: jobs: - Ubuntu-20.04-multilib-rtSanitizers - Ubuntu-20.04-sharedLibsOnly-gdmd - - macOS-x64 - - macOS-x64-sharedLibsOnly diff --git a/.cirrus.yml b/.cirrus.yml index d0e798040a0..79649e2c0ac 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -27,7 +27,7 @@ common_steps_template: &COMMON_STEPS_TEMPLATE -DINCLUDE_INSTALL_DIR=$installDir/import \ -DLDC_LINK_MANUALLY=OFF \ "${extraFlags[@]}" - ninja -j$PARALLELISM all ldc2-unittest all-test-runners + ninja -j$PARALLELISM obj/ldc2.o all ldc2-unittest all-test-runners bin/ldc2 -version always: run_compiler_unittests_script: | @@ -65,6 +65,12 @@ common_steps_template: &COMMON_STEPS_TEMPLATE excludes+='|^std.math.exponential(-shared)?$' # FIXME: failure excludes+='|^druntime-test-exceptions-debug$' + # std.path unittests apparently need HOME, which happens not to be set + export HOME=~ + echo "Setting HOME to '$HOME'" + elif [[ "$CI_OS-$CI_ARCH" == "osx-arm64" ]]; then + # FIXME: sporadic segfaults/bus errors with enabled optimizations + excludes+='|^core.thread.fiber(-shared)?$' fi ctest -j$PARALLELISM --output-on-failure -E "$excludes" @@ -184,10 +190,22 @@ install_ubuntu_prerequisites_template: &INSTALL_UBUNTU_PREREQUISITES_TEMPLATE fi apt-get -q update apt-get -yq install \ - git-core cmake ninja-build $gcc_pkg \ + git-core ninja-build $gcc_pkg \ zlib1g-dev $libcurl_pkg curl gdb python3 python3-pip tzdata unzip zip \ $EXTRA_APT_PACKAGES python3 --version + # Download & extract CMake + curl -fL --retry 3 --max-time 300 -o cmake.tar.gz https://github.com/Kitware/CMake/releases/download/v3.27.1/cmake-3.27.1-linux-$CI_ARCH.tar.gz + mkdir cmake + tar -xf cmake.tar.gz --strip 1 -C cmake + rm cmake.tar.gz + # Download & extract custom Ninja on x86_64 + if [[ "$CI_ARCH" == "x86_64" ]]; then + curl -fL --retry 3 --max-time 60 -O https://github.com/symmetryinvestments/ninja/releases/download/v1.11.1-sym1/ninja-linux.zip + mkdir ninja + unzip ninja-linux.zip -d ninja + rm ninja-linux.zip + fi # Download & extract host LDC curl -fL --retry 3 --max-time 300 -o ldc2.tar.xz https://github.com/ldc-developers/ldc/releases/download/v$HOST_LDC_VERSION/ldc2-$HOST_LDC_VERSION-linux-$CI_ARCH.tar.xz mkdir host-ldc @@ -201,12 +219,12 @@ install_macos_prerequisites_template: &INSTALL_MACOS_PREREQUISITES_TEMPLATE cd $CIRRUS_WORKING_DIR/.. sysctl -n hw.logicalcpu # Download & extract CMake - curl -fL --retry 3 --max-time 300 -o cmake.tar.gz https://github.com/Kitware/CMake/releases/download/v3.20.3/cmake-3.20.3-macos-universal.tar.gz + curl -fL --retry 3 --max-time 300 -o cmake.tar.gz https://github.com/Kitware/CMake/releases/download/v3.27.1/cmake-3.27.1-macos-universal.tar.gz mkdir cmake tar -xf cmake.tar.gz --strip 3 -C cmake rm cmake.tar.gz # Download & extract Ninja - curl -fL --retry 3 --max-time 60 -O https://github.com/ninja-build/ninja/releases/download/v1.10.2/ninja-mac.zip + curl -fL --retry 3 --max-time 60 -O https://github.com/symmetryinvestments/ninja/releases/download/v1.11.1-sym1/ninja-mac.zip mkdir ninja tar -xf ninja-mac.zip -C ninja rm ninja-mac.zip @@ -231,25 +249,9 @@ install_macos_prerequisites_template: &INSTALL_MACOS_PREREQUISITES_TEMPLATE environment: CIRRUS_CLONE_DEPTH: 50 HOST_LDC_VERSION: 1.31.0 - LLVM_VERSION: 15.0.7 + LLVM_VERSION: 16.0.6 GITHUB_TOKEN: ENCRYPTED[0955bd48c8d4e5391446fc0149d0719ad0b63df27ec9e6c180a5730a5b10dc7f28f09d1383423db158d21380ee2b022a] -task: - name: Ubuntu 20.04 x64 multilib rtSanitizers - container: - image: ubuntu:20.04 - cpu: 8 - memory: 16G - timeout_in: 20m - environment: - CI_ARCH: x86_64 - CI_OS: linux - EXTRA_APT_PACKAGES: "llvm-dev libclang-common-10-dev" - EXTRA_CMAKE_FLAGS: "-DMULTILIB=ON -DRT_SUPPORT_SANITIZERS=ON -DBUILD_LTO_LIBS=ON" - PARALLELISM: 8 - << : *INSTALL_UBUNTU_PREREQUISITES_TEMPLATE - << : *COMMON_STEPS_TEMPLATE - task: name: Ubuntu rolling x64 shared-libs-only gdmd # allow failures - gdb v10 came with regressions @@ -265,28 +267,12 @@ task: EXTRA_APT_PACKAGES: "gdmd llvm-dev libclang-common-15-dev" EXTRA_CMAKE_FLAGS: "-DBUILD_SHARED_LIBS=ON -DBUILD_LTO_LIBS=ON -DD_COMPILER=gdmd -DLDC_LINK_MANUALLY=ON" PARALLELISM: 8 + PATH: ${CIRRUS_WORKING_DIR}/../cmake/bin:${CIRRUS_WORKING_DIR}/../ninja:${PATH} # for gdmd: LANG: C.UTF-8 << : *INSTALL_UBUNTU_PREREQUISITES_TEMPLATE << : *COMMON_STEPS_TEMPLATE -task: - name: Ubuntu 20.04 x64 bootstrap - container: - image: ubuntu:20.04 - cpu: 8 - memory: 16G - timeout_in: 15m - environment: - CI_ARCH: x86_64 - CI_OS: linux - HOST_LDC_VERSION: 1.9.0 - EXTRA_APT_PACKAGES: "llvm-dev libclang-common-10-dev" - EXTRA_CMAKE_FLAGS: "-DBUILD_LTO_LIBS=ON" - PARALLELISM: 8 - << : *INSTALL_UBUNTU_PREREQUISITES_TEMPLATE - << : *COMMON_STEPS_TEMPLATE - task: name: macOS 12 $TASK_NAME_SUFFIX macos_instance: @@ -318,10 +304,14 @@ task: task: name: Ubuntu 20.04 aarch64 - arm_container: - image: ubuntu:20.04 + compute_engine_instance: + image_project: ubuntu-os-cloud + image: family/ubuntu-2004-lts-arm64 + platform: linux + architecture: arm64 cpu: 4 memory: 8G + disk: 20 timeout_in: 60m environment: CI_ARCH: aarch64 @@ -337,6 +327,7 @@ task: -DLLVM_ROOT_DIR=$CIRRUS_WORKING_DIR/../llvm -DD_COMPILER=$CIRRUS_WORKING_DIR/../bootstrap-ldc/bin/ldmd2 PARALLELISM: 4 + PATH: ${CIRRUS_WORKING_DIR}/../cmake/bin:${PATH} CLANG_VERSION: '15.0.3' # 15.0.6 requires a more recent libstdc++.so.6 than shipped with Ubuntu 20 CC: $CIRRUS_WORKING_DIR/../clang/bin/clang CXX: $CIRRUS_WORKING_DIR/../clang/bin/clang++ @@ -380,7 +371,7 @@ task: -DD_COMPILER=$PWD/../host-ldc/bin/ldmd2 \ -DBUILD_SHARED_LIBS=OFF \ -DBUILD_LTO_LIBS=ON - ninja -j$PARALLELISM + ninja -j$PARALLELISM obj/ldc2.o all bin/ldc2 -version << : *COMMON_STEPS_TEMPLATE << : *PACKAGING_STEPS_TEMPLATE diff --git a/.github/actions/1-setup/action.yml b/.github/actions/1-setup/action.yml index cb140e00041..b8e9bf55da2 100644 --- a/.github/actions/1-setup/action.yml +++ b/.github/actions/1-setup/action.yml @@ -99,11 +99,11 @@ runs: shell: bash run: | set -eux - sudo ln -sf "$(dirname "$PWD")/clang/bin/ld.lld" /usr/bin/ld + sudo ln -sf "$(dirname "$PWD")/llvm/bin/ld.lld" /usr/bin/ld ld --version - name: Install ninja - uses: seanmiddleditch/gha-setup-ninja@v3 + uses: symmetryinvestments/gha-setup-ninja@v1 - name: Install D host compiler uses: dlang-community/setup-dlang@v1 diff --git a/.github/actions/3-build-cross/action.yml b/.github/actions/3-build-cross/action.yml index f583c3213ae..fb1473281da 100644 --- a/.github/actions/3-build-cross/action.yml +++ b/.github/actions/3-build-cross/action.yml @@ -145,4 +145,4 @@ runs: ${{ inputs.cmake_flags }} ${{ inputs.with_pgo == 'true' && '-DDFLAGS_LDC=-fprofile-use=../pgo-ldc/merged.profdata' || '' }} ${{ env.CROSS_CMAKE_FLAGS }} - build_targets: ldc2 ldmd2 ldc-build-runtime ldc-profdata ldc-prune-cache timetrace2txt + build_targets: ldc2 ldmd2 ldc-build-runtime ldc-build-plugin ldc-profdata ldc-prune-cache timetrace2txt diff --git a/.github/actions/3-build-cross/android-llvm-config.in b/.github/actions/3-build-cross/android-llvm-config.in index 34060eb4948..d2cc7bb4d97 100644 --- a/.github/actions/3-build-cross/android-llvm-config.in +++ b/.github/actions/3-build-cross/android-llvm-config.in @@ -45,7 +45,7 @@ prefix=@LLVM_INSTALL_DIR@ has_rtti=NO CPPFLAGS="-I${prefix}/include -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS" CFLAGS="${CPPFLAGS} ${CFLAGS}" -CXXFLAGS="${CFLAGS} -std=c++14 -fno-exceptions -fno-unwind-tables -fno-asynchronous-unwind-tables" +CXXFLAGS="${CFLAGS} -std=c++17 -fno-exceptions -fno-unwind-tables -fno-asynchronous-unwind-tables" if [ "$has_rtti" != "YES" ]; then CXXFLAGS="$CXXFLAGS -fno-rtti"; fi LDFLAGS="-L${prefix}/lib" LIBFILE="${prefix}/lib/libLLVM-$version.so" @@ -53,28 +53,29 @@ LIBFILE="${prefix}/lib/libLLVM-$version.so" components="aarch64 aarch64asmparser aarch64codegen aarch64desc aarch64disassembler aarch64info aarch64utils \ aggressiveinstcombine all all-targets analysis arm armasmparser armcodegen armdesc armdisassembler arminfo armutils \ asmparser asmprinter binaryformat bitreader bitstreamreader bitwriter cfguard codegen core coroutines coverage \ -debuginfocodeview debuginfodwarf debuginfogsym debuginfomsf debuginfopdb demangle dlltooldriver dwarflinker dwp \ -engine executionengine extensions filecheck frontendopenacc frontendopenmp fuzzercli fuzzmutate globalisel instcombine \ -instrumentation interfacestub interpreter ipo irreader jitlink libdriver lineeditor linker lto mc mca mcdisassembler \ +debuginfocodeview debuginfodwarf debuginfogsym debuginfologicalview debuginfomsf debuginfopdb demangle dlltooldriver dwarflinker dwarflinkerparallel dwp \ +engine executionengine extensions filecheck frontendhlsl frontendopenacc frontendopenmp fuzzercli fuzzmutate globalisel instcombine \ +instrumentation interfacestub interpreter ipo irprinter irreader jitlink libdriver lineeditor linker lto mc mca mcdisassembler \ mcjit mcparser mirparser native nativecodegen objcarcopts objcopy object objectyaml option orcjit orcshared orctargetprocess \ -passes profiledata remarks runtimedyld scalaropts selectiondag support symbolize tablegen target textapi \ +passes profiledata remarks runtimedyld scalaropts selectiondag spirv spirvcodegen spirvdesc spirvinfo support symbolize tablegen target targetparser textapi \ transformutils vectorize webassembly webassemblyasmparser webassemblycodegen webassemblydesc webassemblydisassembler \ webassemblyinfo webassemblyutils windowsdriver windowsmanifest x86 x86asmparser x86codegen x86desc x86disassembler x86info \ x86targetmca xray" -static_libs="-lLLVMWindowsManifest -lLLVMWindowsDriver -lLLVMXRay -lLLVMLibDriver -lLLVMDlltoolDriver -lLLVMCoverage -lLLVMLineEditor \ +static_libs="-lLLVMWindowsManifest -lLLVMXRay -lLLVMLibDriver -lLLVMDlltoolDriver -lLLVMCoverage -lLLVMLineEditor \ +-lLLVMSPIRVCodeGen -lLLVMSPIRVDesc -lLLVMSPIRVInfo \ -lLLVMX86TargetMCA -lLLVMX86Disassembler -lLLVMX86AsmParser -lLLVMX86CodeGen -lLLVMX86Desc -lLLVMX86Info -lLLVMWebAssemblyDisassembler \ -lLLVMWebAssemblyAsmParser -lLLVMWebAssemblyCodeGen -lLLVMWebAssemblyDesc -lLLVMWebAssemblyUtils -lLLVMWebAssemblyInfo -lLLVMARMDisassembler \ -lLLVMARMAsmParser -lLLVMARMCodeGen -lLLVMARMDesc -lLLVMARMUtils -lLLVMARMInfo -lLLVMAArch64Disassembler \ -lLLVMAArch64AsmParser -lLLVMAArch64CodeGen -lLLVMAArch64Desc -lLLVMAArch64Utils -lLLVMAArch64Info -lLLVMOrcJIT \ --lLLVMMCJIT -lLLVMJITLink -lLLVMInterpreter -lLLVMExecutionEngine -lLLVMRuntimeDyld -lLLVMOrcTargetProcess -lLLVMOrcShared \ --lLLVMDWP -lLLVMDebugInfoGSYM -lLLVMOption -lLLVMObjectYAML -lLLVMObjCopy -lLLVMMCA \ --lLLVMMCDisassembler -lLLVMLTO -lLLVMPasses -lLLVMCFGuard -lLLVMCoroutines -lLLVMObjCARCOpts -lLLVMipo \ --lLLVMVectorize -lLLVMLinker -lLLVMInstrumentation -lLLVMFrontendOpenMP -lLLVMFrontendOpenACC -lLLVMExtensions \ --lLLVMDWARFLinker -lLLVMGlobalISel -lLLVMMIRParser -lLLVMAsmPrinter -lLLVMSelectionDAG \ --lLLVMCodeGen -lLLVMIRReader -lLLVMAsmParser -lLLVMInterfaceStub -lLLVMFileCheck -lLLVMFuzzMutate -lLLVMTarget \ +-lLLVMWindowsDriver -lLLVMMCJIT -lLLVMJITLink -lLLVMInterpreter -lLLVMExecutionEngine -lLLVMRuntimeDyld -lLLVMOrcTargetProcess -lLLVMOrcShared \ +-lLLVMDWP -lLLVMDebugInfoLogicalView -lLLVMDebugInfoGSYM -lLLVMOption -lLLVMObjectYAML -lLLVMObjCopy -lLLVMMCA \ +-lLLVMMCDisassembler -lLLVMLTO -lLLVMPasses -lLLVMCFGuard -lLLVMCoroutines -lLLVMipo \ +-lLLVMVectorize -lLLVMLinker -lLLVMInstrumentation -lLLVMFrontendOpenMP -lLLVMFrontendOpenACC -lLLVMFrontendHLSL -lLLVMExtensions \ +-lLLVMDWARFLinkerParallel -lLLVMDWARFLinker -lLLVMGlobalISel -lLLVMMIRParser -lLLVMAsmPrinter -lLLVMSelectionDAG \ +-lLLVMCodeGen -lLLVMObjCARCOpts -lLLVMIRPrinter -lLLVMInterfaceStub -lLLVMFileCheck -lLLVMFuzzMutate -lLLVMTarget \ -lLLVMScalarOpts -lLLVMInstCombine -lLLVMAggressiveInstCombine -lLLVMTransformUtils -lLLVMBitWriter -lLLVMAnalysis \ --lLLVMProfileData -lLLVMSymbolize -lLLVMDebugInfoPDB -lLLVMDebugInfoMSF -lLLVMDebugInfoDWARF -lLLVMObject -lLLVMTextAPI -lLLVMMCParser -lLLVMMC -lLLVMDebugInfoCodeView \ --lLLVMBitReader -lLLVMFuzzerCLI -lLLVMCore -lLLVMRemarks -lLLVMBitstreamReader -lLLVMBinaryFormat -lLLVMTableGen -lLLVMSupport \ +-lLLVMProfileData -lLLVMSymbolize -lLLVMDebugInfoPDB -lLLVMDebugInfoMSF -lLLVMDebugInfoDWARF -lLLVMObject -lLLVMTextAPI -lLLVMMCParser -lLLVMIRReader -lLLVMAsmParser -lLLVMMC -lLLVMDebugInfoCodeView \ +-lLLVMBitReader -lLLVMFuzzerCLI -lLLVMCore -lLLVMRemarks -lLLVMBitstreamReader -lLLVMBinaryFormat -lLLVMTargetParser -lLLVMTableGen -lLLVMSupport \ -lLLVMDemangle" shared_libs="-lLLVM-$version" libs=$static_libs diff --git a/.github/actions/5-install/action.yml b/.github/actions/5-install/action.yml index 4a442af58ac..f9388388db1 100644 --- a/.github/actions/5-install/action.yml +++ b/.github/actions/5-install/action.yml @@ -22,6 +22,7 @@ runs: else mkdir -p install/bin cp build-cross/bin/{ldc2,ldmd2,ldc-build-runtime,ldc-profdata,ldc-prune-cache,timetrace2txt} install/bin/ + cp build-cross/bin/ldc-build-plugin install/bin/ || true cp -R build-cross-libs/lib install/ cp build-cross/lib/{libldc_rt.*,libLTO-ldc.dylib,LLVMgold-ldc.so} install/lib/ || true mkdir install/etc diff --git a/.github/actions/5a-ios/action.yml b/.github/actions/5a-ios/action.yml index 3d41c168be5..cc01e0384bd 100644 --- a/.github/actions/5a-ios/action.yml +++ b/.github/actions/5a-ios/action.yml @@ -41,6 +41,7 @@ runs: { switches = [ \"-defaultlib=phobos2-ldc,druntime-ldc\", + \"-func-specialization-size-threshold=1000000000\", \"-Xcc=-target\", \"-Xcc=$triple\", \"-Xcc=-miphoneos-version-min=$deployment_target\", diff --git a/.github/actions/helper-build-ldc/action.yml b/.github/actions/helper-build-ldc/action.yml index ed31a3e9860..3fafd106f0c 100644 --- a/.github/actions/helper-build-ldc/action.yml +++ b/.github/actions/helper-build-ldc/action.yml @@ -15,7 +15,7 @@ inputs: default: '' build_targets: required: false - default: '' # all + default: 'all' arch: required: false # Windows only runs: @@ -39,7 +39,7 @@ runs: ${{ inputs.specify_install_dir == 'true' && '-DINCLUDE_INSTALL_DIR="$installDir/import"' || '' }} \ ${{ inputs.cmake_flags }} - ninja ${{ inputs.build_targets }} + ninja obj/ldc2.o ${{ inputs.build_targets }} # Windows: invoke CMake & ninja in MSVC env - if: runner.os == 'Windows' @@ -62,4 +62,4 @@ runs: ${{ inputs.cmake_flags }} if %errorlevel% neq 0 exit /b %errorlevel% - ninja ${{ inputs.build_targets }} || exit /b + ninja obj/ldc2.obj ${{ inputs.build_targets }} || exit /b diff --git a/.github/actions/merge-macos/action.yml b/.github/actions/merge-macos/action.yml index 7efd0e4a906..b94eea63837 100644 --- a/.github/actions/merge-macos/action.yml +++ b/.github/actions/merge-macos/action.yml @@ -48,6 +48,7 @@ runs: // default switches injected before all explicit command-line switches switches = [ \"-defaultlib=phobos2-ldc,druntime-ldc\", + \"-func-specialization-size-threshold=1000000000\", ]; // default switches appended after all explicit command-line switches post-switches = [ @@ -63,6 +64,7 @@ runs: { switches = [ \"-defaultlib=phobos2-ldc,druntime-ldc\", + \"-func-specialization-size-threshold=1000000000\", \"-Xcc=-target\", \"-Xcc=x86_64-apple-macos\", ]; @@ -76,6 +78,7 @@ runs: { switches = [ \"-defaultlib=phobos2-ldc,druntime-ldc\", + \"-func-specialization-size-threshold=1000000000\", \"-Xcc=-target\", \"-Xcc=arm64-apple-macos\", ]; @@ -89,6 +92,7 @@ runs: { switches = [ \"-defaultlib=phobos2-ldc,druntime-ldc\", + \"-func-specialization-size-threshold=1000000000\", \"-Xcc=-target\", \"-Xcc=arm64-apple-ios$ios_version\", \"-Xcc=-miphoneos-version-min=$ios_version\", diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index fe04ce8658f..6d9bb156337 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -11,7 +11,7 @@ concurrency: env: CLANG_VERSION: 15.0.6 - LLVM_VERSION: 15.0.7 + LLVM_VERSION: 16.0.6 jobs: build-native: @@ -42,9 +42,12 @@ jobs: bootstrap_cmake_flags: >- -DBUILD_LTO_LIBS=ON -DD_COMPILER_FLAGS=-gcc=/usr/bin/c++ + # https://github.com/ldc-developers/ldc/issues/4462: + # When using LTO, we need to explicitly export ~all symbols for plugin support via `ld64 -exported_symbol '__*'`. + # Additionally `-w` to suppress resulting linker warnings. extra_cmake_flags: >- -DBUILD_LTO_LIBS=ON - -DD_COMPILER_FLAGS="-gcc=/usr/bin/c++ -O -flto=full -defaultlib=phobos2-ldc-lto,druntime-ldc-lto" + -DD_COMPILER_FLAGS="-gcc=/usr/bin/c++ -O -flto=full -defaultlib=phobos2-ldc-lto,druntime-ldc-lto -L-exported_symbol '-L__*' -L-w" -DEXTRA_CXXFLAGS=-flto=full with_pgo: true @@ -126,9 +129,10 @@ jobs: os: osx arch: arm64 bootstrap_cmake_flags: -DD_COMPILER_FLAGS=-gcc=/usr/bin/c++ + # see native macOS job comment for extra flags (https://github.com/ldc-developers/ldc/issues/4462) extra_cmake_flags: >- -DBUILD_LTO_LIBS=ON - -DD_COMPILER_FLAGS="-O -flto=full -defaultlib=phobos2-ldc-lto,druntime-ldc-lto" + -DD_COMPILER_FLAGS="-O -flto=full -defaultlib=phobos2-ldc-lto,druntime-ldc-lto -L-exported_symbol '-L__*' -L-w" -DEXTRA_CXXFLAGS=-flto=full with_pgo: true diff --git a/.github/workflows/supported_llvm_versions.yml b/.github/workflows/supported_llvm_versions.yml index 9b9ff4021f0..475223b2cfa 100644 --- a/.github/workflows/supported_llvm_versions.yml +++ b/.github/workflows/supported_llvm_versions.yml @@ -15,50 +15,45 @@ jobs: fail-fast: false matrix: include: - - job_name: Ubuntu 20.04, LLVM 15, latest LDC beta + - job_name: Ubuntu 20.04, LLVM 16, latest LDC beta os: ubuntu-20.04 host_dc: ldc-beta - llvm_version: 15.0.6 - - job_name: Ubuntu 20.04, LLVM 14, latest LDC beta + llvm_version: 16.0.6 # LDC-LLVM + - job_name: Ubuntu 20.04, LLVM 15, latest LDC beta os: ubuntu-20.04 host_dc: ldc-beta - llvm_version: 14.0.0 - # the compiler-rt libs installation is somehow non-standard - cmake_flags: -DLDC_INSTALL_LLVM_RUNTIME_LIBS_OS=x86_64-unknown-linux-gnu -DLDC_INSTALL_LLVM_RUNTIME_LIBS_ARCH="" - - job_name: Ubuntu 20.04, LLVM 12, latest LDC beta + llvm_version: 15.0.6 + - job_name: Ubuntu 20.04, LLVM 12, bootstrap LDC os: ubuntu-20.04 - host_dc: ldc-beta + host_dc: ldc-1.9.0 llvm_version: 12.0.1 - cmake_flags: -DLIB_SUFFIX=64 - - job_name: Ubuntu 20.04, LLVM 11, latest LDC beta - os: ubuntu-20.04 - host_dc: ldc-beta - llvm_version: 11.1.0 - cmake_flags: -DBUILD_SHARED_LIBS=ON -DRT_SUPPORT_SANITIZERS=ON - - job_name: Ubuntu 20.04, LLVM 9, latest DMD beta + cmake_flags: -DBUILD_SHARED_LIBS=ON -DLIB_SUFFIX=64 + - job_name: Ubuntu 20.04, LLVM 11, latest DMD beta os: ubuntu-20.04 host_dc: dmd-beta - llvm_version: 9.0.1 + llvm_version: 11.1.0 cmake_flags: -DBUILD_SHARED_LIBS=OFF -DRT_SUPPORT_SANITIZERS=ON -DLDC_LINK_MANUALLY=ON - - job_name: macOS 11, LLVM 10, latest DMD beta + - job_name: macOS 11, LLVM 13, latest DMD beta os: macos-11 host_dc: dmd-beta - llvm_version: 10.0.1 - cmake_flags: -DBUILD_SHARED_LIBS=ON -DRT_SUPPORT_SANITIZERS=ON -DLDC_LINK_MANUALLY=ON -DCMAKE_CXX_COMPILER=/usr/bin/c++ -DCMAKE_C_COMPILER=/usr/bin/cc -DCMAKE_OSX_DEPLOYMENT_TARGET=11.0 - - job_name: macOS 11, LLVM 13, latest LDC beta + llvm_version: 13.0.1 + cmake_flags: -DBUILD_SHARED_LIBS=ON -DRT_SUPPORT_SANITIZERS=ON -DLDC_LINK_MANUALLY=ON -DCMAKE_CXX_COMPILER=/usr/bin/c++ -DCMAKE_C_COMPILER=/usr/bin/cc + - job_name: macOS 11, LLVM 14, latest LDC beta os: macos-11 host_dc: ldc-beta - llvm_version: 13.0.1 - cmake_flags: -DBUILD_SHARED_LIBS=OFF -DD_COMPILER_FLAGS=-gcc=/usr/bin/c++ -DCMAKE_OSX_DEPLOYMENT_TARGET=11.0 + llvm_version: 14.0.6 + cmake_flags: -DBUILD_SHARED_LIBS=OFF -DD_COMPILER_FLAGS=-gcc=/usr/bin/c++ name: ${{ matrix.job_name }} runs-on: ${{ matrix.os }} + env: + MACOSX_DEPLOYMENT_TARGET: 11.6 # silence `ld: warning: object file (…) was built for newer macOS version (…) than being linked (…)` steps: - uses: actions/checkout@v3 with: submodules: true fetch-depth: 50 - name: Install ninja - uses: seanmiddleditch/gha-setup-ninja@v3 + uses: symmetryinvestments/gha-setup-ninja@v1 - name: Install D host compiler uses: dlang-community/setup-dlang@v1 with: @@ -100,14 +95,18 @@ jobs: else suffix='x86_64-linux-gnu-ubuntu-16.04' fi - curl -fL --retry 3 --max-time 300 -o llvm.tar.xz \ - https://github.com/llvm/llvm-project/releases/download/llvmorg-$version/clang+llvm-$version-$suffix.tar.xz + url="https://github.com/llvm/llvm-project/releases/download/llvmorg-$version/clang+llvm-$version-$suffix.tar.xz" + # FIXME: weird crashes with official v16.0.0 archive; use LDC-LLVM instead + if [[ "$version" =~ ^16\. ]]; then + url="https://github.com/ldc-developers/llvm-project/releases/download/ldc-v$version/llvm-$version-linux-x86_64.tar.xz" + fi + curl -fL --retry 3 --max-time 300 -o llvm.tar.xz "$url" mkdir llvm tar -xf llvm.tar.xz --strip 1 -C llvm rm llvm.tar.xz - name: 'Linux: Make lld the default linker' - if: runner.os == 'Linux' + if: runner.os == 'Linux' && matrix.host_dc != 'ldc-1.9.0' run: | set -eux echo "Using lld to work around sporadic failures" @@ -122,7 +121,7 @@ jobs: -DLLVM_ROOT_DIR="$PWD/llvm" \ -DLDC_LINK_MANUALLY=OFF \ ${{ matrix.cmake_flags }} - ninja all ldc2-unittest all-test-runners + ninja obj/ldc2.o all ldc2-unittest all-test-runners bin/ldc2 --version - name: Run LDC D unittests @@ -132,10 +131,6 @@ jobs: if: success() || failure() run: | set -eux - # LLVM 9: libclang_rt.fuzzer-x86_64.a not compiled with -fPIC - if [[ '${{ matrix.llvm_version }}' = 9.* ]]; then - echo "config.available_features.remove('Fuzzer')" >> tests/sanitizers/lit.local.cfg - fi # LLVM 14+ on Linux: don't use vanilla llvm-symbolizer (no support for zlib-compressed debug sections => failing ASan tests) if [[ '${{ runner.os }}' == 'Linux' && '${{ matrix.llvm_version }}' =~ ^1[4-9]\. ]]; then mv llvm/bin/llvm-symbolizer llvm/bin/llvm-symbolizer.bak diff --git a/CHANGELOG.md b/CHANGELOG.md index a769e61922a..6fd302267b3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,11 +1,42 @@ # LDC master +#### Big news + +#### Platform support + +#### Bug fixes + +# LDC 1.34.0 (2023-08-26) + +#### Big news +- Frontend, druntime and Phobos are at version [2.104.2](https://dlang.org/changelog/2.104.0.html). (#4440) +- Support for [LLVM 16](https://releases.llvm.org/16.0.0/docs/ReleaseNotes.html). The prebuilt packages use v16.0.6. (#4411, #4423) + - We have come across miscompiles with LLVM 16's newly-enabled-by-default function specializations (on Win64 and macOS). To be on the safe side, LDC disables them by default for all targets via `-func-specialization-size-threshold=1000000000` in `etc/ldc2.conf` (and separately for LTO on Posix platforms). To enable the function specializations, explicitly override it with e.g. `-func-specialization-size-threshold=100` (the LLVM 16 default) and, for LTO on Posix, a similar LTO plugin option in the linker cmdline (see linker cmdline with `-v`). + +#### Platform support +- Supports LLVM 11.0 - 16.0. Support for LLVM 9 and 10 was dropped. +- 64-bit RISC-V: Now defaults to `-mattr=+m,+a,+f,+d,+c` ('rv64gc' ABI) for non-bare-metal targets, i.e., if the target triple includes a valid operating system. (#4390) + +#### Bug fixes +- Fix function pointers/delegates on Harvard architectures (e.g., AVR). (#4432, #4465) + +# LDC 1.33.0 (2023-07-23) + #### Big news - Frontend, druntime and Phobos are at version [2.103.1](https://dlang.org/changelog/2.103.0.html), incl. new command-line option `-verror-supplements`. (#4345) +- The `--plugin` commandline option now also accepts semantic analysis plugins. Semantic analysis plugins are recognized by exporting the symbol: `extern(C) void runSemanticAnalysis(Module m)`. The plugin's `runSemanticAnalysis` function is called for each module, after all other semantic analysis steps (also after DCompute SemA), just before object codegen. (#4430) +- New tool `ldc-build-plugin` that helps compiling user plugins. It downloads the correct LDC source version (if it's not already available), and calls LDC with the correct commandline flags to build a plugin. (#4430) +- New commandline option `-femit-local-var-lifetime` that enables variable lifetime (scope) annotation to LLVM IR codegen. Lifetime annotation enables stack memory reuse for local variables with non-overlapping scope. (#4395) +- C files are now automatically preprocessed using the external C compiler (configurable via `-gcc` or the `CC` environment variable, and `-Xcc` for extra flags). Extra preprocessor flags (e.g., include dirs and manual defines) can be added via new command-line option `-P`. (#4417) + - Windows: If `clang-cl.exe` is on `PATH`, it is preferred over Microsoft's `cl.exe` by default (e.g., to avoid printing the C source file name to stderr during preprocessing). +- Less pedantic checks for conflicting C(++) function declarations when compiling multiple modules to a single object file ('Error: Function type does not match previously declared function with the same mangled name'). The error now only appears if an object file actually references multiple conflicting functions. (#4420) +- New command-line option `--fcf-protection`, which enables Intel's Control-Flow Enforcement Technology (CET). (#4437) #### Platform support +- Supports LLVM 9.0 - 15.0. #### Bug fixes +- Handle potential lambda mangle collisions across separately compiled object files (and the linker then silently picking an arbitrary implementation). Lambdas (and their nested global variables) are now internal to each referencing object file (`static` linkage in C). (#4415) # LDC 1.32.2 (2023-05-12) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0f7b64fc701..406a896bffa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,12 @@ cmake_minimum_required(VERSION 3.4.3) if(POLICY CMP0025) - cmake_policy(SET CMP0025 NEW) + cmake_policy(SET CMP0025 NEW) +endif() +if(${CMAKE_VERSION} VERSION_GREATER "3.26.9") + # Prevent implicit dependencies for custom commands, e.g., + # `obj/ldc2.o` depending on `lib/libldc.a` with LDC_LINK_MANUALLY=ON. + # Only supported since CMake v3.27 unfortunately. + set(CMAKE_ADD_CUSTOM_COMMAND_DEPENDS_EXPLICIT_ONLY ON) endif() project(ldc) @@ -28,7 +34,7 @@ endfunction() # Locate LLVM. # -find_package(LLVM 9.0 REQUIRED +find_package(LLVM 11.0 REQUIRED all-targets analysis asmparser asmprinter bitreader bitwriter codegen core debuginfodwarf debuginfomsf debuginfopdb demangle instcombine ipo instrumentation irreader libdriver linker lto mc @@ -46,9 +52,9 @@ foreach(LLVM_SUPPORTED_TARGET ${LLVM_TARGETS_TO_BUILD}) add_definitions("-DLDC_LLVM_SUPPORTED_TARGET_${LLVM_SUPPORTED_TARGET}=1") endforeach() -# Set MLIR support variables if it is found. Only try for LLVM >= 10.0. +# Set MLIR support variables if it is found. # FIXME: LLVM 14+ (`mlir::OwningModuleRef` replacement) -if(NOT (LDC_LLVM_VER LESS 1000) AND NOT LDC_WITH_MLIR STREQUAL "OFF" AND LDC_LLVM_VER LESS 1400) +if(NOT LDC_WITH_MLIR STREQUAL "OFF" AND LDC_LLVM_VER LESS 1400) include(FindMLIR) if(MLIR_FOUND) message(STATUS "-- Building LDC with MLIR support (${MLIR_ROOT_DIR})") @@ -111,10 +117,10 @@ include(GetLinuxDistribution) # # Version information -set(LDC_VERSION "1.33.0") # May be overridden by git hash tag +set(LDC_VERSION "1.34.0") # May be overridden by git hash tag set(DMDFE_MAJOR_VERSION 2) -set(DMDFE_MINOR_VERSION 103) -set(DMDFE_PATCH_VERSION 1) +set(DMDFE_MINOR_VERSION 104) +set(DMDFE_PATCH_VERSION 2) set(DMD_VERSION ${DMDFE_MAJOR_VERSION}.${DMDFE_MINOR_VERSION}.${DMDFE_PATCH_VERSION}) @@ -249,17 +255,6 @@ if(CMAKE_COMPILER_IS_GNUCXX OR (${CMAKE_CXX_COMPILER_ID} MATCHES "Clang")) endif() endif() -if(NOT WIN32 AND NOT CYGWIN) - # Unify symbol visibility with LLVM to silence linker warning "direct access in function X to global - # weak symbol Y means the weak symbol cannot be overridden at runtime. This was likely caused by - # different translation units being compiled with different visibility settings." - # See LLVM's cmake/modules/HandleLLVMOptions.cmake. - check_cxx_compiler_flag("-fvisibility-inlines-hidden" SUPPORTS_FVISIBILITY_INLINES_HIDDEN_FLAG) - if (${SUPPORTS_FVISIBILITY_INLINES_HIDDEN_FLAG}) - append("-fvisibility-inlines-hidden" LDC_CXXFLAGS) - endif() -endif() - if(MSVC) # Remove flags here, for exceptions and RTTI. # CL.EXE complains to override flags like "/GR /GR-". @@ -378,6 +373,7 @@ set(DRV_SRC driver/cl_options-llvm.cpp driver/codegenerator.cpp driver/configfile.cpp + driver/cpreprocessor.cpp driver/dcomputecodegenerator.cpp driver/exe_path.cpp driver/targetmachine.cpp @@ -605,12 +601,10 @@ if(MSVC) endif() endif() if(LDC_WITH_LLD) - if(NOT (LDC_LLVM_VER LESS 1000)) - if(MSVC) - list(APPEND LDC_LINKERFLAG_LIST LLVMSymbolize.lib) - else() - set(LDC_LINKERFLAG_LIST -lLLVMSymbolize ${LDC_LINKERFLAG_LIST}) - endif() + if(MSVC) + list(APPEND LDC_LINKERFLAG_LIST LLVMSymbolize.lib) + else() + set(LDC_LINKERFLAG_LIST -lLLVMSymbolize ${LDC_LINKERFLAG_LIST}) endif() if (LDC_LLVM_VER LESS 1200 OR NOT LDC_LLVM_VER LESS 1400) set(LLD_MACHO lldMachO) @@ -684,10 +678,26 @@ if(LDC_ENABLE_PLUGINS) if(LINKER_ACCEPTS_EXPORT_DYNAMIC_FLAG) set(LDC_LINKERFLAG_LIST "${LDC_LINKERFLAG_LIST};-Wl,--export-dynamic") + else() + message(WARNING "Linker does not accept --export-dynamic, user plugins may give missing symbol errors upon load") endif() endif() endif() message(STATUS "-- Building LDC with plugin support (LDC_ENABLE_PLUGINS): ${LDC_ENABLE_PLUGINS}") +message(STATUS "-- Linking LDC with flags: ${ALTERNATIVE_MALLOC_O};${LDC_LINKERFLAG_LIST}") + +if(NOT WIN32 AND NOT CYGWIN) + # Unify symbol visibility with LLVM to silence linker warning "direct access in function X to global + # weak symbol Y means the weak symbol cannot be overridden at runtime. This was likely caused by + # different translation units being compiled with different visibility settings." + # See LLVM's cmake/modules/HandleLLVMOptions.cmake. + check_cxx_compiler_flag("-fvisibility-inlines-hidden" SUPPORTS_FVISIBILITY_INLINES_HIDDEN_FLAG) + if (LDC_ENABLE_PLUGINS AND NOT APPLE) + # For plugins, we shouldn't apply this flag because it hides the inline methods of e.g. Visitor. On macOS it's OK to add. + elseif (${SUPPORTS_FVISIBILITY_INLINES_HIDDEN_FLAG}) + append("-fvisibility-inlines-hidden" LDC_CXXFLAGS) + endif() +endif() build_d_executable( "${LDC_EXE}" @@ -784,7 +794,11 @@ else() endif() set(LDC_INSTALL_LLVM_RUNTIME_LIBS ${LDC_INSTALL_LLVM_RUNTIME_LIBS_DEFAULT} CACHE BOOL "Copy/install LLVM compiler-rt libraries (ASan, libFuzzer, ...) from LLVM/Clang into LDC lib dir when available.") function(copy_compilerrt_lib llvm_lib_name ldc_lib_name fixup_dylib) - set(llvm_lib_path ${LLVM_LIBRARY_DIRS}/clang/${LLVM_VERSION_BASE_STRING}/lib/${llvm_lib_name}) + if(LDC_LLVM_VER LESS 1600) + set(llvm_lib_path ${LLVM_LIBRARY_DIRS}/clang/${LLVM_VERSION_BASE_STRING}/lib/${llvm_lib_name}) + else() + set(llvm_lib_path ${LLVM_LIBRARY_DIRS}/clang/${LLVM_VERSION_MAJOR}/lib/${llvm_lib_name}) + endif() if(EXISTS ${llvm_lib_path}) message(STATUS "-- - ${llvm_lib_path} --> ${ldc_lib_name}") set(ldc_lib_path ${PROJECT_BINARY_DIR}/lib${LIB_SUFFIX}/${ldc_lib_name}) diff --git a/README.md b/README.md index 390a1409f04..f13d12a0a94 100644 --- a/README.md +++ b/README.md @@ -77,9 +77,8 @@ libraries is available on the project wiki for [Windows](http://wiki.dlang.org/Building_and_hacking_LDC_on_Windows_using_MSVC). If you have a working C++/D build environment, CMake, and a recent LLVM -version (≥ 6.0) available, there should be no big surprises. Do not -forget to make sure all the submodules (druntime, phobos, dmd-testsuite) -are up to date: +version (≥ 11) available, there should be no big surprises. Do not +forget to make sure the Phobos submodule is up to date: $ cd ldc $ git submodule update --init diff --git a/cmake/Modules/BuildDExecutable.cmake b/cmake/Modules/BuildDExecutable.cmake index 32101dafd5c..6318433ea75 100644 --- a/cmake/Modules/BuildDExecutable.cmake +++ b/cmake/Modules/BuildDExecutable.cmake @@ -25,6 +25,7 @@ endmacro() # - DFLAGS_BASE # - LDC_LINK_MANUALLY # - D_LINKER_ARGS +# - LDC_ENABLE_PLUGINS function(build_d_executable target_name output_exe d_src_files compiler_args linker_args extra_compile_deps link_deps compile_separately) set(dflags "${D_COMPILER_FLAGS} ${DFLAGS_BASE} ${compiler_args}") if(UNIX) @@ -40,7 +41,9 @@ function(build_d_executable target_name output_exe d_src_files compiler_args lin # Compile all D modules to a single object. set(object_file ${PROJECT_BINARY_DIR}/obj/${target_name}${CMAKE_CXX_OUTPUT_EXTENSION}) # Default to -linkonce-templates with LDMD host compiler, to speed-up optimization. - if("${D_COMPILER_ID}" STREQUAL "LDMD") + if("${target_name}" STREQUAL "ldc2" AND LDC_ENABLE_PLUGINS) + # For plugin support we need ldc2's symbols to be global, don't use -linkonce-templates. + elseif("${D_COMPILER_ID}" STREQUAL "LDMD") set(dflags -linkonce-templates ${dflags}) endif() add_custom_command( diff --git a/cmake/Modules/FindLLVM.cmake b/cmake/Modules/FindLLVM.cmake index 693227b5b08..6add0c18448 100644 --- a/cmake/Modules/FindLLVM.cmake +++ b/cmake/Modules/FindLLVM.cmake @@ -32,13 +32,12 @@ # We also want an user-specified LLVM_ROOT_DIR to take precedence over the # system default locations such as /usr/local/bin. Executing find_program() # multiples times is the approach recommended in the docs. -set(llvm_config_names llvm-config-15.0 llvm-config150 llvm-config-15 +set(llvm_config_names llvm-config-16.0 llvm-config160 llvm-config-16 + llvm-config-15.0 llvm-config150 llvm-config-15 llvm-config-14.0 llvm-config140 llvm-config-14 llvm-config-13.0 llvm-config130 llvm-config-13 llvm-config-12.0 llvm-config120 llvm-config-12 llvm-config-11.0 llvm-config110 llvm-config-11 - llvm-config-10.0 llvm-config100 llvm-config-10 - llvm-config-9.0 llvm-config90 llvm-config-9 llvm-config) find_program(LLVM_CONFIG NAMES ${llvm_config_names} @@ -49,13 +48,13 @@ if(APPLE) # extra fallbacks for MacPorts & Homebrew find_program(LLVM_CONFIG NAMES ${llvm_config_names} - PATHS /opt/local/libexec/llvm-15/bin - /opt/local/libexec/llvm-14/bin /opt/local/libexec/llvm-13/bin /opt/local/libexec/llvm-12/bin - /opt/local/libexec/llvm-11/bin /opt/local/libexec/llvm-10/bin /opt/local/libexec/llvm-9.0/bin + PATHS /opt/local/libexec/llvm-16/bin /opt/local/libexec/llvm-15/bin + /opt/local/libexec/llvm-14/bin /opt/local/libexec/llvm-13/bin + /opt/local/libexec/llvm-12/bin /opt/local/libexec/llvm-11/bin /opt/local/libexec/llvm/bin - /usr/local/opt/llvm@15/bin - /usr/local/opt/llvm@14/bin /usr/local/opt/llvm@13/bin /usr/local/opt/llvm@12/bin - /usr/local/opt/llvm@11/bin /usr/local/opt/llvm@10/bin /usr/local/opt/llvm@9/bin + /usr/local/opt/llvm@16/bin /usr/local/opt/llvm@15/bin + /usr/local/opt/llvm@14/bin /usr/local/opt/llvm@13/bin + /usr/local/opt/llvm@12/bin /usr/local/opt/llvm@11/bin /usr/local/opt/llvm/bin NO_DEFAULT_PATH) endif() diff --git a/dmd/aggregate.h b/dmd/aggregate.h index ac086679ddb..6ee2b32c5aa 100644 --- a/dmd/aggregate.h +++ b/dmd/aggregate.h @@ -108,8 +108,8 @@ class AggregateDeclaration : public ScopeDsymbol Expression *getRTInfo; // pointer to GC info generated by object.RTInfo(this) Visibility visibility; - bool noDefaultCtor; // no default construction - bool disableNew; // disallow allocations using `new` + d_bool noDefaultCtor; // no default construction + d_bool disableNew; // disallow allocations using `new` Sizeok sizeok; // set when structsize contains valid data virtual Scope *newScope(Scope *sc); @@ -273,10 +273,10 @@ class ClassDeclaration : public AggregateDeclaration // their own vtbl[] TypeInfoClassDeclaration *vclassinfo; // the ClassInfo object for this ClassDeclaration - bool com; // true if this is a COM class (meaning it derives from IUnknown) - bool stack; // true if this is a scope class + d_bool com; // true if this is a COM class (meaning it derives from IUnknown) + d_bool stack; // true if this is a scope class int cppDtorVtblIndex; // slot reserved for the virtual destructor [extern(C++)] - bool inuse; // to prevent recursive attempts + d_bool inuse; // to prevent recursive attempts ThreeState isabstract; // if abstract class Baseok baseok; // set the progress of base classes resolving diff --git a/dmd/aliasthis.d b/dmd/aliasthis.d index ef839fae536..ce384593c59 100644 --- a/dmd/aliasthis.d +++ b/dmd/aliasthis.d @@ -78,7 +78,7 @@ extern (C++) final class AliasThis : Dsymbol * Params: * sc = context * e = expression forming the `this` - * gag = if true do not print errors, return null instead + * gag = do not print errors, return `null` instead * findOnly = don't do further processing like resolving properties, * i.e. just return plain dotExp() result. * Returns: @@ -93,7 +93,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 = DotExpFlag.noAliasThis | (gag ? DotExpFlag.gag : 0); + const flags = cast(DotExpFlag) (DotExpFlag.noAliasThis | (gag * DotExpFlag.gag)); uint olderrors = gag ? global.startGagging() : 0; e = dotExp(ad.type, sc, e, ad.aliasthis.ident, flags); if (!e || findOnly) @@ -200,15 +200,29 @@ bool checkDeprecatedAliasThis(AliasThis at, const ref Loc loc, Scope* sc) /************************************** * Check and set 'att' if 't' is a recursive 'alias this' type + * + * The goal is to prevent endless loops when there is a cycle in the alias this chain. + * Since there is no multiple `alias this`, the chain either ends in a leaf, + * or it loops back on itself as some point. + * + * Example: S0 -> (S1 -> S2 -> S3 -> S1) + * + * `S0` is not a recursive alias this, so this returns `false`, and a rewrite to `S1` can be tried. + * `S1` is a recursive alias this type, but since `att` is initialized to `null`, + * this still returns `false`, but `att1` is set to `S1`. + * A rewrite to `S2` and `S3` can be tried, but when we want to try a rewrite to `S1` again, + * we notice `att == t`, so we're back at the start of the loop, and this returns `true`. + * * Params: - * att = type reference used to detect recursion - * t = 'alias this' type + * att = type reference used to detect recursion. Should be initialized to `null`. + * t = type of 'alias this' rewrite to attempt * * Returns: - * Whether the 'alias this' is recursive or not + * `false` if the rewrite is safe, `true` if it would loop back around */ bool isRecursiveAliasThis(ref Type att, Type t) { + //printf("+isRecursiveAliasThis(att = %s, t = %s)\n", att ? att.toChars() : "null", t.toChars()); auto tb = t.toBasetype(); if (att && tb.equivalent(att)) return true; diff --git a/dmd/apply.d b/dmd/apply.d index 59ba9f5ecd6..d18b81f044f 100644 --- a/dmd/apply.d +++ b/dmd/apply.d @@ -170,7 +170,7 @@ public: { if (e.stageflags & stageApply) return; - int old = e.stageflags; + const old = e.stageflags; e.stageflags |= stageApply; doCond(e.elements.peekSlice()) || applyTo(e); e.stageflags = old; diff --git a/dmd/astenums.d b/dmd/astenums.d index 6e882082bed..77f36f304a5 100644 --- a/dmd/astenums.d +++ b/dmd/astenums.d @@ -214,8 +214,8 @@ enum TY : ubyte Tmixin, Tnoreturn, Ttag, - TMAX } +enum TMAX = TY.max + 1; alias Tarray = TY.Tarray; alias Tsarray = TY.Tsarray; @@ -265,7 +265,6 @@ alias Ttraits = TY.Ttraits; alias Tmixin = TY.Tmixin; alias Tnoreturn = TY.Tnoreturn; alias Ttag = TY.Ttag; -alias TMAX = TY.TMAX; enum TFlags { @@ -328,6 +327,7 @@ enum VarArg : ubyte variadic = 1, /// (T t, ...) can be C-style (core.stdc.stdarg) or D-style (core.vararg) typesafe = 2, /// (T t ...) typesafe https://dlang.org/spec/function.html#typesafe_variadic_functions /// or https://dlang.org/spec/function.html#typesafe_variadic_functions + KRvariadic = 3, /// K+R C style variadics (no function prototype) } /************************* @@ -339,7 +339,7 @@ enum STMT : ubyte Error, Peel, Exp, DtorExp, - Compile, + Mixin, Compound, CompoundDeclaration, CompoundAsm, UnrolledLoop, Scope, @@ -439,3 +439,22 @@ enum FileType : ubyte ddoc, /// Ddoc documentation file (.dd) c, /// C source file } + +extern (C++) struct structalign_t +{ + private: + ushort value = 0; // unknown + enum STRUCTALIGN_DEFAULT = 1234; // default = match whatever the corresponding C compiler does + bool pack; // use #pragma pack semantics + + public: + pure @safe @nogc nothrow: + bool isDefault() const { return value == STRUCTALIGN_DEFAULT; } + void setDefault() { value = STRUCTALIGN_DEFAULT; } + bool isUnknown() const { return value == 0; } // value is not set + void setUnknown() { value = 0; } + void set(uint value) { this.value = cast(ushort)value; } + uint get() const { return value; } + bool isPack() const { return pack; } + void setPack(bool pack) { this.pack = pack; } +} diff --git a/dmd/attrib.d b/dmd/attrib.d index a73119078ec..d5f1cb0bda3 100644 --- a/dmd/attrib.d +++ b/dmd/attrib.d @@ -62,6 +62,12 @@ extern (C++) abstract class AttribDeclaration : Dsymbol this.decl = decl; } + extern (D) this(const ref Loc loc, Dsymbols* decl) + { + super(loc, null); + this.decl = decl; + } + extern (D) this(const ref Loc loc, Identifier ident, Dsymbols* decl) { super(loc, ident); @@ -228,6 +234,12 @@ extern (C++) class StorageClassDeclaration : AttribDeclaration this.stc = stc; } + extern (D) this(const ref Loc loc, StorageClass stc, Dsymbols* decl) + { + super(loc, decl); + this.stc = stc; + } + override StorageClassDeclaration syntaxCopy(Dsymbol s) { assert(!s); @@ -1300,7 +1312,8 @@ extern(C++) final class ForwardingAttribDeclaration : AttribDeclaration * mixin("int x"); * https://dlang.org/spec/module.html#mixin-declaration */ -extern (C++) final class CompileDeclaration : AttribDeclaration +// Note: was CompileDeclaration +extern (C++) final class MixinDeclaration : AttribDeclaration { Expressions* exps; ScopeDsymbol scopesym; @@ -1309,19 +1322,19 @@ extern (C++) final class CompileDeclaration : AttribDeclaration extern (D) this(const ref Loc loc, Expressions* exps) { super(loc, null, null); - //printf("CompileDeclaration(loc = %d)\n", loc.linnum); + //printf("MixinDeclaration(loc = %d)\n", loc.linnum); this.exps = exps; } - override CompileDeclaration syntaxCopy(Dsymbol s) + override MixinDeclaration syntaxCopy(Dsymbol s) { - //printf("CompileDeclaration::syntaxCopy('%s')\n", toChars()); - return new CompileDeclaration(loc, Expression.arraySyntaxCopy(exps)); + //printf("MixinDeclaration::syntaxCopy('%s')\n", toChars()); + return new MixinDeclaration(loc, Expression.arraySyntaxCopy(exps)); } override void addMember(Scope* sc, ScopeDsymbol sds) { - //printf("CompileDeclaration::addMember(sc = %p, sds = %p, memnum = %d)\n", sc, sds, memnum); + //printf("MixinDeclaration::addMember(sc = %p, sds = %p, memnum = %d)\n", sc, sds, memnum); this.scopesym = sds; } @@ -1335,7 +1348,7 @@ extern (C++) final class CompileDeclaration : AttribDeclaration return "mixin"; } - override inout(CompileDeclaration) isCompileDeclaration() inout + override inout(MixinDeclaration) isMixinDeclaration() inout { return this; } diff --git a/dmd/attrib.h b/dmd/attrib.h index 44ceb12e0d0..1e75598c72a 100644 --- a/dmd/attrib.h +++ b/dmd/attrib.h @@ -132,7 +132,7 @@ class AlignDeclaration final : public AttribDeclaration class AnonDeclaration final : public AttribDeclaration { public: - bool isunion; + d_bool isunion; int sem; // 1 if successful semantic() unsigned anonoffset; // offset of anonymous struct unsigned anonstructsize; // size of anonymous struct @@ -175,8 +175,8 @@ class StaticIfDeclaration final : public ConditionalDeclaration { public: ScopeDsymbol *scopesym; - bool addisdone; - bool onStack; + d_bool addisdone; + d_bool onStack; StaticIfDeclaration *syntaxCopy(Dsymbol *s) override; Dsymbols *include(Scope *sc) override; @@ -193,8 +193,8 @@ class StaticForeachDeclaration final : public AttribDeclaration public: StaticForeach *sfe; ScopeDsymbol *scopesym; - bool onStack; - bool cached; + d_bool onStack; + d_bool cached; Dsymbols *cache; StaticForeachDeclaration *syntaxCopy(Dsymbol *s) override; @@ -221,15 +221,15 @@ class ForwardingAttribDeclaration final : public AttribDeclaration // Mixin declarations -class CompileDeclaration final : public AttribDeclaration +class MixinDeclaration final : public AttribDeclaration { public: Expressions *exps; ScopeDsymbol *scopesym; - bool compiled; + d_bool compiled; - CompileDeclaration *syntaxCopy(Dsymbol *s) override; + MixinDeclaration *syntaxCopy(Dsymbol *s) override; void addMember(Scope *sc, ScopeDsymbol *sds) override; void setScope(Scope *sc) override; const char *kind() const override; diff --git a/dmd/blockexit.d b/dmd/blockexit.d index bd5b78e92dc..db738b4076c 100644 --- a/dmd/blockexit.d +++ b/dmd/blockexit.d @@ -63,34 +63,21 @@ enum BE : int */ int blockExit(Statement s, FuncDeclaration func, bool mustNotThrow) { - extern (C++) final class BlockExit : Visitor - { - alias visit = Visitor.visit; - public: - FuncDeclaration func; - bool mustNotThrow; - int result; - - extern (D) this(FuncDeclaration func, bool mustNotThrow) scope - { - this.func = func; - this.mustNotThrow = mustNotThrow; - result = BE.none; - } + int result = BE.none; - override void visit(Statement s) + void visitDefaultCase(Statement s) { printf("Statement::blockExit(%p)\n", s); printf("%s\n", s.toChars()); assert(0); } - override void visit(ErrorStatement s) + void visitError(ErrorStatement s) { result = BE.none; } - override void visit(ExpStatement s) + void visitExp(ExpStatement s) { result = BE.fallthru; if (s.exp) @@ -115,13 +102,18 @@ int blockExit(Statement s, FuncDeclaration func, bool mustNotThrow) } } - override void visit(CompileStatement s) + void visitDtorExp(DtorExpStatement s) + { + visitExp(s); + } + + void visitMixin(MixinStatement s) { assert(global.errors); result = BE.fallthru; } - override void visit(CompoundStatement cs) + void visitCompound(CompoundStatement cs) { //printf("CompoundStatement.blockExit(%p) %d result = x%X\n", cs, cs.statements.length, result); result = BE.fallthru; @@ -175,7 +167,7 @@ int blockExit(Statement s, FuncDeclaration func, bool mustNotThrow) } } - override void visit(UnrolledLoopStatement uls) + void visitUnrolledLoop(UnrolledLoopStatement uls) { result = BE.fallthru; foreach (s; *uls.statements) @@ -190,19 +182,19 @@ int blockExit(Statement s, FuncDeclaration func, bool mustNotThrow) } } - override void visit(ScopeStatement s) + void visitScope(ScopeStatement s) { //printf("ScopeStatement::blockExit(%p)\n", s.statement); result = blockExit(s.statement, func, mustNotThrow); } - override void visit(WhileStatement s) + void visitWhile(WhileStatement s) { assert(global.errors); result = BE.fallthru; } - override void visit(DoStatement s) + void visitDo(DoStatement s) { if (s._body) { @@ -227,7 +219,7 @@ int blockExit(Statement s, FuncDeclaration func, bool mustNotThrow) result &= ~(BE.break_ | BE.continue_); } - override void visit(ForStatement s) + void visitFor(ForStatement s) { result = BE.fallthru; if (s._init) @@ -259,7 +251,7 @@ int blockExit(Statement s, FuncDeclaration func, bool mustNotThrow) result |= canThrow(s.increment, func, mustNotThrow); } - override void visit(ForeachStatement s) + void visitForeach(ForeachStatement s) { result = BE.fallthru; result |= canThrow(s.aggr, func, mustNotThrow); @@ -268,13 +260,13 @@ int blockExit(Statement s, FuncDeclaration func, bool mustNotThrow) result |= blockExit(s._body, func, mustNotThrow) & ~(BE.break_ | BE.continue_); } - override void visit(ForeachRangeStatement s) + void visitForeachRange(ForeachRangeStatement s) { assert(global.errors); result = BE.fallthru; } - override void visit(IfStatement s) + void visitIf(IfStatement s) { //printf("IfStatement::blockExit(%p)\n", s); result = BE.none; @@ -297,24 +289,24 @@ int blockExit(Statement s, FuncDeclaration func, bool mustNotThrow) //printf("IfStatement::blockExit(%p) = x%x\n", s, result); } - override void visit(ConditionalStatement s) + void visitConditional(ConditionalStatement s) { result = blockExit(s.ifbody, func, mustNotThrow); if (s.elsebody) result |= blockExit(s.elsebody, func, mustNotThrow); } - override void visit(PragmaStatement s) + void visitPragma(PragmaStatement s) { result = BE.fallthru; } - override void visit(StaticAssertStatement s) + void visitStaticAssert(StaticAssertStatement s) { result = BE.fallthru; } - override void visit(SwitchStatement s) + void visitSwitch(SwitchStatement s) { result = BE.none; result |= canThrow(s.condition, func, mustNotThrow); @@ -332,63 +324,63 @@ int blockExit(Statement s, FuncDeclaration func, bool mustNotThrow) result |= BE.fallthru; } - override void visit(CaseStatement s) + void visitCase(CaseStatement s) { result = blockExit(s.statement, func, mustNotThrow); } - override void visit(DefaultStatement s) + void visitDefault(DefaultStatement s) { result = blockExit(s.statement, func, mustNotThrow); } - override void visit(GotoDefaultStatement s) + void visitGotoDefault(GotoDefaultStatement s) { result = BE.goto_; } - override void visit(GotoCaseStatement s) + void visitGotoCase(GotoCaseStatement s) { result = BE.goto_; } - override void visit(SwitchErrorStatement s) + void visitSwitchError(SwitchErrorStatement s) { // Switch errors are non-recoverable result = BE.halt; } - override void visit(ReturnStatement s) + void visitReturn(ReturnStatement s) { result = BE.return_; if (s.exp) result |= canThrow(s.exp, func, mustNotThrow); } - override void visit(BreakStatement s) + void visitBreak(BreakStatement s) { //printf("BreakStatement::blockExit(%p) = x%x\n", s, s.ident ? BE.goto_ : BE.break_); result = s.ident ? BE.goto_ : BE.break_; } - override void visit(ContinueStatement s) + void visitContinue(ContinueStatement s) { result = s.ident ? BE.continue_ | BE.goto_ : BE.continue_; } - override void visit(SynchronizedStatement s) + void visitSynchronized(SynchronizedStatement s) { result = blockExit(s._body, func, mustNotThrow); } - override void visit(WithStatement s) + void visitWith(WithStatement s) { result = BE.none; result |= canThrow(s.exp, func, mustNotThrow); result |= blockExit(s._body, func, mustNotThrow); } - override void visit(TryCatchStatement s) + void visitTryCatch(TryCatchStatement s) { assert(s._body); result = blockExit(s._body, func, false); @@ -428,7 +420,7 @@ int blockExit(Statement s, FuncDeclaration func, bool mustNotThrow) result |= catchresult; } - override void visit(TryFinallyStatement s) + void visitTryFinally(TryFinallyStatement s) { result = BE.fallthru; if (s._body) @@ -470,13 +462,13 @@ int blockExit(Statement s, FuncDeclaration func, bool mustNotThrow) result |= finalresult & ~BE.fallthru; } - override void visit(ScopeGuardStatement s) + void visitScopeGuard(ScopeGuardStatement s) { // At this point, this statement is just an empty placeholder result = BE.fallthru; } - override void visit(ThrowStatement s) + void visitThrow(ThrowStatement s) { if (s.internalThrow) { @@ -486,16 +478,16 @@ int blockExit(Statement s, FuncDeclaration func, bool mustNotThrow) return; } - result = checkThrow(s.loc, s.exp, mustNotThrow); + result = checkThrow(s.loc, s.exp, mustNotThrow, func); } - override void visit(GotoStatement s) + void visitGoto(GotoStatement s) { //printf("GotoStatement::blockExit(%p)\n", s); result = BE.goto_; } - override void visit(LabelStatement s) + void visitLabel(LabelStatement s) { //printf("LabelStatement::blockExit(%p)\n", s); result = blockExit(s.statement, func, mustNotThrow); @@ -503,30 +495,31 @@ int blockExit(Statement s, FuncDeclaration func, bool mustNotThrow) result |= BE.fallthru; } - override void visit(CompoundAsmStatement s) + void visitCompoundAsm(CompoundAsmStatement s) { // Assume the worst result = BE.fallthru | BE.return_ | BE.goto_ | BE.halt; if (!(s.stc & STC.nothrow_)) { - if (mustNotThrow && !(s.stc & STC.nothrow_)) - s.error("`asm` statement is assumed to throw - mark it with `nothrow` if it does not"); + if(func) + func.setThrow(s.loc, "`asm` statement is assumed to throw - mark it with `nothrow` if it does not"); + if (mustNotThrow) + s.error("`asm` statement is assumed to throw - mark it with `nothrow` if it does not"); // TODO else result |= BE.throw_; } } - override void visit(ImportStatement s) + void visitImport(ImportStatement s) { result = BE.fallthru; } - } if (!s) return BE.fallthru; - scope BlockExit be = new BlockExit(func, mustNotThrow); - s.accept(be); - return be.result; + mixin VisitStatement!void visit; + visit.VisitStatement(s); + return result; } /++ @@ -537,10 +530,11 @@ int blockExit(Statement s, FuncDeclaration func, bool mustNotThrow) + loc = location of the `throw` + exp = expression yielding the throwable + mustNotThrow = inside of a `nothrow` scope? + + func = function containing the `throw` + + Returns: `BE.[err]throw` depending on the type of `exp` +/ -BE checkThrow(ref const Loc loc, Expression exp, const bool mustNotThrow) +BE checkThrow(ref const Loc loc, Expression exp, const bool mustNotThrow, FuncDeclaration func) { import dmd.errors : error; @@ -554,6 +548,8 @@ BE checkThrow(ref const Loc loc, Expression exp, const bool mustNotThrow) } if (mustNotThrow) loc.error("`%s` is thrown but not caught", exp.type.toChars()); + else if (func) + func.setThrow(loc, "`%s` is thrown but not caught", exp.type); return BE.throw_; } diff --git a/dmd/canthrow.d b/dmd/canthrow.d index 0c237e6da56..e0e473d6ff0 100644 --- a/dmd/canthrow.d +++ b/dmd/canthrow.d @@ -53,7 +53,7 @@ enum CT : BE */ extern (C++) /* CT */ BE canThrow(Expression e, FuncDeclaration func, bool mustNotThrow) { - //printf("Expression::canThrow(%d) %s\n", mustNotThrow, toChars()); + //printf("Expression::canThrow(%d) %s\n", mustNotThrow, e.toChars()); // stop walking if we determine this expression can throw extern (C++) final class CanThrow : StoppableVisitor { @@ -76,11 +76,16 @@ extern (C++) /* CT */ BE canThrow(Expression e, FuncDeclaration func, bool mustN { if (mustNotThrow) { - e.error("%s `%s` is not `nothrow`", - f.kind(), f.toPrettyChars()); + e.error("%s `%s` is not `nothrow`", f.kind(), f.toPrettyChars()); + if (!f.isDtorDeclaration()) + errorSupplementalInferredAttr(f, 10, false, STC.nothrow_); e.checkOverridenDtor(null, f, dd => dd.type.toTypeFunction().isnothrow, "not nothrow"); } + else if (func) + { + func.setThrowCall(e.loc, f); + } result |= CT.exception; } } @@ -113,7 +118,7 @@ extern (C++) /* CT */ BE canThrow(Expression e, FuncDeclaration func, bool mustN { auto sd = ts.sym; const id = ce.f.ident; - if (sd.postblit && isArrayConstructionOrAssign(id)) + if (sd.postblit && isArrayConstruction(id)) { checkFuncThrows(ce, sd.postblit); return; @@ -205,7 +210,7 @@ extern (C++) /* CT */ BE canThrow(Expression e, FuncDeclaration func, bool mustN override void visit(ThrowExp te) { - const res = checkThrow(te.loc, te.e1, mustNotThrow); + const res = checkThrow(te.loc, te.e1, mustNotThrow, func); assert((res & ~(CT.exception | CT.error)) == 0); result |= res; } diff --git a/dmd/cli.d b/dmd/cli.d index 68f92c16c85..90e123a96b6 100644 --- a/dmd/cli.d +++ b/dmd/cli.d @@ -272,6 +272,12 @@ dmd -cov -unittest myprog.d --- `, ), + Option("cpp=", + "use filename as the name of the C preprocessor to use for ImportC files", + `Normally the C preprocessor used by the associated C compiler is used to + preprocess ImportC files, + this is overridden by the $(TT -cpp) switch.` + ), Option("D", "generate documentation", `$(P Generate $(LINK2 $(ROOT_DIR)spec/ddoc.html, documentation) from source.) diff --git a/dmd/clone.d b/dmd/clone.d index 19bf83e4ec3..60e373c502b 100644 --- a/dmd/clone.d +++ b/dmd/clone.d @@ -840,7 +840,7 @@ FuncDeclaration buildXtoHash(StructDeclaration sd, Scope* sc) " else " ~ " h = h * 33 + typeid(T).getHash(cast(const void*)&p.tupleof[i]);" ~ "return h;"; - fop.fbody = new CompileStatement(loc, new StringExp(loc, code)); + fop.fbody = new MixinStatement(loc, new StringExp(loc, code)); Scope* sc2 = sc.push(); sc2.stc = 0; sc2.linkage = LINK.d; @@ -1261,8 +1261,9 @@ FuncDeclaration buildPostBlit(StructDeclaration sd, Scope* sc) // if this field's postblit is not `nothrow`, add a `scope(failure)` // block to destroy any prior successfully postblitted fields should - // this field's postblit fail - if (fieldsToDestroy.length > 0 && !(cast(TypeFunction)sdv.postblit.type).isnothrow) + // 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.betterC) { // create a list of destructors that need to be called Expression[] dtorCalls; diff --git a/dmd/common/outbuffer.h b/dmd/common/outbuffer.h index b672842e74d..4c1dceea3f6 100644 --- a/dmd/common/outbuffer.h +++ b/dmd/common/outbuffer.h @@ -21,11 +21,11 @@ struct OutBuffer private: DArray data; d_size_t offset; - bool notlinehead; + d_bool notlinehead; void *fileMapping; // pointer to a file mapping object not used on the C++ side public: - bool doindent; - bool spaces; + d_bool doindent; + d_bool spaces; int level; OutBuffer() diff --git a/dmd/common/string.d b/dmd/common/string.d index 1111cec2cf1..cfae1bb6749 100644 --- a/dmd/common/string.d +++ b/dmd/common/string.d @@ -13,7 +13,7 @@ module dmd.common.string; nothrow: /** -Defines a temporary array using a fixed-length buffer as back store. If the length +Defines a temporary array of `Element`s using a fixed-length buffer as back store. If the length of the buffer suffices, it is readily used. Otherwise, `malloc` is used to allocate memory for the array and `free` is used for deallocation in the destructor. @@ -21,19 +21,26 @@ destructor. This type is meant to use exclusively as an automatic variable. It is not default constructible or copyable. */ -struct SmallBuffer(T) +struct SmallBuffer(Element) { import core.stdc.stdlib : malloc, free; - private T[] _extent; + private Element[] _extent; private bool needsFree; nothrow: + @nogc: @disable this(); // no default ctor - @disable this(ref const SmallBuffer!T); // noncopyable, nonassignable - - this(size_t len, T[] buffer) + @disable this(ref const SmallBuffer!Element); // noncopyable, nonassignable + + /*********** + * Construct a SmallBuffer + * Params: + * len = number of elements in array + * buffer = slice to use as backing-store, if len will fit in it + */ + scope this(size_t len, return scope Element[] buffer) { if (len <= buffer.length) { @@ -41,7 +48,8 @@ struct SmallBuffer(T) } else { - _extent = (cast(typeof(_extent.ptr)) malloc(len * _extent[0].sizeof))[0 .. len]; + assert(len < sizeof.max / Element.sizeof); + _extent = (cast(typeof(_extent.ptr)) malloc(len * Element.sizeof))[0 .. len]; _extent.ptr || assert(0, "Out of memory."); needsFree = true; } @@ -54,16 +62,22 @@ struct SmallBuffer(T) free(_extent.ptr); } - void create(size_t len) + /****** + * Resize existing SmallBuffer. + * Params: + * len = number of elements after resize + */ + scope void create(size_t len) { if (len <= _extent.length) { - _extent = _extent[0 .. len]; + _extent = _extent[0 .. len]; // reuse existing storage } else { __dtor(); - _extent = (cast(typeof(_extent.ptr)) malloc(len * _extent[0].sizeof))[0 .. len]; + assert(len < sizeof.max / Element.sizeof); + _extent = (cast(typeof(_extent.ptr)) malloc(len * Element.sizeof))[0 .. len]; _extent.ptr || assert(0, "Out of memory."); needsFree = true; } @@ -122,8 +136,16 @@ but is guaranteed to follow it. version(Windows) wchar[] toWStringz(const(char)[] narrow, ref SmallBuffer!wchar buffer) nothrow { import core.sys.windows.winnls : CP_ACP, MultiByteToWideChar; + +version (IN_LLVM) +{ + import dmd.root.filename : CodePage; +} +else +{ // assume filenames encoded in system default Windows ANSI code page enum CodePage = CP_ACP; +} if (narrow is null) return null; diff --git a/dmd/cond.h b/dmd/cond.h index 422a715bdba..45094d14991 100644 --- a/dmd/cond.h +++ b/dmd/cond.h @@ -52,7 +52,7 @@ class StaticForeach final : public RootObject ForeachStatement *aggrfe; ForeachRangeStatement *rangefe; - bool needExpansion; + d_bool needExpansion; StaticForeach *syntaxCopy(); }; diff --git a/dmd/constfold.d b/dmd/constfold.d index 4ece94496f6..c542ee444fb 100644 --- a/dmd/constfold.d +++ b/dmd/constfold.d @@ -1450,7 +1450,7 @@ UnionExp Cat(const ref Loc loc, Type type, Expression e1, Expression e2) emplaceExp!(StringExp)(&ue, loc, s[0 .. len * sz], len, sz); StringExp es = ue.exp().isStringExp(); es.type = type; - es.committed = 1; + es.committed = true; } else { diff --git a/dmd/cparse.d b/dmd/cparse.d index a39fa1d3491..d25a23868c4 100644 --- a/dmd/cparse.d +++ b/dmd/cparse.d @@ -14,22 +14,18 @@ module dmd.cparse; import core.stdc.stdio; -import core.stdc.string; +import core.stdc.string : memcpy; + import dmd.astenums; import dmd.errorsink; -import dmd.globals; import dmd.id; import dmd.identifier; import dmd.lexer; import dmd.location; import dmd.parse; -import dmd.errors; import dmd.root.array; -import dmd.root.filename; import dmd.common.outbuffer; import dmd.root.rmem; -import dmd.root.rootobject; -import dmd.root.string; import dmd.tokens; version (LDC) private enum LDC_pre_2084 = __VERSION__ < 2084; // workaround bug with LDC < v1.14 host compilers @@ -74,9 +70,10 @@ final class CParser(AST) : Parser!AST extern (D) this(TARGET)(AST.Module _module, const(char)[] input, bool doDocComment, ErrorSink errorSink, - const ref TARGET target, OutBuffer* defines) scope + const ref TARGET target, OutBuffer* defines, const CompileEnv* compileEnv) scope { - super(_module, input, doDocComment, errorSink); + const bool doUnittests = false; + super(_module, input, doDocComment, errorSink, compileEnv, doUnittests); //printf("CParser.this()\n"); mod = _module; @@ -205,7 +202,7 @@ final class CParser(AST) : Parser!AST else if (token.value == TOK.leftCurly) s = cparseStatement(ParseStatementFlags.curly | ParseStatementFlags.scope_); else - s = cparseStatement(ParseStatementFlags.semiOk); + s = cparseStatement(0); s = new AST.LabelStatement(loc, ident, s); break; } @@ -310,6 +307,7 @@ final class CParser(AST) : Parser!AST case TOK.extern_: case TOK.static_: case TOK._Thread_local: + case TOK.__thread: case TOK.auto_: case TOK.register: @@ -379,7 +377,7 @@ final class CParser(AST) : Parser!AST auto statements = new AST.Statements(); while (token.value != TOK.rightCurly && token.value != TOK.endOfFile) { - statements.push(cparseStatement(ParseStatementFlags.semi | ParseStatementFlags.curlyScope)); + statements.push(cparseStatement(ParseStatementFlags.curlyScope)); } if (endPtr) *endPtr = token.ptr; @@ -513,6 +511,14 @@ final class CParser(AST) : Parser!AST nextToken(); auto exp = cparseAssignExp(); + AST.Expression expHigh; + if (token.value == TOK.dotDotDot) + { + /* Case Ranges https://gcc.gnu.org/onlinedocs/gcc/Case-Ranges.html + */ + nextToken(); + expHigh = cparseAssignExp(); + } check(TOK.colon); if (flags & ParseStatementFlags.curlyScope) @@ -520,7 +526,7 @@ final class CParser(AST) : Parser!AST auto statements = new AST.Statements(); while (token.value != TOK.case_ && token.value != TOK.default_ && token.value != TOK.endOfFile && token.value != TOK.rightCurly) { - auto cur = cparseStatement(ParseStatementFlags.semi | ParseStatementFlags.curlyScope); + auto cur = cparseStatement(ParseStatementFlags.curlyScope); statements.push(cur); // https://issues.dlang.org/show_bug.cgi?id=21739 @@ -535,10 +541,13 @@ final class CParser(AST) : Parser!AST } else { - s = cparseStatement(ParseStatementFlags.semi); + s = cparseStatement(0); } s = new AST.ScopeStatement(loc, s, token.loc); - s = new AST.CaseStatement(loc, exp, s); + if (expHigh) + s = new AST.CaseRangeStatement(loc, exp, expHigh, s); + else + s = new AST.CaseStatement(loc, exp, s); break; } @@ -552,12 +561,12 @@ final class CParser(AST) : Parser!AST auto statements = new AST.Statements(); while (token.value != TOK.case_ && token.value != TOK.default_ && token.value != TOK.endOfFile && token.value != TOK.rightCurly) { - statements.push(cparseStatement(ParseStatementFlags.semi | ParseStatementFlags.curlyScope)); + statements.push(cparseStatement(ParseStatementFlags.curlyScope)); } s = new AST.CompoundStatement(loc, statements); } else - s = cparseStatement(ParseStatementFlags.semi); + s = cparseStatement(0); s = new AST.ScopeStatement(loc, s, token.loc); s = new AST.DefaultStatement(loc, s); break; @@ -607,7 +616,20 @@ final class CParser(AST) : Parser!AST } case TOK.asm_: - s = parseAsm(); + switch (peekNext()) + { + case TOK.goto_: + case TOK.inline: + case TOK.volatile: + case TOK.leftParenthesis: + s = cparseGnuAsm(); + break; + + default: + // ImportC extensions: parse as a D asm block. + s = parseAsm(); + break; + } break; default: @@ -780,7 +802,10 @@ final class CParser(AST) : Parser!AST case TOK.leftParenthesis: nextToken(); - e = cparseExpression(); + if (token.value == TOK.leftCurly) + e = cparseStatementExpression(); // gcc extension + else + e = cparseExpression(); check(TOK.rightParenthesis); break; @@ -1603,6 +1628,41 @@ final class CParser(AST) : Parser!AST return e; } + /***************************** + * gcc extension: https://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html + * Represent as a function literal, then call the function literal. + * Parser is on opening curly brace. + */ + private AST.Expression cparseStatementExpression() + { + AST.ParameterList parameterList; + StorageClass stc = 0; + const loc = token.loc; + typedefTab.push(null); + auto fbody = cparseStatement(ParseStatementFlags.scope_); + typedefTab.pop(); // end of function scope + + // Rewrite last ExpStatement (if there is one) as a ReturnStatement + auto ss = fbody.isScopeStatement(); + auto cs = ss.statement.isCompoundStatement(); + assert(cs); + if (const len = (*cs.statements).length) + { + auto s = (*cs.statements)[len - 1]; + if (auto es = s.isExpStatement()) + (*cs.statements)[len - 1] = new AST.ReturnStatement(es.loc, es.exp); + } + + auto tf = new AST.TypeFunction(parameterList, null, LINK.d, stc); + auto fd = new AST.FuncLiteralDeclaration(loc, token.loc, tf, TOK.delegate_, null, null, 0); + fd.fbody = fbody; + + auto fe = new AST.FuncExp(loc, fd); + auto args = new AST.Expressions(); + auto e = new AST.CallExp(loc, fe, args); // call the function literal + return e; + } + //} /********************************************************************************/ /********************************* Declaration Parser ***************************/ @@ -1663,6 +1723,12 @@ final class CParser(AST) : Parser!AST auto stag = (tt.tok == TOK.struct_) ? new AST.StructDeclaration(tt.loc, tt.id, false) : (tt.tok == TOK.union_) ? new AST.UnionDeclaration(tt.loc, tt.id) : new AST.EnumDeclaration(tt.loc, tt.id, tt.base); + if (!tt.packalign.isUnknown()) + { + // saw `struct __declspec(align(N)) Tag ...` + auto st = stag.isStructDeclaration(); + st.alignment = tt.packalign; + } stag.members = tt.members; tt.members = null; if (!symbols) @@ -1762,7 +1828,7 @@ final class CParser(AST) : Parser!AST case TOK.asm_: case TOK.__attribute__: if (token.value == TOK.asm_) - asmName = cparseSimpleAsmExpr(); + asmName = cparseGnuAsmLabel(); if (token.value == TOK.__attribute__) { cparseGnuAttributes(specifier); @@ -1846,14 +1912,12 @@ final class CParser(AST) : Parser!AST if (tt.id || tt.tok == TOK.enum_) { if (!tt.id && id) + /* This applies for enums declared as + * typedef enum {A} E; + */ tt.id = id; Specifier spec; - auto stag = declareTag(tt, spec); - if (tt.tok == TOK.enum_) - { - isalias = false; - s = new AST.AliasDeclaration(token.loc, id, stag); - } + declareTag(tt, spec); } } if (isalias) @@ -1895,6 +1959,7 @@ final class CParser(AST) : Parser!AST if (specifier.scw & SCW.x_Thread_local) error("functions cannot be `_Thread_local`"); // C11 6.7.1-4 auto fd = new AST.FuncDeclaration(token.loc, Loc.initial, id, specifiersToSTC(level, specifier), dt, specifier.noreturn); + specifiersToFuncDeclaration(fd, specifier); s = fd; } else @@ -1904,7 +1969,9 @@ final class CParser(AST) : Parser!AST if (!hasInitializer && !(specifier.scw & (SCW.xextern | SCW.xstatic | SCW.x_Thread_local) || level == LVL.global)) initializer = new AST.VoidInitializer(token.loc); - s = new AST.VarDeclaration(token.loc, dt, id, initializer, specifiersToSTC(level, specifier)); + auto vd = new AST.VarDeclaration(token.loc, dt, id, initializer, specifiersToSTC(level, specifier)); + specifiersToVarDeclaration(vd, specifier); + s = vd; } if (level != LVL.global) insertIdToTypedefTab(id); // non-typedef declarations can hide typedefs in outer scopes @@ -2015,8 +2082,7 @@ final class CParser(AST) : Parser!AST auto pl = ft.parameterList; if (pl.varargs != AST.VarArg.none && pl.length) error("function identifier-list cannot end with `...`"); - ft.parameterList.varargs = AST.VarArg.variadic; // but C11 allows extra arguments - importBuiltins = true; // will need __va_list_tag + ft.parameterList.varargs = AST.VarArg.KRvariadic; // but C11 allows extra arguments auto plLength = pl.length; if (symbols.length != plLength) error(token.loc, "%d identifiers does not match %d declarations", cast(int)plLength, cast(int)symbols.length); @@ -2041,6 +2107,10 @@ final class CParser(AST) : Parser!AST error("storage class and type are not allowed in identifier-list"); foreach (s; (*symbols)[]) // yes, quadratic { + auto ad = s.isAttribDeclaration(); + if (ad) + s = (*ad.decl)[0]; // AlignDeclaration wrapping the declaration + auto d = s.isDeclaration(); if (d && p.ident == d.ident && d.type) { @@ -2065,6 +2135,7 @@ final class CParser(AST) : Parser!AST typedefTab.pop(); // end of function scope auto fd = new AST.FuncDeclaration(locFunc, prevloc, id, specifiersToSTC(LVL.global, specifier), ft, specifier.noreturn); + specifiersToFuncDeclaration(fd, specifier); if (addFuncName) { @@ -2237,6 +2308,7 @@ final class CParser(AST) : Parser!AST case TOK.typedef_: scwx = SCW.xtypedef; break; case TOK.inline: scwx = SCW.xinline; break; case TOK._Noreturn: scwx = SCW.x_Noreturn; break; + case TOK.__thread: case TOK._Thread_local: scwx = SCW.x_Thread_local; break; // Type qualifiers @@ -2272,15 +2344,23 @@ final class CParser(AST) : Parser!AST const sloc = token.loc; nextToken(); + Specifier tagSpecifier; + /* GNU Extensions * struct-or-union-specifier: * struct-or-union gnu-attributes (opt) identifier (opt) { struct-declaration-list } gnu-attributes (opt) * struct-or-union gnu-attribute (opt) identifier */ - if (token.value == TOK.__attribute__) - cparseGnuAttributes(specifier); - - t = cparseStruct(sloc, structOrUnion, symbols); + while (1) + { + if (token.value == TOK.__attribute__) + cparseGnuAttributes(tagSpecifier); + else if (token.value == TOK.__declspec) + cparseDeclspec(tagSpecifier); + else + break; + } + t = cparseStruct(sloc, structOrUnion, tagSpecifier.packalign, symbols); tkwx = TKW.xtag; break; } @@ -2300,9 +2380,9 @@ final class CParser(AST) : Parser!AST tk = peek(tk); if (isTypeName(tk) && tk.value == TOK.rightParenthesis) { + nextToken(); nextToken(); t = cparseTypeName(); - // TODO - implement the "atomic" part of t tkwx = TKW.x_Atomic; break; } @@ -2471,6 +2551,12 @@ final class CParser(AST) : Parser!AST error("`inline` and `_Noreturn` function specifiers not allowed for `_Thread_local`"); scw &= ~scwx; } + if (level == LVL.local && + scw & (SCW.x_Thread_local) && !(scw & (SCW.xstatic | SCW.xextern))) + { + error("`_Thread_local` in block scope must be accompanied with `static` or `extern`"); // C11 6.7.1-3 + scw &= ~scwx; + } if (level & (LVL.parameter | LVL.prototype) && scw & ~SCW.xregister) { @@ -2571,6 +2657,7 @@ final class CParser(AST) : Parser!AST } case TKW.xtag: + case TKW.x_Atomic: // no atomics for you break; // t is already set default: @@ -2809,7 +2896,10 @@ final class CParser(AST) : Parser!AST auto parameterList = cparseParameterList(); const lkg = specifier.mod & MOD.x__stdcall ? LINK.windows : linkage; - AST.Type tf = new AST.TypeFunction(parameterList, t, lkg, 0); + StorageClass stc = specifier._nothrow ? STC.nothrow_ : 0; + if (specifier._pure) + stc |= STC.pure_; + AST.Type tf = new AST.TypeFunction(parameterList, t, lkg, stc); // tf = tf.addSTC(storageClass); // TODO insertTx(ts, tf, t); // ts -> ... -> tf -> t @@ -2962,8 +3052,7 @@ final class CParser(AST) : Parser!AST if (token.value == TOK.rightParenthesis) // func() { nextToken(); - importBuiltins = true; // will need __va_list_tag - return AST.ParameterList(parameters, AST.VarArg.variadic, varargsStc); + return AST.ParameterList(parameters, AST.VarArg.KRvariadic, varargsStc); } /* Create function prototype scope @@ -3087,9 +3176,15 @@ final class CParser(AST) : Parser!AST * extended-decl-modifier extended-decl-modifier-seq * * extended-decl-modifier: + * align(number) + * deprecated(depMsg) * dllimport * dllexport + * naked + * noinline * noreturn + * nothrow + * thread * Params: * specifier = filled in with the attribute(s) */ @@ -3099,8 +3194,6 @@ final class CParser(AST) : Parser!AST /* Check for dllexport, dllimport * Ignore the rest */ - bool dllimport; // TODO implement - bool dllexport; // TODO implement nextToken(); // move past __declspec check(TOK.leftParenthesis); while (1) @@ -3116,12 +3209,22 @@ final class CParser(AST) : Parser!AST { if (token.ident == Id.dllimport) { - dllimport = true; + specifier.dllimport = true; nextToken(); } else if (token.ident == Id.dllexport) { - dllexport = true; + specifier.dllexport = true; + nextToken(); + } + else if (token.ident == Id.naked) + { + specifier.naked = true; + nextToken(); + } + else if (token.ident == Id.noinline) + { + specifier.scw |= SCW.xnoinline; nextToken(); } else if (token.ident == Id.noreturn) @@ -3129,6 +3232,49 @@ final class CParser(AST) : Parser!AST specifier.noreturn = true; nextToken(); } + else if (token.ident == Id._nothrow) + { + specifier._nothrow = true; + nextToken(); + } + else if (token.ident == Id.thread) + { + specifier.scw |= SCW.x_Thread_local; + nextToken(); + } + else if (token.ident == Id._align) + { + // Microsoft spec is very imprecise as to how this actually works + nextToken(); + check(TOK.leftParenthesis); + if (token.value == TOK.int32Literal) + { + const n = token.unsvalue; + if (n < 1 || n & (n - 1) || 8192 < n) + error("__decspec(align(%lld)) must be an integer positive power of 2 and be <= 8,192", cast(ulong)n); + specifier.packalign.set(cast(uint)n); + specifier.packalign.setPack(true); + nextToken(); + } + else + { + error("alignment value expected, not `%s`", token.toChars()); + nextToken(); + } + + check(TOK.rightParenthesis); + } + else if (token.ident == Id._deprecated) + { + specifier._deprecated = true; + nextToken(); + if (token.value == TOK.leftParenthesis) // optional deprecation message + { + nextToken(); + specifier.depMsg = cparseExpression(); + check(TOK.rightParenthesis); + } + } else { nextToken(); @@ -3136,6 +3282,8 @@ final class CParser(AST) : Parser!AST cparseParens(); } } + else if (token.value == TOK.restrict) // ImportC assigns no semantics to `restrict`, so just ignore the keyword. + nextToken(); else { error("extended-decl-modifier expected"); @@ -3145,7 +3293,8 @@ final class CParser(AST) : Parser!AST } /************************* - * Simple asm parser + * Parser for asm label. It appears after the declarator, and has apparently + * nothing to do with inline assembler. * https://gcc.gnu.org/onlinedocs/gcc/Asm-Labels.html * simple-asm-expr: * asm ( asm-string-literal ) @@ -3153,17 +3302,107 @@ final class CParser(AST) : Parser!AST * asm-string-literal: * string-literal */ - private AST.StringExp cparseSimpleAsmExpr() + private AST.StringExp cparseGnuAsmLabel() { nextToken(); // move past asm check(TOK.leftParenthesis); if (token.value != TOK.string_) - error("string literal expected"); + error("string literal expected for Asm Label, not `%s`", token.toChars()); auto label = cparsePrimaryExp(); check(TOK.rightParenthesis); return cast(AST.StringExp) label; } + /******************** + * Parse C inline assembler statement in Gnu format. + * https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html + * asm asm-qualifiers ( AssemblerTemplate : OutputOperands : InputOperands : Clobbers : GotoLabels ) + * Current token is on the `asm`. + * Returns: + * inline assembler expression as a Statement + */ + private AST.Statement cparseGnuAsm() + { + // Defer parsing of AsmStatements until semantic processing. + const loc = token.loc; + + nextToken(); + + // Consume all asm-qualifiers. As a future optimization, we could record + // the `inline` and `volatile` storage classes against the statement. + while (token.value == TOK.goto_ || + token.value == TOK.inline || + token.value == TOK.volatile) + nextToken(); + + check(TOK.leftParenthesis); + if (token.value != TOK.string_) + error("string literal expected for Assembler Template, not `%s`", token.toChars()); + Token* toklist = null; + Token** ptoklist = &toklist; + //Identifier label = null; + auto statements = new AST.Statements(); + + int parens; + while (1) + { + switch (token.value) + { + case TOK.leftParenthesis: + ++parens; + goto default; + + case TOK.rightParenthesis: + --parens; + if (parens >= 0) + goto default; + break; + + case TOK.semicolon: + error("matching `)` expected, not `;`"); + break; + + case TOK.endOfFile: + /* ( */ + error("matching `)` expected, not end of file"); + break; + + case TOK.colonColon: // treat as two separate : tokens for iasmgcc + *ptoklist = allocateToken(); + memcpy(*ptoklist, &token, Token.sizeof); + (*ptoklist).value = TOK.colon; + ptoklist = &(*ptoklist).next; + + *ptoklist = allocateToken(); + memcpy(*ptoklist, &token, Token.sizeof); + (*ptoklist).value = TOK.colon; + ptoklist = &(*ptoklist).next; + + *ptoklist = null; + nextToken(); + continue; + + default: + *ptoklist = allocateToken(); + memcpy(*ptoklist, &token, Token.sizeof); + ptoklist = &(*ptoklist).next; + *ptoklist = null; + nextToken(); + continue; + } + if (toklist) + { + // Create AsmStatement from list of tokens we've saved + AST.Statement s = new AST.AsmStatement(token.loc, toklist); + statements.push(s); + } + break; + } + nextToken(); + auto s = new AST.CompoundAsmStatement(loc, statements, 0); + return s; + } + /************************* * __attribute__ parser * https://gcc.gnu.org/onlinedocs/gcc/Attribute-Syntax.html @@ -3225,25 +3464,75 @@ final class CParser(AST) : Parser!AST */ private void cparseGnuAttribute(ref Specifier specifier) { - /* Check for dllimport, dllexport, vector_size(bytes) + /* Check for dllimport, dllexport, naked, noreturn, vector_size(bytes) * Ignore the rest */ - bool dllimport; // TODO implement - bool dllexport; // TODO implement - if (!isGnuAttributeName()) return; if (token.value == TOK.identifier) { - if (token.ident == Id.dllimport) + if (token.ident == Id.aligned) { - dllimport = true; + nextToken(); + if (token.value == TOK.leftParenthesis) + { + nextToken(); + if (token.value == TOK.int32Literal) + { + const n = token.unsvalue; + if (n < 1 || n & (n - 1) || ushort.max < n) + error("__attribute__((aligned(%lld))) must be an integer positive power of 2 and be <= 32,768", cast(ulong)n); + specifier.packalign.set(cast(uint)n); + specifier.packalign.setPack(true); + nextToken(); + } + else + { + error("alignment value expected, not `%s`", token.toChars()); + nextToken(); + } + + check(TOK.rightParenthesis); + } + /* ignore __attribute__((aligned)), which sets the alignment to the largest value for any data + * type on the target machine. It's the opposite of __attribute__((packed)) + */ + } + else if (token.ident == Id.always_inline) // https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html + { + specifier.scw |= SCW.xinline; + nextToken(); + } + else if (token.ident == Id._deprecated) + { + specifier._deprecated = true; + nextToken(); + if (token.value == TOK.leftParenthesis) // optional deprecation message + { + nextToken(); + specifier.depMsg = cparseExpression(); + check(TOK.rightParenthesis); + } + } + else if (token.ident == Id.dllimport) + { + specifier.dllimport = true; nextToken(); } else if (token.ident == Id.dllexport) { - dllexport = true; + specifier.dllexport = true; + nextToken(); + } + else if (token.ident == Id.naked) + { + specifier.naked = true; + nextToken(); + } + else if (token.ident == Id.noinline) + { + specifier.scw |= SCW.xnoinline; nextToken(); } else if (token.ident == Id.noreturn) @@ -3251,6 +3540,16 @@ final class CParser(AST) : Parser!AST specifier.noreturn = true; nextToken(); } + else if (token.ident == Id._nothrow) + { + specifier._nothrow = true; + nextToken(); + } + else if (token.ident == Id._pure) + { + specifier._pure = true; + nextToken(); + } else if (token.ident == Id.vector_size) { nextToken(); @@ -3411,7 +3710,8 @@ final class CParser(AST) : Parser!AST * https://en.cppreference.com/w/cpp/language/enum * enum Identifier : Type */ - AST.Type base = AST.Type.tint32; // C11 6.7.2.2-4 implementation defined default base type + //AST.Type base = AST.Type.tint32; // C11 6.7.2.2-4 implementation defined default base type + AST.Type base = null; // C23 says base type is determined by enum member values if (token.value == TOK.colon) { nextToken(); @@ -3491,7 +3791,7 @@ final class CParser(AST) : Parser!AST * redeclaration, or reference to existing declaration. * Defer to the semantic() pass with a TypeTag. */ - return new AST.TypeTag(loc, TOK.enum_, tag, base, members); + return new AST.TypeTag(loc, TOK.enum_, tag, structalign_t.init, base, members); } /************************************* @@ -3513,11 +3813,12 @@ final class CParser(AST) : Parser!AST * Params: * loc = location of `struct` or `union` * structOrUnion = TOK.struct_ or TOK.union_ + * packalign = alignment to use for struct members * symbols = symbols to add struct-or-union declaration to * Returns: * type of the struct */ - private AST.Type cparseStruct(Loc loc, TOK structOrUnion, ref AST.Dsymbols* symbols) + private AST.Type cparseStruct(Loc loc, TOK structOrUnion, structalign_t packalign, ref AST.Dsymbols* symbols) { Identifier tag; @@ -3556,7 +3857,7 @@ final class CParser(AST) : Parser!AST * redeclaration, or reference to existing declaration. * Defer to the semantic() pass with a TypeTag. */ - return new AST.TypeTag(loc, structOrUnion, tag, null, members); + return new AST.TypeTag(loc, structOrUnion, tag, packalign, null, members); } /************************************* @@ -3999,6 +4300,13 @@ final class CParser(AST) : Parser!AST case TOK.union_: case TOK.enum_: t = peek(t); + if (t.value == TOK.__attribute__ || + t.value == TOK.__declspec) + { + t = peek(t); + if (!skipParens(t, &t)) + return false; + } if (t.value == TOK.identifier) { t = peek(t); @@ -4022,6 +4330,7 @@ final class CParser(AST) : Parser!AST case TOK.typedef_: case TOK.extern_: case TOK.static_: + case TOK.__thread: case TOK._Thread_local: case TOK.auto_: case TOK.register: @@ -4623,6 +4932,8 @@ final class CParser(AST) : Parser!AST // C11 6.7.4 Function specifiers xinline = 0x40, x_Noreturn = 0x80, + + xnoinline = 0x100, } /// C11 6.7.3 Type qualifiers @@ -4642,6 +4953,14 @@ final class CParser(AST) : Parser!AST struct Specifier { bool noreturn; /// noreturn attribute + bool naked; /// naked attribute + bool _nothrow; /// nothrow attribute + bool _pure; /// pure attribute + bool dllimport; /// dllimport attribute + bool dllexport; /// dllexport attribute + bool _deprecated; /// deprecated attribute + AST.Expression depMsg; /// deprecated message + SCW scw; /// storage-class specifiers MOD mod; /// type qualifiers AST.Expressions* alignExps; /// alignment @@ -4665,6 +4984,8 @@ final class CParser(AST) : Parser!AST { if (specifier.scw & SCW.xextern) stc = AST.STC.extern_; + else if (specifier.scw & SCW.xstatic) + stc = AST.STC.static_; } else if (level == LVL.local) { @@ -4716,9 +5037,41 @@ final class CParser(AST) : Parser!AST stc = AST.STC.gshared; } } + if (specifier._deprecated && !specifier.depMsg) + stc |= AST.STC.deprecated_; return stc; } + /*********************** + * Add attributes from Specifier to function + * Params: + * fd = function to apply them to + * specifier = specifiers + */ + void specifiersToFuncDeclaration(AST.FuncDeclaration fd, const ref Specifier specifier) + { + fd.isNaked = specifier.naked; + fd.dllImport = specifier.dllimport; + fd.dllExport = specifier.dllexport; + + if (specifier.scw & SCW.xnoinline) + fd.inlining = PINLINE.never; + else if (specifier.scw & SCW.xinline) + fd.inlining = PINLINE.always; + } + + /*********************** + * Add attributes from Specifier to variable + * Params: + * vd = function to apply them to + * specifier = specifiers + */ + void specifiersToVarDeclaration(AST.VarDeclaration vd, const ref Specifier specifier) + { + vd.dllImport = specifier.dllimport; + vd.dllExport = specifier.dllexport; + } + /*********************** * Return suitable signed integer type for the given size * Params: @@ -4829,7 +5182,7 @@ final class CParser(AST) : Parser!AST auto lenfn = new AST.IntegerExp(loc, fn.length + 1, AST.Type.tuns32); // +1 for terminating 0 auto tfn = new AST.TypeSArray(AST.Type.tchar, lenfn); efn.type = tfn.immutableOf(); - efn.committed = 1; + efn.committed = true; auto sfn = new AST.VarDeclaration(loc, tfn, Id.__func__, ifn, STC.gshared | STC.immutable_); auto e = new AST.DeclarationExp(loc, sfn); return new AST.ExpStatement(loc, e); @@ -4882,6 +5235,17 @@ final class CParser(AST) : Parser!AST private AST.Dsymbol applySpecifier(AST.Dsymbol s, ref Specifier specifier) { //printf("applySpecifier() %s\n", s.toChars()); + if (specifier._deprecated) + { + if (specifier.depMsg) + { + // Wrap declaration in a DeprecatedDeclaration + auto decls = new AST.Dsymbols(1); + (*decls)[0] = s; + s = new AST.DeprecatedDeclaration(specifier.depMsg, decls); + } + } + if (specifier.alignExps) { //printf(" applying _Alignas %s, packalign %d\n", (*specifier.alignExps)[0].toChars(), cast(int)specifier.packalign); diff --git a/dmd/cppmangle.d b/dmd/cppmangle.d index 32b38518953..ee1340d6342 100644 --- a/dmd/cppmangle.d +++ b/dmd/cppmangle.d @@ -211,7 +211,7 @@ private final class CppMangleVisitor : Visitor */ void mangleReturnType(TypeFunction preSemantic) { - auto tf = cast(TypeFunction)this.context.res.asFuncDecl().type; + auto tf = this.context.res.asFuncDecl().type.isTypeFunction(); Type rt = preSemantic.nextOf(); // https://issues.dlang.org/show_bug.cgi?id=22739 // auto return type means that rt is null. @@ -347,14 +347,14 @@ private final class CppMangleVisitor : Visitor * * Params: * off = Offset to insert at - * fd = Type of the function to mangle the return type of + * tf = Type of the function to mangle the return type of */ void writeRemainingTags(size_t off, TypeFunction tf) { - scope remainingVisitor = new LeftoverVisitor(&this.abiTags.written); - tf.next.accept(remainingVisitor); + Array!StringExp toWrite; + leftOver(tf, &this.abiTags.written, &toWrite); OutBuffer b2; - foreach (se; remainingVisitor.toWrite) + foreach (se; toWrite) { auto tag = se.peekString(); // We can only insert a slice, and each insert is a memmove, @@ -446,7 +446,15 @@ private final class CppMangleVisitor : Visitor if (this.context.res.dyncast() == DYNCAST.dsymbol) parentti = this.context.res.asFuncDecl().parent.isTemplateInstance(); else - parentti = this.context.res.asType().toDsymbol(null).parent.isTemplateInstance(); + { + auto parent = this.context.res.asType().toDsymbol(null).parent; + parentti = parent.isTemplateInstance(); + // https://issues.dlang.org/show_bug.cgi?id=22760 + // The template instance may sometimes have the form + // S1!int.S1, therefore the above instruction might yield null + if (parentti is null && parent.parent) + parentti = parent.parent.isTemplateInstance(); + } return (*parentti.tiargs)[arg]; }()); scope (exit) this.context.pop(prev); @@ -496,9 +504,9 @@ private final class CppMangleVisitor : Visitor mangle_function(d.isFuncDeclaration()); buf.writestring("EE"); } - else if (e && e.op == EXP.variable && (cast(VarExp)e).var.isVarDeclaration()) + else if (e && e.isVarExp() && e.isVarExp().var.isVarDeclaration()) { - VarDeclaration vd = (cast(VarExp)e).var.isVarDeclaration(); + VarDeclaration vd = e.isVarExp().var.isVarDeclaration(); buf.writeByte('L'); mangle_variable(vd, true); buf.writeByte('E'); @@ -757,9 +765,9 @@ private final class CppMangleVisitor : Visitor bool isIdent_char(Identifier ident, RootObject o) { Type t = isType(o); - if (!t || t.ty != Tstruct) + if (!t || !t.isTypeStruct()) return false; - Dsymbol s = (cast(TypeStruct)t).toDsymbol(null); + Dsymbol s = t.toDsymbol(null); if (s.ident != ident) return false; Dsymbol p = s.toParent(); @@ -1059,7 +1067,7 @@ private final class CppMangleVisitor : Visitor * ::= * ::= */ - TypeFunction tf = cast(TypeFunction)d.type; + TypeFunction tf = d.type.isTypeFunction(); if (TemplateDeclaration ftd = getFuncTemplateDecl(d)) { @@ -1173,7 +1181,7 @@ private final class CppMangleVisitor : Visitor this.context.ti = ti; this.context.fd = d; this.context.res = d; - TypeFunction preSemantic = cast(TypeFunction)d.originalType; + TypeFunction preSemantic = d.originalType.isTypeFunction(); auto nspace = ti.toParent(); if (nspace && nspace.isNspace()) this.writeChained(ti.toParent(), () => source_name(ti, true)); @@ -1347,7 +1355,7 @@ private final class CppMangleVisitor : Visitor auto prev = this.context.push({ TypeFunction tf; if (isDsymbol(this.context.res)) - tf = cast(TypeFunction)this.context.res.asFuncDecl().type; + tf = this.context.res.asFuncDecl().type.isTypeFunction(); else tf = this.context.res.asType().isTypeFunction(); assert(tf); @@ -1391,9 +1399,9 @@ private final class CppMangleVisitor : Visitor */ void headOfType(Type t) { - if (t.ty == Tclass) + if (auto tc = t.isTypeClass()) { - mangleTypeClass(cast(TypeClass)t, true); + mangleTypeClass(tc, true); } else { @@ -1960,7 +1968,7 @@ extern(C++): */ override void visit(TypeIdentifier t) { - auto decl = cast(TemplateDeclaration)this.context.ti.tempdecl; + auto decl = this.context.ti.tempdecl.isTemplateDeclaration(); assert(decl.parameters !is null); auto idx = templateParamIndex(t.ident, decl.parameters); // If not found, default to the post-semantic type @@ -2019,7 +2027,7 @@ extern(C++): { // If the resolved AST has more args than the parse one, // we have default arguments - auto oparams = (cast(TemplateDeclaration)analyzed_ti.tempdecl).origParameters; + auto oparams = analyzed_ti.tempdecl.isTemplateDeclaration().origParameters; foreach (idx, arg; (*oparams)[t.tiargs.length .. $]) { this.context.res = (*analyzed_ti.tiargs)[idx + t.tiargs.length]; @@ -2044,7 +2052,7 @@ extern(C++): assert(t.tiargs !is null); bool needsTa; - auto decl = cast(TemplateDeclaration)this.context.ti.tempdecl; + auto decl = this.context.ti.tempdecl.isTemplateDeclaration(); // Attempt to substitute the template itself auto idx = templateParamIndex(t.name, decl.parameters); if (idx < decl.parameters.length) @@ -2125,12 +2133,13 @@ private void visitObject(V : Visitor)(RootObject o, V this_) /// Helper function to safely get a type out of a `RootObject` private Type asType(RootObject o) { - Type ta = isType(o); + if (Type ta = isType(o)) + return ta; + // When called with context.res as argument, it can be `FuncDeclaration` - if (!ta && o.asFuncDecl()) - ta = (cast(FuncDeclaration)o).type; - assert(ta !is null, o.toString()); - return ta; + if (auto fd = o.asFuncDecl()) + return fd.type; + assert(0); } /// Helper function to safely get a `FuncDeclaration` out of a `RootObject` @@ -2183,12 +2192,12 @@ private extern(C++) final class ComponentVisitor : Visitor case DYNCAST.type: auto t = cast(Type)base; - if (t.ty == Tpointer) - this.tpointer = cast(TypePointer)t; - else if (t.ty == Treference) - this.tref = cast(TypeReference)t; - else if (t.ty == Tident) - this.tident = cast(TypeIdentifier)t; + if (auto tp = t.isTypePointer()) + this.tpointer = tp; + else if (auto tr = t.isTypeReference()) + this.tref = tr; + else if (auto ti = t.isTypeIdentifier()) + this.tident = ti; else goto default; break; @@ -2531,58 +2540,70 @@ unittest assert(closestIndex([s1, s2, s4], s5, match) == 3 && !match); } -/** +/*** * Visits the return type of a function and writes leftover ABI tags + * Params: + * tf = Type of the function to mangle the return type of + * previous = already written ones + * toWrite = where to put StringExp's to be written */ -extern(C++) private final class LeftoverVisitor : Visitor +private +void leftOver(TypeFunction tf, const(Array!StringExp)* previous, Array!StringExp* toWrite) { - /// List of tags to write - private Array!StringExp toWrite; - /// List of tags to ignore - private const(Array!StringExp)* ignore; - - /// - public this(const(Array!StringExp)* previous) + extern(C++) final class LeftoverVisitor : Visitor { - this.ignore = previous; - } + /// List of tags to write + private Array!StringExp* toWrite; + /// List of tags to ignore + private const(Array!StringExp)* ignore; - /// Reintroduce base class overloads - public alias visit = Visitor.visit; + /// + public this(const(Array!StringExp)* previous, Array!StringExp* toWrite) + { + this.ignore = previous; + this.toWrite = toWrite; + } - /// Least specialized overload of each direct child of `RootObject` - public override void visit(Dsymbol o) - { - auto ale = ABITagContainer.forSymbol(o); - if (!ale) return; + /// Reintroduce base class overloads + public alias visit = Visitor.visit; - bool match; - foreach (elem; *ale.elements) + /// Least specialized overload of each direct child of `RootObject` + public override void visit(Dsymbol o) { - auto se = elem.toStringExp(); - closestIndex((*this.ignore)[], se, match); - if (match) continue; - auto idx = closestIndex(this.toWrite[], se, match); - if (!match) - this.toWrite.insert(idx, se); + auto ale = ABITagContainer.forSymbol(o); + if (!ale) return; + + bool match; + foreach (elem; *ale.elements) + { + auto se = elem.toStringExp(); + closestIndex((*this.ignore)[], se, match); + if (match) continue; + auto idx = closestIndex((*this.toWrite)[], se, match); + if (!match) + (*this.toWrite).insert(idx, se); + } } - } - /// Ditto - public override void visit(Type o) - { - if (auto sym = o.toDsymbol(null)) - sym.accept(this); - } + /// Ditto + public override void visit(Type o) + { + if (auto sym = o.toDsymbol(null)) + sym.accept(this); + } - /// Composite type - public override void visit(TypePointer o) - { - o.next.accept(this); - } + /// Composite type + public override void visit(TypePointer o) + { + o.next.accept(this); + } - public override void visit(TypeReference o) - { - o.next.accept(this); + public override void visit(TypeReference o) + { + o.next.accept(this); + } } + + scope remainingVisitor = new LeftoverVisitor(previous, toWrite); + tf.next.accept(remainingVisitor); } diff --git a/dmd/cppmanglewin.d b/dmd/cppmanglewin.d index c59a9a6de1a..a8113c9381b 100644 --- a/dmd/cppmanglewin.d +++ b/dmd/cppmanglewin.d @@ -101,57 +101,37 @@ private extern (D) bool checkImmutableShared(Type type, Loc loc) private final class VisualCPPMangler : Visitor { - enum VC_SAVED_TYPE_CNT = 10u; - enum VC_SAVED_IDENT_CNT = 10u; - alias visit = Visitor.visit; - Identifier[VC_SAVED_IDENT_CNT] saved_idents; - Type[VC_SAVED_TYPE_CNT] saved_types; - Loc loc; /// location for use in error messages - - // IS_NOT_TOP_TYPE: when we mangling one argument, we can call visit several times (for base types of arg type) - // but we must save only arg type: - // For example: if we have an int** argument, we should save "int**" but visit will be called for "int**", "int*", "int" - // This flag is set up by the visit(NextType, ) function and should be reset when the arg type output is finished. - // MANGLE_RETURN_TYPE: return type shouldn't be saved and substituted in arguments - // IGNORE_CONST: in some cases we should ignore CV-modifiers. - // ESCAPE: toplevel const non-pointer types need a '$$C' escape in addition to a cv qualifier. - - enum Flags : int - { - IS_NOT_TOP_TYPE = 0x1, - MANGLE_RETURN_TYPE = 0x2, - IGNORE_CONST = 0x4, - IS_DMC = 0x8, - ESCAPE = 0x10, - } + Identifier[10] saved_idents; + Type[10] saved_types; + Loc loc; /// location for use in error messages + + bool isNotTopType; /** When mangling one argument, we can call visit several times (for base types of arg type) + * but must save only arg type: + * For example: if we have an int** argument, we should save "int**" but visit will be called for "int**", "int*", "int" + * This flag is set up by the visit(NextType, ) function and should be reset when the arg type output is finished. + */ + bool ignoreConst; /// in some cases we should ignore CV-modifiers. + bool escape; /// toplevel const non-pointer types need a '$$C' escape in addition to a cv qualifier. + bool mangleReturnType; /// return type shouldn't be saved and substituted in arguments + bool isDmc; /// Digital Mars C++ name mangling - alias IS_NOT_TOP_TYPE = Flags.IS_NOT_TOP_TYPE; - alias MANGLE_RETURN_TYPE = Flags.MANGLE_RETURN_TYPE; - alias IGNORE_CONST = Flags.IGNORE_CONST; - alias IS_DMC = Flags.IS_DMC; - alias ESCAPE = Flags.ESCAPE; - - int flags; OutBuffer buf; extern (D) this(VisualCPPMangler rvl) scope { - flags |= (rvl.flags & IS_DMC); saved_idents[] = rvl.saved_idents[]; - saved_types[] = rvl.saved_types[]; - loc = rvl.loc; + saved_types[] = rvl.saved_types[]; + isDmc = rvl.isDmc; + loc = rvl.loc; } public: - extern (D) this(bool isdmc, Loc loc) scope + extern (D) this(bool isDmc, Loc loc) scope { - if (isdmc) - { - flags |= IS_DMC; - } saved_idents[] = null; saved_types[] = null; + this.isDmc = isDmc; this.loc = loc; } @@ -172,8 +152,8 @@ public: return; buf.writestring("$$T"); - flags &= ~IS_NOT_TOP_TYPE; - flags &= ~IGNORE_CONST; + isNotTopType = false; + ignoreConst = false; } override void visit(TypeNoreturn type) @@ -184,17 +164,17 @@ public: return; buf.writeByte('X'); // yes, mangle it like `void` - flags &= ~IS_NOT_TOP_TYPE; - flags &= ~IGNORE_CONST; + isNotTopType = false; + ignoreConst = false; } override void visit(TypeBasic type) { - //printf("visit(TypeBasic); is_not_top_type = %d\n", cast(int)(flags & IS_NOT_TOP_TYPE)); + //printf("visit(TypeBasic); is_not_top_type = %d\n", isNotTopType); if (checkImmutableShared(type, loc)) return; - if (type.isConst() && ((flags & IS_NOT_TOP_TYPE) || (flags & IS_DMC))) + if (type.isConst() && (isNotTopType || isDmc)) { if (checkTypeSaved(type)) return; @@ -203,7 +183,7 @@ public: { return; } - if (!(flags & IS_DMC)) + if (!isDmc) { switch (type.ty) { @@ -275,7 +255,7 @@ version (IN_LLVM) } else { - if (flags & IS_DMC) + if (isDmc) buf.writestring("_Z"); // DigitalMars long double else buf.writestring("_T"); // Intel long double @@ -297,33 +277,33 @@ else visit(cast(Type)type); return; } - flags &= ~IS_NOT_TOP_TYPE; - flags &= ~IGNORE_CONST; + isNotTopType = false; + ignoreConst = false; } override void visit(TypeVector type) { - //printf("visit(TypeVector); is_not_top_type = %d\n", cast(int)(flags & IS_NOT_TOP_TYPE)); + //printf("visit(TypeVector); is_not_top_type = %d\n", isNotTopType); if (checkTypeSaved(type)) return; mangleModifier(type); buf.writestring("T__m128@@"); // may be better as __m128i or __m128d? - flags &= ~IS_NOT_TOP_TYPE; - flags &= ~IGNORE_CONST; + isNotTopType = false; + ignoreConst = false; } override void visit(TypeSArray type) { // This method can be called only for static variable type mangling. - //printf("visit(TypeSArray); is_not_top_type = %d\n", cast(int)(flags & IS_NOT_TOP_TYPE)); + //printf("visit(TypeSArray); is_not_top_type = %d\n", isNotTopType); if (checkTypeSaved(type)) return; // first dimension always mangled as const pointer - if (flags & IS_DMC) + if (isDmc) buf.writeByte('Q'); else buf.writeByte('P'); - flags |= IS_NOT_TOP_TYPE; + isNotTopType = true; assert(type.next); if (type.next.ty == Tsarray) { @@ -339,7 +319,7 @@ else // There is not way to map int C++ (*arr)[2][1] to D override void visit(TypePointer type) { - //printf("visit(TypePointer); is_not_top_type = %d\n", cast(int)(flags & IS_NOT_TOP_TYPE)); + //printf("visit(TypePointer); is_not_top_type = %d\n", isNotTopType); if (checkImmutableShared(type, loc)) return; @@ -359,8 +339,8 @@ else buf.writeByte('P'); // mutable buf.writeByte('6'); // pointer to a function buf.writestring(arg); - flags &= ~IS_NOT_TOP_TYPE; - flags &= ~IGNORE_CONST; + isNotTopType = false; + ignoreConst = false; return; } else if (type.next.ty == Tsarray) @@ -368,13 +348,13 @@ else if (checkTypeSaved(type)) return; mangleModifier(type); - if (type.isConst() || !(flags & IS_DMC)) + if (type.isConst() || !isDmc) buf.writeByte('Q'); // const else buf.writeByte('P'); // mutable if (target.isLP64) buf.writeByte('E'); - flags |= IS_NOT_TOP_TYPE; + isNotTopType = true; mangleArray(cast(TypeSArray)type.next); return; } @@ -393,7 +373,7 @@ else } if (target.isLP64) buf.writeByte('E'); - flags |= IS_NOT_TOP_TYPE; + isNotTopType = true; type.next.accept(this); } } @@ -410,7 +390,7 @@ else buf.writeByte('A'); // mutable if (target.isLP64) buf.writeByte('E'); - flags |= IS_NOT_TOP_TYPE; + isNotTopType = true; assert(type.next); if (type.next.ty == Tsarray) { @@ -425,7 +405,7 @@ else override void visit(TypeFunction type) { const(char)* arg = mangleFunctionType(type); - if ((flags & IS_DMC)) + if (isDmc) { if (checkTypeSaved(type)) return; @@ -435,14 +415,15 @@ else buf.writestring("$$A6"); } buf.writestring(arg); - flags &= ~(IS_NOT_TOP_TYPE | IGNORE_CONST); + isNotTopType = false; + ignoreConst = false; } override void visit(TypeStruct type) { if (checkTypeSaved(type)) return; - //printf("visit(TypeStruct); is_not_top_type = %d\n", cast(int)(flags & IS_NOT_TOP_TYPE)); + //printf("visit(TypeStruct); is_not_top_type = %d\n", isNotTopType); mangleModifier(type); const agg = type.sym.isStructDeclaration(); if (type.sym.isUnionDeclaration()) @@ -450,13 +431,13 @@ else else buf.writeByte(agg.cppmangle == CPPMANGLE.asClass ? 'V' : 'U'); mangleIdent(type.sym); - flags &= ~IS_NOT_TOP_TYPE; - flags &= ~IGNORE_CONST; + isNotTopType = false; + ignoreConst = false; } override void visit(TypeEnum type) { - //printf("visit(TypeEnum); is_not_top_type = %d\n", cast(int)(flags & IS_NOT_TOP_TYPE)); + //printf("visit(TypeEnum); is_not_top_type = %d\n", cast(int)(flags & isNotTopType)); const id = type.sym.ident; string c; if (id == Id.__c_long_double) @@ -473,7 +454,7 @@ else c = "D"; // VC++ char else if (id == Id.__c_wchar_t) { - c = (flags & IS_DMC) ? "_Y" : "_W"; + c = isDmc ? "_Y" : "_W"; } if (c.length) @@ -481,7 +462,7 @@ else if (checkImmutableShared(type, loc)) return; - if (type.isConst() && ((flags & IS_NOT_TOP_TYPE) || (flags & IS_DMC))) + if (type.isConst() && (isNotTopType || isDmc)) { if (checkTypeSaved(type)) return; @@ -497,18 +478,18 @@ else buf.writestring("W4"); mangleIdent(type.sym); } - flags &= ~IS_NOT_TOP_TYPE; - flags &= ~IGNORE_CONST; + isNotTopType = false; + ignoreConst = false; } // D class mangled as pointer to C++ class // const(Object) mangled as Object const* const override void visit(TypeClass type) { - //printf("visit(TypeClass); is_not_top_type = %d\n", cast(int)(flags & IS_NOT_TOP_TYPE)); + //printf("visit(TypeClass); is_not_top_type = %d\n", isNotTopType); if (checkTypeSaved(type)) return; - if (flags & IS_NOT_TOP_TYPE) + if (isNotTopType) mangleModifier(type); if (type.isConst()) buf.writeByte('Q'); @@ -516,13 +497,13 @@ else buf.writeByte('P'); if (target.isLP64) buf.writeByte('E'); - flags |= IS_NOT_TOP_TYPE; + isNotTopType = true; mangleModifier(type); const cldecl = type.sym.isClassDeclaration(); buf.writeByte(cldecl.cppmangle == CPPMANGLE.asStruct ? 'U' : 'V'); mangleIdent(type.sym); - flags &= ~IS_NOT_TOP_TYPE; - flags &= ~IGNORE_CONST; + isNotTopType = false; + ignoreConst = false; } const(char)* mangleOf(Dsymbol s) @@ -547,22 +528,6 @@ else private: extern(D): - void mangleVisibility(Declaration d, string privProtDef) - { - switch (d.visibility.kind) - { - case Visibility.Kind.private_: - buf.writeByte(privProtDef[0]); - break; - case Visibility.Kind.protected_: - buf.writeByte(privProtDef[1]); - break; - default: - buf.writeByte(privProtDef[2]); - break; - } - } - void mangleFunction(FuncDeclaration d) { // ? @@ -576,11 +541,11 @@ extern(D): //d.toChars(), d.isVirtualMethod(), d.isVirtual(), cast(int)d.vtblIndex, d.interfaceVirtual); if ((d.isVirtual() && (d.vtblIndex != -1 || d.interfaceVirtual || d.overrideInterface())) || (d.isDtorDeclaration() && d.parent.isClassDeclaration() && !d.isFinal())) { - mangleVisibility(d, "EMU"); + mangleVisibility(buf, d, "EMU"); } else { - mangleVisibility(d, "AIQ"); + mangleVisibility(buf, d, "AIQ"); } if (target.isLP64) buf.writeByte('E'); @@ -596,7 +561,7 @@ extern(D): else if (d.isMember2()) // static function { // ::= - mangleVisibility(d, "CKS"); + mangleVisibility(buf, d, "CKS"); } else // top-level function { @@ -631,7 +596,7 @@ extern(D): } else { - mangleVisibility(d, "012"); + mangleVisibility(buf, d, "012"); } Type t = d.type; @@ -649,141 +614,6 @@ extern(D): buf.writeByte(cv_mod); } - /** - * Computes mangling for symbols with special mangling. - * Params: - * sym = symbol to mangle - * Returns: - * mangling for special symbols, - * null if not a special symbol - */ - static string mangleSpecialName(Dsymbol sym) - { - string mangle; - if (sym.isCtorDeclaration()) - mangle = "?0"; - else if (sym.isAggregateDtor()) - mangle = "?1"; - else if (!sym.ident) - return null; - else if (sym.ident == Id.assign) - mangle = "?4"; - else if (sym.ident == Id.eq) - mangle = "?8"; - else if (sym.ident == Id.index) - mangle = "?A"; - else if (sym.ident == Id.call) - mangle = "?R"; - else if (sym.ident == Id.cppdtor) - mangle = "?_G"; - else - return null; - - return mangle; - } - - /** - * Mangles an operator, if any - * - * Params: - * ti = associated template instance of the operator - * symName = symbol name - * firstTemplateArg = index if the first argument of the template (because the corresponding c++ operator is not a template) - * Returns: - * true if sym has no further mangling needed - * false otherwise - */ - bool mangleOperator(TemplateInstance ti, ref const(char)[] symName, ref int firstTemplateArg) - { - auto whichOp = isCppOperator(ti.name); - final switch (whichOp) - { - case CppOperator.Unknown: - return false; - case CppOperator.Cast: - buf.writestring("?B"); - return true; - case CppOperator.Assign: - symName = "?4"; - return false; - case CppOperator.Eq: - symName = "?8"; - return false; - case CppOperator.Index: - symName = "?A"; - return false; - case CppOperator.Call: - symName = "?R"; - return false; - - case CppOperator.Unary: - case CppOperator.Binary: - case CppOperator.OpAssign: - TemplateDeclaration td = ti.tempdecl.isTemplateDeclaration(); - assert(td); - assert(ti.tiargs.length >= 1); - TemplateParameter tp = (*td.parameters)[0]; - TemplateValueParameter tv = tp.isTemplateValueParameter(); - if (!tv || !tv.valType.isString()) - return false; // expecting a string argument to operators! - Expression exp = (*ti.tiargs)[0].isExpression(); - StringExp str = exp.toStringExp(); - switch (whichOp) - { - case CppOperator.Unary: - switch (str.peekString()) - { - case "*": symName = "?D"; goto continue_template; - case "++": symName = "?E"; goto continue_template; - case "--": symName = "?F"; goto continue_template; - case "-": symName = "?G"; goto continue_template; - case "+": symName = "?H"; goto continue_template; - case "~": symName = "?S"; goto continue_template; - default: return false; - } - case CppOperator.Binary: - switch (str.peekString()) - { - case ">>": symName = "?5"; goto continue_template; - case "<<": symName = "?6"; goto continue_template; - case "*": symName = "?D"; goto continue_template; - case "-": symName = "?G"; goto continue_template; - case "+": symName = "?H"; goto continue_template; - case "&": symName = "?I"; goto continue_template; - case "/": symName = "?K"; goto continue_template; - case "%": symName = "?L"; goto continue_template; - case "^": symName = "?T"; goto continue_template; - case "|": symName = "?U"; goto continue_template; - default: return false; - } - case CppOperator.OpAssign: - switch (str.peekString()) - { - case "*": symName = "?X"; goto continue_template; - case "+": symName = "?Y"; goto continue_template; - case "-": symName = "?Z"; goto continue_template; - case "/": symName = "?_0"; goto continue_template; - case "%": symName = "?_1"; goto continue_template; - case ">>": symName = "?_2"; goto continue_template; - case "<<": symName = "?_3"; goto continue_template; - case "&": symName = "?_4"; goto continue_template; - case "|": symName = "?_5"; goto continue_template; - case "^": symName = "?_6"; goto continue_template; - default: return false; - } - default: assert(0); - } - } - continue_template: - if (ti.tiargs.length == 1) - { - buf.writestring(symName); - return true; - } - firstTemplateArg = 1; - return false; - } - /** * Mangles a template value * @@ -806,13 +636,13 @@ extern(D): assert(e); if (tv.valType.isunsigned()) { - mangleNumber(e.toUInteger()); + mangleNumber(buf, e.toUInteger()); } else if (is_dmc_template) { // NOTE: DMC mangles everything based on // unsigned int - mangleNumber(e.toInteger()); + mangleNumber(buf, e.toInteger()); } else { @@ -822,7 +652,7 @@ extern(D): val = -val; buf.writeByte('?'); } - mangleNumber(val); + mangleNumber(buf, val); } } @@ -846,7 +676,7 @@ extern(D): else if (e && e.op == EXP.variable && (cast(VarExp)e).var.isVarDeclaration()) { buf.writeByte('$'); - if (flags & IS_DMC) + if (isDmc) buf.writeByte('1'); else buf.writeByte('E'); @@ -855,7 +685,7 @@ extern(D): else if (d && d.isTemplateDeclaration() && d.isTemplateDeclaration().onemember) { Dsymbol ds = d.isTemplateDeclaration().onemember; - if (flags & IS_DMC) + if (isDmc) { buf.writeByte('V'); } @@ -896,11 +726,11 @@ extern(D): */ void mangleTemplateType(RootObject o) { - flags |= ESCAPE; + escape = true; Type t = isType(o); assert(t); t.accept(this); - flags &= ~ESCAPE; + escape = false; } /** @@ -952,7 +782,7 @@ extern(D): int firstTemplateArg = 0; // test for special symbols - if (mangleOperator(ti,symName,firstTemplateArg)) + if (mangleOperator(buf, ti,symName,firstTemplateArg)) return; TemplateInstance actualti = ti; bool needNamespaces; @@ -986,14 +816,14 @@ extern(D): } } - scope VisualCPPMangler tmp = new VisualCPPMangler((flags & IS_DMC) ? true : false, loc); + scope VisualCPPMangler tmp = new VisualCPPMangler(isDmc ? true : false, loc); tmp.buf.writeByte('?'); tmp.buf.writeByte('$'); tmp.buf.writestring(symName); tmp.saved_idents[0] = id; if (symName == id.toString()) tmp.buf.writeByte('@'); - if (flags & IS_DMC) + if (isDmc) { tmp.mangleIdent(sym.parent, true); is_dmc_template = true; @@ -1054,16 +884,16 @@ extern(D): // returns true if name already saved bool checkAndSaveIdent(Identifier name) { - foreach (i; 0 .. VC_SAVED_IDENT_CNT) + foreach (i, ref id; saved_idents) { - if (!saved_idents[i]) // no saved same name + if (!id) // no saved same name { - saved_idents[i] = name; + id = name; break; } - if (saved_idents[i] == name) // ok, we've found same name. use index instead of name + if (id == name) // ok, we've found same name. use index instead of name { - buf.writeByte(i + '0'); + buf.writeByte(cast(uint)i + '0'); return true; } } @@ -1072,14 +902,14 @@ extern(D): void saveIdent(Identifier name) { - foreach (i; 0 .. VC_SAVED_IDENT_CNT) + foreach (ref id; saved_idents) { - if (!saved_idents[i]) // no saved same name + if (!id) // no saved same name { - saved_idents[i] = name; + id = name; break; } - if (saved_idents[i] == name) // ok, we've found same name. use index instead of name + if (id == name) // ok, we've found same name. use index instead of name { return; } @@ -1122,50 +952,24 @@ extern(D): buf.writeByte('@'); } - void mangleNumber(dinteger_t num) - { - if (!num) // 0 encoded as "A@" - { - buf.writeByte('A'); - buf.writeByte('@'); - return; - } - if (num <= 10) // 5 encoded as "4" - { - buf.writeByte(cast(char)(num - 1 + '0')); - return; - } - char[17] buff; - buff[16] = 0; - size_t i = 16; - while (num) - { - --i; - buff[i] = num % 16 + 'A'; - num /= 16; - } - buf.writestring(&buff[i]); - buf.writeByte('@'); - } - bool checkTypeSaved(Type type) { - if (flags & IS_NOT_TOP_TYPE) + if (isNotTopType) return false; - if (flags & MANGLE_RETURN_TYPE) + if (mangleReturnType) return false; - for (uint i = 0; i < VC_SAVED_TYPE_CNT; i++) + foreach (i, ref ty; saved_types) { - if (!saved_types[i]) // no saved same type + if (!ty) // no saved same type { - saved_types[i] = type; + ty = type; return false; } - if (saved_types[i].equals(type)) // ok, we've found same type. use index instead of type + if (ty.equals(type)) // ok, we've found same type. use index instead of type { - buf.writeByte(i + '0'); - flags &= ~IS_NOT_TOP_TYPE; - flags &= ~IGNORE_CONST; + buf.writeByte(cast(uint)i + '0'); + isNotTopType = false; + ignoreConst = false; return true; } } @@ -1174,7 +978,7 @@ extern(D): void mangleModifier(Type type) { - if (flags & IGNORE_CONST) + if (ignoreConst) return; if (checkImmutableShared(type, loc)) return; @@ -1183,17 +987,17 @@ extern(D): { // Template parameters that are not pointers and are const need an $$C escape // in addition to 'B' (const). - if ((flags & ESCAPE) && type.ty != Tpointer) + if (escape && type.ty != Tpointer) buf.writestring("$$CB"); - else if (flags & IS_NOT_TOP_TYPE) + else if (isNotTopType) buf.writeByte('B'); // const - else if ((flags & IS_DMC) && type.ty != Tpointer) + else if (isDmc && type.ty != Tpointer) buf.writestring("_O"); } - else if (flags & IS_NOT_TOP_TYPE) + else if (isNotTopType) buf.writeByte('A'); // mutable - flags &= ~ESCAPE; + escape = false; } void mangleArray(TypeSArray type) @@ -1207,15 +1011,15 @@ extern(D): cur = cur.nextOf(); } buf.writeByte('Y'); - mangleNumber(i); // count of dimensions + mangleNumber(buf, i); // count of dimensions cur = type; while (cur && cur.ty == Tsarray) // sizes of dimensions { TypeSArray sa = cast(TypeSArray)cur; - mangleNumber(sa.dim ? sa.dim.toInteger() : 0); + mangleNumber(buf, sa.dim ? sa.dim.toInteger() : 0); cur = cur.nextOf(); } - flags |= IGNORE_CONST; + ignoreConst = true; cur.accept(this); } @@ -1252,7 +1056,7 @@ extern(D): assert(0); } } - tmp.flags &= ~IS_NOT_TOP_TYPE; + tmp.isNotTopType = false; if (noreturn) { tmp.buf.writeByte('@'); @@ -1262,7 +1066,7 @@ extern(D): Type rettype = type.next; if (type.isref) rettype = rettype.referenceTo(); - flags &= ~IGNORE_CONST; + ignoreConst = false; if (rettype.ty == Tstruct) { tmp.buf.writeByte('?'); @@ -1277,9 +1081,9 @@ extern(D): tmp.buf.writeByte('A'); } } - tmp.flags |= MANGLE_RETURN_TYPE; + tmp.mangleReturnType = true; rettype.accept(tmp); - tmp.flags &= ~MANGLE_RETURN_TYPE; + tmp.mangleReturnType = false; } if (!type.parameterList.parameters || !type.parameterList.parameters.length) { @@ -1310,8 +1114,8 @@ extern(D): errorSupplemental(loc, "Use pointer instead."); assert(0); } - tmp.flags &= ~IS_NOT_TOP_TYPE; - tmp.flags &= ~IGNORE_CONST; + tmp.isNotTopType = false; + ignoreConst = false; t.accept(tmp); } @@ -1331,3 +1135,188 @@ extern(D): return ret; } } + +private: +extern(D): + +/** + * Computes mangling for symbols with special mangling. + * Params: + * sym = symbol to mangle + * Returns: + * mangling for special symbols, + * null if not a special symbol + */ +string mangleSpecialName(Dsymbol sym) +{ + string mangle; + if (sym.isCtorDeclaration()) + mangle = "?0"; + else if (sym.isAggregateDtor()) + mangle = "?1"; + else if (!sym.ident) + return null; + else if (sym.ident == Id.assign) + mangle = "?4"; + else if (sym.ident == Id.eq) + mangle = "?8"; + else if (sym.ident == Id.index) + mangle = "?A"; + else if (sym.ident == Id.call) + mangle = "?R"; + else if (sym.ident == Id.cppdtor) + mangle = "?_G"; + else + return null; + + return mangle; +} + +/** + * Mangles an operator, if any + * + * Params: + * buf = buffer to write mangling to + * ti = associated template instance of the operator + * symName = symbol name + * firstTemplateArg = index if the first argument of the template (because the corresponding c++ operator is not a template) + * Returns: + * true if sym has no further mangling needed + * false otherwise + */ +bool mangleOperator(ref OutBuffer buf, TemplateInstance ti, ref const(char)[] symName, ref int firstTemplateArg) +{ + auto whichOp = isCppOperator(ti.name); + final switch (whichOp) + { + case CppOperator.Unknown: + return false; + case CppOperator.Cast: + buf.writestring("?B"); + return true; + case CppOperator.Assign: + symName = "?4"; + return false; + case CppOperator.Eq: + symName = "?8"; + return false; + case CppOperator.Index: + symName = "?A"; + return false; + case CppOperator.Call: + symName = "?R"; + return false; + + case CppOperator.Unary: + case CppOperator.Binary: + case CppOperator.OpAssign: + TemplateDeclaration td = ti.tempdecl.isTemplateDeclaration(); + assert(td); + assert(ti.tiargs.length >= 1); + TemplateParameter tp = (*td.parameters)[0]; + TemplateValueParameter tv = tp.isTemplateValueParameter(); + if (!tv || !tv.valType.isString()) + return false; // expecting a string argument to operators! + Expression exp = (*ti.tiargs)[0].isExpression(); + StringExp str = exp.toStringExp(); + switch (whichOp) + { + case CppOperator.Unary: + switch (str.peekString()) + { + case "*": symName = "?D"; goto continue_template; + case "++": symName = "?E"; goto continue_template; + case "--": symName = "?F"; goto continue_template; + case "-": symName = "?G"; goto continue_template; + case "+": symName = "?H"; goto continue_template; + case "~": symName = "?S"; goto continue_template; + default: return false; + } + case CppOperator.Binary: + switch (str.peekString()) + { + case ">>": symName = "?5"; goto continue_template; + case "<<": symName = "?6"; goto continue_template; + case "*": symName = "?D"; goto continue_template; + case "-": symName = "?G"; goto continue_template; + case "+": symName = "?H"; goto continue_template; + case "&": symName = "?I"; goto continue_template; + case "/": symName = "?K"; goto continue_template; + case "%": symName = "?L"; goto continue_template; + case "^": symName = "?T"; goto continue_template; + case "|": symName = "?U"; goto continue_template; + default: return false; + } + case CppOperator.OpAssign: + switch (str.peekString()) + { + case "*": symName = "?X"; goto continue_template; + case "+": symName = "?Y"; goto continue_template; + case "-": symName = "?Z"; goto continue_template; + case "/": symName = "?_0"; goto continue_template; + case "%": symName = "?_1"; goto continue_template; + case ">>": symName = "?_2"; goto continue_template; + case "<<": symName = "?_3"; goto continue_template; + case "&": symName = "?_4"; goto continue_template; + case "|": symName = "?_5"; goto continue_template; + case "^": symName = "?_6"; goto continue_template; + default: return false; + } + default: assert(0); + } + } + continue_template: + if (ti.tiargs.length == 1) + { + buf.writestring(symName); + return true; + } + firstTemplateArg = 1; + return false; +} + +/**********************************' + */ +void mangleNumber(ref OutBuffer buf, dinteger_t num) +{ + if (!num) // 0 encoded as "A@" + { + buf.writeByte('A'); + buf.writeByte('@'); + return; + } + if (num <= 10) // 5 encoded as "4" + { + buf.writeByte(cast(char)(num - 1 + '0')); + return; + } + char[17] buff = void; + buff[16] = 0; + size_t i = 16; + while (num) + { + --i; + buff[i] = num % 16 + 'A'; + num /= 16; + } + buf.writestring(&buff[i]); + buf.writeByte('@'); +} + +/************************************* + */ +void mangleVisibility(ref OutBuffer buf, Declaration d, string privProtDef) +{ + switch (d.visibility.kind) + { + case Visibility.Kind.private_: + buf.writeByte(privProtDef[0]); + break; + case Visibility.Kind.protected_: + buf.writeByte(privProtDef[1]); + break; + default: + buf.writeByte(privProtDef[2]); + break; + } +} diff --git a/dmd/ctfeexpr.d b/dmd/ctfeexpr.d index 801246fcfa1..d9941d9ecb8 100644 --- a/dmd/ctfeexpr.d +++ b/dmd/ctfeexpr.d @@ -47,7 +47,7 @@ extern (C++) final class ClassReferenceExp : Expression extern (D) this(const ref Loc loc, StructLiteralExp lit, Type type) { - super(loc, EXP.classReference, __traits(classInstanceSize, ClassReferenceExp)); + super(loc, EXP.classReference); assert(lit && lit.sd && lit.sd.isClassDeclaration()); this.value = lit; this.type = type; @@ -132,7 +132,7 @@ extern (C++) final class ThrownExceptionExp : Expression extern (D) this(const ref Loc loc, ClassReferenceExp victim) { - super(loc, EXP.thrownException, __traits(classInstanceSize, ThrownExceptionExp)); + super(loc, EXP.thrownException); this.thrown = victim; this.type = victim.type; } @@ -170,7 +170,7 @@ extern (C++) final class CTFEExp : Expression { extern (D) this(EXP tok) { - super(Loc.initial, tok, __traits(classInstanceSize, CTFEExp)); + super(Loc.initial, tok); type = Type.tvoid; } @@ -369,7 +369,6 @@ UnionExp copyLiteral(Expression e) case EXP.dotVariable: case EXP.int64: case EXP.float64: - case EXP.char_: case EXP.complex80: case EXP.void_: case EXP.vector: @@ -1468,7 +1467,7 @@ UnionExp ctfeCat(const ref Loc loc, Type type, Expression e1, Expression e2) memset(cast(char*)s + len * sz, 0, sz); emplaceExp!(StringExp)(&ue, loc, s[0 .. len * sz], len, sz); StringExp es = ue.exp().isStringExp(); - es.committed = 0; + es.committed = false; es.type = type; return ue; } @@ -1499,7 +1498,7 @@ UnionExp ctfeCat(const ref Loc loc, Type type, Expression e1, Expression e2) emplaceExp!(StringExp)(&ue, loc, s[0 .. len * sz], len, sz); StringExp es = ue.exp().isStringExp(); es.sz = sz; - es.committed = 0; //es1.committed; + es.committed = false; //es1.committed; es.type = type; return ue; } @@ -1837,7 +1836,6 @@ bool isCtfeValueValid(Expression newval) { case EXP.int64: case EXP.float64: - case EXP.char_: case EXP.complex80: return tb.isscalar(); diff --git a/dmd/dcast.d b/dmd/dcast.d index 2830b25d651..6fcc2806585 100644 --- a/dmd/dcast.d +++ b/dmd/dcast.d @@ -71,6 +71,8 @@ Expression implicitCastTo(Expression e, Scope* sc, Type t) if (const match = (sc && sc.flags & SCOPE.Cfile) ? e.cimplicitConvTo(t) : e.implicitConvTo(t)) { + // no need for an extra cast when matching is exact + if (match == MATCH.convert && e.type.isTypeNoreturn()) { return specialNoreturnCast(e, t); @@ -88,6 +90,8 @@ Expression implicitCastTo(Expression e, Scope* sc, Type t) auto ad = isAggregate(e.type); if (ad && ad.aliasthis) { + if (!ad.type || ad.type.isTypeError()) + return e; auto ts = ad.type.isTypeStruct(); const adMatch = ts ? ts.implicitConvToWithoutAliasThis(t) @@ -1845,7 +1849,7 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null) if (!e.committed) { se = e.copy().isStringExp(); - se.committed = 1; + se.committed = true; copied = 1; } @@ -1887,7 +1891,7 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null) assert(szx <= 255); se.sz = cast(ubyte)szx; se.len = cast(size_t)tb.isTypeSArray().dim.toInteger(); - se.committed = 1; + se.committed = true; se.type = t; /* If larger than source, pad with zeros. diff --git a/dmd/declaration.d b/dmd/declaration.d index e87bd3baa53..a92d3fb2c42 100644 --- a/dmd/declaration.d +++ b/dmd/declaration.d @@ -747,7 +747,7 @@ extern (C++) final class AliasDeclaration : Declaration extern (D) this(const ref Loc loc, Identifier ident, Type type) { super(loc, ident); - //printf("AliasDeclaration(id = '%s', type = %p)\n", id.toChars(), type); + //printf("AliasDeclaration(id = '%s', type = %p)\n", ident.toChars(), type); //printf("type = '%s'\n", type.toChars()); this.type = type; assert(type); @@ -756,7 +756,7 @@ extern (C++) final class AliasDeclaration : Declaration extern (D) this(const ref Loc loc, Identifier ident, Dsymbol s) { super(loc, ident); - //printf("AliasDeclaration(id = '%s', s = %p)\n", id.toChars(), s); + //printf("AliasDeclaration(id = '%s', s = %p)\n", ident.toChars(), s); assert(s != this); this.aliassym = s; assert(s); @@ -1162,6 +1162,8 @@ version (IN_LLVM) bool isArgDtorVar; /// temporary created to handle scope destruction of a function argument bool isCmacro; /// it is a C macro turned into a C declaration + bool dllImport; /// __declspec(dllimport) + bool dllExport; /// __declspec(dllexport) version (MARS) { bool inClosure; /// is inserted into a GC allocated closure @@ -1327,7 +1329,7 @@ version (IN_LLVM) override final bool isExport() const { - return visibility.kind == Visibility.Kind.export_; + return visibility.kind == Visibility.Kind.export_ || dllExport; } override final bool isImportedSymbol() const @@ -1338,6 +1340,7 @@ version (IN_LLVM) * export extern int sym3 = 0; // error, extern cannot have initializer */ bool result = + dllImport || visibility.kind == Visibility.Kind.export_ && storage_class & STC.extern_ && (storage_class & STC.static_ || parent.isModule()); @@ -1944,8 +1947,12 @@ extern (C++) class BitFieldDeclaration : VarDeclaration { // 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 (fieldState.bitOffset == fieldState.fieldSize * 8 && + fieldState.bitOffset + fieldWidth > memalignsize * 8) + { + if (log) printf("more units of alignment than its type\n"); startNewField(); // the bit field is full + } else { // if alignment boundary is crossed @@ -1954,7 +1961,7 @@ extern (C++) class BitFieldDeclaration : VarDeclaration //printf("%s start: %d end: %d memalignsize: %d\n", ad.toChars(), start, end, memalignsize); if (start / (memalignsize * 8) != (end - 1) / (memalignsize * 8)) { - //printf("alignment is crossed\n"); + if (log) printf("alignment is crossed\n"); startNewField(); } } @@ -1994,6 +2001,7 @@ extern (C++) class BitFieldDeclaration : VarDeclaration fieldState.bitOffset = pastField; } + //printf("\t%s: offset = %d bitOffset = %d fieldWidth = %d memsize = %d\n", toChars(), offset, bitOffset, fieldWidth, memsize); //printf("\t%s: memalignsize = %d\n", toChars(), memalignsize); //printf(" addField '%s' to '%s' at offset %d, size = %d\n", toChars(), ad.toChars(), offset, memsize); } diff --git a/dmd/declaration.h b/dmd/declaration.h index b4b78af9ce8..6b1677d03d3 100644 --- a/dmd/declaration.h +++ b/dmd/declaration.h @@ -169,8 +169,8 @@ class TupleDeclaration final : public Declaration public: Objects *objects; TypeTuple *tupletype; // !=NULL if this is a type tuple - bool isexp; // true: expression tuple - bool building; // it's growing in AliasAssign semantic + d_bool isexp; // true: expression tuple + d_bool building; // it's growing in AliasAssign semantic TupleDeclaration *syntaxCopy(Dsymbol *) override; const char *kind() const override; @@ -637,7 +637,7 @@ class FuncDeclaration : public Declaration // set if someone took the address of this function int tookAddressOf; - bool requiresClosure; // this function needs a closure + d_bool requiresClosure; // this function needs a closure // local variables in this function which are referenced by nested functions VarDeclarations closureVars; @@ -653,6 +653,9 @@ class FuncDeclaration : public Declaration FuncDeclarations *inlinedNestedCallees; AttributeViolation* safetyViolation; + AttributeViolation* nogcViolation; + AttributeViolation* pureViolation; + AttributeViolation* nothrowViolation; // Formerly FUNCFLAGS uint32_t flags; @@ -702,6 +705,10 @@ class FuncDeclaration : public Declaration bool isCrtCtor(bool v); bool isCrtDtor() const; bool isCrtDtor(bool v); + bool dllImport() const; + bool dllImport(bool v); + bool dllExport() const; + bool dllExport(bool v); // Data for a function declaration that is needed for the Objective-C // integration. @@ -772,7 +779,7 @@ class FuncAliasDeclaration final : public FuncDeclaration { public: FuncDeclaration *funcalias; - bool hasOverloads; + d_bool hasOverloads; FuncAliasDeclaration *isFuncAliasDeclaration() override { return this; } const char *kind() const override; @@ -788,7 +795,7 @@ class FuncLiteralDeclaration final : public FuncDeclaration Type *treq; // target of return type inference // backend - bool deferToObj; + d_bool deferToObj; FuncLiteralDeclaration *syntaxCopy(Dsymbol *) override; bool isNested() const override; @@ -808,7 +815,7 @@ class FuncLiteralDeclaration final : public FuncDeclaration class CtorDeclaration final : public FuncDeclaration { public: - bool isCpCtor; + d_bool isCpCtor; CtorDeclaration *syntaxCopy(Dsymbol *) override; const char *kind() const override; const char *toChars() const override; diff --git a/dmd/denum.d b/dmd/denum.d index a2e4c858e46..62a6e30636a 100644 --- a/dmd/denum.d +++ b/dmd/denum.d @@ -169,7 +169,12 @@ extern (C++) final class EnumDeclaration : ScopeDsymbol return defaultval; } //printf("EnumDeclaration::getDefaultValue() %p %s\n", this, toChars()); - if (defaultval) + // https://issues.dlang.org/show_bug.cgi?id=23904 + // Return defaultval only if it is not ErrorExp. + // A speculative context may set defaultval to ErrorExp; + // subsequent non-speculative contexts need to be able + // to print the error. + if (defaultval && !defaultval.isErrorExp()) return defaultval; if (isCsymbol()) diff --git a/dmd/dimport.d b/dmd/dimport.d index b653d9bbf89..c4d5ddbc079 100644 --- a/dmd/dimport.d +++ b/dmd/dimport.d @@ -26,6 +26,7 @@ import dmd.location; import dmd.mtype; import dmd.visitor; +import core.stdc.stdio; /*********************************************************** */ extern (C++) final class Import : Dsymbol @@ -232,7 +233,20 @@ extern (C++) final class Import : Dsymbol * most likely because of parsing errors. * Therefore we cannot trust the resulting AST. */ - if (load(sc)) return; + if (load(sc)) + { + // https://issues.dlang.org/show_bug.cgi?id=23873 + // For imports that are not at module or function level, + // e.g. aggregate level, the import symbol is added to the + // symbol table and later semantic is performed on it. + // This leads to semantic analysis on an malformed AST + // which causes all kinds of segfaults. + // The fix is to note that the module has errors and avoid + // semantic analysis on it. + if(mod) + mod.errors = true; + return; + } if (!mod) return; // Failed diff --git a/dmd/dinterpret.d b/dmd/dinterpret.d index ff406296343..bdac73a8ae6 100644 --- a/dmd/dinterpret.d +++ b/dmd/dinterpret.d @@ -673,7 +673,7 @@ private Expression interpretFunction(UnionExp* pue, FuncDeclaration fd, InterSta e = CTFEExp.cantexp; break; } - e = interpret(pue, fd.fbody, &istatex); + e = interpretStatement(pue, fd.fbody, &istatex); if (CTFEExp.isCantExp(e)) { debug (LOG) @@ -769,21 +769,30 @@ void incUsageCtfe(InterState* istate, const ref Loc loc) } } -private extern (C++) final class Interpreter : Visitor +/*********************************** + * Interpret the statement. + * Params: + * s = Statement to interpret + * istate = context + * Returns: + * NULL continue to next statement + * EXP.cantExpression cannot interpret statement at compile time + * !NULL expression from return statement, or thrown exception + */ + +Expression interpretStatement(Statement s, InterState* istate) { - alias visit = Visitor.visit; -public: - InterState* istate; - CTFEGoal goal; - Expression result; - UnionExp* pue; // storage for `result` + UnionExp ue = void; + auto result = interpretStatement(&ue, s, istate); + if (result == ue.exp()) + result = ue.copy(); + return result; +} - extern (D) this(UnionExp* pue, InterState* istate, CTFEGoal goal) scope - { - this.pue = pue; - this.istate = istate; - this.goal = goal; - } +/// +Expression interpretStatement(UnionExp* pue, Statement s, InterState* istate) +{ + Expression result; // If e is EXP.throw_exception or EXP.cantExpression, // set it to 'result' and returns true. @@ -798,26 +807,13 @@ public: return false; } - static Expressions* copyArrayOnWrite(Expressions* exps, Expressions* original) - { - if (exps is original) - { - if (!original) - exps = new Expressions(); - else - exps = original.copy(); - ++ctfeGlobals.numArrayAllocs; - } - return exps; - } - /******************************** Statement ***************************/ - override void visit(Statement s) + void visitDefaultCase(Statement s) { debug (LOG) { - printf("%s Statement::interpret()\n", s.loc.toChars()); + printf("%s Statement::interpret() %s\n", s.loc.toChars(), s.toChars()); } if (istate.start) { @@ -830,7 +826,7 @@ public: result = CTFEExp.cantexp; } - override void visit(ExpStatement s) + void visitExp(ExpStatement s) { debug (LOG) { @@ -850,7 +846,12 @@ public: return; } - override void visit(CompoundStatement s) + void visitDtorExp(DtorExpStatement s) + { + visitExp(s); + } + + void visitCompound(CompoundStatement s) { debug (LOG) { @@ -863,7 +864,7 @@ public: foreach (i; 0 .. dim) { Statement sx = (*s.statements)[i]; - result = interpret(pue, sx, istate); + result = interpretStatement(pue, sx, istate); if (result) break; } @@ -873,7 +874,12 @@ public: } } - override void visit(UnrolledLoopStatement s) + void visitCompoundAsm(CompoundAsmStatement s) + { + visitCompound(s); + } + + void visitUnrolledLoop(UnrolledLoopStatement s) { debug (LOG) { @@ -886,7 +892,7 @@ public: foreach (i; 0 .. dim) { Statement sx = (*s.statements)[i]; - Expression e = interpret(pue, sx, istate); + Expression e = interpretStatement(pue, sx, istate); if (!e) // succeeds to interpret, or goto target was not found continue; if (exceptionOrCant(e)) @@ -919,7 +925,7 @@ public: } } - override void visit(IfStatement s) + void visitIf(IfStatement s) { debug (LOG) { @@ -931,9 +937,9 @@ public: if (istate.start) { Expression e = null; - e = interpret(s.ifbody, istate); + e = interpretStatement(s.ifbody, istate); if (!e && istate.start) - e = interpret(s.elsebody, istate); + e = interpretStatement(s.elsebody, istate); result = e; return; } @@ -945,9 +951,9 @@ public: return; if (isTrueBool(e)) - result = interpret(pue, s.ifbody, istate); + result = interpretStatement(pue, s.ifbody, istate); else if (e.toBool().hasValue(false)) - result = interpret(pue, s.elsebody, istate); + result = interpretStatement(pue, s.elsebody, istate); else { // no error, or assert(0)? @@ -955,7 +961,7 @@ public: } } - override void visit(ScopeStatement s) + void visitScope(ScopeStatement s) { debug (LOG) { @@ -964,78 +970,10 @@ public: if (istate.start == s) istate.start = null; - result = interpret(pue, s.statement, istate); - } - - /** - Given an expression e which is about to be returned from the current - function, generate an error if it contains pointers to local variables. - - Only checks expressions passed by value (pointers to local variables - may already be stored in members of classes, arrays, or AAs which - were passed as mutable function parameters). - Returns: - true if it is safe to return, false if an error was generated. - */ - static bool stopPointersEscaping(const ref Loc loc, Expression e) - { - if (!e.type.hasPointers()) - return true; - if (isPointer(e.type)) - { - Expression x = e; - if (auto eaddr = e.isAddrExp()) - x = eaddr.e1; - VarDeclaration v; - while (x.op == EXP.variable && (v = x.isVarExp().var.isVarDeclaration()) !is null) - { - if (v.storage_class & STC.ref_) - { - x = getValue(v); - if (auto eaddr = e.isAddrExp()) - eaddr.e1 = x; - continue; - } - if (ctfeGlobals.stack.isInCurrentFrame(v)) - { - error(loc, "returning a pointer to a local stack variable"); - return false; - } - else - break; - } - // TODO: If it is a EXP.dotVariable or EXP.index, we should check that it is not - // pointing to a local struct or static array. - } - if (auto se = e.isStructLiteralExp()) - { - return stopPointersEscapingFromArray(loc, se.elements); - } - if (auto ale = e.isArrayLiteralExp()) - { - return stopPointersEscapingFromArray(loc, ale.elements); - } - if (auto aae = e.isAssocArrayLiteralExp()) - { - if (!stopPointersEscapingFromArray(loc, aae.keys)) - return false; - return stopPointersEscapingFromArray(loc, aae.values); - } - return true; - } - - // Check all elements of an array for escaping local variables. Return false if error - static bool stopPointersEscapingFromArray(const ref Loc loc, Expressions* elems) - { - foreach (e; *elems) - { - if (e && !stopPointersEscaping(loc, e)) - return false; - } - return true; + result = interpretStatement(pue, s.statement, istate); } - override void visit(ReturnStatement s) + void visitReturn(ReturnStatement s) { debug (LOG) { @@ -1092,7 +1030,7 @@ public: if (isRuntimeHook(s.exp, Id._d_arrayappendT) || isRuntimeHook(s.exp, Id._d_arrayappendTTrace)) { auto rs = new ReturnStatement(s.loc, e); - rs.accept(this); + visitReturn(rs); return; } @@ -1113,20 +1051,7 @@ public: result = e; } - static Statement findGotoTarget(InterState* istate, Identifier ident) - { - Statement target = null; - if (ident) - { - LabelDsymbol label = istate.fd.searchLabel(ident); - assert(label && label.statement); - LabelStatement ls = label.statement; - target = ls.gotoTarget ? ls.gotoTarget : ls.statement; - } - return target; - } - - override void visit(BreakStatement s) + void visitBreak(BreakStatement s) { debug (LOG) { @@ -1144,7 +1069,7 @@ public: result = CTFEExp.breakexp; } - override void visit(ContinueStatement s) + void visitContinue(ContinueStatement s) { debug (LOG) { @@ -1162,7 +1087,7 @@ public: result = CTFEExp.continueexp; } - override void visit(WhileStatement s) + void visitWhile(WhileStatement s) { debug (LOG) { @@ -1171,7 +1096,7 @@ public: assert(0); // rewritten to ForStatement } - override void visit(DoStatement s) + void visitDo(DoStatement s) { debug (LOG) { @@ -1182,7 +1107,7 @@ public: while (1) { - Expression e = interpret(s._body, istate); + Expression e = interpretStatement(s._body, istate); if (!e && istate.start) // goto target was not found return; assert(!istate.start); @@ -1232,7 +1157,7 @@ public: assert(result is null); } - override void visit(ForStatement s) + void visitFor(ForStatement s) { debug (LOG) { @@ -1242,7 +1167,7 @@ public: istate.start = null; UnionExp ueinit = void; - Expression ei = interpret(&ueinit, s._init, istate); + Expression ei = interpretStatement(&ueinit, s._init, istate); if (exceptionOrCant(ei)) return; assert(!ei); // s.init never returns from function, or jumps out from it @@ -1261,7 +1186,7 @@ public: assert(isTrueBool(e)); } - Expression e = interpret(pue, s._body, istate); + Expression e = interpretStatement(pue, s._body, istate); if (!e && istate.start) // goto target was not found return; assert(!istate.start); @@ -1304,17 +1229,17 @@ public: assert(result is null); } - override void visit(ForeachStatement s) + void visitForeach(ForeachStatement s) { assert(0); // rewritten to ForStatement } - override void visit(ForeachRangeStatement s) + void visitForeachRange(ForeachRangeStatement s) { assert(0); // rewritten to ForStatement } - override void visit(SwitchStatement s) + void visitSwitch(SwitchStatement s) { debug (LOG) { @@ -1325,7 +1250,7 @@ public: istate.start = null; if (istate.start) { - Expression e = interpret(s._body, istate); + Expression e = interpretStatement(s._body, istate); if (istate.start) // goto target was not found return; if (exceptionOrCant(e)) @@ -1375,7 +1300,7 @@ public: /* Jump to scase */ istate.start = scase; - Expression e = interpret(pue, s._body, istate); + Expression e = interpretStatement(pue, s._body, istate); assert(!istate.start); // jump must not fail if (e && e.op == EXP.break_) { @@ -1390,7 +1315,7 @@ public: result = e; } - override void visit(CaseStatement s) + void visitCase(CaseStatement s) { debug (LOG) { @@ -1400,10 +1325,10 @@ public: if (istate.start == s) istate.start = null; - result = interpret(pue, s.statement, istate); + result = interpretStatement(pue, s.statement, istate); } - override void visit(DefaultStatement s) + void visitDefault(DefaultStatement s) { debug (LOG) { @@ -1413,10 +1338,10 @@ public: if (istate.start == s) istate.start = null; - result = interpret(pue, s.statement, istate); + result = interpretStatement(pue, s.statement, istate); } - override void visit(GotoStatement s) + void visitGoto(GotoStatement s) { debug (LOG) { @@ -1435,7 +1360,7 @@ public: result = CTFEExp.gotoexp; } - override void visit(GotoCaseStatement s) + void visitGotoCase(GotoCaseStatement s) { debug (LOG) { @@ -1454,7 +1379,7 @@ public: result = CTFEExp.gotoexp; } - override void visit(GotoDefaultStatement s) + void visitGotoDefault(GotoDefaultStatement s) { debug (LOG) { @@ -1473,7 +1398,7 @@ public: result = CTFEExp.gotoexp; } - override void visit(LabelStatement s) + void visitLabel(LabelStatement s) { debug (LOG) { @@ -1482,10 +1407,10 @@ public: if (istate.start == s) istate.start = null; - result = interpret(pue, s.statement, istate); + result = interpretStatement(pue, s.statement, istate); } - override void visit(TryCatchStatement s) + void visitTryCatch(TryCatchStatement s) { debug (LOG) { @@ -1496,18 +1421,18 @@ public: if (istate.start) { Expression e = null; - e = interpret(pue, s._body, istate); + e = interpretStatement(pue, s._body, istate); foreach (ca; *s.catches) { if (e || !istate.start) // goto target was found break; - e = interpret(pue, ca.handler, istate); + e = interpretStatement(pue, ca.handler, istate); } result = e; return; } - Expression e = interpret(s._body, istate); + Expression e = interpretStatement(s._body, istate); // An exception was thrown if (e && e.isThrownExceptionExp()) @@ -1528,7 +1453,7 @@ public: ctfeGlobals.stack.push(ca.var); setValue(ca.var, ex.thrown); } - e = interpret(ca.handler, istate); + e = interpretStatement(ca.handler, istate); if (CTFEExp.isGotoExp(e)) { /* This is an optimization that relies on the locality of the jump target. @@ -1540,7 +1465,7 @@ public: InterState istatex = *istate; istatex.start = istate.gotoTarget; // set starting statement istatex.gotoTarget = null; - Expression eh = interpret(ca.handler, &istatex); + Expression eh = interpretStatement(ca.handler, &istatex); if (!istatex.start) { istate.gotoTarget = null; @@ -1553,39 +1478,7 @@ public: result = e; } - static ThrownExceptionExp chainExceptions(ThrownExceptionExp oldest, ThrownExceptionExp newest) - { - debug (LOG) - { - printf("Collided exceptions %s %s\n", oldest.thrown.toChars(), newest.thrown.toChars()); - } - // Little sanity check to make sure it's really a Throwable - ClassReferenceExp boss = oldest.thrown; - const next = 5; // index of Throwable.next - assert((*boss.value.elements)[next].type.ty == Tclass); // Throwable.next - ClassReferenceExp collateral = newest.thrown; - if (collateral.originalClass().isErrorException() && !boss.originalClass().isErrorException()) - { - /* Find the index of the Error.bypassException field - */ - auto bypass = next + 1; - if ((*collateral.value.elements)[bypass].type.ty == Tuns32) - bypass += 1; // skip over _refcount field - assert((*collateral.value.elements)[bypass].type.ty == Tclass); - - // The new exception bypass the existing chain - (*collateral.value.elements)[bypass] = boss; - return newest; - } - while ((*boss.value.elements)[next].op == EXP.classReference) - { - boss = (*boss.value.elements)[next].isClassReferenceExp(); - } - (*boss.value.elements)[next] = collateral; - return oldest; - } - - override void visit(TryFinallyStatement s) + void visitTryFinally(TryFinallyStatement s) { debug (LOG) { @@ -1596,14 +1489,14 @@ public: if (istate.start) { Expression e = null; - e = interpret(pue, s._body, istate); + e = interpretStatement(pue, s._body, istate); // Jump into/out from finalbody is disabled in semantic analysis. // and jump inside will be handled by the ScopeStatement == finalbody. result = e; return; } - Expression ex = interpret(s._body, istate); + Expression ex = interpretStatement(s._body, istate); if (CTFEExp.isCantExp(ex)) { result = ex; @@ -1616,7 +1509,7 @@ public: InterState istatex = *istate; istatex.start = istate.gotoTarget; // set starting statement istatex.gotoTarget = null; - Expression bex = interpret(s._body, &istatex); + Expression bex = interpretStatement(s._body, &istatex); if (istatex.start) { // The goto target is outside the current scope. @@ -1632,7 +1525,7 @@ public: ex = bex; } - Expression ey = interpret(s.finalbody, istate); + Expression ey = interpretStatement(s.finalbody, istate); if (CTFEExp.isCantExp(ey)) { result = ey; @@ -1649,7 +1542,7 @@ public: result = ex; } - override void visit(ThrowStatement s) + void visitThrow(ThrowStatement s) { debug (LOG) { @@ -1662,35 +1555,15 @@ public: istate.start = null; } - interpretThrow(s.exp, s.loc); + interpretThrow(result, s.exp, s.loc, istate); } - /// Interpret `throw ` found at the specified location `loc` - private void interpretThrow(Expression exp, const ref Loc loc) - { - incUsageCtfe(istate, loc); - - Expression e = interpretRegion(exp, istate); - if (exceptionOrCant(e)) - return; - - if (e.op == EXP.classReference) - { - result = ctfeEmplaceExp!ThrownExceptionExp(loc, e.isClassReferenceExp()); - } - else - { - exp.error("to be thrown `%s` must be non-null", exp.toChars()); - result = ErrorExp.get(); - } - } - - override void visit(ScopeGuardStatement s) + void visitScopeGuard(ScopeGuardStatement s) { assert(0); } - override void visit(WithStatement s) + void visitWith(WithStatement s) { debug (LOG) { @@ -1700,14 +1573,14 @@ public: istate.start = null; if (istate.start) { - result = s._body ? interpret(s._body, istate) : null; + result = s._body ? interpretStatement(s._body, istate) : null; return; } // If it is with(Enum) {...}, just execute the body. if (s.exp.op == EXP.scope_ || s.exp.op == EXP.type) { - result = interpret(pue, s._body, istate); + result = interpretStatement(pue, s._body, istate); return; } @@ -1723,7 +1596,7 @@ public: } ctfeGlobals.stack.push(s.wthis); setValue(s.wthis, e); - e = interpret(s._body, istate); + e = interpretStatement(s._body, istate); if (CTFEExp.isGotoExp(e)) { /* This is an optimization that relies on the locality of the jump target. @@ -1735,7 +1608,7 @@ public: InterState istatex = *istate; istatex.start = istate.gotoTarget; // set starting statement istatex.gotoTarget = null; - Expression ex = interpret(s._body, &istatex); + Expression ex = interpretStatement(s._body, &istatex); if (!istatex.start) { istate.gotoTarget = null; @@ -1746,7 +1619,7 @@ public: result = e; } - override void visit(AsmStatement s) + void visitAsm(AsmStatement s) { debug (LOG) { @@ -1762,7 +1635,17 @@ public: result = CTFEExp.cantexp; } - override void visit(ImportStatement s) + void visitInlineAsm(InlineAsmStatement s) + { + visitAsm(s); + } + + void visitGccAsm(GccAsmStatement s) + { + visitAsm(s); + } + + void visitImport(ImportStatement s) { debug (LOG) { @@ -1776,6 +1659,45 @@ public: } } + if (!s) + return null; + + mixin VisitStatement!void visit; + visit.VisitStatement(s); + return result; +} + +/// + +private extern (C++) final class Interpreter : Visitor +{ + alias visit = Visitor.visit; +public: + InterState* istate; + CTFEGoal goal; + Expression result; + UnionExp* pue; // storage for `result` + + extern (D) this(UnionExp* pue, InterState* istate, CTFEGoal goal) scope + { + this.pue = pue; + this.istate = istate; + this.goal = goal; + } + + // If e is EXP.throw_exception or EXP.cantExpression, + // set it to 'result' and returns true. + bool exceptionOrCant(Expression e) + { + if (exceptionOrCantInterpret(e)) + { + // Make sure e is not pointing to a stack temporary + result = (e.op == EXP.cantExpression) ? CTFEExp.cantexp : e; + return true; + } + return false; + } + /******************************** Expression ***************************/ override void visit(Expression e) @@ -2550,7 +2472,7 @@ public: { debug (LOG) { - printf("%s ArrayLiteralExp::interpret() %s\n", e.loc.toChars(), e.toChars()); + printf("%s ArrayLiteralExp::interpret() %s, %s\n", e.loc.toChars(), e.type.toChars(), e.toChars()); } if (e.ownedByCtfe >= OwnedBy.ctfe) // We've already interpreted all the elements { @@ -2558,7 +2480,8 @@ public: return; } - Type tn = e.type.toBasetype().nextOf().toBasetype(); + Type tb = e.type.toBasetype(); + Type tn = tb.nextOf().toBasetype(); bool wantCopy = (tn.ty == Tsarray || tn.ty == Tstruct); auto basis = interpretRegion(e.basis, istate); @@ -2567,6 +2490,7 @@ public: auto expsx = e.elements; size_t dim = expsx ? expsx.length : 0; + for (size_t i = 0; i < dim; i++) { Expression exp = (*expsx)[i]; @@ -3998,7 +3922,7 @@ public: newval = copyLiteral(newval).copy(); assignInPlace(oldval, newval); } - else if (wantCopy && e.op == EXP.assign) + else if (wantCopy && (e.op == EXP.assign || e.op == EXP.loweredAssignExp)) { // Currently postblit/destructor calls on static array are done // in the druntime internal functions so they don't appear in AST. @@ -4080,6 +4004,8 @@ public: */ private Expression interpretAssignToSlice(UnionExp* pue, BinExp e, Expression e1, Expression newval, bool isBlockAssignment) { + //printf("interpretAssignToSlice(e: %s e1: %s newval: %s\n", e.toChars(), e1.toChars(), newval.toChars()); + dinteger_t lowerbound; dinteger_t upperbound; dinteger_t firstIndex; @@ -4139,7 +4065,7 @@ public: return newval; // For slice assignment, we check that the lengths match. - if (!isBlockAssignment) + if (!isBlockAssignment && e1.type.ty != Tpointer) { const srclen = resolveArrayLength(newval); if (srclen != (upperbound - lowerbound)) @@ -4354,8 +4280,8 @@ public: Expression assignTo(ArrayLiteralExp ae, size_t lwr, size_t upr) { Expressions* w = ae.elements; - assert(ae.type.ty == Tsarray || ae.type.ty == Tarray); - bool directblk = (cast(TypeArray)ae.type).next.equivalent(newval.type); + assert(ae.type.ty == Tsarray || ae.type.ty == Tarray || ae.type.ty == Tpointer); + bool directblk = (cast(TypeNext)ae.type).next.equivalent(newval.type); for (size_t k = lwr; k < upr; k++) { if (!directblk && (*w)[k].op == EXP.arrayLiteral) @@ -4405,7 +4331,7 @@ public: rb.newval = newval; rb.refCopy = wantRef || cow; rb.needsPostblit = sd && sd.postblit && e.op != EXP.blit && e.e2.isLvalue(); - rb.needsDtor = sd && sd.dtor && e.op == EXP.assign; + rb.needsDtor = sd && sd.dtor && (e.op == EXP.assign || e.op == EXP.loweredAssignExp); if (Expression ex = rb.assignTo(existingAE, cast(size_t)lowerbound, cast(size_t)upperbound)) return ex; @@ -4879,31 +4805,11 @@ public: result = CTFEExp.voidexp; return; } - else if (fd.ident == Id._d_arraysetlengthT) + else if (isArrayConstruction(fd.ident)) { - // In expressionsem.d `ea.length = eb;` got lowered to `_d_arraysetlengthT(ea, eb);`. - // The following code will rewrite it back to `ea.length = eb` and then interpret that expression. - assert(e.arguments.length == 2); - - Expression ea = (*e.arguments)[0]; - Expression eb = (*e.arguments)[1]; - - auto ale = ctfeEmplaceExp!ArrayLengthExp(e.loc, ea); - ale.type = Type.tsize_t; - AssignExp ae = ctfeEmplaceExp!AssignExp(e.loc, ale, eb); - ae.type = ea.type; - - // if (global.params.verbose) - // message("interpret %s =>\n %s", e.toChars(), ae.toChars()); - result = interpretRegion(ae, istate); - return; - } - else if (isArrayConstructionOrAssign(fd.ident)) - { - // In expressionsem.d, the following lowerings were performed: - // * `T[x] ea = eb;` to `_d_array{,set}ctor(ea[], eb[]);`. - // * `ea = eb` to `_d_array{,setassign,assign_l,assign_r}(ea[], eb)`. - // The following code will rewrite them back to `ea = eb` and + // In expressionsem.d, `T[x] ea = eb;` was lowered to: + // `_d_array{,set}ctor(ea[], eb[]);`. + // The following code will rewrite it back to `ea = eb` and // then interpret that expression. if (fd.ident == Id._d_arrayctor) @@ -4916,17 +4822,14 @@ public: ea = ea.isCastExp.e1; Expression eb = (*e.arguments)[1]; - if (eb.isCastExp() && fd.ident != Id._d_arraysetctor) + if (eb.isCastExp() && fd.ident == Id._d_arrayctor) eb = eb.isCastExp.e1; - Expression rewrittenExp; - if (fd.ident == Id._d_arrayctor || fd.ident == Id._d_arraysetctor) - rewrittenExp = new ConstructExp(e.loc, ea, eb); - else - rewrittenExp = new AssignExp(e.loc, ea, eb); + ConstructExp ce = new ConstructExp(e.loc, ea, eb); + ce.type = ea.type; - rewrittenExp.type = ea.type; - result = interpret(rewrittenExp, istate); + ce.type = ea.type; + result = interpret(ce, istate); return; } @@ -5878,7 +5781,25 @@ public: e2 = ue2.copy(); } - *pue = ctfeCat(e.loc, e.type, e1, e2); + Expression prepareCatOperand(Expression exp) + { + /* Convert `elem ~ array` to `[elem] ~ array` if `elem` is itself an + * array. This is needed because interpreting the `CatExp` calls + * `Cat()`, which cannot handle concatenations between different + * types, except for strings and chars. + */ + auto tb = e.type.toBasetype(); + auto tbNext = tb.nextOf(); + auto expTb = exp.type.toBasetype(); + + if (exp.type.implicitConvTo(tbNext) >= MATCH.convert && + (tb.ty == Tarray || tb.ty == Tsarray) && + (expTb.ty == Tarray || expTb.ty == Tsarray)) + return new ArrayLiteralExp(exp.loc, e.type, exp); + return exp; + } + + *pue = ctfeCat(e.loc, e.type, prepareCatOperand(e1), prepareCatOperand(e2)); result = pue.exp(); if (CTFEExp.isCantExp(result)) @@ -6239,15 +6160,18 @@ public: { printf("%s ThrowExpression::interpret()\n", te.loc.toChars()); } - interpretThrow(te.e1, te.loc); + interpretThrow(result, te.e1, te.loc, istate); } override void visit(PtrExp e) { + // Called for both lvalues and rvalues + const lvalue = goal == CTFEGoal.LValue; debug (LOG) { - printf("%s PtrExp::interpret() %s\n", e.loc.toChars(), e.toChars()); + printf("%s PtrExp::interpret(%d) %s, %s\n", e.loc.toChars(), lvalue, e.type.toChars(), e.toChars()); } + // Check for int<->float and long<->double casts. if (auto soe1 = e.e1.isSymOffExp()) if (soe1.offset == 0 && soe1.var.isVarDeclaration() && isFloatIntPaint(e.type, soe1.var.type)) @@ -6305,6 +6229,20 @@ public: return; } + if (!lvalue && result.isArrayLiteralExp() && + result.type.isTypePointer()) + { + /* A pointer variable can point to an array literal like `[3]`. + * Dereferencing it means accessing the first element value. + * Dereference it only if result should be an rvalue + */ + auto ae = result.isArrayLiteralExp(); + if (ae.elements.length == 1) + { + result = (*ae.elements)[0]; + return; + } + } if (result.isStringExp() || result.isArrayLiteralExp()) return; @@ -6545,33 +6483,57 @@ public: { assert(0); // This should never be interpreted } +} - /********************************************* - * Checks if the given expresion is a call to the runtime hook `id`. - * Params: - * e = the expression to check - * id = the identifier of the runtime hook - * Returns: - * `e` cast to `CallExp` if it's the hook, `null` otherwise - */ - private CallExp isRuntimeHook(Expression e, Identifier id) +/// Interpret `throw ` found at the specified location `loc` +private +void interpretThrow(ref Expression result, Expression exp, const ref Loc loc, InterState* istate) +{ + incUsageCtfe(istate, loc); + + Expression e = interpretRegion(exp, istate); + if (exceptionOrCantInterpret(e)) { - if (auto ce = e.isCallExp()) + // Make sure e is not pointing to a stack temporary + result = (e.op == EXP.cantExpression) ? CTFEExp.cantexp : e; + } + else if (e.op == EXP.classReference) + { + result = ctfeEmplaceExp!ThrownExceptionExp(loc, e.isClassReferenceExp()); + } + else + { + exp.error("to be thrown `%s` must be non-null", exp.toChars()); + result = ErrorExp.get(); + } +} + +/********************************************* + * Checks if the given expresion is a call to the runtime hook `id`. + * + * Params: + * e = the expression to check + * id = the identifier of the runtime hook + * Returns: + * `e` cast to `CallExp` if it's the hook, `null` otherwise + */ +public CallExp isRuntimeHook(Expression e, Identifier id) +{ + if (auto ce = e.isCallExp()) + { + if (auto ve = ce.e1.isVarExp()) { - if (auto ve = ce.e1.isVarExp()) + if (auto fd = ve.var.isFuncDeclaration()) { - if (auto fd = ve.var.isFuncDeclaration()) - { - // If `_d_HookTraceImpl` is found, resolve the underlying - // hook and replace `e` and `fd` with it. - removeHookTraceImpl(ce, fd); - return fd.ident == id ? ce : null; - } + // If `_d_HookTraceImpl` is found, resolve the underlying hook + // and replace `e` and `fd` with it. + removeHookTraceImpl(ce, fd); + return fd.ident == id ? ce : null; } } - - return null; } + + return null; } /******************************************** @@ -6589,10 +6551,12 @@ Expression interpret(UnionExp* pue, Expression e, InterState* istate, CTFEGoal g { if (!e) return null; + //printf("+interpret() e : %s, %s\n", e.type.toChars(), e.toChars()); scope Interpreter v = new Interpreter(pue, istate, goal); e.accept(v); Expression ex = v.result; assert(goal == CTFEGoal.Nothing || ex !is null); + //if (ex) printf("-interpret() ex: %s, %s\n", ex.type.toChars(), ex.toChars()); else printf("-interpret()\n"); return ex; } @@ -6639,34 +6603,135 @@ Expression interpretRegion(Expression e, InterState* istate, CTFEGoal goal = CTF return cast(Expression)memcpy(p, cast(void*)uexp, uexp.size); } -/*********************************** - * Interpret the statement. - * Params: - * pue = non-null pointer to temporary storage that can be used to store the return value - * s = Statement to interpret - * istate = context - * Returns: - * NULL continue to next statement - * EXP.cantExpression cannot interpret statement at compile time - * !NULL expression from return statement, or thrown exception +private +Expressions* copyArrayOnWrite(Expressions* exps, Expressions* original) +{ + if (exps is original) + { + if (!original) + exps = new Expressions(); + else + exps = original.copy(); + ++ctfeGlobals.numArrayAllocs; + } + return exps; +} + +/** + Given an expression e which is about to be returned from the current + function, generate an error if it contains pointers to local variables. + + Only checks expressions passed by value (pointers to local variables + may already be stored in members of classes, arrays, or AAs which + were passed as mutable function parameters). + Returns: + true if it is safe to return, false if an error was generated. */ -Expression interpret(UnionExp* pue, Statement s, InterState* istate) +private +bool stopPointersEscaping(const ref Loc loc, Expression e) { - if (!s) - return null; - scope Interpreter v = new Interpreter(pue, istate, CTFEGoal.Nothing); - s.accept(v); - return v.result; + if (!e.type.hasPointers()) + return true; + if (isPointer(e.type)) + { + Expression x = e; + if (auto eaddr = e.isAddrExp()) + x = eaddr.e1; + VarDeclaration v; + while (x.op == EXP.variable && (v = x.isVarExp().var.isVarDeclaration()) !is null) + { + if (v.storage_class & STC.ref_) + { + x = getValue(v); + if (auto eaddr = e.isAddrExp()) + eaddr.e1 = x; + continue; + } + if (ctfeGlobals.stack.isInCurrentFrame(v)) + { + error(loc, "returning a pointer to a local stack variable"); + return false; + } + else + break; + } + // TODO: If it is a EXP.dotVariable or EXP.index, we should check that it is not + // pointing to a local struct or static array. + } + if (auto se = e.isStructLiteralExp()) + { + return stopPointersEscapingFromArray(loc, se.elements); + } + if (auto ale = e.isArrayLiteralExp()) + { + return stopPointersEscapingFromArray(loc, ale.elements); + } + if (auto aae = e.isAssocArrayLiteralExp()) + { + if (!stopPointersEscapingFromArray(loc, aae.keys)) + return false; + return stopPointersEscapingFromArray(loc, aae.values); + } + return true; } -/// -Expression interpret(Statement s, InterState* istate) +// Check all elements of an array for escaping local variables. Return false if error +private +bool stopPointersEscapingFromArray(const ref Loc loc, Expressions* elems) { - UnionExp ue = void; - auto result = interpret(&ue, s, istate); - if (result == ue.exp()) - result = ue.copy(); - return result; + foreach (e; *elems) + { + if (e && !stopPointersEscaping(loc, e)) + return false; + } + return true; +} + +private +Statement findGotoTarget(InterState* istate, Identifier ident) +{ + Statement target = null; + if (ident) + { + LabelDsymbol label = istate.fd.searchLabel(ident); + assert(label && label.statement); + LabelStatement ls = label.statement; + target = ls.gotoTarget ? ls.gotoTarget : ls.statement; + } + return target; +} + +private +ThrownExceptionExp chainExceptions(ThrownExceptionExp oldest, ThrownExceptionExp newest) +{ + debug (LOG) + { + printf("Collided exceptions %s %s\n", oldest.thrown.toChars(), newest.thrown.toChars()); + } + // Little sanity check to make sure it's really a Throwable + ClassReferenceExp boss = oldest.thrown; + const next = 5; // index of Throwable.next + assert((*boss.value.elements)[next].type.ty == Tclass); // Throwable.next + ClassReferenceExp collateral = newest.thrown; + if (collateral.originalClass().isErrorException() && !boss.originalClass().isErrorException()) + { + /* Find the index of the Error.bypassException field + */ + auto bypass = next + 1; + if ((*collateral.value.elements)[bypass].type.ty == Tuns32) + bypass += 1; // skip over _refcount field + assert((*collateral.value.elements)[bypass].type.ty == Tclass); + + // The new exception bypass the existing chain + (*collateral.value.elements)[bypass] = boss; + return newest; + } + while ((*boss.value.elements)[next].op == EXP.classReference) + { + boss = (*boss.value.elements)[next].isClassReferenceExp(); + } + (*boss.value.elements)[next] = collateral; + return oldest; } /** @@ -7009,7 +7074,6 @@ private Expression copyRegionExp(Expression e) case EXP.null_: case EXP.void_: case EXP.symbolOffset: - case EXP.char_: break; case EXP.cantExpression: diff --git a/dmd/dmodule.d b/dmd/dmodule.d index 4bd20cf1101..58b5b1ff3eb 100644 --- a/dmd/dmodule.d +++ b/dmd/dmodule.d @@ -744,7 +744,14 @@ else scope (exit) { if (ifile) + { File.remove(filename.toChars()); // remove generated file +version (IN_LLVM) +{ + // and the parent directory for LDC (each .i gets its own temp dir) + File.removeDirectory(FileName.path(filename.toChars())); +} + } } if (global.preprocess && @@ -828,7 +835,7 @@ else { filetype = FileType.c; - scope p = new CParser!AST(this, buf, cast(bool) docfile, global.errorSink, target.c, &defines); + scope p = new CParser!AST(this, buf, cast(bool) docfile, global.errorSink, target.c, &defines, &global.compileEnv); p.nextToken(); checkCompiledImport(); members = p.parseModule(); @@ -837,7 +844,9 @@ else } else { - scope p = new Parser!AST(this, buf, cast(bool) docfile, global.errorSink); + const bool doUnittests = global.params.useUnitTests || global.params.ddoc.doOutput || global.params.dihdr.doOutput; + scope p = new Parser!AST(this, buf, cast(bool) docfile, global.errorSink, &global.compileEnv, doUnittests); + p.transitionIn = global.params.vin; p.nextToken(); p.parseModuleDeclaration(); md = p.md; @@ -1290,8 +1299,7 @@ else return this.importedFrom == this; } - // true if the module source file is directly - // listed in command line. + /// Returns: Whether this module is in the `core` package and has name `ident` bool isCoreModule(Identifier ident) nothrow { return this.ident == ident && parent && parent.ident == Id.core && !parent.parent; @@ -1371,6 +1379,20 @@ else return _escapetable; } + /**************************** + * A Singleton that loads core.stdc.config + * Returns: + * Module of core.stdc.config, null if couldn't find it + */ + extern (D) static Module loadCoreStdcConfig() + { + __gshared Module core_stdc_config; + auto pkgids = new Identifier[2]; + pkgids[0] = Id.core; + pkgids[1] = Id.stdc; + return loadModuleFromLibrary(core_stdc_config, pkgids, Id.config); + } + /**************************** * A Singleton that loads core.atomic * Returns: @@ -1379,7 +1401,9 @@ else extern (D) static Module loadCoreAtomic() { __gshared Module core_atomic; - return loadModuleFromLibrary(core_atomic, Id.core, Id.atomic); + auto pkgids = new Identifier[1]; + pkgids[0] = Id.core; + return loadModuleFromLibrary(core_atomic, pkgids, Id.atomic); } /**************************** @@ -1390,26 +1414,26 @@ else extern (D) static Module loadStdMath() { __gshared Module std_math; - return loadModuleFromLibrary(std_math, Id.std, Id.math); + auto pkgids = new Identifier[1]; + pkgids[0] = Id.std; + return loadModuleFromLibrary(std_math, pkgids, Id.math); } /********************************** * Load a Module from the library. * Params: * mod = cached return value of this call - * pkgid = package id + * pkgids = package identifiers * modid = module id * Returns: * Module loaded, null if cannot load it */ - private static Module loadModuleFromLibrary(ref Module mod, Identifier pkgid, Identifier modid) + extern (D) private static Module loadModuleFromLibrary(ref Module mod, Identifier[] pkgids, Identifier modid) { if (mod) return mod; - auto ids = new Identifier[1]; - ids[0] = pkgid; - auto imp = new Import(Loc.initial, ids[], modid, null, true); + auto imp = new Import(Loc.initial, pkgids[], modid, null, true); // Module.load will call fatal() if there's no module available. // Gag the error here, pushing the error handling to the caller. const errors = global.startGagging(); diff --git a/dmd/doc.d b/dmd/doc.d index 65c917909f2..1b54844b206 100644 --- a/dmd/doc.d +++ b/dmd/doc.d @@ -5188,7 +5188,7 @@ private void highlightCode2(Scope* sc, Dsymbols* a, ref OutBuffer buf, size_t of scope Lexer lex = new Lexer(null, cast(char*)buf[].ptr, 0, buf.length - 1, 0, 1, global.errorSink, - global.vendor, global.versionNumber()); + &global.compileEnv); OutBuffer res; const(char)* lastp = cast(char*)buf[].ptr; //printf("highlightCode2('%.*s')\n", cast(int)(buf.length - 1), buf[].ptr); diff --git a/dmd/dscope.d b/dmd/dscope.d index a0a80b27264..749c9f1454c 100644 --- a/dmd/dscope.d +++ b/dmd/dscope.d @@ -818,4 +818,14 @@ version (IN_LLVM) { return this.intypeof || this.flags & SCOPE.compile; } + + + /** + * Returns: true if the code needs to go all the way through to code generation. + * This implies things like needing lowering to simpler forms. + */ + extern (D) bool needsCodegen() + { + return (flags & (SCOPE.ctfe | SCOPE.ctfeBlock | SCOPE.compile)) == 0; + } } diff --git a/dmd/dstruct.d b/dmd/dstruct.d index bb5ecbbdff6..dfda7836613 100644 --- a/dmd/dstruct.d +++ b/dmd/dstruct.d @@ -13,6 +13,8 @@ module dmd.dstruct; +import core.stdc.stdio; + import dmd.aggregate; import dmd.arraytypes; import dmd.astenums; @@ -75,7 +77,7 @@ extern (C++) void semanticTypeInfo(Scope* sc, Type t) { if (sc.intypeof) return; - if (sc.flags & (SCOPE.ctfe | SCOPE.compile | SCOPE.ctfeBlock)) + if (!sc.needsCodegen()) return; } @@ -576,7 +578,7 @@ version (IN_LLVM) {} else * Returns: * true if it's all binary 0 */ -private bool _isZeroInit(Expression exp) +bool _isZeroInit(Expression exp) { switch (exp.op) { @@ -584,20 +586,22 @@ private bool _isZeroInit(Expression exp) return exp.toInteger() == 0; case EXP.null_: - case EXP.false_: return true; case EXP.structLiteral: { - auto sle = cast(StructLiteralExp) exp; + auto sle = exp.isStructLiteralExp(); + if (sle.sd.isNested()) + return false; + const isCstruct = sle.sd.isCsymbol(); // C structs are default initialized to all zeros foreach (i; 0 .. sle.sd.fields.length) { auto field = sle.sd.fields[i]; if (field.type.size(field.loc)) { - auto e = (*sle.elements)[i]; + auto e = sle.elements && i < sle.elements.length ? (*sle.elements)[i] : null; if (e ? !_isZeroInit(e) - : !field.type.isZeroInit(field.loc)) + : !isCstruct && !field.type.isZeroInit(field.loc)) return false; } } diff --git a/dmd/dsymbol.d b/dmd/dsymbol.d index a9239be6d0c..c91327c4da1 100644 --- a/dmd/dsymbol.d +++ b/dmd/dsymbol.d @@ -1443,7 +1443,7 @@ version (IN_LLVM) inout(CPPNamespaceDeclaration) isCPPNamespaceDeclaration() inout { return null; } inout(VisibilityDeclaration) isVisibilityDeclaration() inout { return null; } inout(OverloadSet) isOverloadSet() inout { return null; } - inout(CompileDeclaration) isCompileDeclaration() inout { return null; } + inout(MixinDeclaration) isMixinDeclaration() inout { return null; } inout(StaticAssert) isStaticAssert() inout { return null; } inout(StaticIfDeclaration) isStaticIfDeclaration() inout { return null; } } diff --git a/dmd/dsymbol.h b/dmd/dsymbol.h index 0e6bb72b439..81442d69731 100644 --- a/dmd/dsymbol.h +++ b/dmd/dsymbol.h @@ -174,7 +174,7 @@ struct FieldState unsigned fieldAlign; unsigned bitOffset; - bool inFlight; + d_bool inFlight; }; struct DsymbolAttributes; @@ -196,7 +196,7 @@ class Dsymbol : public ASTNode private: DsymbolAttributes* atts; public: - bool errors; // this symbol failed to pass semantic() + d_bool errors; // this symbol failed to pass semantic() PASS semanticRun; unsigned short localNum; // perturb mangled name to avoid collisions with those in FuncDeclaration.localsymtab static Dsymbol *create(Identifier *); @@ -328,7 +328,7 @@ class Dsymbol : public ASTNode virtual CPPNamespaceDeclaration *isCPPNamespaceDeclaration() { return NULL; } virtual VisibilityDeclaration *isVisibilityDeclaration() { return NULL; } virtual OverloadSet *isOverloadSet() { return NULL; } - virtual CompileDeclaration *isCompileDeclaration() { return NULL; } + virtual MixinDeclaration *isMixinDeclaration() { return NULL; } virtual StaticAssert *isStaticAssert() { return NULL; } virtual StaticIfDeclaration *isStaticIfDeclaration() { return NULL; } void accept(Visitor *v) override { v->visit(this); } diff --git a/dmd/dsymbolsem.d b/dmd/dsymbolsem.d index dd92286c88c..53c2c39fbec 100644 --- a/dmd/dsymbolsem.d +++ b/dmd/dsymbolsem.d @@ -494,7 +494,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor // Infering the type requires running semantic, // so mark the scope as ctfe if required - bool needctfe = (dsym.storage_class & (STC.manifest | STC.static_)) != 0; + bool needctfe = (dsym.storage_class & (STC.manifest | STC.static_)) != 0 || !sc.func; if (needctfe) { sc.flags |= SCOPE.condition; @@ -1345,6 +1345,9 @@ version (IN_LLVM) if (dsym.errors) return; + if (!(global.params.bitfields || sc.flags & SCOPE.Cfile)) + dsym.error("use -preview=bitfields for bitfield support"); + if (!dsym.parent.isStructDeclaration() && !dsym.parent.isClassDeclaration()) { dsym.error("- bit-field must be member of struct, union, or class"); @@ -1387,9 +1390,9 @@ version (IN_LLVM) { static if (LOG) { - printf("Import::semantic('%s') %s\n", toPrettyChars(), id.toChars()); + printf("Import::semantic('%s') %s\n", imp.toPrettyChars(), imp.id.toChars()); scope(exit) - printf("-Import::semantic('%s'), pkg = %p\n", toChars(), pkg); + printf("-Import::semantic('%s'), pkg = %p\n", imp.toChars(), imp.pkg); } if (imp.semanticRun > PASS.initial) return; @@ -1455,7 +1458,9 @@ version (IN_LLVM) imp.addPackageAccess(scopesym); } - imp.mod.dsymbolSemantic(null); + // if a module has errors it means that parsing has failed. + if (!imp.mod.errors) + imp.mod.dsymbolSemantic(null); if (imp.mod.needmoduleinfo) { @@ -1994,9 +1999,9 @@ else // !IN_LLVM attribSemantic(sfd); } - private Dsymbols* compileIt(CompileDeclaration cd) + private Dsymbols* compileIt(MixinDeclaration cd) { - //printf("CompileDeclaration::compileIt(loc = %d) %s\n", cd.loc.linnum, cd.exp.toChars()); + //printf("MixinDeclaration::compileIt(loc = %d) %s\n", cd.loc.linnum, cd.exp.toChars()); OutBuffer buf; if (expressionsToString(buf, sc, cd.exps)) return null; @@ -2005,7 +2010,10 @@ else // !IN_LLVM const len = buf.length; buf.writeByte(0); const str = buf.extractSlice()[0 .. len]; - scope p = new Parser!ASTCodegen(cd.loc, sc._module, str, false, global.errorSink); + const bool doUnittests = global.params.useUnitTests || global.params.ddoc.doOutput || global.params.dihdr.doOutput; + 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.vin; p.nextToken(); auto d = p.parseDeclDefs(0); @@ -2023,9 +2031,9 @@ else // !IN_LLVM /*********************************************************** * https://dlang.org/spec/module.html#mixin-declaration */ - override void visit(CompileDeclaration cd) + override void visit(MixinDeclaration cd) { - //printf("CompileDeclaration::semantic()\n"); + //printf("MixinDeclaration::semantic()\n"); if (!cd.compiled) { cd.decl = compileIt(cd); @@ -2323,32 +2331,39 @@ else // !IN_LLVM { /* C11 6.7.2.2 */ - assert(ed.memtype); - int nextValue = 0; // C11 6.7.2.2-3 first member value defaults to 0 + Type commonType = ed.memtype; + if (!commonType) + commonType = Type.tint32; + ulong nextValue = 0; // C11 6.7.2.2-3 first member value defaults to 0 // C11 6.7.2.2-2 value must be representable as an int. // The sizemask represents all values that int will fit into, // from 0..uint.max. We want to cover int.min..uint.max. - const mask = Type.tint32.sizemask(); - IntRange ir = IntRange(SignExtendedNumber(~(mask >> 1), true), - SignExtendedNumber(mask)); + IntRange ir = IntRange.fromType(commonType); - void emSemantic(EnumMember em, ref int nextValue) + void emSemantic(EnumMember em, ref ulong nextValue) { static void errorReturn(EnumMember em) { + em.value = ErrorExp.get(); em.errors = true; em.semanticRun = PASS.semanticdone; } em.semanticRun = PASS.semantic; - em.type = Type.tint32; + em.type = commonType; em._linkage = LINK.c; em.storage_class |= STC.manifest; if (em.value) { Expression e = em.value; assert(e.dyncast() == DYNCAST.expression); + + /* To merge the type of e with commonType, add 0 of type commonType + */ + if (!ed.memtype) + e = new AddExp(em.loc, e, new IntegerExp(em.loc, 0, commonType)); + e = e.expressionSemantic(sc); e = resolveProperties(sc, e); e = e.integralPromotions(sc); @@ -2362,14 +2377,16 @@ else // !IN_LLVM em.error("enum member must be an integral constant expression, not `%s` of type `%s`", e.toChars(), e.type.toChars()); return errorReturn(em); } - if (!ir.contains(getIntRange(ie))) + if (ed.memtype && !ir.contains(getIntRange(ie))) { // C11 6.7.2.2-2 - em.error("enum member value `%s` does not fit in an `int`", e.toChars()); + em.error("enum member value `%s` does not fit in `%s`", e.toChars(), commonType.toChars()); return errorReturn(em); } - nextValue = cast(int)ie.toInteger(); - em.value = new IntegerExp(em.loc, nextValue, Type.tint32); + nextValue = ie.toInteger(); + if (!ed.memtype) + commonType = e.type; + em.value = new IntegerExp(em.loc, nextValue, commonType); } else { @@ -2377,17 +2394,17 @@ else // !IN_LLVM bool first = (em == (*em.ed.members)[0]); if (!first) { - import core.checkedint : adds; - bool overflow; - nextValue = adds(nextValue, 1, overflow); - if (overflow) + Expression max = getProperty(commonType, null, em.loc, Id.max, 0); + if (nextValue == max.toInteger()) { - em.error("initialization with `%d+1` causes overflow for type `int`", nextValue - 1); + em.error("initialization with `%s+1` causes overflow for type `%s`", max.toChars(), commonType.toChars()); return errorReturn(em); } + nextValue += 1; } - em.value = new IntegerExp(em.loc, nextValue, Type.tint32); + em.value = new IntegerExp(em.loc, nextValue, commonType); } + em.type = commonType; em.semanticRun = PASS.semanticdone; } @@ -2396,6 +2413,21 @@ else // !IN_LLVM if (EnumMember em = s.isEnumMember()) emSemantic(em, nextValue); }); + + if (!ed.memtype) + { + // cast all members to commonType + ed.members.foreachDsymbol( (s) + { + if (EnumMember em = s.isEnumMember()) + { + em.type = commonType; + em.value = em.value.castTo(sc, commonType); + } + }); + } + + ed.memtype = commonType; ed.semanticRun = PASS.semanticdone; return; } @@ -4742,7 +4774,8 @@ version (IN_LLVM) { sd.visibility = sc.visibility; - sd.alignment = sc.alignment(); + if (sd.alignment.isUnknown()) // can be set already by `struct __declspec(align(N)) Tag { ... }` + sd.alignment = sc.alignment(); sd.storage_class |= sc.stc; if (sd.storage_class & STC.abstract_) @@ -5939,6 +5972,31 @@ void addEnumMembers(EnumDeclaration ed, Scope* sc, ScopeDsymbol sds) }); } +/****************************************************** + * Verifies if the given Identifier is a DRuntime hook. It uses the hooks + * defined in `id.d`. + * + * Params: + * id = Identifier to verify + * Returns: + * true if `id` is a DRuntime hook + * false otherwise + */ +private bool isDRuntimeHook(Identifier id) +{ + return id == Id._d_HookTraceImpl || + id == Id._d_newclassT || id == Id._d_newclassTTrace || + id == Id._d_arraycatnTX || id == Id._d_arraycatnTXTrace || + id == Id._d_newThrowable || id == Id._d_delThrowable || + id == Id._d_arrayassign_l || id == Id._d_arrayassign_r || + id == Id._d_arraysetassign || id == Id._d_arraysetctor || + id == Id._d_arrayctor || + id == Id._d_arraysetlengthTImpl || id == Id._d_arraysetlengthT || + id == Id._d_arraysetlengthTTrace || + id == Id._d_arrayappendT || id == Id._d_arrayappendTTrace || + id == Id._d_arrayappendcTXImpl; +} + void templateInstanceSemantic(TemplateInstance tempinst, Scope* sc, ArgumentList argumentList) { //printf("[%s] TemplateInstance.dsymbolSemantic('%s', this=%p, gag = %d, sc = %p)\n", tempinst.loc.toChars(), tempinst.toChars(), tempinst, global.gag, sc); @@ -6440,8 +6498,20 @@ version (IN_LLVM) tempinst.deferred = &deferred; //printf("Run semantic3 on %s\n", toChars()); + + /* https://issues.dlang.org/show_bug.cgi?id=23965 + * DRuntime hooks are not deprecated, but may be used for deprecated + * types. Deprecations are disabled while analysing hooks to avoid + * spurious error messages. + */ + auto saveUseDeprecated = global.params.useDeprecated; + if (sc.isDeprecated() && isDRuntimeHook(tempinst.name)) + global.params.useDeprecated = DiagnosticReporting.off; + tempinst.trySemantic3(sc2); + global.params.useDeprecated = saveUseDeprecated; + for (size_t i = 0; i < deferred.length; i++) { //printf("+ run deferred semantic3 on %s\n", deferred[i].toChars()); @@ -7330,3 +7400,83 @@ PINLINE evalPragmaInline(Loc loc, Scope* sc, Expressions* args) else return PINLINE.never; } + +/*************************************************** + * Set up loc for a parse of a mixin. Append the input text to the mixin. + * Params: + * input = mixin text + * loc = 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) +{ + 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); + } + else if (loc.filename) + { + /* 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); + } + else + result = loc; + return result; +} + +/************************************** + * Append source code text to output for better debugging. + * Canonicalize line endings. + * Params: + * s = source code text + * loc = location of source code text + * 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) +{ + buf.writestring("// expansion at "); + buf.writestring(loc.toChars()); + buf.writenl(); + + ++lines; + + // write by line to create consistent line endings + size_t lastpos = 0; + for (size_t i = 0; i < s.length; ++i) + { + // detect LF and CRLF + const c = s[i]; + if (c == '\n' || (c == '\r' && i+1 < s.length && s[i+1] == '\n')) + { + buf.writestring(s[lastpos .. i]); + buf.writenl(); + ++lines; + if (c == '\r') + ++i; + lastpos = i + 1; + } + } + + if(lastpos < s.length) + buf.writestring(s[lastpos .. $]); + + if (s.length == 0 || s[$-1] != '\n') + { + buf.writenl(); // ensure empty line after expansion + ++lines; + } + buf.writenl(); + ++lines; +} diff --git a/dmd/dtemplate.d b/dmd/dtemplate.d index a4e9d017bbd..e22d6ced137 100644 --- a/dmd/dtemplate.d +++ b/dmd/dtemplate.d @@ -757,9 +757,6 @@ else const(char)* toCharsMaybeConstraints(bool includeConstraints) const { - if (literal) - return Dsymbol.toChars(); - OutBuffer buf; HdrGenState hgs; @@ -1874,19 +1871,16 @@ else Type taai; if (argtype.ty == Tarray && (prmtype.ty == Tsarray || prmtype.ty == Taarray && (taai = (cast(TypeAArray)prmtype).index).ty == Tident && (cast(TypeIdentifier)taai).idents.length == 0)) { - if (farg.op == EXP.string_) + if (StringExp se = farg.isStringExp()) { - StringExp se = cast(StringExp)farg; argtype = se.type.nextOf().sarrayOf(se.len); } - else if (farg.op == EXP.arrayLiteral) + else if (ArrayLiteralExp ae = farg.isArrayLiteralExp()) { - ArrayLiteralExp ae = cast(ArrayLiteralExp)farg; argtype = ae.type.nextOf().sarrayOf(ae.elements.length); } - else if (farg.op == EXP.slice) + else if (SliceExp se = farg.isSliceExp()) { - SliceExp se = cast(SliceExp)farg; if (Type tsa = toStaticArrayType(se)) argtype = tsa; } @@ -2299,18 +2293,23 @@ else Declaration d; VarDeclaration v = null; - if (ea && ea.op == EXP.type) - ta = ea.type; - else if (ea && ea.op == EXP.scope_) - sa = (cast(ScopeExp)ea).sds; - else if (ea && (ea.op == EXP.this_ || ea.op == EXP.super_)) - sa = (cast(ThisExp)ea).var; - else if (ea && ea.op == EXP.function_) + if (ea) { - if ((cast(FuncExp)ea).td) - sa = (cast(FuncExp)ea).td; - else - sa = (cast(FuncExp)ea).fd; + if (ea.op == EXP.type) + ta = ea.type; + else if (auto se = ea.isScopeExp()) + sa = se.sds; + else if (auto te = ea.isThisExp()) + sa = te.var; + else if (auto se = ea.isSuperExp()) + sa = se.var; + else if (auto fe = ea.isFuncExp()) + { + if (fe.td) + sa = fe.td; + else + sa = fe.fd; + } } if (ta) @@ -3872,9 +3871,9 @@ MATCH deduceType(RootObject o, Scope* sc, Type tparam, TemplateParameters* param if (tparam.ty == Tsarray) { TypeSArray tsa = cast(TypeSArray)tparam; - if (tsa.dim.op == EXP.variable && (cast(VarExp)tsa.dim).var.storage_class & STC.templateparameter) + if (tsa.dim.isVarExp() && tsa.dim.isVarExp().var.storage_class & STC.templateparameter) { - Identifier id = (cast(VarExp)tsa.dim).var.ident; + Identifier id = tsa.dim.isVarExp().var.ident; i = templateIdentifierLookup(id, parameters); assert(i != IDX_NOTFOUND); tp = (*parameters)[i]; @@ -4266,12 +4265,12 @@ MATCH deduceType(RootObject o, Scope* sc, Type tparam, TemplateParameters* param /* If it is one of the template parameters for this template, * we should not attempt to interpret it. It already has a value. */ - if (e2.op == EXP.variable && ((cast(VarExp)e2).var.storage_class & STC.templateparameter)) + if (e2.op == EXP.variable && (e2.isVarExp().var.storage_class & STC.templateparameter)) { /* * (T:Number!(e2), int e2) */ - j = templateIdentifierLookup((cast(VarExp)e2).var.ident, parameters); + j = templateIdentifierLookup(e2.isVarExp().var.ident, parameters); if (j != IDX_NOTFOUND) goto L1; // The template parameter was not from this template @@ -5252,7 +5251,7 @@ private bool reliesOnTemplateParameters(Expression e, TemplateParameter[] tparam override void visit(DotTemplateInstanceExp e) { //printf("DotTemplateInstanceExp.reliesOnTemplateParameters('%s')\n", e.toChars()); - visit(cast(UnaExp)e); + visit(e.isUnaExp()); if (!result && e.ti.tiargs) { foreach (oa; *e.ti.tiargs) @@ -5270,7 +5269,7 @@ private bool reliesOnTemplateParameters(Expression e, TemplateParameter[] tparam override void visit(CallExp e) { //printf("CallExp.reliesOnTemplateParameters('%s')\n", e.toChars()); - visit(cast(UnaExp)e); + visit(e.isUnaExp()); if (!result && e.arguments) { foreach (ea; *e.arguments) @@ -5285,7 +5284,7 @@ private bool reliesOnTemplateParameters(Expression e, TemplateParameter[] tparam override void visit(CastExp e) { //printf("CallExp.reliesOnTemplateParameters('%s')\n", e.toChars()); - visit(cast(UnaExp)e); + visit(e.isUnaExp()); // e.to can be null for cast() with no type if (!result && e.to) result = e.to.reliesOnTemplateParameters(tparams); @@ -5294,7 +5293,7 @@ private bool reliesOnTemplateParameters(Expression e, TemplateParameter[] tparam override void visit(SliceExp e) { //printf("SliceExp.reliesOnTemplateParameters('%s')\n", e.toChars()); - visit(cast(UnaExp)e); + visit(e.isUnaExp()); if (!result && e.lwr) e.lwr.accept(this); if (!result && e.upr) @@ -5312,7 +5311,7 @@ private bool reliesOnTemplateParameters(Expression e, TemplateParameter[] tparam override void visit(ArrayExp e) { //printf("ArrayExp.reliesOnTemplateParameters('%s')\n", e.toChars()); - visit(cast(UnaExp)e); + visit(e.isUnaExp()); if (!result && e.arguments) { foreach (ea; *e.arguments) @@ -5333,7 +5332,7 @@ private bool reliesOnTemplateParameters(Expression e, TemplateParameter[] tparam //printf("BinExp.reliesOnTemplateParameters('%s')\n", e.toChars()); e.econd.accept(this); if (!result) - visit(cast(BinExp)e); + visit(e.isBinExp()); } } @@ -6757,7 +6756,7 @@ version (IN_LLVM) ea = ea.expressionSemantic(sc); // must not interpret the args, excepting template parameters - if (ea.op != EXP.variable || ((cast(VarExp)ea).var.storage_class & STC.templateparameter)) + if (!ea.isVarExp() || (ea.isVarExp().var.storage_class & STC.templateparameter)) { ea = ea.optimize(WANTvalue); } @@ -6768,13 +6767,13 @@ version (IN_LLVM) ea = ea.expressionSemantic(sc); sc = sc.endCTFE(); - if (ea.op == EXP.variable) + if (auto varExp = ea.isVarExp()) { /* If the parameter is a function that is not called * explicitly, i.e. `foo!func` as opposed to `foo!func()`, * then it is a dsymbol, not the return value of `func()` */ - Declaration vd = (cast(VarExp)ea).var; + Declaration vd = varExp.var; if (auto fd = vd.isFuncDeclaration()) { sa = fd; @@ -6797,10 +6796,9 @@ version (IN_LLVM) } } //printf("-[%d] ea = %s %s\n", j, EXPtoString(ea.op).ptr, ea.toChars()); - if (ea.op == EXP.tuple) + if (TupleExp te = ea.isTupleExp()) { // Expand tuple - TupleExp te = cast(TupleExp)ea; size_t dim = te.exps.length; tiargs.remove(j); if (dim) @@ -6826,12 +6824,11 @@ version (IN_LLVM) } if (ea.op == EXP.scope_) { - sa = (cast(ScopeExp)ea).sds; + sa = ea.isScopeExp().sds; goto Ldsym; } - if (ea.op == EXP.function_) + if (FuncExp fe = ea.isFuncExp()) { - FuncExp fe = cast(FuncExp)ea; /* A function literal, that is passed to template and * already semanticed as function pointer, never requires * outer frame. So convert it to global function is valid. @@ -6853,23 +6850,23 @@ version (IN_LLVM) if (ea.op == EXP.dotVariable && !(flags & 1)) { // translate expression to dsymbol. - sa = (cast(DotVarExp)ea).var; + sa = ea.isDotVarExp().var; goto Ldsym; } - if (ea.op == EXP.template_) + if (auto te = ea.isTemplateExp()) { - sa = (cast(TemplateExp)ea).td; + sa = te.td; goto Ldsym; } if (ea.op == EXP.dotTemplateDeclaration && !(flags & 1)) { // translate expression to dsymbol. - sa = (cast(DotTemplateExp)ea).td; + sa = ea.isDotTemplateExp().td; goto Ldsym; } - if (ea.op == EXP.dot) + if (auto de = ea.isDotExp()) { - if (auto se = (cast(DotExp)ea).e2.isScopeExp()) + if (auto se = de.e2.isScopeExp()) { sa = se.sds; goto Ldsym; @@ -7370,22 +7367,22 @@ version (IN_LLVM) Tuple va = isTuple(o); if (ea) { - if (ea.op == EXP.variable) + if (auto ve = ea.isVarExp()) { - sa = (cast(VarExp)ea).var; + sa = ve.var; goto Lsa; } - if (ea.op == EXP.this_) + if (auto te = ea.isThisExp()) { - sa = (cast(ThisExp)ea).var; + sa = te.var; goto Lsa; } - if (ea.op == EXP.function_) + if (auto fe = ea.isFuncExp()) { - if ((cast(FuncExp)ea).td) - sa = (cast(FuncExp)ea).td; + if (fe.td) + sa = fe.td; else - sa = (cast(FuncExp)ea).fd; + sa = fe.fd; goto Lsa; } // Emulate Expression.toMangleBuffer call that had exist in TemplateInstance.genIdent. @@ -7776,13 +7773,13 @@ bool definitelyValueParameter(Expression e) */ // x.y.f cannot be a value - FuncDeclaration f = (cast(DotVarExp)e).var.isFuncDeclaration(); + FuncDeclaration f = e.isDotVarExp().var.isFuncDeclaration(); if (f) return false; while (e.op == EXP.dotVariable) { - e = (cast(DotVarExp)e).e1; + e = e.isDotVarExp().e1; } // this.x.y and super.x.y couldn't possibly be valid values. if (e.op == EXP.this_ || e.op == EXP.super_) @@ -7796,7 +7793,7 @@ bool definitelyValueParameter(Expression e) if (e.op != EXP.variable) return true; - VarDeclaration v = (cast(VarExp)e).var.isVarDeclaration(); + VarDeclaration v = e.isVarExp().var.isVarDeclaration(); // func.x.y is not an alias if (!v) return true; @@ -8291,10 +8288,15 @@ MATCH matchArg(TemplateParameter tp, Scope* sc, RootObject oarg, size_t i, Templ Type ta = isType(oarg); RootObject sa = ta && !ta.deco ? null : getDsymbol(oarg); Expression ea = isExpression(oarg); - if (ea && (ea.op == EXP.this_ || ea.op == EXP.super_)) - sa = (cast(ThisExp)ea).var; - else if (ea && ea.op == EXP.scope_) - sa = (cast(ScopeExp)ea).sds; + if (ea) + { + if (auto te = ea.isThisExp()) + sa = te.var; + else if (auto se = ea.isSuperExp()) + sa = se.var; + else if (auto se = ea.isScopeExp()) + sa = se.sds; + } if (sa) { if ((cast(Dsymbol)sa).isAggregateDeclaration()) diff --git a/dmd/dtoh.d b/dmd/dtoh.d index 7c3ff4bccc2..f00b8dba86c 100644 --- a/dmd/dtoh.d +++ b/dmd/dtoh.d @@ -84,9 +84,9 @@ extern(C++) void genCppHdrFiles(ref Modules ms) m.accept(v); if (global.params.cxxhdr.fullOutput) - buf.printf("// Automatically generated by %s Compiler v%d", global.vendor.ptr, global.versionNumber()); + buf.printf("// Automatically generated by %s Compiler v%d", global.compileEnv.vendor.ptr, global.versionNumber()); else - buf.printf("// Automatically generated by %s Compiler", global.vendor.ptr); + buf.printf("// Automatically generated by %s Compiler", global.compileEnv.vendor.ptr); buf.writenl(); buf.writenl(); diff --git a/dmd/errors.d b/dmd/errors.d index 05b884c280e..f1087ad9429 100644 --- a/dmd/errors.d +++ b/dmd/errors.d @@ -73,6 +73,14 @@ class ErrorSinkCompiler : ErrorSink vdeprecationSupplemental(loc, format, ap); va_end(ap); } + + void message(const ref Loc loc, const(char)* format, ...) + { + va_list ap; + va_start(ap, format); + vmessage(loc, format, ap); + va_end(ap); + } } @@ -819,8 +827,11 @@ private void colorHighlightCode(ref OutBuffer buf) } ++nested; - auto gaggedErrorsSave = global.startGagging(); - scope Lexer lex = new Lexer(null, cast(char*)buf[].ptr, 0, buf.length - 1, 0, 1, global.errorSink, global.vendor, global.versionNumber()); + __gshared ErrorSinkNull errorSinkNull; + if (!errorSinkNull) + errorSinkNull = new ErrorSinkNull; + + scope Lexer lex = new Lexer(null, cast(char*)buf[].ptr, 0, buf.length - 1, 0, 1, errorSinkNull, &global.compileEnv); OutBuffer res; const(char)* lastp = cast(char*)buf[].ptr; //printf("colorHighlightCode('%.*s')\n", cast(int)(buf.length - 1), buf[].ptr); @@ -871,7 +882,6 @@ private void colorHighlightCode(ref OutBuffer buf) //printf("res = '%.*s'\n", cast(int)buf.length, buf[].ptr); buf.setsize(0); buf.write(&res); - global.endGagging(gaggedErrorsSave); --nested; } diff --git a/dmd/errorsink.d b/dmd/errorsink.d index b519db7e9bc..e57c2b6e388 100644 --- a/dmd/errorsink.d +++ b/dmd/errorsink.d @@ -27,6 +27,8 @@ abstract class ErrorSink void warning(const ref Loc loc, const(char)* format, ...); + void message(const ref Loc loc, const(char)* format, ...); + void deprecation(const ref Loc loc, const(char)* format, ...); void deprecationSupplemental(const ref Loc loc, const(char)* format, ...); @@ -47,6 +49,8 @@ class ErrorSinkNull : ErrorSink void warning(const ref Loc loc, const(char)* format, ...) { } + void message(const ref Loc loc, const(char)* format, ...) { } + void deprecation(const ref Loc loc, const(char)* format, ...) { } void deprecationSupplemental(const ref Loc loc, const(char)* format, ...) { } @@ -117,5 +121,21 @@ class ErrorSinkStderr : ErrorSink va_end(ap); } + void message(const ref Loc loc, const(char)* format, ...) + { + const p = loc.toChars(); + if (*p) + { + fprintf(stderr, "%s: ", p); + //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, ...) { } } diff --git a/dmd/escape.d b/dmd/escape.d index 420fa7f80bb..b4b0fd92472 100644 --- a/dmd/escape.d +++ b/dmd/escape.d @@ -93,22 +93,7 @@ bool checkMutableArguments(Scope* sc, FuncDeclaration fd, TypeFunction tf, bool isMutable; // true if reference to mutable } - /* Store escapeBy as static data escapeByStorage so we can keep reusing the same - * arrays rather than reallocating them. - */ - __gshared EscapeBy[] escapeByStorage; - auto escapeBy = escapeByStorage; - if (escapeBy.length < len) - { - auto newPtr = cast(EscapeBy*)mem.xrealloc(escapeBy.ptr, len * EscapeBy.sizeof); - // Clear the new section - memset(newPtr + escapeBy.length, 0, (len - escapeBy.length) * EscapeBy.sizeof); - escapeBy = newPtr[0 .. len]; - escapeByStorage = escapeBy; - } - else - escapeBy = escapeBy[0 .. len]; - + auto escapeBy = new EscapeBy[len]; const paramLength = tf.parameterList.length; // Fill in escapeBy[] with arguments[], ethis, and outerVars[] @@ -181,7 +166,7 @@ bool checkMutableArguments(Scope* sc, FuncDeclaration fd, TypeFunction tf, if (!(eb.isMutable || eb2.isMutable)) return; - if (!tf.islive && !(global.params.useDIP1000 == FeatureState.enabled && sc.func.setUnsafe())) + if (!tf.islive && !(global.params.useDIP1000 == FeatureState.enabled && sc.func && sc.func.setUnsafe())) return; if (!gag) @@ -228,13 +213,6 @@ bool checkMutableArguments(Scope* sc, FuncDeclaration fd, TypeFunction tf, escape(i, eb, false); } - /* Reset the arrays in escapeBy[] so we can reuse them next time through - */ - foreach (ref eb; escapeBy) - { - eb.er.reset(); - } - return errors; } @@ -503,7 +481,7 @@ bool checkParamArgumentReturn(Scope* sc, Expression firstArg, Expression arg, Pa const byRef = param.isReference() && !(param.storageClass & STC.scope_) && !(param.storageClass & STC.returnScope); // fixme: it's possible to infer returnScope without scope with vaIsFirstRef - scope e = new AssignExp(arg.loc, firstArg, arg); + auto e = new AssignExp(arg.loc, firstArg, arg); return checkAssignEscape(sc, e, gag, byRef); } @@ -2377,7 +2355,7 @@ void finishScopeParamInference(FuncDeclaration funcdecl, ref TypeFunction f) foreach (u, p; f.parameterList) { auto v = (*funcdecl.parameters)[u]; - if (!v.isScope() && inferScope(v)) + if (!v.isScope() && v.type.hasPointers() && inferScope(v)) { //printf("Inferring scope for %s\n", v.toChars()); p.storageClass |= STC.scope_ | STC.scopeinferred; @@ -2497,6 +2475,9 @@ bool isReferenceToMutable(Type t) } break; + case Tnull: + return false; + default: assert(0); } diff --git a/dmd/expression.d b/dmd/expression.d index b27afc7f42b..9b8e6a1a3a6 100644 --- a/dmd/expression.d +++ b/dmd/expression.d @@ -721,20 +721,21 @@ 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 { - const EXP op; // to minimize use of dynamic_cast - ubyte size; // # of bytes in Expression so we can copy() it - bool parens; // if this is a parenthesized expression Type type; // !=null means that semantic() has been run Loc loc; // file location + const EXP op; // to minimize use of dynamic_cast + bool parens; // if this is a parenthesized expression - extern (D) this(const ref Loc loc, EXP op, int size) scope + extern (D) this(const ref Loc loc, EXP op) scope { //printf("Expression::Expression(op = %d) this = %p\n", op, this); this.loc = loc; this.op = op; - this.size = cast(ubyte)size; } + /// Returns: class instance size of this expression (implemented manually because `extern(C++)`) + final size_t size() nothrow @nogc pure @safe const { return expSize[op]; } + static void _init() { CTFEExp.cantexp = new CTFEExp(EXP.cantExpression); @@ -1220,12 +1221,15 @@ extern (C++) /* IN_LLVM abstract */ class Expression : ASTNode return false; // If the call has a pure parent, then the called func must be pure. - if (!f.isPure() && checkImpure(sc)) + if (!f.isPure() && checkImpure(sc, loc, null, f)) { error("`pure` %s `%s` cannot call impure %s `%s`", sc.func.kind(), sc.func.toPrettyChars(), f.kind(), f.toPrettyChars()); + if (!f.isDtorDeclaration()) + errorSupplementalInferredAttr(f, /*max depth*/ 10, /*deprecation*/ false, STC.pure_); + checkOverridenDtor(sc, f, dd => dd.type.toTypeFunction().purity != PURE.impure, "impure"); return true; } @@ -1356,7 +1360,7 @@ extern (C++) /* IN_LLVM abstract */ class Expression : ASTNode if (v.ident == Id.gate) return false; - if (checkImpure(sc)) + if (checkImpure(sc, loc, "`pure` %s `%s` cannot access mutable static data `%s`", v)) { error("`pure` %s `%s` cannot access mutable static data `%s`", sc.func.kind(), sc.func.toPrettyChars(), v.toChars()); @@ -1432,11 +1436,11 @@ extern (C++) /* IN_LLVM abstract */ class Expression : ASTNode Check if sc.func is impure or can be made impure. Returns true on error, i.e. if sc.func is pure and cannot be made impure. */ - private static bool checkImpure(Scope* sc) + private static bool checkImpure(Scope* sc, Loc loc, const(char)* fmt, RootObject arg0) { return sc.func && (isRootTraitsCompilesScope(sc) ? sc.func.isPureBypassingInference() >= PURE.weak - : sc.func.setImpure()); + : sc.func.setImpure(loc, fmt, arg0)); } /********************************************* @@ -1485,7 +1489,8 @@ extern (C++) /* IN_LLVM abstract */ class Expression : ASTNode error("`@safe` %s `%s` cannot call `@system` %s `%s`", sc.func.kind(), sc.func.toPrettyChars(), f.kind(), prettyChars); - f.errorSupplementalInferredSafety(/*max depth*/ 10, /*deprecation*/ false); + if (!f.isDtorDeclaration) + errorSupplementalInferredAttr(f, /*max depth*/ 10, /*deprecation*/ false, STC.safe); .errorSupplemental(f.loc, "`%s` is declared here", prettyChars); checkOverridenDtor(sc, f, dd => dd.type.toTypeFunction().trust > TRUST.system, "@system"); @@ -1499,7 +1504,7 @@ extern (C++) /* IN_LLVM abstract */ class Expression : ASTNode if (sc.func.isSafeBypassingInference()) { .deprecation(this.loc, "`@safe` function `%s` calling `%s`", sc.func.toChars(), f.toChars()); - errorSupplementalInferredSafety(f, 10, true); + errorSupplementalInferredAttr(f, 10, true, STC.safe); } else if (!sc.func.safetyViolation) { @@ -1529,7 +1534,7 @@ extern (C++) /* IN_LLVM abstract */ class Expression : ASTNode if (!f.isNogc()) { - if (isRootTraitsCompilesScope(sc) ? sc.func.isNogcBypassingInference() : sc.func.setGC()) + if (isRootTraitsCompilesScope(sc) ? sc.func.isNogcBypassingInference() : sc.func.setGCCall(f)) { if (loc.linnum == 0) // e.g. implicitly generated dtor loc = sc.func.loc; @@ -1538,10 +1543,15 @@ extern (C++) /* IN_LLVM abstract */ class Expression : ASTNode // 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_newclassT)) + || f.ident == Id._d_arraycatnTX || f.ident == Id._d_newclassT)) + { error("`@nogc` %s `%s` cannot call non-@nogc %s `%s`", sc.func.kind(), sc.func.toPrettyChars(), f.kind(), f.toPrettyChars()); + if (!f.isDtorDeclaration) + f.errorSupplementalInferredAttr(/*max depth*/ 10, /*deprecation*/ false, STC.nogc); + } + checkOverridenDtor(sc, f, dd => dd.type.toTypeFunction().isnogc, "non-@nogc"); return true; @@ -1776,6 +1786,7 @@ extern (C++) /* IN_LLVM abstract */ class Expression : ASTNode inout(PostExp) isPostExp() { return (op == EXP.plusPlus || op == EXP.minusMinus) ? cast(typeof(return))this : null; } inout(PreExp) isPreExp() { return (op == EXP.prePlusPlus || op == EXP.preMinusMinus) ? cast(typeof(return))this : null; } inout(AssignExp) isAssignExp() { return op == EXP.assign ? cast(typeof(return))this : null; } + inout(LoweredAssignExp) isLoweredAssignExp() { return op == EXP.loweredAssignExp ? cast(typeof(return))this : null; } inout(ConstructExp) isConstructExp() { return op == EXP.construct ? cast(typeof(return))this : null; } inout(BlitExp) isBlitExp() { return op == EXP.blit ? cast(typeof(return))this : null; } inout(AddAssignExp) isAddAssignExp() { return op == EXP.addAssign ? cast(typeof(return))this : null; } @@ -1867,7 +1878,7 @@ extern (C++) final class IntegerExp : Expression extern (D) this(const ref Loc loc, dinteger_t value, Type type) { - super(loc, EXP.int64, __traits(classInstanceSize, IntegerExp)); + super(loc, EXP.int64); //printf("IntegerExp(value = %lld, type = '%s')\n", value, type ? type.toChars() : ""); assert(type); if (!type.isscalar()) @@ -1883,7 +1894,7 @@ extern (C++) final class IntegerExp : Expression extern (D) this(dinteger_t value) { - super(Loc.initial, EXP.int64, __traits(classInstanceSize, IntegerExp)); + super(Loc.initial, EXP.int64); this.type = Type.tint32; this.value = cast(int)value; } @@ -2083,7 +2094,7 @@ extern (C++) final class ErrorExp : Expression { private extern (D) this() { - super(Loc.initial, EXP.error, __traits(classInstanceSize, ErrorExp)); + super(Loc.initial, EXP.error); type = Type.terror; } @@ -2131,7 +2142,7 @@ extern (C++) final class VoidInitExp : Expression extern (D) this(VarDeclaration var) { - super(var.loc, EXP.void_, __traits(classInstanceSize, VoidInitExp)); + super(var.loc, EXP.void_); this.var = var; this.type = var.type; } @@ -2157,7 +2168,7 @@ extern (C++) final class RealExp : Expression extern (D) this(const ref Loc loc, real_t value, Type type) { - super(loc, EXP.float64, __traits(classInstanceSize, RealExp)); + super(loc, EXP.float64); //printf("RealExp::RealExp(%Lg)\n", value); this.value = value; this.type = type; @@ -2240,7 +2251,7 @@ extern (C++) final class ComplexExp : Expression extern (D) this(const ref Loc loc, complex_t value, Type type) { - super(loc, EXP.complex80, __traits(classInstanceSize, ComplexExp)); + super(loc, EXP.complex80); this.value = value; this.type = type; //printf("ComplexExp::ComplexExp(%s)\n", toChars()); @@ -2331,7 +2342,7 @@ extern (C++) class IdentifierExp : Expression extern (D) this(const ref Loc loc, Identifier ident) scope { - super(loc, EXP.identifier, __traits(classInstanceSize, IdentifierExp)); + super(loc, EXP.identifier); this.ident = ident; } @@ -2384,7 +2395,7 @@ extern (C++) final class DsymbolExp : Expression extern (D) this(const ref Loc loc, Dsymbol s, bool hasOverloads = true) { - super(loc, EXP.dSymbol, __traits(classInstanceSize, DsymbolExp)); + super(loc, EXP.dSymbol); this.s = s; this.hasOverloads = hasOverloads; } @@ -2414,13 +2425,13 @@ extern (C++) class ThisExp : Expression extern (D) this(const ref Loc loc) { - super(loc, EXP.this_, __traits(classInstanceSize, ThisExp)); + super(loc, EXP.this_); //printf("ThisExp::ThisExp() loc = %d\n", loc.linnum); } this(const ref Loc loc, const EXP tok) { - super(loc, tok, __traits(classInstanceSize, ThisExp)); + super(loc, tok); //printf("ThisExp::ThisExp() loc = %d\n", loc.linnum); } @@ -2486,7 +2497,7 @@ extern (C++) final class NullExp : Expression { extern (D) this(const ref Loc loc, Type type = null) scope { - super(loc, EXP.null_, __traits(classInstanceSize, NullExp)); + super(loc, EXP.null_); this.type = type; } @@ -2530,6 +2541,8 @@ extern (C++) final class NullExp : Expression */ extern (C++) final class StringExp : Expression { + char postfix = NoPostfix; // 'c', 'w', 'd' + OwnedBy ownedByCtfe = OwnedBy.code; private union { char* string; // if sz == 1 @@ -2538,14 +2551,22 @@ extern (C++) final class StringExp : Expression } // (const if ownedByCtfe == OwnedBy.code) size_t len; // number of code units ubyte sz = 1; // 1: char, 2: wchar, 4: dchar - ubyte committed; // !=0 if type is committed + + /** + * Whether the string literal's type is fixed + * Example: + * --- + * wstring x = "abc"; // OK, string literal is flexible + * wstring y = cast(string) "abc"; // Error: type was committed after cast + * --- + */ + bool committed; + enum char NoPostfix = 0; - char postfix = NoPostfix; // 'c', 'w', 'd' - OwnedBy ownedByCtfe = OwnedBy.code; extern (D) this(const ref Loc loc, const(void)[] string) scope { - super(loc, EXP.string_, __traits(classInstanceSize, StringExp)); + super(loc, EXP.string_); this.string = cast(char*)string.ptr; // note that this.string should be const this.len = string.length; this.sz = 1; // work around LDC bug #1286 @@ -2553,7 +2574,7 @@ extern (C++) final class StringExp : Expression extern (D) this(const ref Loc loc, const(void)[] string, size_t len, ubyte sz, char postfix = NoPostfix) scope { - super(loc, EXP.string_, __traits(classInstanceSize, StringExp)); + super(loc, EXP.string_); this.string = cast(char*)string.ptr; // note that this.string should be const this.len = len; this.sz = sz; @@ -2751,7 +2772,7 @@ extern (C++) final class StringExp : Expression if (sz != 1) { // Convert to UTF-8 string - committed = 0; + committed = false; Expression e = castTo(sc, Type.tchar.arrayOf()); e = e.optimize(WANTvalue); auto se = e.isStringExp(); @@ -2940,7 +2961,7 @@ extern (C++) final class TupleExp : Expression extern (D) this(const ref Loc loc, Expression e0, Expressions* exps) { - super(loc, EXP.tuple, __traits(classInstanceSize, TupleExp)); + super(loc, EXP.tuple); //printf("TupleExp(this = %p)\n", this); this.e0 = e0; this.exps = exps; @@ -2948,14 +2969,14 @@ extern (C++) final class TupleExp : Expression extern (D) this(const ref Loc loc, Expressions* exps) { - super(loc, EXP.tuple, __traits(classInstanceSize, TupleExp)); + super(loc, EXP.tuple); //printf("TupleExp(this = %p)\n", this); this.exps = exps; } extern (D) this(const ref Loc loc, TupleDeclaration tup) { - super(loc, EXP.tuple, __traits(classInstanceSize, TupleExp)); + super(loc, EXP.tuple); this.exps = new Expressions(); this.exps.reserve(tup.objects.length); @@ -3032,6 +3053,9 @@ extern (C++) final class TupleExp : Expression */ extern (C++) final class ArrayLiteralExp : Expression { + OwnedBy ownedByCtfe = OwnedBy.code; + bool onstack = false; + /** If !is null, elements[] can be sparse and basis is used for the * "default" element value. In other words, non-null elements[i] overrides * this 'basis' value. @@ -3039,19 +3063,17 @@ extern (C++) final class ArrayLiteralExp : Expression Expression basis; Expressions* elements; - OwnedBy ownedByCtfe = OwnedBy.code; - bool onstack = false; extern (D) this(const ref Loc loc, Type type, Expressions* elements) { - super(loc, EXP.arrayLiteral, __traits(classInstanceSize, ArrayLiteralExp)); + super(loc, EXP.arrayLiteral); this.type = type; this.elements = elements; } extern (D) this(const ref Loc loc, Type type, Expression e) { - super(loc, EXP.arrayLiteral, __traits(classInstanceSize, ArrayLiteralExp)); + super(loc, EXP.arrayLiteral); this.type = type; elements = new Expressions(); elements.push(e); @@ -3059,7 +3081,7 @@ extern (C++) final class ArrayLiteralExp : Expression extern (D) this(const ref Loc loc, Type type, Expression basis, Expressions* elements) { - super(loc, EXP.arrayLiteral, __traits(classInstanceSize, ArrayLiteralExp)); + super(loc, EXP.arrayLiteral); this.type = type; this.basis = basis; this.elements = elements; @@ -3197,14 +3219,14 @@ extern (C++) final class ArrayLiteralExp : Expression */ extern (C++) final class AssocArrayLiteralExp : Expression { + OwnedBy ownedByCtfe = OwnedBy.code; + Expressions* keys; Expressions* values; - OwnedBy ownedByCtfe = OwnedBy.code; - extern (D) this(const ref Loc loc, Expressions* keys, Expressions* values) { - super(loc, EXP.assocArrayLiteral, __traits(classInstanceSize, AssocArrayLiteralExp)); + super(loc, EXP.assocArrayLiteral); assert(keys.length == values.length); this.keys = keys; this.values = values; @@ -3272,18 +3294,26 @@ extern (C++) final class StructLiteralExp : Expression 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) + // `inlineCopy` is only used temporarily in the `inline.d` pass, + // while `sym` is only used in `e2ir/s2ir/tocsym` which comes after + union + { version (IN_LLVM) { - // With the introduction of pointers returned from CTFE, struct literals can - // now contain pointers to themselves. While in toElem, contains a pointer - // to the memory used to build the literal for resolving such references. - void* inProgressMemory; // llvm::Value* + // With the introduction of pointers returned from CTFE, struct literals can + // now contain pointers to themselves. While in toElem, contains a pointer + // to the memory used to build the literal for resolving such references. + void* inProgressMemory; // llvm::Value* } else { - Symbol* sym; /// back end symbol to initialize with literal + Symbol* sym; /// back end symbol to initialize with literal } + /// those fields need to prevent a infinite recursion when one field of struct initialized with 'this' pointer. + StructLiteralExp inlinecopy; + } + /** pointer to the origin instance of the expression. * once a new expression is created, origin is set to 'this'. * anytime when an expression copy is created, 'origin' pointer is set to @@ -3291,15 +3321,13 @@ else */ StructLiteralExp origin; - /// those fields need to prevent a infinite recursion when one field of struct initialized with 'this' pointer. - StructLiteralExp inlinecopy; /** anytime when recursive function is calling, 'stageflags' marks with bit flag of * current stage and unmarks before return from this function. * 'inlinecopy' uses similar 'stageflags' and from multiple evaluation 'doInline' * (with infinite recursion) of this expression. */ - int stageflags; + 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` @@ -3307,7 +3335,7 @@ else extern (D) this(const ref Loc loc, StructDeclaration sd, Expressions* elements, Type stype = null) { - super(loc, EXP.structLiteral, __traits(classInstanceSize, StructLiteralExp)); + super(loc, EXP.structLiteral); this.sd = sd; if (!elements) elements = new Expressions(); @@ -3486,7 +3514,7 @@ extern (C++) final class CompoundLiteralExp : Expression extern (D) this(const ref Loc loc, Type type_name, Initializer initializer) { - super(loc, EXP.compoundLiteral, __traits(classInstanceSize, CompoundLiteralExp)); + super(loc, EXP.compoundLiteral); super.type = type_name; this.initializer = initializer; //printf("CompoundLiteralExp::CompoundLiteralExp(%s)\n", toChars()); @@ -3505,7 +3533,7 @@ extern (C++) final class TypeExp : Expression { extern (D) this(const ref Loc loc, Type type) { - super(loc, EXP.type, __traits(classInstanceSize, TypeExp)); + super(loc, EXP.type); //printf("TypeExp::TypeExp(%s)\n", type.toChars()); this.type = type; } @@ -3547,7 +3575,7 @@ extern (C++) final class ScopeExp : Expression extern (D) this(const ref Loc loc, ScopeDsymbol sds) { - super(loc, EXP.scope_, __traits(classInstanceSize, ScopeExp)); + super(loc, EXP.scope_); //printf("ScopeExp::ScopeExp(sds = '%s')\n", sds.toChars()); //static int count; if (++count == 38) *(char*)0=0; this.sds = sds; @@ -3602,7 +3630,7 @@ extern (C++) final class TemplateExp : Expression extern (D) this(const ref Loc loc, TemplateDeclaration td, FuncDeclaration fd = null) { - super(loc, EXP.template_, __traits(classInstanceSize, TemplateExp)); + super(loc, EXP.template_); //printf("TemplateExp(): %s\n", td.toChars()); this.td = td; this.fd = fd; @@ -3663,7 +3691,7 @@ extern (C++) final class NewExp : Expression extern (D) this(const ref Loc loc, Expression thisexp, Type newtype, Expressions* arguments, Identifiers* names = null) { - super(loc, EXP.new_, __traits(classInstanceSize, NewExp)); + super(loc, EXP.new_); this.thisexp = thisexp; this.newtype = newtype; this.arguments = arguments; @@ -3701,7 +3729,7 @@ extern (C++) final class NewAnonClassExp : Expression extern (D) this(const ref Loc loc, Expression thisexp, ClassDeclaration cd, Expressions* arguments) { - super(loc, EXP.newAnonymousClass, __traits(classInstanceSize, NewAnonClassExp)); + super(loc, EXP.newAnonymousClass); this.thisexp = thisexp; this.cd = cd; this.arguments = arguments; @@ -3726,9 +3754,9 @@ extern (C++) class SymbolExp : Expression Dsymbol originalScope; // original scope before inlining bool hasOverloads; - extern (D) this(const ref Loc loc, EXP op, int size, Declaration var, bool hasOverloads) + extern (D) this(const ref Loc loc, EXP op, Declaration var, bool hasOverloads) { - super(loc, op, size); + super(loc, op); assert(var); this.var = var; this.hasOverloads = hasOverloads; @@ -3757,7 +3785,7 @@ extern (C++) final class SymOffExp : SymbolExp .error(loc, "need `this` for address of `%s`", v.toChars()); hasOverloads = false; } - super(loc, EXP.symbolOffset, __traits(classInstanceSize, SymOffExp), var, hasOverloads); + super(loc, EXP.symbolOffset, var, hasOverloads); this.offset = offset; } @@ -3792,7 +3820,7 @@ extern (C++) final class VarExp : SymbolExp if (var.isVarDeclaration()) hasOverloads = false; - super(loc, EXP.variable, __traits(classInstanceSize, VarExp), var, hasOverloads); + super(loc, EXP.variable, var, hasOverloads); //printf("VarExp(this = %p, '%s', loc = %s)\n", this, var.toChars(), loc.toChars()); //if (strcmp(var.ident.toChars(), "func") == 0) assert(0); this.type = var.type; @@ -3876,7 +3904,7 @@ extern (C++) final class OverExp : Expression extern (D) this(const ref Loc loc, OverloadSet s) { - super(loc, EXP.overloadSet, __traits(classInstanceSize, OverExp)); + super(loc, EXP.overloadSet); //printf("OverExp(this = %p, '%s')\n", this, var.toChars()); vars = s; type = Type.tvoid; @@ -3910,7 +3938,7 @@ extern (C++) final class FuncExp : Expression extern (D) this(const ref Loc loc, Dsymbol s) { - super(loc, EXP.function_, __traits(classInstanceSize, FuncExp)); + super(loc, EXP.function_); this.td = s.isTemplateDeclaration(); this.fd = s.isFuncLiteralDeclaration(); if (td) @@ -4209,7 +4237,7 @@ extern (C++) final class DeclarationExp : Expression extern (D) this(const ref Loc loc, Dsymbol declaration) { - super(loc, EXP.declaration, __traits(classInstanceSize, DeclarationExp)); + super(loc, EXP.declaration); this.declaration = declaration; } @@ -4242,7 +4270,7 @@ extern (C++) final class TypeidExp : Expression extern (D) this(const ref Loc loc, RootObject o) { - super(loc, EXP.typeid_, __traits(classInstanceSize, TypeidExp)); + super(loc, EXP.typeid_); this.obj = o; } @@ -4267,7 +4295,7 @@ extern (C++) final class TraitsExp : Expression extern (D) this(const ref Loc loc, Identifier ident, Objects* args) { - super(loc, EXP.traits, __traits(classInstanceSize, TraitsExp)); + super(loc, EXP.traits); this.ident = ident; this.args = args; } @@ -4292,7 +4320,7 @@ extern (C++) final class HaltExp : Expression { extern (D) this(const ref Loc loc) { - super(loc, EXP.halt, __traits(classInstanceSize, HaltExp)); + super(loc, EXP.halt); } override void accept(Visitor v) @@ -4316,7 +4344,7 @@ extern (C++) final class IsExp : Expression extern (D) this(const ref Loc loc, Type targ, Identifier id, TOK tok, Type tspec, TOK tok2, TemplateParameters* parameters) scope { - super(loc, EXP.is_, __traits(classInstanceSize, IsExp)); + super(loc, EXP.is_); this.targ = targ; this.id = id; this.tok = tok; @@ -4352,11 +4380,10 @@ extern (C++) final class IsExp : Expression extern (C++) abstract class UnaExp : Expression { Expression e1; - Type att1; // Save alias this type to detect recursion - extern (D) this(const ref Loc loc, EXP op, int size, Expression e1) scope + extern (D) this(const ref Loc loc, EXP op, Expression e1) scope { - super(loc, op, size); + super(loc, op); this.e1 = e1; } @@ -4427,9 +4454,9 @@ extern (C++) abstract class BinExp : Expression 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, int size, Expression e1, Expression e2) scope + extern (D) this(const ref Loc loc, EXP op, Expression e1, Expression e2) scope { - super(loc, op, size); + super(loc, op); this.e1 = e1; this.e2 = e2; } @@ -4718,9 +4745,9 @@ extern (C++) abstract class BinExp : Expression */ extern (C++) class BinAssignExp : BinExp { - extern (D) this(const ref Loc loc, EXP op, int size, Expression e1, Expression e2) scope + extern (D) this(const ref Loc loc, EXP op, Expression e1, Expression e2) scope { - super(loc, op, size, e1, e2); + super(loc, op, e1, e2); } override final bool isLvalue() @@ -4757,7 +4784,7 @@ extern (C++) final class MixinExp : Expression extern (D) this(const ref Loc loc, Expressions* exps) { - super(loc, EXP.mixin_, __traits(classInstanceSize, MixinExp)); + super(loc, EXP.mixin_); this.exps = exps; } @@ -4805,7 +4832,7 @@ extern (C++) final class ImportExp : UnaExp { extern (D) this(const ref Loc loc, Expression e) { - super(loc, EXP.import_, __traits(classInstanceSize, ImportExp), e); + super(loc, EXP.import_, e); } override void accept(Visitor v) @@ -4825,7 +4852,7 @@ extern (C++) final class AssertExp : UnaExp extern (D) this(const ref Loc loc, Expression e, Expression msg = null) { - super(loc, EXP.assert_, __traits(classInstanceSize, AssertExp), e); + super(loc, EXP.assert_, e); this.msg = msg; } @@ -4850,7 +4877,7 @@ extern (C++) final class ThrowExp : UnaExp { extern (D) this(const ref Loc loc, Expression e) { - super(loc, EXP.throw_, __traits(classInstanceSize, ThrowExp), e); + super(loc, EXP.throw_, e); this.type = Type.tnoreturn; } @@ -4876,7 +4903,7 @@ extern (C++) final class DotIdExp : UnaExp extern (D) this(const ref Loc loc, Expression e, Identifier ident) { - super(loc, EXP.dotIdentifier, __traits(classInstanceSize, DotIdExp), e); + super(loc, EXP.dotIdentifier, e); this.ident = ident; } @@ -4900,7 +4927,7 @@ extern (C++) final class DotTemplateExp : UnaExp extern (D) this(const ref Loc loc, Expression e, TemplateDeclaration td) { - super(loc, EXP.dotTemplateDeclaration, __traits(classInstanceSize, DotTemplateExp), e); + super(loc, EXP.dotTemplateDeclaration, e); this.td = td; } @@ -4934,7 +4961,7 @@ extern (C++) final class DotVarExp : UnaExp if (var.isVarDeclaration()) hasOverloads = false; - super(loc, EXP.dotVariable, __traits(classInstanceSize, DotVarExp), e); + super(loc, EXP.dotVariable, e); //printf("DotVarExp()\n"); this.var = var; this.hasOverloads = hasOverloads; @@ -5015,14 +5042,14 @@ extern (C++) final class DotTemplateInstanceExp : UnaExp extern (D) this(const ref Loc loc, Expression e, Identifier name, Objects* tiargs) { - super(loc, EXP.dotTemplateInstance, __traits(classInstanceSize, DotTemplateInstanceExp), e); + 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) { - super(loc, EXP.dotTemplateInstance, __traits(classInstanceSize, DotTemplateInstanceExp), e); + super(loc, EXP.dotTemplateInstance, e); this.ti = ti; } @@ -5115,7 +5142,7 @@ extern (C++) final class DelegateExp : UnaExp extern (D) this(const ref Loc loc, Expression e, FuncDeclaration f, bool hasOverloads = true, VarDeclaration vthis2 = null) { - super(loc, EXP.delegate_, __traits(classInstanceSize, DelegateExp), e); + super(loc, EXP.delegate_, e); this.func = f; this.hasOverloads = hasOverloads; this.vthis2 = vthis2; @@ -5135,7 +5162,7 @@ extern (C++) final class DotTypeExp : UnaExp extern (D) this(const ref Loc loc, Expression e, Dsymbol s) { - super(loc, EXP.dotType, __traits(classInstanceSize, DotTypeExp), e); + super(loc, EXP.dotType, e); this.sym = s; } @@ -5189,19 +5216,19 @@ extern (C++) final class CallExp : UnaExp extern (D) this(const ref Loc loc, Expression e, Expressions* exps, Identifiers* names = null) { - super(loc, EXP.call, __traits(classInstanceSize, CallExp), e); + super(loc, EXP.call, e); this.arguments = exps; this.names = names; } extern (D) this(const ref Loc loc, Expression e) { - super(loc, EXP.call, __traits(classInstanceSize, CallExp), e); + super(loc, EXP.call, e); } extern (D) this(const ref Loc loc, Expression e, Expression earg1) { - super(loc, EXP.call, __traits(classInstanceSize, CallExp), e); + super(loc, EXP.call, e); this.arguments = new Expressions(); if (earg1) this.arguments.push(earg1); @@ -5209,7 +5236,7 @@ extern (C++) final class CallExp : UnaExp extern (D) this(const ref Loc loc, Expression e, Expression earg1, Expression earg2) { - super(loc, EXP.call, __traits(classInstanceSize, CallExp), e); + super(loc, EXP.call, e); auto arguments = new Expressions(2); (*arguments)[0] = earg1; (*arguments)[1] = earg2; @@ -5365,7 +5392,7 @@ extern (C++) final class AddrExp : UnaExp { extern (D) this(const ref Loc loc, Expression e) { - super(loc, EXP.address, __traits(classInstanceSize, AddrExp), e); + super(loc, EXP.address, e); } extern (D) this(const ref Loc loc, Expression e, Type t) @@ -5387,14 +5414,14 @@ extern (C++) final class PtrExp : UnaExp { extern (D) this(const ref Loc loc, Expression e) { - super(loc, EXP.star, __traits(classInstanceSize, PtrExp), e); + super(loc, EXP.star, e); //if (e.type) // type = ((TypePointer *)e.type).next; } extern (D) this(const ref Loc loc, Expression e, Type t) { - super(loc, EXP.star, __traits(classInstanceSize, PtrExp), e); + super(loc, EXP.star, e); type = t; } @@ -5440,7 +5467,7 @@ extern (C++) final class NegExp : UnaExp { extern (D) this(const ref Loc loc, Expression e) { - super(loc, EXP.negate, __traits(classInstanceSize, NegExp), e); + super(loc, EXP.negate, e); } override void accept(Visitor v) @@ -5456,7 +5483,7 @@ extern (C++) final class UAddExp : UnaExp { extern (D) this(const ref Loc loc, Expression e) scope { - super(loc, EXP.uadd, __traits(classInstanceSize, UAddExp), e); + super(loc, EXP.uadd, e); } override void accept(Visitor v) @@ -5472,7 +5499,7 @@ extern (C++) final class ComExp : UnaExp { extern (D) this(const ref Loc loc, Expression e) { - super(loc, EXP.tilde, __traits(classInstanceSize, ComExp), e); + super(loc, EXP.tilde, e); } override void accept(Visitor v) @@ -5488,7 +5515,7 @@ extern (C++) final class NotExp : UnaExp { extern (D) this(const ref Loc loc, Expression e) { - super(loc, EXP.not, __traits(classInstanceSize, NotExp), e); + super(loc, EXP.not, e); } override void accept(Visitor v) @@ -5508,7 +5535,7 @@ extern (C++) final class DeleteExp : UnaExp extern (D) this(const ref Loc loc, Expression e, bool isRAII) { - super(loc, EXP.delete_, __traits(classInstanceSize, DeleteExp), e); + super(loc, EXP.delete_, e); this.isRAII = isRAII; } @@ -5532,7 +5559,7 @@ extern (C++) final class CastExp : UnaExp extern (D) this(const ref Loc loc, Expression e, Type t) { - super(loc, EXP.cast_, __traits(classInstanceSize, CastExp), e); + super(loc, EXP.cast_, e); this.to = t; } @@ -5540,7 +5567,7 @@ extern (C++) final class CastExp : UnaExp */ extern (D) this(const ref Loc loc, Expression e, ubyte mod) { - super(loc, EXP.cast_, __traits(classInstanceSize, CastExp), e); + super(loc, EXP.cast_, e); this.mod = mod; } @@ -5594,7 +5621,7 @@ extern (C++) final class VectorExp : UnaExp extern (D) this(const ref Loc loc, Expression e, Type t) { - super(loc, EXP.vector, __traits(classInstanceSize, VectorExp), e); + super(loc, EXP.vector, e); assert(t.ty == Tvector); to = cast(TypeVector)t; } @@ -5630,7 +5657,7 @@ extern (C++) final class VectorArrayExp : UnaExp { extern (D) this(const ref Loc loc, Expression e1) { - super(loc, EXP.vectorArray, __traits(classInstanceSize, VectorArrayExp), e1); + super(loc, EXP.vectorArray, e1); } override bool isLvalue() @@ -5661,21 +5688,27 @@ extern (C++) final class SliceExp : UnaExp Expression lwr; // null if implicit [length - 1] VarDeclaration lengthVar; - bool upperIsInBounds; // true if upr <= e1.length - bool lowerIsLessThanUpper; // true if lwr <= upr - bool arrayop; // an array operation, rather than a slice + + private extern(D) static struct BitFields + { + bool upperIsInBounds; // true if upr <= e1.length + bool lowerIsLessThanUpper; // true if lwr <= upr + bool arrayop; // an array operation, rather than a slice + } + import dmd.common.bitfields : generateBitFields; + mixin(generateBitFields!(BitFields, ubyte)); /************************************************************/ extern (D) this(const ref Loc loc, Expression e1, IntervalExp ie) { - super(loc, EXP.slice, __traits(classInstanceSize, SliceExp), e1); + 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) { - super(loc, EXP.slice, __traits(classInstanceSize, SliceExp), e1); + super(loc, EXP.slice, e1); this.upr = upr; this.lwr = lwr; } @@ -5725,7 +5758,7 @@ extern (C++) final class ArrayLengthExp : UnaExp { extern (D) this(const ref Loc loc, Expression e1) { - super(loc, EXP.arrayLength, __traits(classInstanceSize, ArrayLengthExp), e1); + super(loc, EXP.arrayLength, e1); } override void accept(Visitor v) @@ -5748,7 +5781,7 @@ extern (C++) final class ArrayExp : UnaExp extern (D) this(const ref Loc loc, Expression e1, Expression index = null) { - super(loc, EXP.array, __traits(classInstanceSize, ArrayExp), e1); + super(loc, EXP.array, e1); arguments = new Expressions(); if (index) arguments.push(index); @@ -5756,7 +5789,7 @@ extern (C++) final class ArrayExp : UnaExp extern (D) this(const ref Loc loc, Expression e1, Expressions* args) { - super(loc, EXP.array, __traits(classInstanceSize, ArrayExp), e1); + super(loc, EXP.array, e1); arguments = args; } @@ -5793,7 +5826,7 @@ extern (C++) final class DotExp : BinExp { extern (D) this(const ref Loc loc, Expression e1, Expression e2) { - super(loc, EXP.dot, __traits(classInstanceSize, DotExp), e1, e2); + super(loc, EXP.dot, e1, e2); } override void accept(Visitor v) @@ -5819,7 +5852,7 @@ extern (C++) final class CommaExp : BinExp extern (D) this(const ref Loc loc, Expression e1, Expression e2, bool generated = true) { - super(loc, EXP.comma, __traits(classInstanceSize, CommaExp), e1, e2); + super(loc, EXP.comma, e1, e2); allowCommaExp = isGenerated = generated; } @@ -5888,7 +5921,7 @@ extern (C++) final class IntervalExp : Expression extern (D) this(const ref Loc loc, Expression lwr, Expression upr) { - super(loc, EXP.interval, __traits(classInstanceSize, IntervalExp)); + super(loc, EXP.interval); this.lwr = lwr; this.upr = upr; } @@ -5913,7 +5946,7 @@ extern (C++) final class DelegatePtrExp : UnaExp { extern (D) this(const ref Loc loc, Expression e1) { - super(loc, EXP.delegatePointer, __traits(classInstanceSize, DelegatePtrExp), e1); + super(loc, EXP.delegatePointer, e1); } override bool isLvalue() @@ -5951,7 +5984,7 @@ extern (C++) final class DelegateFuncptrExp : UnaExp { extern (D) this(const ref Loc loc, Expression e1) { - super(loc, EXP.delegateFunctionPointer, __traits(classInstanceSize, DelegateFuncptrExp), e1); + super(loc, EXP.delegateFunctionPointer, e1); } override bool isLvalue() @@ -5991,13 +6024,13 @@ extern (C++) final class IndexExp : BinExp extern (D) this(const ref Loc loc, Expression e1, Expression e2) { - super(loc, EXP.index, __traits(classInstanceSize, IndexExp), e1, e2); + 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) { - super(loc, EXP.index, __traits(classInstanceSize, IndexExp), e1, e2); + super(loc, EXP.index, e1, e2); this.indexIsInBounds = indexIsInBounds; //printf("IndexExp::IndexExp('%s')\n", toChars()); } @@ -6074,7 +6107,7 @@ extern (C++) final class PostExp : BinExp { extern (D) this(EXP op, const ref Loc loc, Expression e) { - super(loc, op, __traits(classInstanceSize, PostExp), e, IntegerExp.literal!1); + super(loc, op, e, IntegerExp.literal!1); assert(op == EXP.minusMinus || op == EXP.plusPlus); } @@ -6091,7 +6124,7 @@ extern (C++) final class PreExp : UnaExp { extern (D) this(EXP op, const ref Loc loc, Expression e) { - super(loc, op, __traits(classInstanceSize, PreExp), e); + super(loc, op, e); assert(op == EXP.preMinusMinus || op == EXP.prePlusPlus); } @@ -6121,12 +6154,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) { - super(loc, EXP.assign, __traits(classInstanceSize, AssignExp), e1, e2); + super(loc, EXP.assign, e1, e2); } this(const ref Loc loc, EXP tok, Expression e1, Expression e2) { - super(loc, tok, __traits(classInstanceSize, AssignExp), e1, e2); + super(loc, tok, e1, e2); } override final bool isLvalue() @@ -6160,6 +6193,32 @@ extern (C++) class AssignExp : BinExp } } +/*********************************************************** + * When an assignment expression is lowered to a druntime call + * this class is used to store the lowering. + * It essentially behaves the same as an AssignExp, but it is + * used to not waste space for other AssignExp that are not + * lowered to anything. + */ +extern (C++) final class LoweredAssignExp : AssignExp +{ + Expression lowering; + extern (D) this(AssignExp exp, Expression lowering) + { + super(exp.loc, EXP.loweredAssignExp, exp.e1, exp.e2); + this.lowering = lowering; + } + + override const(char)* toChars() const + { + return lowering.toChars(); + } + override void accept(Visitor v) + { + v.visit(this); + } +} + /*********************************************************** */ extern (C++) final class ConstructExp : AssignExp @@ -6224,7 +6283,7 @@ extern (C++) final class AddAssignExp : BinAssignExp { extern (D) this(const ref Loc loc, Expression e1, Expression e2) { - super(loc, EXP.addAssign, __traits(classInstanceSize, AddAssignExp), e1, e2); + super(loc, EXP.addAssign, e1, e2); } override void accept(Visitor v) @@ -6240,7 +6299,7 @@ extern (C++) final class MinAssignExp : BinAssignExp { extern (D) this(const ref Loc loc, Expression e1, Expression e2) { - super(loc, EXP.minAssign, __traits(classInstanceSize, MinAssignExp), e1, e2); + super(loc, EXP.minAssign, e1, e2); } override void accept(Visitor v) @@ -6256,7 +6315,7 @@ extern (C++) final class MulAssignExp : BinAssignExp { extern (D) this(const ref Loc loc, Expression e1, Expression e2) { - super(loc, EXP.mulAssign, __traits(classInstanceSize, MulAssignExp), e1, e2); + super(loc, EXP.mulAssign, e1, e2); } override void accept(Visitor v) @@ -6272,7 +6331,7 @@ extern (C++) final class DivAssignExp : BinAssignExp { extern (D) this(const ref Loc loc, Expression e1, Expression e2) { - super(loc, EXP.divAssign, __traits(classInstanceSize, DivAssignExp), e1, e2); + super(loc, EXP.divAssign, e1, e2); } override void accept(Visitor v) @@ -6288,7 +6347,7 @@ extern (C++) final class ModAssignExp : BinAssignExp { extern (D) this(const ref Loc loc, Expression e1, Expression e2) { - super(loc, EXP.modAssign, __traits(classInstanceSize, ModAssignExp), e1, e2); + super(loc, EXP.modAssign, e1, e2); } override void accept(Visitor v) @@ -6304,7 +6363,7 @@ extern (C++) final class AndAssignExp : BinAssignExp { extern (D) this(const ref Loc loc, Expression e1, Expression e2) { - super(loc, EXP.andAssign, __traits(classInstanceSize, AndAssignExp), e1, e2); + super(loc, EXP.andAssign, e1, e2); } override void accept(Visitor v) @@ -6320,7 +6379,7 @@ extern (C++) final class OrAssignExp : BinAssignExp { extern (D) this(const ref Loc loc, Expression e1, Expression e2) { - super(loc, EXP.orAssign, __traits(classInstanceSize, OrAssignExp), e1, e2); + super(loc, EXP.orAssign, e1, e2); } override void accept(Visitor v) @@ -6336,7 +6395,7 @@ extern (C++) final class XorAssignExp : BinAssignExp { extern (D) this(const ref Loc loc, Expression e1, Expression e2) { - super(loc, EXP.xorAssign, __traits(classInstanceSize, XorAssignExp), e1, e2); + super(loc, EXP.xorAssign, e1, e2); } override void accept(Visitor v) @@ -6352,7 +6411,7 @@ extern (C++) final class PowAssignExp : BinAssignExp { extern (D) this(const ref Loc loc, Expression e1, Expression e2) { - super(loc, EXP.powAssign, __traits(classInstanceSize, PowAssignExp), e1, e2); + super(loc, EXP.powAssign, e1, e2); } override void accept(Visitor v) @@ -6368,7 +6427,7 @@ extern (C++) final class ShlAssignExp : BinAssignExp { extern (D) this(const ref Loc loc, Expression e1, Expression e2) { - super(loc, EXP.leftShiftAssign, __traits(classInstanceSize, ShlAssignExp), e1, e2); + super(loc, EXP.leftShiftAssign, e1, e2); } override void accept(Visitor v) @@ -6384,7 +6443,7 @@ extern (C++) final class ShrAssignExp : BinAssignExp { extern (D) this(const ref Loc loc, Expression e1, Expression e2) { - super(loc, EXP.rightShiftAssign, __traits(classInstanceSize, ShrAssignExp), e1, e2); + super(loc, EXP.rightShiftAssign, e1, e2); } override void accept(Visitor v) @@ -6400,7 +6459,7 @@ extern (C++) final class UshrAssignExp : BinAssignExp { extern (D) this(const ref Loc loc, Expression e1, Expression e2) { - super(loc, EXP.unsignedRightShiftAssign, __traits(classInstanceSize, UshrAssignExp), e1, e2); + super(loc, EXP.unsignedRightShiftAssign, e1, e2); } override void accept(Visitor v) @@ -6425,12 +6484,12 @@ extern (C++) class CatAssignExp : BinAssignExp { extern (D) this(const ref Loc loc, Expression e1, Expression e2) { - super(loc, EXP.concatenateAssign, __traits(classInstanceSize, CatAssignExp), e1, e2); + super(loc, EXP.concatenateAssign, e1, e2); } extern (D) this(const ref Loc loc, EXP tok, Expression e1, Expression e2) { - super(loc, tok, __traits(classInstanceSize, CatAssignExp), e1, e2); + super(loc, tok, e1, e2); } override void accept(Visitor v) @@ -6482,7 +6541,7 @@ extern (C++) final class AddExp : BinExp { extern (D) this(const ref Loc loc, Expression e1, Expression e2) { - super(loc, EXP.add, __traits(classInstanceSize, AddExp), e1, e2); + super(loc, EXP.add, e1, e2); } override void accept(Visitor v) @@ -6500,7 +6559,7 @@ extern (C++) final class MinExp : BinExp { extern (D) this(const ref Loc loc, Expression e1, Expression e2) { - super(loc, EXP.min, __traits(classInstanceSize, MinExp), e1, e2); + super(loc, EXP.min, e1, e2); } override void accept(Visitor v) @@ -6516,9 +6575,11 @@ extern (C++) final class MinExp : BinExp */ 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 { - super(loc, EXP.concatenate, __traits(classInstanceSize, CatExp), e1, e2); + super(loc, EXP.concatenate, e1, e2); } override Expression resolveLoc(const ref Loc loc, Scope* sc) @@ -6543,7 +6604,7 @@ extern (C++) final class MulExp : BinExp { extern (D) this(const ref Loc loc, Expression e1, Expression e2) { - super(loc, EXP.mul, __traits(classInstanceSize, MulExp), e1, e2); + super(loc, EXP.mul, e1, e2); } override void accept(Visitor v) @@ -6561,7 +6622,7 @@ extern (C++) final class DivExp : BinExp { extern (D) this(const ref Loc loc, Expression e1, Expression e2) { - super(loc, EXP.div, __traits(classInstanceSize, DivExp), e1, e2); + super(loc, EXP.div, e1, e2); } override void accept(Visitor v) @@ -6579,7 +6640,7 @@ extern (C++) final class ModExp : BinExp { extern (D) this(const ref Loc loc, Expression e1, Expression e2) { - super(loc, EXP.mod, __traits(classInstanceSize, ModExp), e1, e2); + super(loc, EXP.mod, e1, e2); } override void accept(Visitor v) @@ -6597,7 +6658,7 @@ extern (C++) final class PowExp : BinExp { extern (D) this(const ref Loc loc, Expression e1, Expression e2) { - super(loc, EXP.pow, __traits(classInstanceSize, PowExp), e1, e2); + super(loc, EXP.pow, e1, e2); } override void accept(Visitor v) @@ -6615,7 +6676,7 @@ extern (C++) final class ShlExp : BinExp { extern (D) this(const ref Loc loc, Expression e1, Expression e2) { - super(loc, EXP.leftShift, __traits(classInstanceSize, ShlExp), e1, e2); + super(loc, EXP.leftShift, e1, e2); } override void accept(Visitor v) @@ -6633,7 +6694,7 @@ extern (C++) final class ShrExp : BinExp { extern (D) this(const ref Loc loc, Expression e1, Expression e2) { - super(loc, EXP.rightShift, __traits(classInstanceSize, ShrExp), e1, e2); + super(loc, EXP.rightShift, e1, e2); } override void accept(Visitor v) @@ -6651,7 +6712,7 @@ extern (C++) final class UshrExp : BinExp { extern (D) this(const ref Loc loc, Expression e1, Expression e2) { - super(loc, EXP.unsignedRightShift, __traits(classInstanceSize, UshrExp), e1, e2); + super(loc, EXP.unsignedRightShift, e1, e2); } override void accept(Visitor v) @@ -6669,7 +6730,7 @@ extern (C++) final class AndExp : BinExp { extern (D) this(const ref Loc loc, Expression e1, Expression e2) { - super(loc, EXP.and, __traits(classInstanceSize, AndExp), e1, e2); + super(loc, EXP.and, e1, e2); } override void accept(Visitor v) @@ -6687,7 +6748,7 @@ extern (C++) final class OrExp : BinExp { extern (D) this(const ref Loc loc, Expression e1, Expression e2) { - super(loc, EXP.or, __traits(classInstanceSize, OrExp), e1, e2); + super(loc, EXP.or, e1, e2); } override void accept(Visitor v) @@ -6705,7 +6766,7 @@ extern (C++) final class XorExp : BinExp { extern (D) this(const ref Loc loc, Expression e1, Expression e2) { - super(loc, EXP.xor, __traits(classInstanceSize, XorExp), e1, e2); + super(loc, EXP.xor, e1, e2); } override void accept(Visitor v) @@ -6724,7 +6785,7 @@ extern (C++) final class LogicalExp : BinExp { extern (D) this(const ref Loc loc, EXP op, Expression e1, Expression e2) { - super(loc, op, __traits(classInstanceSize, LogicalExp), e1, e2); + super(loc, op, e1, e2); assert(op == EXP.andAnd || op == EXP.orOr); } @@ -6746,7 +6807,7 @@ extern (C++) final class CmpExp : BinExp { extern (D) this(EXP op, const ref Loc loc, Expression e1, Expression e2) { - super(loc, op, __traits(classInstanceSize, CmpExp), e1, e2); + super(loc, op, e1, e2); assert(op == EXP.lessThan || op == EXP.lessOrEqual || op == EXP.greaterThan || op == EXP.greaterOrEqual); } @@ -6767,7 +6828,7 @@ extern (C++) final class InExp : BinExp { extern (D) this(const ref Loc loc, Expression e1, Expression e2) { - super(loc, EXP.in_, __traits(classInstanceSize, InExp), e1, e2); + super(loc, EXP.in_, e1, e2); } override void accept(Visitor v) @@ -6785,7 +6846,7 @@ extern (C++) final class RemoveExp : BinExp { extern (D) this(const ref Loc loc, Expression e1, Expression e2) { - super(loc, EXP.remove, __traits(classInstanceSize, RemoveExp), e1, e2); + super(loc, EXP.remove, e1, e2); type = Type.tbool; } @@ -6806,7 +6867,7 @@ extern (C++) final class EqualExp : BinExp { extern (D) this(EXP op, const ref Loc loc, Expression e1, Expression e2) { - super(loc, op, __traits(classInstanceSize, EqualExp), e1, e2); + super(loc, op, e1, e2); assert(op == EXP.equal || op == EXP.notEqual); } @@ -6827,7 +6888,7 @@ extern (C++) final class IdentityExp : BinExp { extern (D) this(EXP op, const ref Loc loc, Expression e1, Expression e2) { - super(loc, op, __traits(classInstanceSize, IdentityExp), e1, e2); + super(loc, op, e1, e2); assert(op == EXP.identity || op == EXP.notIdentity); } @@ -6848,7 +6909,7 @@ extern (C++) final class CondExp : BinExp extern (D) this(const ref Loc loc, Expression econd, Expression e1, Expression e2) scope { - super(loc, EXP.question, __traits(classInstanceSize, CondExp), e1, e2); + super(loc, EXP.question, e1, e2); this.econd = econd; } @@ -6986,9 +7047,9 @@ bool isDefaultInitOp(EXP op) pure nothrow @safe @nogc */ extern (C++) class DefaultInitExp : Expression { - extern (D) this(const ref Loc loc, EXP op, int size) + extern (D) this(const ref Loc loc, EXP op) { - super(loc, op, size); + super(loc, op); } override void accept(Visitor v) @@ -7004,7 +7065,7 @@ extern (C++) final class FileInitExp : DefaultInitExp { extern (D) this(const ref Loc loc, EXP tok) { - super(loc, tok, __traits(classInstanceSize, FileInitExp)); + super(loc, tok); } override Expression resolveLoc(const ref Loc loc, Scope* sc) @@ -7035,7 +7096,7 @@ extern (C++) final class LineInitExp : DefaultInitExp { extern (D) this(const ref Loc loc) { - super(loc, EXP.line, __traits(classInstanceSize, LineInitExp)); + super(loc, EXP.line); } override Expression resolveLoc(const ref Loc loc, Scope* sc) @@ -7058,7 +7119,7 @@ extern (C++) final class ModuleInitExp : DefaultInitExp { extern (D) this(const ref Loc loc) { - super(loc, EXP.moduleString, __traits(classInstanceSize, ModuleInitExp)); + super(loc, EXP.moduleString); } override Expression resolveLoc(const ref Loc loc, Scope* sc) @@ -7083,7 +7144,7 @@ extern (C++) final class FuncInitExp : DefaultInitExp { extern (D) this(const ref Loc loc) { - super(loc, EXP.functionString, __traits(classInstanceSize, FuncInitExp)); + super(loc, EXP.functionString); } override Expression resolveLoc(const ref Loc loc, Scope* sc) @@ -7114,7 +7175,7 @@ extern (C++) final class PrettyFuncInitExp : DefaultInitExp { extern (D) this(const ref Loc loc) { - super(loc, EXP.prettyFunction, __traits(classInstanceSize, PrettyFuncInitExp)); + super(loc, EXP.prettyFunction); } override Expression resolveLoc(const ref Loc loc, Scope* sc) @@ -7159,8 +7220,7 @@ extern (C++) final class ObjcClassReferenceExp : Expression extern (D) this(const ref Loc loc, ClassDeclaration classDeclaration) { - super(loc, EXP.objcClassReference, - __traits(classInstanceSize, ObjcClassReferenceExp)); + super(loc, EXP.objcClassReference); this.classDeclaration = classDeclaration; type = objc.getRuntimeMetaclass(classDeclaration).getType(); } @@ -7183,7 +7243,7 @@ extern (C++) final class GenericExp : Expression extern (D) this(const ref Loc loc, Expression cntlExp, Types* types, Expressions* exps) { - super(loc, EXP._Generic, __traits(classInstanceSize, GenericExp)); + super(loc, EXP._Generic); this.cntlExp = cntlExp; this.types = types; this.exps = exps; @@ -7350,23 +7410,20 @@ extern(D) Modifiable checkModifiable(Expression exp, Scope* sc, ModifyFlags flag } /** - * Verify if the given identifier is any of - * _d_array{ctor,setctor,setassign,assign_l, assign_r}. + * Verify if the given identifier is _d_array{,set}ctor. * * Params: * id = the identifier to verify * * Returns: - * `true` if the identifier corresponds to a construction of assignement - * runtime hook, `false` otherwise. + * `true` if the identifier corresponds to a construction runtime hook, + * `false` otherwise. */ -bool isArrayConstructionOrAssign(const Identifier id) +bool isArrayConstruction(const Identifier id) { import dmd.id : Id; - return id == Id._d_arrayctor || id == Id._d_arraysetctor || - id == Id._d_arrayassign_l || id == Id._d_arrayassign_r || - id == Id._d_arraysetassign; + return id == Id._d_arrayctor || id == Id._d_arraysetctor; } /****************************** @@ -7418,3 +7475,135 @@ private enum EbinaryAssign = EXP.leftShiftAssign, EXP.rightShiftAssign, EXP.unsignedRightShiftAssign, EXP.concatenateAssign, EXP.concatenateElemAssign, EXP.concatenateDcharAssign, ]; + +/// Given a member of the EXP enum, get the class instance size of the corresponding Expression class. +/// Needed because the classes are `extern(C++)` +private immutable ubyte[EXP.max+1] expSize = [ + EXP.reserved: 0, + EXP.negate: __traits(classInstanceSize, NegExp), + EXP.cast_: __traits(classInstanceSize, CastExp), + EXP.null_: __traits(classInstanceSize, NullExp), + EXP.assert_: __traits(classInstanceSize, AssertExp), + EXP.array: __traits(classInstanceSize, ArrayExp), + EXP.call: __traits(classInstanceSize, CallExp), + EXP.address: __traits(classInstanceSize, AddrExp), + EXP.type: __traits(classInstanceSize, TypeExp), + EXP.throw_: __traits(classInstanceSize, ThrowExp), + EXP.new_: __traits(classInstanceSize, NewExp), + EXP.delete_: __traits(classInstanceSize, DeleteExp), + EXP.star: __traits(classInstanceSize, PtrExp), + EXP.symbolOffset: __traits(classInstanceSize, SymOffExp), + EXP.variable: __traits(classInstanceSize, VarExp), + EXP.dotVariable: __traits(classInstanceSize, DotVarExp), + EXP.dotIdentifier: __traits(classInstanceSize, DotIdExp), + EXP.dotTemplateInstance: __traits(classInstanceSize, DotTemplateInstanceExp), + EXP.dotType: __traits(classInstanceSize, DotTypeExp), + EXP.slice: __traits(classInstanceSize, SliceExp), + EXP.arrayLength: __traits(classInstanceSize, ArrayLengthExp), + EXP.dollar: __traits(classInstanceSize, DollarExp), + EXP.template_: __traits(classInstanceSize, TemplateExp), + EXP.dotTemplateDeclaration: __traits(classInstanceSize, DotTemplateExp), + EXP.declaration: __traits(classInstanceSize, DeclarationExp), + EXP.dSymbol: __traits(classInstanceSize, DsymbolExp), + EXP.typeid_: __traits(classInstanceSize, TypeidExp), + EXP.uadd: __traits(classInstanceSize, UAddExp), + EXP.remove: __traits(classInstanceSize, RemoveExp), + EXP.newAnonymousClass: __traits(classInstanceSize, NewAnonClassExp), + EXP.arrayLiteral: __traits(classInstanceSize, ArrayLiteralExp), + EXP.assocArrayLiteral: __traits(classInstanceSize, AssocArrayLiteralExp), + EXP.structLiteral: __traits(classInstanceSize, StructLiteralExp), + EXP.classReference: __traits(classInstanceSize, ClassReferenceExp), + EXP.thrownException: __traits(classInstanceSize, ThrownExceptionExp), + EXP.delegatePointer: __traits(classInstanceSize, DelegatePtrExp), + EXP.delegateFunctionPointer: __traits(classInstanceSize, DelegateFuncptrExp), + EXP.lessThan: __traits(classInstanceSize, CmpExp), + EXP.greaterThan: __traits(classInstanceSize, CmpExp), + EXP.lessOrEqual: __traits(classInstanceSize, CmpExp), + EXP.greaterOrEqual: __traits(classInstanceSize, CmpExp), + EXP.equal: __traits(classInstanceSize, EqualExp), + EXP.notEqual: __traits(classInstanceSize, EqualExp), + EXP.identity: __traits(classInstanceSize, IdentityExp), + EXP.notIdentity: __traits(classInstanceSize, IdentityExp), + EXP.index: __traits(classInstanceSize, IndexExp), + EXP.is_: __traits(classInstanceSize, IsExp), + EXP.leftShift: __traits(classInstanceSize, ShlExp), + EXP.rightShift: __traits(classInstanceSize, ShrExp), + EXP.leftShiftAssign: __traits(classInstanceSize, ShlAssignExp), + EXP.rightShiftAssign: __traits(classInstanceSize, ShrAssignExp), + EXP.unsignedRightShift: __traits(classInstanceSize, UshrExp), + EXP.unsignedRightShiftAssign: __traits(classInstanceSize, UshrAssignExp), + EXP.concatenate: __traits(classInstanceSize, CatExp), + EXP.concatenateAssign: __traits(classInstanceSize, CatAssignExp), + EXP.concatenateElemAssign: __traits(classInstanceSize, CatElemAssignExp), + EXP.concatenateDcharAssign: __traits(classInstanceSize, CatDcharAssignExp), + EXP.add: __traits(classInstanceSize, AddExp), + EXP.min: __traits(classInstanceSize, MinExp), + EXP.addAssign: __traits(classInstanceSize, AddAssignExp), + EXP.minAssign: __traits(classInstanceSize, MinAssignExp), + EXP.mul: __traits(classInstanceSize, MulExp), + EXP.div: __traits(classInstanceSize, DivExp), + EXP.mod: __traits(classInstanceSize, ModExp), + EXP.mulAssign: __traits(classInstanceSize, MulAssignExp), + EXP.divAssign: __traits(classInstanceSize, DivAssignExp), + EXP.modAssign: __traits(classInstanceSize, ModAssignExp), + EXP.and: __traits(classInstanceSize, AndExp), + EXP.or: __traits(classInstanceSize, OrExp), + EXP.xor: __traits(classInstanceSize, XorExp), + EXP.andAssign: __traits(classInstanceSize, AndAssignExp), + EXP.orAssign: __traits(classInstanceSize, OrAssignExp), + EXP.xorAssign: __traits(classInstanceSize, XorAssignExp), + EXP.assign: __traits(classInstanceSize, AssignExp), + EXP.not: __traits(classInstanceSize, NotExp), + EXP.tilde: __traits(classInstanceSize, ComExp), + EXP.plusPlus: __traits(classInstanceSize, PostExp), + EXP.minusMinus: __traits(classInstanceSize, PostExp), + EXP.construct: __traits(classInstanceSize, ConstructExp), + EXP.blit: __traits(classInstanceSize, BlitExp), + EXP.dot: __traits(classInstanceSize, DotExp), + EXP.comma: __traits(classInstanceSize, CommaExp), + EXP.question: __traits(classInstanceSize, CondExp), + EXP.andAnd: __traits(classInstanceSize, LogicalExp), + EXP.orOr: __traits(classInstanceSize, LogicalExp), + EXP.prePlusPlus: __traits(classInstanceSize, PreExp), + EXP.preMinusMinus: __traits(classInstanceSize, PreExp), + EXP.identifier: __traits(classInstanceSize, IdentifierExp), + EXP.string_: __traits(classInstanceSize, StringExp), + EXP.this_: __traits(classInstanceSize, ThisExp), + EXP.super_: __traits(classInstanceSize, SuperExp), + EXP.halt: __traits(classInstanceSize, HaltExp), + EXP.tuple: __traits(classInstanceSize, TupleExp), + EXP.error: __traits(classInstanceSize, ErrorExp), + EXP.void_: __traits(classInstanceSize, VoidInitExp), + EXP.int64: __traits(classInstanceSize, IntegerExp), + EXP.float64: __traits(classInstanceSize, RealExp), + EXP.complex80: __traits(classInstanceSize, ComplexExp), + EXP.import_: __traits(classInstanceSize, ImportExp), + EXP.delegate_: __traits(classInstanceSize, DelegateExp), + EXP.function_: __traits(classInstanceSize, FuncExp), + EXP.mixin_: __traits(classInstanceSize, MixinExp), + EXP.in_: __traits(classInstanceSize, InExp), + EXP.break_: __traits(classInstanceSize, CTFEExp), + EXP.continue_: __traits(classInstanceSize, CTFEExp), + EXP.goto_: __traits(classInstanceSize, CTFEExp), + EXP.scope_: __traits(classInstanceSize, ScopeExp), + EXP.traits: __traits(classInstanceSize, TraitsExp), + EXP.overloadSet: __traits(classInstanceSize, OverExp), + EXP.line: __traits(classInstanceSize, LineInitExp), + EXP.file: __traits(classInstanceSize, FileInitExp), + EXP.fileFullPath: __traits(classInstanceSize, FileInitExp), + EXP.moduleString: __traits(classInstanceSize, ModuleInitExp), + EXP.functionString: __traits(classInstanceSize, FuncInitExp), + EXP.prettyFunction: __traits(classInstanceSize, PrettyFuncInitExp), + EXP.pow: __traits(classInstanceSize, PowExp), + EXP.powAssign: __traits(classInstanceSize, PowAssignExp), + EXP.vector: __traits(classInstanceSize, VectorExp), + EXP.voidExpression: __traits(classInstanceSize, CTFEExp), + EXP.cantExpression: __traits(classInstanceSize, CTFEExp), + EXP.showCtfeContext: __traits(classInstanceSize, CTFEExp), + EXP.objcClassReference: __traits(classInstanceSize, ObjcClassReferenceExp), + EXP.vectorArray: __traits(classInstanceSize, VectorArrayExp), + EXP.compoundLiteral: __traits(classInstanceSize, CompoundLiteralExp), + EXP._Generic: __traits(classInstanceSize, GenericExp), + EXP.interval: __traits(classInstanceSize, IntervalExp), + EXP.loweredAssignExp : __traits(classInstanceSize, LoweredAssignExp), +]; diff --git a/dmd/expression.h b/dmd/expression.h index b4dc9988db0..35ec908b5a5 100644 --- a/dmd/expression.h +++ b/dmd/expression.h @@ -38,6 +38,7 @@ class TemplateDeclaration; class ClassDeclaration; class OverloadSet; class StringExp; +class LoweredAssignExp; struct UnionExp; #ifdef IN_GCC typedef union tree_node Symbol; @@ -95,12 +96,12 @@ enum class ModifyFlags class Expression : public ASTNode { public: - EXP op; // to minimize use of dynamic_cast - unsigned char size; // # of bytes in Expression so we can copy() it - bool parens; // if this is a parenthesized expression 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 + size_t size() const; static void _init(); Expression *copy(); virtual Expression *syntaxCopy(); @@ -256,6 +257,7 @@ class Expression : public ASTNode UnaExp* isUnaExp(); BinExp* isBinExp(); BinAssignExp* isBinAssignExp(); + LoweredAssignExp* isLoweredAssignExp(); void accept(Visitor *v) override { v->visit(this); } }; @@ -347,7 +349,7 @@ class DsymbolExp final : public Expression { public: Dsymbol *s; - bool hasOverloads; + d_bool hasOverloads; DsymbolExp *syntaxCopy() override; bool isLvalue() override; @@ -386,12 +388,12 @@ class NullExp final : public Expression class StringExp final : public Expression { public: + utf8_t postfix; // 'c', 'w', 'd' + OwnedBy ownedByCtfe; void *string; // char, wchar, or dchar data size_t len; // number of chars, wchars, or dchars unsigned char sz; // 1: char, 2: wchar, 4: dchar - unsigned char committed; // !=0 if type is committed - utf8_t postfix; // 'c', 'w', 'd' - OwnedBy ownedByCtfe; + bool committed; // if type is committed static StringExp *create(const Loc &loc, const char *s); static StringExp *create(const Loc &loc, const void *s, d_size_t len); @@ -448,10 +450,10 @@ class TupleExp final : public Expression class ArrayLiteralExp final : public Expression { public: + OwnedBy ownedByCtfe; + d_bool onstack; Expression *basis; Expressions *elements; - OwnedBy ownedByCtfe; - bool onstack; static ArrayLiteralExp *create(const Loc &loc, Expressions *elements); static void emplace(UnionExp *pue, const Loc &loc, Expressions *elements); @@ -468,9 +470,9 @@ class ArrayLiteralExp final : public Expression class AssocArrayLiteralExp final : public Expression { public: + OwnedBy ownedByCtfe; Expressions *keys; Expressions *values; - OwnedBy ownedByCtfe; bool equals(const RootObject * const o) const override; AssocArrayLiteralExp *syntaxCopy() override; @@ -486,15 +488,21 @@ class StructLiteralExp final : public Expression 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) + union + { #if IN_LLVM - // With the introduction of pointers returned from CTFE, struct literals can - // now contain pointers to themselves. While in toElem, contains a pointer - // to the memory used to build the literal for resolving such references. - llvm::Value *inProgressMemory; + // With the introduction of pointers returned from CTFE, struct literals can + // now contain pointers to themselves. While in toElem, contains a pointer + // to the memory used to build the literal for resolving such references. + llvm::Value *inProgressMemory; #else - Symbol *sym; // back end symbol to initialize with literal + Symbol *sym; // back end symbol to initialize with literal #endif + // those fields need to prevent a infinite recursion when one field of struct initialized with 'this' pointer. + StructLiteralExp *inlinecopy; + }; + /** pointer to the origin instance of the expression. * once a new expression is created, origin is set to 'this'. * anytime when an expression copy is created, 'origin' pointer is set to @@ -502,18 +510,16 @@ class StructLiteralExp final : public Expression */ StructLiteralExp *origin; - // those fields need to prevent a infinite recursion when one field of struct initialized with 'this' pointer. - StructLiteralExp *inlinecopy; /** anytime when recursive function is calling, 'stageflags' marks with bit flag of * current stage and unmarks before return from this function. * 'inlinecopy' uses similar 'stageflags' and from multiple evaluation 'doInline' * (with infinite recursion) of this expression. */ - int stageflags; + uint8_t stageflags; - bool useStaticInit; // if this is true, use the StructDeclaration's init symbol - bool isOriginal; // used when moving instances to indicate `this is this.origin` + 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 = NULL); @@ -573,8 +579,8 @@ class NewExp final : public Expression Expression *argprefix; // expression to be evaluated just before arguments[] CtorDeclaration *member; // constructor function - bool onstack; // allocate on stack - bool thrownew; // this NewExp is the expression of a ThrowStatement + d_bool onstack; // allocate on stack + d_bool thrownew; // this NewExp is the expression of a ThrowStatement Expression *lowering; // lowered druntime hook: `_d_newclass` @@ -602,7 +608,7 @@ class SymbolExp : public Expression public: Declaration *var; Dsymbol *originalScope; - bool hasOverloads; + d_bool hasOverloads; void accept(Visitor *v) override { v->visit(this); } }; @@ -624,7 +630,7 @@ class SymOffExp final : public SymbolExp class VarExp final : public SymbolExp { public: - bool delegateWasExtracted; + d_bool delegateWasExtracted; static VarExp *create(const Loc &loc, Declaration *var, bool hasOverloads = true); bool equals(const RootObject * const o) const override; bool isLvalue() override; @@ -729,7 +735,6 @@ class UnaExp : public Expression { public: Expression *e1; - Type *att1; // Save alias this type to detect recursion UnaExp *syntaxCopy() override; Expression *incompatibleTypes(); @@ -800,9 +805,9 @@ class DotIdExp final : public UnaExp { public: Identifier *ident; - bool noderef; // true if the result of the expression will never be dereferenced - bool wantsym; // do not replace Symbol with its initializer during semantic() - bool arrow; // ImportC: if -> instead of . + d_bool noderef; // true if the result of the expression will never be dereferenced + 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); void accept(Visitor *v) override { v->visit(this); } @@ -822,7 +827,7 @@ class DotVarExp final : public UnaExp { public: Declaration *var; - bool hasOverloads; + d_bool hasOverloads; bool isLvalue() override; Expression *toLvalue(Scope *sc, Expression *e) override; @@ -846,7 +851,7 @@ class DelegateExp final : public UnaExp { public: FuncDeclaration *func; - bool hasOverloads; + d_bool hasOverloads; VarDeclaration *vthis2; // container for multi-context @@ -867,9 +872,9 @@ class CallExp final : public UnaExp Expressions *arguments; // function arguments Identifiers *names; FuncDeclaration *f; // symbol to call - bool directcall; // true if a virtual call is devirtualized - bool inDebugStatement; // true if this was in a debug statement - bool ignoreAttributes; // don't enforce attributes (e.g. call @gc function in @nogc code) + d_bool directcall; // true if a virtual call is devirtualized + d_bool inDebugStatement; // true if this was in a debug statement + d_bool ignoreAttributes; // don't enforce attributes (e.g. call @gc function in @nogc code) VarDeclaration *vthis2; // container for multi-context static CallExp *create(const Loc &loc, Expression *e, Expressions *exps); @@ -928,7 +933,7 @@ class NotExp final : public UnaExp class DeleteExp final : public UnaExp { public: - bool isRAII; + d_bool isRAII; void accept(Visitor *v) override { v->visit(this); } }; @@ -973,10 +978,17 @@ class SliceExp final : public UnaExp Expression *upr; // NULL if implicit 0 Expression *lwr; // NULL if implicit [length - 1] VarDeclaration *lengthVar; - bool upperIsInBounds; // true if upr <= e1.length - bool lowerIsLessThanUpper; // true if lwr <= upr - bool arrayop; // an array operation, rather than a slice + bool upperIsInBounds() const; // true if upr <= e1.length + bool upperIsInBounds(bool v); + bool lowerIsLessThanUpper() const; // true if lwr <= upr + bool lowerIsLessThanUpper(bool v); + bool arrayop() const; // an array operation, rather than a slice + bool arrayop(bool v); +private: + uint8_t bitFields; + +public: SliceExp *syntaxCopy() override; bool isLvalue() override; Expression *toLvalue(Scope *sc, Expression *e) override; @@ -1047,9 +1059,8 @@ class DotExp final : public BinExp class CommaExp final : public BinExp { public: - bool isGenerated; - bool allowCommaExp; - + d_bool isGenerated; + d_bool allowCommaExp; bool isLvalue() override; Expression *toLvalue(Scope *sc, Expression *e) override; Expression *modifiableLvalue(Scope *sc, Expression *e) override; @@ -1082,8 +1093,8 @@ class IndexExp final : public BinExp { public: VarDeclaration *lengthVar; - bool modifiable; - bool indexIsInBounds; // true if 0 <= e2 && e2 <= e1.length - 1 + d_bool modifiable; + d_bool indexIsInBounds; // true if 0 <= e2 && e2 <= e1.length - 1 IndexExp *syntaxCopy() override; bool isLvalue() override; @@ -1133,6 +1144,15 @@ class ConstructExp final : public AssignExp void accept(Visitor *v) override { v->visit(this); } }; +class LoweredAssignExp final : public AssignExp +{ +public: + Expression *lowering; + + const char *toChars() const override; + void accept(Visitor *v) override { v->visit(this); } +}; + class BlitExp final : public AssignExp { public: @@ -1244,6 +1264,8 @@ class MinExp final : public BinExp class CatExp final : public BinExp { public: + Expression *lowering; // call to druntime hook `_d_arraycatnTX` + void accept(Visitor *v) override { v->visit(this); } }; @@ -1429,7 +1451,7 @@ struct UnionExp UnionExp(Expression *e) { - memcpy(this, (void *)e, e->size); + memcpy(this, (void *)e, e->size()); } /* Extract pointer to Expression diff --git a/dmd/expressionsem.d b/dmd/expressionsem.d index f662f49c93f..9fc6c96007f 100644 --- a/dmd/expressionsem.d +++ b/dmd/expressionsem.d @@ -65,7 +65,6 @@ import dmd.parse; import dmd.printast; import dmd.root.array; import dmd.root.ctfloat; -import dmd.root.file; import dmd.root.filename; import dmd.common.outbuffer; import dmd.root.rootobject; @@ -635,7 +634,7 @@ private Expression resolveUFCS(Scope* sc, CallExp ce) } else if (auto dti = ce.e1.isDotTemplateInstanceExp()) { - if (Expression ey = dti.dotTemplateSemanticProp(sc, 1)) + if (Expression ey = dti.dotTemplateSemanticProp(sc, DotExpFlag.gag)) { ce.e1 = ey; return null; @@ -1190,6 +1189,13 @@ L1: */ private bool haveSameThis(FuncDeclaration outerFunc, FuncDeclaration calledFunc) { + // https://issues.dlang.org/show_bug.cgi?id=24013 + // traits(getOverloads) inserts an alias to select the overload. + // When searching for the right this we need to use the aliased + // overload/function, not the alias. + outerFunc = outerFunc.toAliasFunc(); + calledFunc = calledFunc.toAliasFunc(); + auto thisAd = outerFunc.isMemberLocal(); if (!thisAd) return false; @@ -1221,7 +1227,7 @@ private bool haveSameThis(FuncDeclaration outerFunc, FuncDeclaration calledFunc) /*************************************** * Pull out any properties. */ -private Expression resolvePropertiesX(Scope* sc, Expression e1, Expression e2 = null) +private Expression resolvePropertiesX(Scope* sc, Expression e1, Expression e2 = null, BinExp saveAtts = null) { //printf("resolvePropertiesX, e1 = %s %s, e2 = %s\n", EXPtoString(e1.op).ptr, e1.toChars(), e2 ? e2.toChars() : null); Loc loc = e1.loc; @@ -1295,7 +1301,14 @@ private Expression resolvePropertiesX(Scope* sc, Expression e1, Expression 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); } } @@ -1413,7 +1426,14 @@ private Expression resolvePropertiesX(Scope* sc, Expression e1, Expression 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); } } @@ -2177,7 +2197,11 @@ private bool functionParameters(const ref Loc loc, Scope* sc, } // Allow 'lazy' to imply 'scope' - lazy parameters can be passed along // as lazy parameters to the next function, but that isn't escaping. - else if (!(pStc & STC.lazy_)) + // The arguments of `_d_arraycatnTX` are already handled in + // expressionsem.d, via `checkNewEscape`. Without `-dip1000`, the + // check does not return an error, so the lowering of `a ~ b` to + // `_d_arraycatnTX(a, b)` still occurs. + else if (!(pStc & STC.lazy_) && (!fd || fd.ident != Id._d_arraycatnTX)) { /* Argument value can escape from the called function. * Check arg to see if it matters. @@ -2208,6 +2232,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); + tmp.storage_class |= STC.exptemp; auto declareTmp = new DeclarationExp(ale.loc, tmp); auto castToSlice = new CastExp(ale.loc, new VarExp(ale.loc, tmp), p.type.substWildTo(MODFlags.mutable)); @@ -2270,7 +2295,8 @@ private bool functionParameters(const ref Loc loc, Scope* sc, default: break; } - if (tf.parameterList.varargs == VarArg.variadic) + if (tf.parameterList.varargs == VarArg.variadic || + tf.parameterList.varargs == VarArg.KRvariadic) { const(char)* p = tf.linkage == LINK.c ? "extern(C)" : "extern(C++)"; if (arg.type.ty == Tarray) @@ -2353,30 +2379,18 @@ private bool functionParameters(const ref Loc loc, Scope* sc, } /* Remaining problems: - * 1. order of evaluation - some function push L-to-R, others R-to-L. Until we resolve what array assignment does (which is - * implemented by calling a function) we'll defer this for now. - * 2. value structs (or static arrays of them) that need to be copy constructed - * 3. value structs (or static arrays of them) that have destructors, and subsequent arguments that may throw before the + * 1. value structs (or static arrays of them) that need to be copy constructed + * 2. value structs (or static arrays of them) that have destructors, and subsequent arguments that may throw before the * function gets called. - * 4. value structs need to be destructed after the function call for platforms where the caller destroys the arguments. - * 2, 3 and 4 are handled by doing the argument construction in 'eprefix' so that if a later argument throws, they are cleaned + * 3. value structs need to be destructed after the function call for platforms where the caller destroys the arguments. + * Those are handled by doing the argument construction in 'eprefix' so that if a later argument throws, they are cleaned * up properly. Pushing arguments on the stack then cannot fail. */ { - /* TODO: tackle problem 1) - */ - const bool leftToRight = true; // TODO: Any cases that need rightToLeft? - if (!leftToRight) - assert(nargs == nparams); // no variadics for RTL order, as they would probably be evaluated LTR and so add complexity - - /* Does Problem (4) apply? + /* Does Problem (3) apply? */ const bool callerDestroysArgs = !target.isCalleeDestroyingArgs(tf); - const ptrdiff_t start = (leftToRight ? 0 : cast(ptrdiff_t)nargs - 1); - const ptrdiff_t end = (leftToRight ? cast(ptrdiff_t)nargs : -1); - const ptrdiff_t step = (leftToRight ? 1 : -1); - /* Compute indices of last throwing argument and first arg needing destruction. * Used to not set up destructors unless an arg needs destruction on a throw * in a later argument. @@ -2384,7 +2398,7 @@ private bool functionParameters(const ref Loc loc, Scope* sc, ptrdiff_t lastthrow = -1; // last argument that may throw ptrdiff_t firstdtor = -1; // first argument that needs destruction ptrdiff_t lastdtor = -1; // last argument that needs destruction - for (ptrdiff_t i = start; i != end; i += step) + for (ptrdiff_t i = 0; i != nargs; i++) { Expression arg = (*arguments)[i]; if (canThrow(arg, sc.func, false)) @@ -2401,12 +2415,12 @@ private bool functionParameters(const ref Loc loc, Scope* sc, } } - /* Do we need 'eprefix' for problems 3 or 4? + /* Do we need 'eprefix' for problems 2 or 3? */ const bool needsPrefix = callerDestroysArgs ? firstdtor >= 0 // true if any argument needs destruction : firstdtor >= 0 && lastthrow >= 0 && - (lastthrow - firstdtor) * step > 0; // last throw after first destruction + (lastthrow - firstdtor) > 0; // last throw after first destruction const ptrdiff_t lastPrefix = callerDestroysArgs ? lastdtor // up to last argument requiring destruction : lastthrow; // up to last potentially throwing argument @@ -2426,7 +2440,7 @@ private bool functionParameters(const ref Loc loc, Scope* sc, eprefix = ae.expressionSemantic(sc); } - for (ptrdiff_t i = start; i != end; i += step) + for (ptrdiff_t i = 0; i != nargs; i++) { Expression arg = (*arguments)[i]; //printf("arg[%d]: %s\n", cast(int)i, arg.toChars()); @@ -2442,12 +2456,12 @@ private bool functionParameters(const ref Loc loc, Scope* sc, /* Do we have 'eprefix' and aren't past 'lastPrefix' yet? * Then declare a temporary variable for this arg and append that declaration - * to 'eprefix', which will implicitly take care of potential problem 2) for + * to 'eprefix', which will implicitly take care of potential problem 1) for * this arg. * 'eprefix' will therefore finally contain all args up to and including 'lastPrefix', * excluding all lazy parameters. */ - if (needsPrefix && (lastPrefix - i) * step >= 0) + if (needsPrefix && (lastPrefix - i) >= 0) { const bool needsDtor = !isRef && arg.type.needsDestruction() && // Problem 3: last throwing arg doesn't require dtor patching @@ -2470,7 +2484,7 @@ private bool functionParameters(const ref Loc loc, Scope* sc, } else { - /* Problem 3: Modify the destructor so it only runs if gate==false, + /* Problem 2: Modify the destructor so it only runs if gate==false, * i.e., only if there was a throw while constructing the args */ if (!needsDtor) @@ -2505,7 +2519,7 @@ private bool functionParameters(const ref Loc loc, Scope* sc, arg = arg.expressionSemantic(sc); } - /* Problem 3: Last throwing arg? + /* Problem 2: Last throwing arg? * Then finalize eprefix => (eprefix, gate = true), i.e., disable the * dtors right after constructing the last throwing arg. * From now on, the callee will take care of destructing the args because @@ -2519,7 +2533,7 @@ private bool functionParameters(const ref Loc loc, Scope* sc, } else // not part of 'eprefix' { - /* Handle problem 2) by calling the copy constructor for value structs + /* Handle problem 1) by calling the copy constructor for value structs * (or static arrays of them) if appropriate. */ Type tv = arg.type.baseElemOf(); @@ -2676,6 +2690,34 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { if (!e.type) e.type = Type.tfloat64; + else if (e.type.isimaginary && sc.flags & SCOPE.Cfile) + { + /* Convert to core.stdc.config.complex + */ + Type t = getComplexLibraryType(e.loc, sc, e.type.ty); + if (t.ty == Terror) + return setError(); + + Type tf; + switch (e.type.ty) + { + case Timaginary32: tf = Type.tfloat32; break; + case Timaginary64: tf = Type.tfloat64; break; + case Timaginary80: tf = Type.tfloat80; break; + default: + assert(0); + } + + /* Construct ts{re : 0.0, im : e} + */ + TypeStruct ts = t.isTypeStruct; + Expressions* elements = new Expressions(2); + (*elements)[0] = new RealExp(e.loc, CTFloat.zero, tf); + (*elements)[1] = new RealExp(e.loc, e.toImaginary(), tf); + Expression sle = new StructLiteralExp(e.loc, ts.sym, elements); + result = sle.expressionSemantic(sc); + return; + } else e.type = e.type.typeSemantic(e.loc, sc); result = e; @@ -2938,7 +2980,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (!s) { e.error("`%s` is not in a class or struct scope", e.toChars()); - goto Lerr; + return setError(); } ClassDeclaration cd = s.isClassDeclaration(); if (cd) @@ -2957,7 +2999,10 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } } if (!fd) - goto Lerr; + { + e.error("`this` is only defined in non-static member functions, not `%s`", sc.parent.toChars()); + return setError(); + } assert(fd.vthis); e.var = fd.vthis; @@ -2972,11 +3017,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor return setError(); result = e; - return; - - Lerr: - e.error("`this` is only defined in non-static member functions, not `%s`", sc.parent.toChars()); - result = ErrorExp.get(); } override void visit(SuperExp e) @@ -3006,7 +3046,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (!s) { e.error("`%s` is not in a class scope", e.toChars()); - goto Lerr; + return setError(); } cd = s.isClassDeclaration(); if (cd) @@ -3015,7 +3055,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (!cd) { e.error("class `%s` has no `super`", s.toChars()); - goto Lerr; + return setError(); } e.type = cd.type; result = e; @@ -3114,7 +3154,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor e.type = Type.tuns32.sarrayOf(e.len + 1); else e.type = Type.tdchar.immutableOf().arrayOf(); - e.committed = 1; + e.committed = true; break; case 'w': @@ -3139,11 +3179,11 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor e.type = Type.tuns16.sarrayOf(e.len + 1); else e.type = Type.twchar.immutableOf().arrayOf(); - e.committed = 1; + e.committed = true; break; case 'c': - e.committed = 1; + e.committed = true; goto default; default: @@ -3865,9 +3905,13 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor result = id.expressionSemantic(sc); return; } - // LDC: not using the `_d_newclassT` lowering yet - else if (!IN_LLVM && !exp.onstack && !exp.type.isscope()) + 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 { + /* replace `new T(arguments)` with `core.lifetime._d_newclassT!T(arguments)` + * or `_d_newclassTTrace` + */ auto hook = global.params.tracegc ? Id._d_newclassTTrace : Id._d_newclassT; if (!verifyHookExist(exp.loc, *sc, hook, "new class")) return setError(); @@ -4539,6 +4583,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } } + Type att = null; Lagain: //printf("Lagain: %s\n", toChars()); exp.f = null; @@ -4749,7 +4794,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor // overload of opCall, therefore it's a call if (exp.e1.op != EXP.type) { - if (sd.aliasthis && !isRecursiveAliasThis(exp.att1, exp.e1.type)) + if (sd.aliasthis && !isRecursiveAliasThis(att, exp.e1.type)) { exp.e1 = resolveAliasThis(sc, exp.e1); goto Lagain; @@ -4838,10 +4883,17 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor return null; if (f) { - /* Error if match in more than one overload set, + /* Match in more than one overload set, * even if one is a 'better' match than the other. */ - ScopeDsymbol.multiplyDefined(loc, f, f2); + 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; @@ -5209,13 +5261,13 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor else if (sc.func && sc.intypeof != 1 && !(sc.flags & (SCOPE.ctfe | SCOPE.debug_))) { bool err = false; - if (!tf.purity && sc.func.setImpure()) + if (!tf.purity && sc.func.setImpure(exp.loc, "`pure` %s `%s` cannot call impure `%s`", exp.e1)) { exp.error("`pure` %s `%s` cannot call impure %s `%s`", sc.func.kind(), sc.func.toPrettyChars(), p, exp.e1.toChars()); err = true; } - if (!tf.isnogc && sc.func.setGC()) + if (!tf.isnogc && sc.func.setGC(exp.loc, "`@nogc` %s `%s` cannot call non-@nogc `%s`", exp.e1)) { exp.error("`@nogc` %s `%s` cannot call non-@nogc %s `%s`", sc.func.kind(), sc.func.toPrettyChars(), p, exp.e1.toChars()); @@ -6172,7 +6224,10 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor uint errors = global.errors; const len = buf.length; const str = buf.extractChars()[0 .. len]; - scope p = new Parser!ASTCodegen(exp.loc, sc._module, str, false, global.errorSink); + const bool doUnittests = global.params.useUnitTests || global.params.ddoc.doOutput || global.params.dihdr.doOutput; + 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.vin; p.nextToken(); //printf("p.loc.linnum = %d\n", p.loc.linnum); @@ -6301,19 +6356,8 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } else { - auto readResult = File.read(resolvedNamez); - if (!readResult.success) - { - e.error("cannot read file `%s`", resolvedNamez.ptr); - return setError(); - } - else - { - // take ownership of buffer (probably leaking) - auto data = readResult.extractSlice(); - se = new StringExp(e.loc, data); - global.fileManager.add(fileName, data); - } + e.error("cannot read file `%s`", resolvedNamez.ptr); + return setError(); } } result = se.expressionSemantic(sc); @@ -6327,7 +6371,10 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor printf("AssertExp::semantic('%s')\n", exp.toChars()); } - const generateMsg = !exp.msg && global.params.checkAction == CHECKACTION.context && global.params.useAssert == CHECKENABLE.on; + const generateMsg = !exp.msg && + sc.needsCodegen() && // let ctfe interpreter handle the error message + global.params.checkAction == CHECKACTION.context && + global.params.useAssert == CHECKENABLE.on; Expression temporariesPrefix; if (generateMsg) @@ -6640,7 +6687,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { import dmd.statementsem; - if (StatementSemanticVisitor.throwSemantic(te.loc, te.e1, sc)) + if (throwSemantic(te.loc, te.e1, sc)) result = te; else setError(); @@ -6933,7 +6980,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor return; } // Indicate we need to resolve by UFCS. - Expression e = exp.dotTemplateSemanticProp(sc, 1); + Expression e = exp.dotTemplateSemanticProp(sc, DotExpFlag.gag); if (!e) e = resolveUFCSProperties(sc, exp); if (e is exp) @@ -7840,7 +7887,7 @@ version (IN_LLVM) } } - if(t1b.ty == Tarray && exp.e1.op != EXP.arrayLiteral && (sc.flags & SCOPE.ctfe) == 0) + if(t1b.ty == Tarray && exp.e1.op != EXP.arrayLiteral && sc.needsCodegen()) { auto tFrom = t1b.nextOf(); auto tTo = tob.nextOf(); @@ -8930,6 +8977,7 @@ version (IN_LLVM) assert((*ae.arguments)[0].op == EXP.interval); ie = cast(IntervalExp)(*ae.arguments)[0]; } + Type att = null; // first cyclic `alias this` type while (true) { if (ae.e1.op == EXP.error) @@ -9004,7 +9052,7 @@ version (IN_LLVM) // No operator overloading member function found yet, but // there might be an alias this to try. - if (ad.aliasthis && !isRecursiveAliasThis(ae.att1, ae.e1.type)) + if (ad.aliasthis && !isRecursiveAliasThis(att, ae.e1.type)) { /* Rewrite (a[arguments] op e2) as: * a.aliasthis[arguments] op e2 @@ -9032,7 +9080,7 @@ version (IN_LLVM) */ if (auto dti = e1x.isDotTemplateInstanceExp()) { - Expression e = dti.dotTemplateSemanticProp(sc, 1); + Expression e = dti.dotTemplateSemanticProp(sc, DotExpFlag.gag); if (!e) { return setResult(resolveUFCSProperties(sc, e1x, exp.e2)); @@ -9086,7 +9134,7 @@ version (IN_LLVM) * or: * f() = value */ - if (Expression e = resolvePropertiesX(sc, e1x, exp.e2)) + if (Expression e = resolvePropertiesX(sc, e1x, exp.e2, exp)) return setResult(e); if (e1x.checkRightThis(sc)) @@ -9780,6 +9828,12 @@ version (IN_LLVM) return setResult(res); } + if (!sc.needsCodegen()) // if compile time creature only + { + exp.type = Type.tsize_t; + return setResult(exp); + } + // Lower to object._d_arraysetlengthTImpl!(typeof(e1))._d_arraysetlengthT{,Trace}(e1, e2) Expression id = new IdentifierExp(ale.loc, Id.empty); id = new DotIdExp(ale.loc, id, Id.object); @@ -9801,10 +9855,11 @@ version (IN_LLVM) arguments.push(ale.e1); arguments.push(exp.e2); - Expression ce = new CallExp(ale.loc, id, arguments); - auto res = ce.expressionSemantic(sc); + Expression ce = new CallExp(ale.loc, id, arguments).expressionSemantic(sc); + auto res = new LoweredAssignExp(exp, ce); // if (global.params.verbose) // message("lowered %s =>\n %s", exp.toChars(), res.toChars()); + res.type = Type.tsize_t; return setResult(res); } else if (auto se = exp.e1.isSliceExp()) @@ -10133,6 +10188,10 @@ version (IN_LLVM) } } } + + if (!sc.needsCodegen()) // interpreter can handle these + return setResult(res); + const lowerToArrayCtor = ( (rhsType.ty == Tarray && !rhs.isArrayLiteralExp) || (rhsType.ty == Tsarray && rhs.isLvalue) ) && @@ -10272,6 +10331,9 @@ version (IN_LLVM) if (global.params.verbose) message("lowered %s =>\n %s", ae.toChars(), res.toChars()); + res = new LoweredAssignExp(ae, res); + res.type = ae.type; + return res; } @@ -10521,7 +10583,7 @@ version (IN_LLVM) result = res; if ((exp.op == EXP.concatenateAssign || exp.op == EXP.concatenateElemAssign) && - !(sc.flags & (SCOPE.ctfe | SCOPE.compile))) + sc.needsCodegen()) { // if aa ordering is triggered, `res` will be a CommaExp // and `.e2` will be the rewritten original expression. @@ -10914,6 +10976,86 @@ version (IN_LLVM) return; } + /** + * If the given expression is a `CatExp`, the function tries to lower it to + * `_d_arraycatnTX`. + * + * Params: + * ee = the `CatExp` to lower + * Returns: + * `_d_arraycatnTX(e1, e2, ..., en)` if `ee` is `e1 ~ e2 ~ ... en` + * `ee` otherwise + */ + private Expression lowerToArrayCat(CatExp exp) + { + // String literals are concatenated by the compiler. No lowering is needed. + if ((exp.e1.isStringExp() && (exp.e2.isIntegerExp() || exp.e2.isStringExp())) || + (exp.e2.isStringExp() && (exp.e1.isIntegerExp() || exp.e1.isStringExp()))) + return exp; + + Identifier hook = global.params.tracegc ? Id._d_arraycatnTXTrace : Id._d_arraycatnTX; + if (!verifyHookExist(exp.loc, *sc, hook, "concatenating arrays")) + { + setError(); + return result; + } + + void handleCatArgument(Expressions *arguments, Expression e) + { + if (auto ce = e.isCatExp()) + { + Expression lowering = ce.lowering; + + /* Skip `file`, `line`, and `funcname` if the hook of the parent + * `CatExp` is `_d_arraycatnTXTrace`. + */ + if (auto callExp = isRuntimeHook(lowering, hook)) + { + if (hook == Id._d_arraycatnTX) + arguments.pushSlice((*callExp.arguments)[]); + else + arguments.pushSlice((*callExp.arguments)[3 .. $]); + } + } + else + arguments.push(e); + } + + 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())); + } + + handleCatArgument(arguments, exp.e1); + handleCatArgument(arguments, exp.e2); + + Expression id = new IdentifierExp(exp.loc, Id.empty); + id = new DotIdExp(exp.loc, id, Id.object); + + auto tiargs = new Objects(); + tiargs.push(exp.type); + id = new DotTemplateInstanceExp(exp.loc, id, hook, tiargs); + id = new CallExp(exp.loc, id, arguments); + return id.expressionSemantic(sc); + } + + void trySetCatExpLowering(Expression exp) + { + /* `_d_arraycatnTX` canot be used with `-betterC`, but `CatExp`s may be + * used with `-betterC`, but only during CTFE. + */ + if (global.params.betterC || !sc.needsCodegen()) + return; + + if (auto ce = exp.isCatExp()) + ce.lowering = lowerToArrayCat(ce); + } + override void visit(CatExp exp) { // https://dlang.org/spec/expression.html#cat_expressions @@ -11001,14 +11143,10 @@ version (IN_LLVM) exp.e2 = exp.e2.implicitCastTo(sc, tb1next); exp.type = tb1next.arrayOf(); L2elem: - if (tb2.ty == Tarray || tb2.ty == Tsarray) - { - // Make e2 into [e2] - exp.e2 = new ArrayLiteralExp(exp.e2.loc, exp.type, exp.e2); - } - else if (checkNewEscape(sc, exp.e2, false)) + if (checkNewEscape(sc, exp.e2, false)) return setError(); result = exp.optimize(WANTvalue); + trySetCatExpLowering(result); return; } } @@ -11039,14 +11177,10 @@ version (IN_LLVM) exp.e1 = exp.e1.implicitCastTo(sc, tb2next); exp.type = tb2next.arrayOf(); L1elem: - if (tb1.ty == Tarray || tb1.ty == Tsarray) - { - // Make e1 into [e1] - exp.e1 = new ArrayLiteralExp(exp.e1.loc, exp.type, exp.e1); - } - else if (checkNewEscape(sc, exp.e1, false)) + if (checkNewEscape(sc, exp.e1, false)) return setError(); result = exp.optimize(WANTvalue); + trySetCatExpLowering(result); return; } } @@ -11069,6 +11203,7 @@ version (IN_LLVM) if (Expression ex = typeCombine(exp, sc)) { result = ex; + trySetCatExpLowering(result); return; } exp.type = exp.type.toHeadMutable(); @@ -11101,6 +11236,7 @@ version (IN_LLVM) } result = e; + trySetCatExpLowering(result); } override void visit(MulExp exp) @@ -11911,7 +12047,10 @@ version (IN_LLVM) exp.error("array comparison type mismatch, `%s` vs `%s`", t1next.toChars(), t2next.toChars()); return setError(); } - if ((t1.ty == Tarray || t1.ty == Tsarray) && (t2.ty == Tarray || t2.ty == Tsarray)) + + if (sc.needsCodegen() && + (t1.ty == Tarray || t1.ty == Tsarray) && + (t2.ty == Tarray || t2.ty == Tsarray)) { if (!verifyHookExist(exp.loc, *sc, Id.__cmp, "comparing arrays")) return setError(); @@ -12778,7 +12917,7 @@ private Expression dotIdSemanticPropX(DotIdExp exp, Scope* sc) if (exp.e1.isVarExp() && exp.e1.type.toBasetype().isTypeSArray() && exp.ident == Id.length) { // bypass checkPurity - return exp.e1.type.dotExp(sc, exp.e1, exp.ident, exp.noderef ? DotExpFlag.noDeref : 0); + return exp.e1.type.dotExp(sc, exp.e1, exp.ident, cast(DotExpFlag) (exp.noderef * DotExpFlag.noDeref)); } if (!exp.e1.isDotExp()) @@ -12830,11 +12969,11 @@ private Expression dotIdSemanticPropX(DotIdExp exp, Scope* sc) * Params: * exp = expression to resolve * sc = context - * flag = if 1 then do not emit error messages, just return null + * gag = do not emit error messages, just return `null` * Returns: * resolved expression, null if error */ -Expression dotIdSemanticProp(DotIdExp exp, Scope* sc, int flag) +Expression dotIdSemanticProp(DotIdExp exp, Scope* sc, bool gag) { //printf("DotIdExp::semanticY(this = %p, '%s')\n", exp, exp.toChars()); @@ -13062,10 +13201,20 @@ Expression dotIdSemanticProp(DotIdExp exp, Scope* sc, int flag) Expression se = new ScopeExp(exp.loc, imp.pkg); return se.expressionSemantic(sc); } + + if (auto attr = s.isAttribDeclaration()) + { + if (auto sm = ie.sds.search(exp.loc, exp.ident, flags)) + { + auto es = new DsymbolExp(exp.loc, sm); + return es; + } + } + // BUG: handle other cases like in IdentifierExp::semantic() debug { - printf("s = '%s', kind = '%s'\n", s.toChars(), s.kind()); + printf("s = %p '%s', kind = '%s'\n", s, s.toChars(), s.kind()); } assert(0); } @@ -13077,9 +13226,9 @@ Expression dotIdSemanticProp(DotIdExp exp, Scope* sc, int flag) } if (ie.sds.isPackage() || ie.sds.isImport() || ie.sds.isModule()) { - flag = 0; + gag = false; } - if (flag) + if (gag) return null; s = ie.sds.search_correct(exp.ident); if (s && symbolIsVisible(sc, s)) @@ -13105,7 +13254,7 @@ Expression dotIdSemanticProp(DotIdExp exp, Scope* sc, int flag) )) { Type t1bn = t1b.nextOf(); - if (flag) + if (gag) { if (AggregateDeclaration ad = isAggregate(t1bn)) { @@ -13119,11 +13268,12 @@ Expression dotIdSemanticProp(DotIdExp exp, Scope* sc, int flag) * as: * (*p).ident */ - if (flag && t1bn.ty == Tvoid) + if (gag && t1bn.ty == Tvoid) return null; Expression e = new PtrExp(exp.loc, exp.e1); e = e.expressionSemantic(sc); - return e.type.dotExp(sc, e, exp.ident, flag | (exp.noderef ? DotExpFlag.noDeref : 0)); + const newFlag = cast(DotExpFlag) (gag * DotExpFlag.gag | exp.noderef * DotExpFlag.noDeref); + return e.type.dotExp(sc, e, exp.ident, newFlag); } else if (exp.ident == Id.__xalignof && exp.e1.isVarExp() && @@ -13156,8 +13306,11 @@ Expression dotIdSemanticProp(DotIdExp exp, Scope* sc, int flag) else { if (exp.e1.isTypeExp() || exp.e1.isTemplateExp()) - flag = 0; - Expression e = exp.e1.type.dotExp(sc, exp.e1, exp.ident, flag | (exp.noderef ? DotExpFlag.noDeref : 0)); + gag = false; + + const flag = cast(DotExpFlag) (exp.noderef * DotExpFlag.noDeref | gag * DotExpFlag.gag); + + Expression e = exp.e1.type.dotExp(sc, exp.e1, exp.ident, flag); if (e) { e = e.expressionSemantic(sc); @@ -13166,9 +13319,16 @@ Expression dotIdSemanticProp(DotIdExp exp, Scope* sc, int flag) } } -// Resolve e1.ident!tiargs without seeing UFCS. -// If flag == 1, stop "not a property" error and return NULL. -Expression dotTemplateSemanticProp(DotTemplateInstanceExp exp, Scope* sc, int flag) +/** + * Resolve `e1.ident!tiargs` without seeing UFCS. + * Params: + * exp = the `DotTemplateInstanceExp` to resolve + * sc = the semantic scope + * gag = stop "not a property" error and return `null`. + * Returns: + * `null` if error or not found, or the resolved expression. + */ +Expression dotTemplateSemanticProp(DotTemplateInstanceExp exp, Scope* sc, bool gag) { static if (LOGSEMANTIC) { @@ -13202,11 +13362,11 @@ Expression dotTemplateSemanticProp(DotTemplateInstanceExp exp, Scope* sc, int fl /* No built-in type has templatized properties, so do shortcut. * It is necessary in: 1024.max!"a < b" */ - if (flag) + if (gag) return null; } - e = die.dotIdSemanticProp(sc, flag); - if (flag) + e = die.dotIdSemanticProp(sc, gag); + if (gag) { if (!e || isDotOpDispatch(e)) @@ -13927,6 +14087,7 @@ Expression toBoolean(Expression exp, Scope* sc) case EXP.assign: case EXP.construct: case EXP.blit: + case EXP.loweredAssignExp: if (sc.flags & SCOPE.Cfile) return exp; // Things like: diff --git a/dmd/foreachvar.d b/dmd/foreachvar.d index ba2825a3098..7a964695f45 100644 --- a/dmd/foreachvar.d +++ b/dmd/foreachvar.d @@ -299,7 +299,7 @@ void foreachExpAndVar(Statement s, case STMT.Conditional: case STMT.While: case STMT.Forwarding: - case STMT.Compile: + case STMT.Mixin: case STMT.Peel: case STMT.Synchronized: assert(0); // should have been rewritten diff --git a/dmd/frontend.h b/dmd/frontend.h index 480b91922c2..e8c2acc134a 100644 --- a/dmd/frontend.h +++ b/dmd/frontend.h @@ -100,7 +100,7 @@ class AttribDeclaration; class AnonDeclaration; class VisibilityDeclaration; class OverloadSet; -class CompileDeclaration; +class MixinDeclaration; class StaticAssert; class StaticIfDeclaration; class DsymbolTable; @@ -181,6 +181,7 @@ class IndexExp; class PostExp; class PreExp; class AssignExp; +class LoweredAssignExp; class ConstructExp; class BlitExp; class AddAssignExp; @@ -315,11 +316,18 @@ class StaticForeachStatement; class GotoDefaultStatement; class BreakStatement; class DtorExpStatement; -class CompileStatement; +class MixinStatement; class ForwardingStatement; class ContinueStatement; class ThrowStatement; class SwitchErrorStatement; +class CompoundAsmStatement; +class PragmaStatement; +class StaticAssertStatement; +class AsmStatement; +class InlineAsmStatement; +class GccAsmStatement; +class ImportStatement; struct Token; struct code; class Object; @@ -570,7 +578,7 @@ class Dsymbol : public ASTNode virtual CPPNamespaceDeclaration* isCPPNamespaceDeclaration(); virtual VisibilityDeclaration* isVisibilityDeclaration(); virtual OverloadSet* isOverloadSet(); - virtual CompileDeclaration* isCompileDeclaration(); + virtual MixinDeclaration* isMixinDeclaration(); virtual StaticAssert* isStaticAssert(); virtual StaticIfDeclaration* isStaticIfDeclaration(); }; @@ -694,31 +702,6 @@ enum class LINK : uint8_t system = 6u, }; -struct structalign_t final -{ -private: - uint16_t value; - bool pack; -public: - bool isDefault() const; - void setDefault(); - bool isUnknown() const; - void setUnknown(); - void set(uint32_t value); - uint32_t get() const; - bool isPack() const; - void setPack(bool pack); - structalign_t() : - value(0u), - pack() - { - } - structalign_t(uint16_t value, bool pack = false) : - value(value), - pack(pack) - {} -}; - enum class BUILTIN : uint8_t { unknown = 255u, @@ -821,136 +804,128 @@ enum class EXP : uint8_t cast_ = 2u, null_ = 3u, assert_ = 4u, - true_ = 5u, - false_ = 6u, - array = 7u, - call = 8u, - address = 9u, - type = 10u, - throw_ = 11u, - new_ = 12u, - delete_ = 13u, - star = 14u, - symbolOffset = 15u, - variable = 16u, - dotVariable = 17u, - dotIdentifier = 18u, - dotTemplateInstance = 19u, - dotType = 20u, - slice = 21u, - arrayLength = 22u, - version_ = 23u, - dollar = 24u, - template_ = 25u, - dotTemplateDeclaration = 26u, - declaration = 27u, - typeof_ = 28u, - pragma_ = 29u, - dSymbol = 30u, - typeid_ = 31u, - uadd = 32u, - remove = 33u, - newAnonymousClass = 34u, - arrayLiteral = 35u, - assocArrayLiteral = 36u, - structLiteral = 37u, - classReference = 38u, - thrownException = 39u, - delegatePointer = 40u, - delegateFunctionPointer = 41u, - lessThan = 42u, - greaterThan = 43u, - lessOrEqual = 44u, - greaterOrEqual = 45u, - equal = 46u, - notEqual = 47u, - identity = 48u, - notIdentity = 49u, - index = 50u, - is_ = 51u, - leftShift = 52u, - rightShift = 53u, - leftShiftAssign = 54u, - rightShiftAssign = 55u, - unsignedRightShift = 56u, - unsignedRightShiftAssign = 57u, - concatenate = 58u, - concatenateAssign = 59u, - concatenateElemAssign = 60u, - concatenateDcharAssign = 61u, - add = 62u, - min = 63u, - addAssign = 64u, - minAssign = 65u, - mul = 66u, - div = 67u, - mod = 68u, - mulAssign = 69u, - divAssign = 70u, - modAssign = 71u, - and_ = 72u, - or_ = 73u, - xor_ = 74u, - andAssign = 75u, - orAssign = 76u, - xorAssign = 77u, - assign = 78u, - not_ = 79u, - tilde = 80u, - plusPlus = 81u, - minusMinus = 82u, - construct = 83u, - blit = 84u, - dot = 85u, - comma = 86u, - question = 87u, - andAnd = 88u, - orOr = 89u, - prePlusPlus = 90u, - preMinusMinus = 91u, - identifier = 92u, - string_ = 93u, - this_ = 94u, - super_ = 95u, - halt = 96u, - tuple = 97u, - error = 98u, - void_ = 99u, - int64 = 100u, - float64 = 101u, - complex80 = 102u, - char_ = 103u, - import_ = 104u, - delegate_ = 105u, - function_ = 106u, - mixin_ = 107u, - in_ = 108u, - default_ = 109u, - break_ = 110u, - continue_ = 111u, - goto_ = 112u, - scope_ = 113u, - traits = 114u, - overloadSet = 115u, - line = 116u, - file = 117u, - fileFullPath = 118u, - moduleString = 119u, - functionString = 120u, - prettyFunction = 121u, - shared_ = 122u, - pow = 123u, - powAssign = 124u, - vector = 125u, - voidExpression = 126u, - cantExpression = 127u, - showCtfeContext = 128u, - objcClassReference = 129u, - vectorArray = 130u, - arrow = 131u, - compoundLiteral = 132u, - _Generic = 133u, - interval = 134u, + array = 5u, + call = 6u, + address = 7u, + type = 8u, + throw_ = 9u, + new_ = 10u, + delete_ = 11u, + star = 12u, + symbolOffset = 13u, + variable = 14u, + dotVariable = 15u, + dotIdentifier = 16u, + dotTemplateInstance = 17u, + dotType = 18u, + slice = 19u, + arrayLength = 20u, + dollar = 21u, + template_ = 22u, + dotTemplateDeclaration = 23u, + declaration = 24u, + dSymbol = 25u, + typeid_ = 26u, + uadd = 27u, + remove = 28u, + newAnonymousClass = 29u, + arrayLiteral = 30u, + assocArrayLiteral = 31u, + structLiteral = 32u, + classReference = 33u, + thrownException = 34u, + delegatePointer = 35u, + delegateFunctionPointer = 36u, + lessThan = 37u, + greaterThan = 38u, + lessOrEqual = 39u, + greaterOrEqual = 40u, + equal = 41u, + notEqual = 42u, + identity = 43u, + notIdentity = 44u, + index = 45u, + is_ = 46u, + leftShift = 47u, + rightShift = 48u, + leftShiftAssign = 49u, + rightShiftAssign = 50u, + unsignedRightShift = 51u, + unsignedRightShiftAssign = 52u, + concatenate = 53u, + concatenateAssign = 54u, + concatenateElemAssign = 55u, + concatenateDcharAssign = 56u, + add = 57u, + min = 58u, + addAssign = 59u, + minAssign = 60u, + mul = 61u, + div = 62u, + mod = 63u, + mulAssign = 64u, + divAssign = 65u, + modAssign = 66u, + and_ = 67u, + or_ = 68u, + xor_ = 69u, + andAssign = 70u, + orAssign = 71u, + xorAssign = 72u, + assign = 73u, + not_ = 74u, + tilde = 75u, + plusPlus = 76u, + minusMinus = 77u, + construct = 78u, + blit = 79u, + dot = 80u, + comma = 81u, + question = 82u, + andAnd = 83u, + orOr = 84u, + prePlusPlus = 85u, + preMinusMinus = 86u, + identifier = 87u, + string_ = 88u, + this_ = 89u, + super_ = 90u, + halt = 91u, + tuple = 92u, + error = 93u, + void_ = 94u, + int64 = 95u, + float64 = 96u, + complex80 = 97u, + import_ = 98u, + delegate_ = 99u, + function_ = 100u, + mixin_ = 101u, + in_ = 102u, + break_ = 103u, + continue_ = 104u, + goto_ = 105u, + scope_ = 106u, + traits = 107u, + overloadSet = 108u, + line = 109u, + file = 110u, + fileFullPath = 111u, + moduleString = 112u, + functionString = 113u, + prettyFunction = 114u, + pow = 115u, + powAssign = 116u, + vector = 117u, + voidExpression = 118u, + cantExpression = 119u, + showCtfeContext = 120u, + objcClassReference = 121u, + vectorArray = 122u, + compoundLiteral = 123u, + _Generic = 124u, + interval = 125u, + loweredAssignExp = 126u, }; typedef uint64_t dinteger_t; @@ -992,11 +967,11 @@ struct Optional final class Expression : public ASTNode { public: - const EXP op; - uint8_t size; - bool parens; Type* type; Loc loc; + const EXP op; + bool parens; + size_t size() const; static void _init(); static void deinitialize(); Expression* copy(); @@ -1096,6 +1071,7 @@ class Expression : public ASTNode PostExp* isPostExp(); PreExp* isPreExp(); AssignExp* isAssignExp(); + LoweredAssignExp* isLoweredAssignExp(); ConstructExp* isConstructExp(); BlitExp* isBlitExp(); AddAssignExp* isAddAssignExp(); @@ -1366,7 +1342,6 @@ enum class TY : uint8_t Tmixin = 45u, Tnoreturn = 46u, Ttag = 47u, - TMAX = 48u, }; enum class Covariant @@ -1835,9 +1810,10 @@ enum class TOK : uint8_t __cdecl_ = 217u, __declspec_ = 218u, __stdcall_ = 219u, - __pragma_ = 220u, - __int128_ = 221u, - __attribute___ = 222u, + __thread_ = 220u, + __pragma_ = 221u, + __int128_ = 222u, + __attribute___ = 223u, }; enum class MemorySet @@ -1889,7 +1865,7 @@ class ParseTimeVisitor virtual void visit(typename AST::StaticDtorDeclaration s); virtual void visit(typename AST::SharedStaticCtorDeclaration s); virtual void visit(typename AST::SharedStaticDtorDeclaration s); - virtual void visit(typename AST::CompileDeclaration s); + virtual void visit(typename AST::MixinDeclaration s); virtual void visit(typename AST::UserAttributeDeclaration s); virtual void visit(typename AST::LinkDeclaration s); virtual void visit(typename AST::AnonDeclaration s); @@ -1916,7 +1892,7 @@ class ParseTimeVisitor virtual void visit(typename AST::ReturnStatement s); virtual void visit(typename AST::LabelStatement s); virtual void visit(typename AST::StaticAssertStatement s); - virtual void visit(typename AST::CompileStatement s); + virtual void visit(typename AST::MixinStatement s); virtual void visit(typename AST::WhileStatement s); virtual void visit(typename AST::ForStatement s); virtual void visit(typename AST::DoStatement s); @@ -2429,6 +2405,7 @@ enum class VarArg : uint8_t none = 0u, variadic = 1u, typesafe = 2u, + KRvariadic = 3u, }; struct ParameterList final @@ -2499,6 +2476,9 @@ class FuncDeclaration : public Declaration Array siblingCallers; Array* inlinedNestedCallees; AttributeViolation* safetyViolation; + AttributeViolation* nogcViolation; + AttributeViolation* pureViolation; + AttributeViolation* nothrowViolation; bool purityInprocess() const; bool purityInprocess(bool v); bool safetyInprocess() const; @@ -2549,6 +2529,10 @@ class FuncDeclaration : public Declaration bool hasEscapingSiblings(bool v); bool computedEscapingSiblings() const; bool computedEscapingSiblings(bool v); + bool dllImport() const; + bool dllImport(bool v); + bool dllExport() const; + bool dllExport(bool v); private: uint32_t bitFields; public: @@ -2706,14 +2690,14 @@ class SemanticTimePermissiveVisitor : public Visitor { public: using Visitor::visit; - void visit(Dsymbol* _param_0) override; - void visit(Parameter* _param_0) override; - void visit(Statement* _param_0) override; - void visit(Type* _param_0) override; - void visit(Expression* _param_0) override; - void visit(TemplateParameter* _param_0) override; - void visit(Condition* _param_0) override; - void visit(Initializer* _param_0) override; + void visit(Dsymbol* __param_0_) override; + void visit(Parameter* __param_0_) override; + void visit(Statement* __param_0_) override; + void visit(Type* __param_0_) override; + void visit(Expression* __param_0_) override; + void visit(TemplateParameter* __param_0_) override; + void visit(Condition* __param_0_) override; + void visit(Initializer* __param_0_) override; }; class StatementRewriteWalker : public SemanticTimePermissiveVisitor @@ -2964,6 +2948,7 @@ extern Expression* initializerToExpression(Initializer* init, Type* itype = null enum class DotExpFlag { + none = 0, gag = 1, noDeref = 2, noAliasThis = 4, @@ -3103,7 +3088,7 @@ struct Param final FeatureState useDIP1000; bool ehnogc; bool useDIP1021; - bool fieldwise; + FeatureState fieldwise; bool fixAliasThis; FeatureState rvalueRefParam; FeatureState noSharedAccess; @@ -3147,6 +3132,7 @@ struct Param final bool run; Array runargs; Array cppswitches; + const char* cpp; Array objfiles; Array linkswitches; Array linkswitchIsForCC; @@ -3206,7 +3192,6 @@ struct Param final useDIP25((FeatureState)1), ehnogc(), useDIP1021(), - fieldwise(), fixAliasThis(), previewIn(), inclusiveInContracts(), @@ -3245,6 +3230,7 @@ struct Param final run(), runargs(), cppswitches(), + cpp(), objfiles(), linkswitches(), linkswitchIsForCC(), @@ -3256,7 +3242,7 @@ struct Param final mapfile() { } - Param(bool obj, bool multiobj = false, bool trace = false, bool tracegc = false, bool verbose = false, bool vcg_ast = false, bool showColumns = false, bool vtls = false, bool vtemplates = false, bool vtemplatesListInstances = false, bool vgc = false, bool vfield = false, bool vcomplex = true, bool vin = false, DiagnosticReporting useDeprecated = (DiagnosticReporting)1u, bool useUnitTests = false, bool useInline = false, bool release = false, bool preservePaths = false, DiagnosticReporting warnings = (DiagnosticReporting)2u, bool color = false, bool cov = false, uint8_t covPercent = 0u, bool ctfe_cov = false, bool ignoreUnsupportedPragmas = false, bool useModuleInfo = true, bool useTypeInfo = true, bool useExceptions = true, bool betterC = false, bool addMain = false, bool allInst = false, bool bitfields = false, CppStdRevision cplusplus = (CppStdRevision)201103u, bool showGaggedErrors = false, bool printErrorContext = false, bool manual = false, bool usage = false, bool mcpuUsage = false, bool transitionUsage = false, bool checkUsage = false, bool checkActionUsage = false, bool revertUsage = false, bool previewUsage = false, bool externStdUsage = false, bool hcUsage = false, bool logo = false, FeatureState useDIP25 = (FeatureState)1, FeatureState useDIP1000 = (FeatureState)-1, bool ehnogc = false, bool useDIP1021 = false, bool fieldwise = false, bool fixAliasThis = false, FeatureState rvalueRefParam = (FeatureState)-1, FeatureState noSharedAccess = (FeatureState)-1, bool previewIn = false, bool inclusiveInContracts = false, bool shortenedMethods = true, bool fixImmutableConv = false, bool fix16997 = true, FeatureState dtorFields = (FeatureState)-1, FeatureState systemVariables = (FeatureState)-1, 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, uint32_t errorLimit = 20u, uint32_t errorSupplementLimit = 6u, _d_dynamicArray< const char > argv0 = {}, Array modFileAliasStrings = Array(), Array* imppath = nullptr, Array* fileImppath = nullptr, _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, Array* debugids = nullptr, uint32_t versionlevel = 0u, Array* versionids = nullptr, MessageStyle messageStyle = (MessageStyle)0u, bool run = false, Array runargs = Array(), Array cppswitches = Array(), 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 multiobj = false, bool trace = false, bool tracegc = false, bool verbose = false, bool vcg_ast = false, bool showColumns = false, bool vtls = false, bool vtemplates = false, bool vtemplatesListInstances = false, bool vgc = false, bool vfield = false, bool vcomplex = true, bool vin = false, DiagnosticReporting useDeprecated = (DiagnosticReporting)1u, bool useUnitTests = false, bool useInline = false, bool release = false, bool preservePaths = false, DiagnosticReporting warnings = (DiagnosticReporting)2u, bool color = false, bool cov = false, uint8_t covPercent = 0u, bool ctfe_cov = false, bool ignoreUnsupportedPragmas = false, bool useModuleInfo = true, bool useTypeInfo = true, bool useExceptions = true, bool betterC = false, bool addMain = false, bool allInst = false, bool bitfields = false, CppStdRevision cplusplus = (CppStdRevision)201103u, bool showGaggedErrors = false, bool printErrorContext = false, bool manual = false, bool usage = false, bool mcpuUsage = false, bool transitionUsage = false, bool checkUsage = false, bool checkActionUsage = false, bool revertUsage = false, bool previewUsage = false, bool externStdUsage = false, bool hcUsage = false, bool logo = false, FeatureState useDIP25 = (FeatureState)1, FeatureState useDIP1000 = (FeatureState)-1, bool ehnogc = false, bool useDIP1021 = false, FeatureState fieldwise = (FeatureState)-1, bool fixAliasThis = false, FeatureState rvalueRefParam = (FeatureState)-1, FeatureState noSharedAccess = (FeatureState)-1, bool previewIn = false, bool inclusiveInContracts = false, bool shortenedMethods = true, bool fixImmutableConv = false, bool fix16997 = true, FeatureState dtorFields = (FeatureState)-1, FeatureState systemVariables = (FeatureState)-1, 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, uint32_t errorLimit = 20u, uint32_t errorSupplementLimit = 6u, _d_dynamicArray< const char > argv0 = {}, Array modFileAliasStrings = Array(), Array* imppath = nullptr, Array* fileImppath = nullptr, _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, Array* debugids = nullptr, uint32_t versionlevel = 0u, Array* versionids = nullptr, MessageStyle messageStyle = (MessageStyle)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 = {}) : obj(obj), multiobj(multiobj), trace(trace), @@ -3351,6 +3337,7 @@ struct Param final run(run), runargs(runargs), cppswitches(cppswitches), + cpp(cpp), objfiles(objfiles), linkswitches(linkswitches), linkswitchIsForCC(linkswitchIsForCC), @@ -3363,6 +3350,39 @@ struct Param final {} }; +struct CompileEnv final +{ + 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; + bool ddocOutput; + bool shortenedMethods; + CompileEnv() : + versionNumber(), + date(), + time(), + vendor(), + timestamp(), + previewIn(), + ddocOutput(), + shortenedMethods(true) + { + } + 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 shortenedMethods = true) : + versionNumber(versionNumber), + date(date), + time(time), + vendor(vendor), + timestamp(timestamp), + previewIn(previewIn), + ddocOutput(ddocOutput), + shortenedMethods(shortenedMethods) + {} +}; + struct Global final { _d_dynamicArray< const char > inifilename; @@ -3370,7 +3390,7 @@ struct Global final _d_dynamicArray< const char > written; Array* path; Array* filePath; - _d_dynamicArray< const char > vendor; + CompileEnv compileEnv; Param params; uint32_t errors; uint32_t warnings; @@ -3399,7 +3419,7 @@ struct Global final written(24, "written by Walter Bright"), path(), filePath(), - vendor(), + compileEnv(), params(), errors(), warnings(), @@ -3416,13 +3436,13 @@ struct Global final preprocess() { } - Global(_d_dynamicArray< const char > inifilename, _d_dynamicArray< const char > copyright = { 73, "Copyright (C) 1999-2023 by The D Language Foundation, All Rights Reserved" }, _d_dynamicArray< const char > written = { 24, "written by Walter Bright" }, Array* path = nullptr, Array* filePath = nullptr, _d_dynamicArray< const char > vendor = {}, Param params = Param(), uint32_t errors = 0u, uint32_t warnings = 0u, uint32_t gag = 0u, uint32_t gaggedErrors = 0u, uint32_t gaggedWarnings = 0u, void* console = nullptr, Array* versionids = nullptr, Array* debugids = nullptr, bool hasMainFunction = false, uint32_t varSequenceNumber = 1u, FileManager* fileManager = nullptr, ErrorSink* errorSink = nullptr, FileName(*preprocess)(FileName , const Loc& , bool& , OutBuffer* ) = nullptr) : + Global(_d_dynamicArray< const char > inifilename, _d_dynamicArray< const char > copyright = { 73, "Copyright (C) 1999-2023 by The D Language Foundation, All Rights Reserved" }, _d_dynamicArray< const char > written = { 24, "written by Walter Bright" }, Array* path = nullptr, Array* filePath = nullptr, CompileEnv compileEnv = CompileEnv(), Param params = Param(), uint32_t errors = 0u, uint32_t warnings = 0u, uint32_t gag = 0u, uint32_t gaggedErrors = 0u, uint32_t gaggedWarnings = 0u, void* console = nullptr, Array* versionids = nullptr, Array* debugids = nullptr, bool hasMainFunction = false, uint32_t varSequenceNumber = 1u, FileManager* fileManager = nullptr, ErrorSink* errorSink = nullptr, FileName(*preprocess)(FileName , const Loc& , bool& , OutBuffer* ) = nullptr) : inifilename(inifilename), copyright(copyright), written(written), path(path), filePath(filePath), - vendor(vendor), + compileEnv(compileEnv), params(params), errors(errors), warnings(warnings), @@ -3774,7 +3794,7 @@ class TypeFunction final : public TypeNext bool isDstyleVariadic() const; StorageClass parameterStorageClass(Type* tthis, Parameter* p); Type* addStorageClass(StorageClass stc) override; - Type* substWildTo(uint32_t _param_0) override; + Type* substWildTo(uint32_t __param_0_) override; MATCH constConv(Type* to) override; bool iswild() const; void accept(Visitor* v) override; @@ -3958,6 +3978,7 @@ class TypeTag final : public Type public: Loc loc; TOK tok; + structalign_t packalign; Identifier* id; Type* base; Array* members; @@ -4053,7 +4074,7 @@ enum class STMT : uint8_t Peel = 1u, Exp = 2u, DtorExp = 3u, - Compile = 4u, + Mixin = 4u, Compound = 5u, CompoundDeclaration = 6u, CompoundAsm = 7u, @@ -4117,6 +4138,7 @@ class Statement : public ASTNode void accept(Visitor* v) override; virtual ReturnStatement* endsWithReturnStatement(); ErrorStatement* isErrorStatement(); + PeelStatement* isPeelStatement(); ScopeStatement* isScopeStatement(); ExpStatement* isExpStatement(); CompoundStatement* isCompoundStatement(); @@ -4132,7 +4154,7 @@ class Statement : public ASTNode GotoCaseStatement* isGotoCaseStatement(); BreakStatement* isBreakStatement(); DtorExpStatement* isDtorExpStatement(); - CompileStatement* isCompileStatement(); + MixinStatement* isMixinStatement(); ForwardingStatement* isForwardingStatement(); DoStatement* isDoStatement(); WhileStatement* isWhileStatement(); @@ -4150,6 +4172,15 @@ class Statement : public ASTNode UnrolledLoopStatement* isUnrolledLoopStatement(); ForeachRangeStatement* isForeachRangeStatement(); CompoundDeclarationStatement* isCompoundDeclarationStatement(); + CompoundAsmStatement* isCompoundAsmStatement(); + PragmaStatement* isPragmaStatement(); + StaticAssertStatement* isStaticAssertStatement(); + CaseRangeStatement* isCaseRangeStatement(); + SynchronizedStatement* isSynchronizedStatement(); + AsmStatement* isAsmStatement(); + InlineAsmStatement* isInlineAsmStatement(); + GccAsmStatement* isGccAsmStatement(); + ImportStatement* isImportStatement(); }; class AsmStatement : public Statement @@ -4203,14 +4234,6 @@ class Catch final : public RootObject Catch* syntaxCopy(); }; -class CompileStatement final : public Statement -{ -public: - Array* exps; - CompileStatement* syntaxCopy() override; - void accept(Visitor* v) override; -}; - class CompoundStatement : public Statement { public: @@ -4477,6 +4500,14 @@ class LabelStatement final : public Statement void accept(Visitor* v) override; }; +class MixinStatement final : public Statement +{ +public: + Array* exps; + MixinStatement* syntaxCopy() override; + void accept(Visitor* v) override; +}; + class PeelStatement final : public Statement { public: @@ -4780,11 +4811,11 @@ struct ASTCodegen final using AttribDeclaration = ::AttribDeclaration; using CPPMangleDeclaration = ::CPPMangleDeclaration; using CPPNamespaceDeclaration = ::CPPNamespaceDeclaration; - using CompileDeclaration = ::CompileDeclaration; using ConditionalDeclaration = ::ConditionalDeclaration; using DeprecatedDeclaration = ::DeprecatedDeclaration; using ForwardingAttribDeclaration = ::ForwardingAttribDeclaration; using LinkDeclaration = ::LinkDeclaration; + using MixinDeclaration = ::MixinDeclaration; using PragmaDeclaration = ::PragmaDeclaration; using StaticForeachDeclaration = ::StaticForeachDeclaration; using StaticIfDeclaration = ::StaticIfDeclaration; @@ -4928,6 +4959,7 @@ struct ASTCodegen final using IsExp = ::IsExp; using LineInitExp = ::LineInitExp; using LogicalExp = ::LogicalExp; + using LoweredAssignExp = ::LoweredAssignExp; using MemorySet = ::MemorySet; using MinAssignExp = ::MinAssignExp; using MinExp = ::MinExp; @@ -5018,6 +5050,7 @@ struct ASTCodegen final using Initializer = ::Initializer; using NeedInterpret = ::NeedInterpret; using StructInitializer = ::StructInitializer; + using VisitInitializer = ::VisitInitializer; using VoidInitializer = ::VoidInitializer; using Covariant = ::Covariant; using DotExpFlag = ::DotExpFlag; @@ -5054,13 +5087,13 @@ struct ASTCodegen final using TypeTuple = ::TypeTuple; using TypeTypeof = ::TypeTypeof; using TypeVector = ::TypeVector; + using VisitType = ::VisitType; using Nspace = ::Nspace; using AsmStatement = ::AsmStatement; using BreakStatement = ::BreakStatement; using CaseRangeStatement = ::CaseRangeStatement; using CaseStatement = ::CaseStatement; using Catch = ::Catch; - using CompileStatement = ::CompileStatement; using CompoundAsmStatement = ::CompoundAsmStatement; using CompoundDeclarationStatement = ::CompoundDeclarationStatement; using CompoundStatement = ::CompoundStatement; @@ -5085,6 +5118,7 @@ struct ASTCodegen final using InlineAsmStatement = ::InlineAsmStatement; using LabelDsymbol = ::LabelDsymbol; using LabelStatement = ::LabelStatement; + using MixinStatement = ::MixinStatement; using PeelStatement = ::PeelStatement; using PragmaStatement = ::PragmaStatement; using ReturnStatement = ::ReturnStatement; @@ -5100,6 +5134,7 @@ struct ASTCodegen final using TryCatchStatement = ::TryCatchStatement; using TryFinallyStatement = ::TryFinallyStatement; using UnrolledLoopStatement = ::UnrolledLoopStatement; + using VisitStatement = ::VisitStatement; using WhileStatement = ::WhileStatement; using WithStatement = ::WithStatement; using StaticAssert = ::StaticAssert; @@ -5188,6 +5223,7 @@ class Visitor : public ParseTimeVisitor virtual void visit(ClassReferenceExp* e); virtual void visit(VoidInitExp* e); virtual void visit(ThrownExceptionExp* e); + virtual void visit(LoweredAssignExp* e); }; class StoppableVisitor : public Visitor @@ -5403,6 +5439,31 @@ extern TypeTuple* toArgTypes_aarch64(Type* t); extern bool isHFVA(Type* t, int32_t maxNumElements = 4, Type** rewriteType = nullptr); +struct structalign_t final +{ +private: + uint16_t value; + bool pack; +public: + bool isDefault() const; + void setDefault(); + bool isUnknown() const; + void setUnknown(); + void set(uint32_t value); + uint32_t get() const; + bool isPack() const; + void setPack(bool pack); + structalign_t() : + value(0u), + pack() + { + } + structalign_t(uint16_t value, bool pack = false) : + value(value), + pack(pack) + {} +}; + class AttribDeclaration : public Dsymbol { public: @@ -5489,7 +5550,7 @@ class VisibilityDeclaration final : public AttribDeclaration Scope* newScope(Scope* sc) override; void addMember(Scope* sc, ScopeDsymbol* sds) override; const char* kind() const override; - const char* toPrettyChars(bool _param_0) override; + const char* toPrettyChars(bool __param_0_) override; VisibilityDeclaration* isVisibilityDeclaration() override; void accept(Visitor* v) override; }; @@ -5591,17 +5652,17 @@ class ForwardingAttribDeclaration final : public AttribDeclaration void accept(Visitor* v) override; }; -class CompileDeclaration final : public AttribDeclaration +class MixinDeclaration final : public AttribDeclaration { public: Array* exps; ScopeDsymbol* scopesym; bool compiled; - CompileDeclaration* syntaxCopy(Dsymbol* s) override; + MixinDeclaration* syntaxCopy(Dsymbol* s) override; void addMember(Scope* sc, ScopeDsymbol* sds) override; void setScope(Scope* sc) override; const char* kind() const override; - CompileDeclaration* isCompileDeclaration() override; + MixinDeclaration* isMixinDeclaration() override; void accept(Visitor* v) override; }; @@ -5981,6 +6042,10 @@ class VarDeclaration : public Declaration bool isArgDtorVar(bool v); bool isCmacro() const; bool isCmacro(bool v); + bool dllImport() const; + bool dllImport(bool v); + bool dllExport() const; + bool dllExport(bool v); bool inClosure() const; bool inClosure(bool v); bool inAlignSection() const; @@ -6702,7 +6767,7 @@ class TemplateDeclaration final : public ScopeDsymbol Array lastConstraintNegs; Array* lastConstraintTiargs; public: - TemplateDeclaration* syntaxCopy(Dsymbol* _param_0) override; + TemplateDeclaration* syntaxCopy(Dsymbol* __param_0_) override; bool overloadInsert(Dsymbol* s) override; bool hasStaticCtorOrDtor() override; const char* kind() const override; @@ -6927,23 +6992,23 @@ struct UnionExp final private: union __AnonStruct__u { - char exp[40LLU]; + char exp[34LLU]; char integerexp[48LLU]; - char errorexp[40LLU]; + char errorexp[34LLU]; char realexp[64LLU]; char complexexp[80LLU]; char symoffexp[72LLU]; - char stringexp[60LLU]; - char arrayliteralexp[58LLU]; - char assocarrayliteralexp[57LLU]; - char structliteralexp[95LLU]; + char stringexp[58LLU]; + char arrayliteralexp[56LLU]; + char assocarrayliteralexp[56LLU]; + char structliteralexp[84LLU]; char compoundliteralexp[48LLU]; - char nullexp[40LLU]; - char dotvarexp[65LLU]; - char addrexp[56LLU]; + char nullexp[34LLU]; + char dotvarexp[57LLU]; + char addrexp[48LLU]; char indexexp[82LLU]; - char sliceexp[83LLU]; - char vectorexp[69LLU]; + char sliceexp[73LLU]; + char vectorexp[61LLU]; }; #pragma pack(pop) @@ -7082,20 +7147,20 @@ class NullExp final : public Expression class StringExp final : public Expression { +public: + char postfix; + OwnedBy ownedByCtfe; union { char* string; char16_t* wstring; char32_t* dstring; }; -public: size_t len; uint8_t sz; - uint8_t committed; + bool committed; enum : char { NoPostfix = 0u }; - char postfix; - OwnedBy ownedByCtfe; static StringExp* create(const Loc& loc, const char* s); static StringExp* create(const Loc& loc, const void* string, size_t len); static void emplace(UnionExp* pue, const Loc& loc, const char* s); @@ -7128,10 +7193,10 @@ class TupleExp final : public Expression class ArrayLiteralExp final : public Expression { public: - Expression* basis; - Array* elements; OwnedBy ownedByCtfe; bool onstack; + Expression* basis; + Array* elements; static ArrayLiteralExp* create(const Loc& loc, Array* elements); static void emplace(UnionExp* pue, const Loc& loc, Array* elements); ArrayLiteralExp* syntaxCopy() override; @@ -7146,9 +7211,9 @@ class ArrayLiteralExp final : public Expression class AssocArrayLiteralExp final : public Expression { public: + OwnedBy ownedByCtfe; Array* keys; Array* values; - OwnedBy ownedByCtfe; bool equals(const RootObject* const o) const override; AssocArrayLiteralExp* syntaxCopy() override; Optional toBool() override; @@ -7161,10 +7226,13 @@ class StructLiteralExp final : public Expression StructDeclaration* sd; Array* elements; Type* stype; - Symbol* sym; + union + { + Symbol* sym; + StructLiteralExp* inlinecopy; + }; StructLiteralExp* origin; - StructLiteralExp* inlinecopy; - int32_t stageflags; + uint8_t stageflags; bool useStaticInit; bool isOriginal; OwnedBy ownedByCtfe; @@ -7344,7 +7412,6 @@ class UnaExp : public Expression { public: Expression* e1; - Type* att1; UnaExp* syntaxCopy() override; Expression* incompatibleTypes(); void setNoderefOperand(); @@ -7568,9 +7635,15 @@ class SliceExp final : public UnaExp Expression* upr; Expression* lwr; VarDeclaration* lengthVar; - bool upperIsInBounds; - bool lowerIsLessThanUpper; - bool arrayop; + bool upperIsInBounds() const; + bool upperIsInBounds(bool v); + bool lowerIsLessThanUpper() const; + bool lowerIsLessThanUpper(bool v); + bool arrayop() const; + bool arrayop(bool v); +private: + uint8_t bitFields; +public: SliceExp* syntaxCopy() override; bool isLvalue() override; Expression* toLvalue(Scope* sc, Expression* e) override; @@ -7679,6 +7752,14 @@ class AssignExp : public BinExp void accept(Visitor* v) override; }; +class LoweredAssignExp final : public AssignExp +{ +public: + Expression* lowering; + const char* toChars() const override; + void accept(Visitor* v) override; +}; + class ConstructExp final : public AssignExp { public: @@ -7796,6 +7877,7 @@ class MinExp final : public BinExp class CatExp final : public BinExp { public: + Expression* lowering; Expression* resolveLoc(const Loc& loc, Scope* sc) override; void accept(Visitor* v) override; }; @@ -8209,7 +8291,7 @@ class SemanticTimeTransitiveVisitor : public SemanticTimePermissiveVisitor public: using SemanticTimePermissiveVisitor::visit; void visit(ExpStatement* s) override; - void visit(CompileStatement* s) override; + void visit(MixinStatement* s) override; void visit(CompoundStatement* s) override; virtual void visitVarDecl(VarDeclaration* v); void visit(CompoundDeclarationStatement* s) override; @@ -8270,7 +8352,7 @@ class SemanticTimeTransitiveVisitor : public SemanticTimePermissiveVisitor void visit(AnonDeclaration* d) override; void visit(PragmaDeclaration* d) override; void visit(ConditionalDeclaration* d) override; - void visit(CompileDeclaration* d) override; + void visit(MixinDeclaration* d) override; void visit(UserAttributeDeclaration* d) override; virtual void visitFuncBody(FuncDeclaration* f); virtual void visitBaseClasses(ClassDeclaration* d); @@ -8359,6 +8441,7 @@ class SemanticTimeTransitiveVisitor : public SemanticTimePermissiveVisitor void visit(DotExp* e) override; void visit(IndexExp* e) override; void visit(RemoveExp* e) override; + void visit(LoweredAssignExp* e) override; }; extern _d_real creall(complex_t x); @@ -8654,11 +8737,17 @@ struct Id final static Identifier* _d_arrayappendcTXImpl; static Identifier* _d_arrayappendcTX; static Identifier* _d_arrayappendcTXTrace; + static Identifier* _d_arraycatnTX; + static Identifier* _d_arraycatnTXTrace; static Identifier* stdc; static Identifier* stdarg; static Identifier* va_start; static Identifier* std; static Identifier* core; + static Identifier* config; + static Identifier* c_complex_float; + static Identifier* c_complex_double; + static Identifier* c_complex_real; static Identifier* etc; static Identifier* attribute; static Identifier* atomic; @@ -8789,8 +8878,16 @@ struct Id final static Identifier* ImportC; static Identifier* dllimport; static Identifier* dllexport; + static Identifier* naked; + static Identifier* thread; static Identifier* vector_size; + static Identifier* always_inline; + static Identifier* noinline; static Identifier* noreturn; + static Identifier* _nothrow; + static Identifier* _deprecated; + static Identifier* _align; + static Identifier* aligned; static Identifier* builtins; static Identifier* builtin_va_list; static Identifier* builtin_va_arg; @@ -8800,6 +8897,7 @@ struct Id final static Identifier* show; static Identifier* push; static Identifier* pop; + static Identifier* _pure; static Identifier* define; static Identifier* undef; static void initialize(); diff --git a/dmd/func.d b/dmd/func.d index 5e80c580303..de1882e0953 100644 --- a/dmd/func.d +++ b/dmd/func.d @@ -231,6 +231,7 @@ private struct FUNCFLAG bool hasCatches; /// function has try-catch statements bool skipCodegen; /// do not generate code for this function. bool printf; /// is a printf-like function + bool scanf; /// is a scanf-like function bool noreturn; /// the function does not return bool isNRVO = true; /// Support for named return value optimization @@ -241,11 +242,14 @@ private struct FUNCFLAG bool hasNoEH; /// No exception unwinding is needed bool inferRetType; /// Return type is to be inferred bool hasDualContext; /// has a dual-context 'this' parameter + bool hasAlwaysInlines; /// Contains references to functions that must be inlined bool isCrtCtor; /// Has attribute pragma(crt_constructor) bool isCrtDtor; /// Has attribute pragma(crt_destructor) bool hasEscapingSiblings;/// Has sibling functions that escape bool computedEscapingSiblings; /// `hasEscapingSiblings` has been computed + bool dllImport; /// __declspec(dllimport) + bool dllExport; /// __declspec(dllexport) } /*********************************************************** @@ -402,6 +406,9 @@ version (IN_LLVM) {} else /// In case of failed `@safe` inference, store the error that made the function `@system` for /// better diagnostics AttributeViolation* safetyViolation; + AttributeViolation* nogcViolation; + AttributeViolation* pureViolation; + AttributeViolation* nothrowViolation; /// See the `FUNCFLAG` struct import dmd.common.bitfields; @@ -416,8 +423,8 @@ version (IN_LLVM) {} else extern (D) this(const ref Loc loc, const ref Loc endloc, Identifier ident, StorageClass storage_class, Type type, bool noreturn = false) { super(loc, ident); - //printf("FuncDeclaration(id = '%s', type = %p)\n", id.toChars(), type); - //printf("storage_class = x%x\n", storage_class); + //.printf("FuncDeclaration(id = '%s', type = %s)\n", ident.toChars(), type.toChars()); + //.printf("storage_class = x%llx\n", storage_class); this.storage_class = storage_class; this.type = type; if (type) @@ -1344,14 +1351,14 @@ version (IN_LLVM) override final bool isExport() const { - return visibility.kind == Visibility.Kind.export_; + return visibility.kind == Visibility.Kind.export_ || dllExport; } override final bool isImportedSymbol() const { //printf("isImportedSymbol()\n"); //printf("protection = %d\n", visibility); - return (visibility.kind == Visibility.Kind.export_) && !fbody; + return (visibility.kind == Visibility.Kind.export_ || dllImport) && !fbody; } override final bool isCodeseg() const pure nothrow @nogc @safe @@ -1491,17 +1498,27 @@ version (IN_LLVM) } /************************************** - * The function is doing something impure, - * so mark it as impure. - * If there's a purity error, return true. + * The function is doing something impure, so mark it as impure. + * + * 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 there's a purity error */ - extern (D) final bool setImpure() + extern (D) final bool setImpure(Loc loc = Loc.init, const(char)* fmt = null, RootObject arg0 = null) { if (purityInprocess) { purityInprocess = false; + if (fmt) + pureViolation = new AttributeViolation(loc, fmt, this, arg0); // impure action + else if (arg0) + pureViolation = new AttributeViolation(loc, fmt, arg0); // call to impure function + if (fes) - fes.func.setImpure(); + fes.func.setImpure(loc, fmt, arg0); } else if (isPure()) return true; @@ -1590,7 +1607,7 @@ version (IN_LLVM) { //printf("isNogc() %s, inprocess: %d\n", toChars(), !!(flags & FUNCFLAG.nogcInprocess)); if (nogcInprocess) - setGC(); + setGC(loc, null); return type.toTypeFunction().isnogc; } @@ -1602,10 +1619,16 @@ version (IN_LLVM) /************************************** * 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() + extern (D) final bool setGC(Loc loc, const(char)* fmt, RootObject arg0 = null) { //printf("setGC() %s\n", toChars()); if (nogcInprocess && semanticRun < PASS.semantic3 && _scope) @@ -1617,15 +1640,59 @@ version (IN_LLVM) 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(); + 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 + */ + extern (D) final void setThrow(Loc loc, const(char)* fmt, RootObject arg0 = null) + { + if (nothrowInprocess && !nothrowViolation) + { + nothrowViolation = new AttributeViolation(loc, fmt, arg0); // action that requires GC + } + } + + /************************************** + * The function calls non-`nothrow` function f, register that in case nothrow is being inferred + * Params: + * loc = location of call + * f = function being called + */ + extern (D) final void setThrowCall(Loc loc, FuncDeclaration f) + { + return setThrow(loc, null, f); + } + extern (D) final void printGCUsage(const ref Loc loc, const(char)* warn) { if (!global.params.vgc) @@ -1951,7 +2018,8 @@ version (IN_LLVM) overloadApply(cast() this, (Dsymbol s) { auto f = s.isFuncDeclaration(); - if (!f) + auto td = s.isTemplateDeclaration(); + if (!f && !td) return 0; if (result) { @@ -2169,7 +2237,7 @@ version (IN_LLVM) if (!needsClosure()) return false; - if (setGC()) + if (setGC(loc, "%s `%s` is `@nogc` yet allocates closure for `%s()` with the GC", this)) { error("is `@nogc` yet allocates closure for `%s()` with the GC", toChars()); if (global.gag) // need not report supplemental errors @@ -4581,18 +4649,51 @@ struct AttributeViolation /// fd = function to check /// maxDepth = up to how many functions deep to report errors /// deprecation = print deprecations instead of errors -void errorSupplementalInferredSafety(FuncDeclaration fd, int maxDepth, bool deprecation) +/// stc = storage class of attribute to check +void errorSupplementalInferredAttr(FuncDeclaration fd, int maxDepth, bool deprecation, STC stc) { auto errorFunc = deprecation ? &deprecationSupplemental : &errorSupplemental; - if (auto s = fd.safetyViolation) + + 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"; + } + + if (s) { if (s.fmtStr) { errorFunc(s.loc, deprecation ? - "which would be `@system` because of:" : - "which was inferred `@system` because of:"); - errorFunc(s.loc, s.fmtStr, - s.arg0 ? s.arg0.toChars() : "", s.arg1 ? s.arg1.toChars() : "", s.arg2 ? s.arg2.toChars() : ""); + "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 (s.arg0.dyncast() == DYNCAST.dsymbol) { @@ -4601,7 +4702,7 @@ void errorSupplementalInferredSafety(FuncDeclaration fd, int maxDepth, bool depr if (maxDepth > 0) { errorFunc(s.loc, "which calls `%s`", fd2.toPrettyChars()); - errorSupplementalInferredSafety(fd2, maxDepth - 1, deprecation); + errorSupplementalInferredAttr(fd2, maxDepth - 1, deprecation, stc); } } } diff --git a/dmd/globals.d b/dmd/globals.d index 4e8d475f336..f4f3bf8519a 100644 --- a/dmd/globals.d +++ b/dmd/globals.d @@ -11,7 +11,10 @@ module dmd.globals; +import core.stdc.stdio; import core.stdc.stdint; +import core.stdc.string; + import dmd.root.array; import dmd.root.filename; import dmd.common.outbuffer; @@ -20,6 +23,8 @@ import dmd.errors; import dmd.file_manager; import dmd.identifier; import dmd.location; +import dmd.lexer : CompileEnv; +import dmd.utils; version(IN_WEKA) { @@ -192,7 +197,7 @@ extern (C++) struct Param FeatureState useDIP1000; // implement https://dlang.org/spec/memory-safe-d.html#scope-return-params bool ehnogc; // use @nogc exception handling bool useDIP1021; // implement https://github.com/dlang/DIPs/blob/master/DIPs/accepted/DIP1021.md - bool fieldwise; // do struct equality testing field-wise rather than by memcmp() + FeatureState fieldwise; // do struct equality testing field-wise rather than by memcmp() bool fixAliasThis; // if the current scope has an alias this, check it before searching upper scopes FeatureState rvalueRefParam; // allow rvalues to be arguments to ref parameters // https://dconf.org/2019/talks/alexandrescu.html @@ -252,6 +257,7 @@ extern (C++) struct Param bool run; // run resulting executable Strings runargs; // arguments for executable Array!(const(char)*) cppswitches; // C preprocessor switches + const(char)* cpp; // if not null, then this specifies the C preprocessor // Linker stuff Array!(const(char)*) objfiles; @@ -316,30 +322,6 @@ version (IN_LLVM) } // IN_LLVM } -extern (C++) struct structalign_t -{ - private: - ushort value = 0; // unknown - enum STRUCTALIGN_DEFAULT = 1234; // default = match whatever the corresponding C compiler does - bool pack; // use #pragma pack semantics - - public: - pure @safe @nogc nothrow: - bool isDefault() const { return value == STRUCTALIGN_DEFAULT; } - void setDefault() { value = STRUCTALIGN_DEFAULT; } - bool isUnknown() const { return value == 0; } // value is not set - void setUnknown() { value = 0; } - void set(uint value) { this.value = cast(ushort)value; } - uint get() const { return value; } - bool isPack() const { return pack; } - void setPack(bool pack) { this.pack = pack; } -} -//alias structalign_t = uint; - -// magic value means "match whatever the underlying C compiler does" -// other values are all powers of 2 -//enum STRUCTALIGN_DEFAULT = (cast(structalign_t)~0); - enum mars_ext = "d"; // for D source files enum doc_ext = "html"; // for Ddoc generated files enum ddoc_ext = "ddoc"; // for Ddoc macro include files @@ -378,10 +360,8 @@ extern (C++) struct Global version (IN_LLVM) {} else { private enum string _version = import("VERSION"); - private enum uint _versionNumber = parseVersionNumber(_version); } - - const(char)[] vendor; /// Compiler backend name + CompileEnv compileEnv; Param params; /// command line parameters uint errors; /// number of errors reported so far @@ -471,12 +451,12 @@ else extern (C++) void _init() { - global.errorSink = new ErrorSinkCompiler; + errorSink = new ErrorSinkCompiler; this.fileManager = new FileManager(); version (MARS) { - vendor = "Digital Mars D"; + compileEnv.vendor = "Digital Mars D"; // -color=auto is the default value import dmd.console : detectTerminal; @@ -484,15 +464,53 @@ else } else version (IN_GCC) { - vendor = "GNU D"; + compileEnv.vendor = "GNU D"; } else version (IN_LLVM) { - vendor = "LDC"; + compileEnv.vendor = "LDC"; import dmd.console : detectTerminal; params.color = detectTerminal(); } + +version (IN_LLVM) +{ + compileEnv.versionNumber = parseVersionNumber(versionString()); +} +else +{ + compileEnv.versionNumber = parseVersionNumber(_version); +} + + /* Initialize date, time, and timestamp + */ + import core.stdc.time; + import core.stdc.stdlib : getenv; + + time_t ct; + // https://issues.dlang.org/show_bug.cgi?id=20444 + if (auto p = getenv("SOURCE_DATE_EPOCH")) + { + if (!ct.parseDigits(p[0 .. strlen(p)])) + errorSink.error(Loc.initial, "value of environment variable `SOURCE_DATE_EPOCH` should be a valid UNIX timestamp, not: `%s`", p); + } + else + core.stdc.time.time(&ct); + const p = ctime(&ct); + assert(p); + + __gshared char[11 + 1] date = 0; // put in BSS segment + __gshared char[8 + 1] time = 0; + __gshared char[24 + 1] timestamp = 0; + + const dsz = snprintf(&date[0], date.length, "%.6s %.4s", p + 4, p + 20); + const tsz = snprintf(&time[0], time.length, "%.8s", p + 11); + const tssz = snprintf(×tamp[0], timestamp.length, "%.24s", p); + assert(dsz > 0 && tsz > 0 && tssz > 0); + compileEnv.time = time[0 .. tsz]; + compileEnv.date = date[0 .. dsz]; + compileEnv.timestamp = timestamp[0 .. tssz]; } /** @@ -543,14 +561,7 @@ else */ extern(C++) uint versionNumber() { -version (IN_LLVM) -{ - return parseVersionNumber(versionString()); -} -else -{ - return _versionNumber; -} + return compileEnv.versionNumber; } /** diff --git a/dmd/globals.h b/dmd/globals.h index 81eba612e24..68b0835bede 100644 --- a/dmd/globals.h +++ b/dmd/globals.h @@ -112,8 +112,8 @@ enum class DLLImport : char struct Output { /// Configuration for the compiler generator - bool doOutput; // Output is enabled - bool fullOutput; // Generate comments for hidden declarations (for -HC), + d_bool doOutput; // Output is enabled + d_bool fullOutput; // Generate comments for hidden declarations (for -HC), // and don't strip the bodies of plain (non-template) functions (for -H) DString dir; // write to directory 'dir' DString name; // write to file 'name' @@ -126,71 +126,71 @@ struct Output // Put command line switches in here struct Param { - bool obj; // write object file - bool multiobj; // break one object file into multiple ones - bool trace; // insert profiling hooks - bool tracegc; // instrument calls to 'new' - bool verbose; // verbose compile - bool vcg_ast; // write-out codegen-ast - bool showColumns; // print character (column) numbers in diagnostics - bool vtls; // identify thread local variables - bool vtemplates; // collect and list statistics on template instantiations - bool vtemplatesListInstances; // collect and list statistics on template instantiations origins - bool vgc; // identify gc usage - bool vfield; // identify non-mutable field variables - bool vcomplex; // identify complex/imaginary type usage - bool vin; // identify 'in' parameters + d_bool obj; // write object file + d_bool multiobj; // break one object file into multiple ones + d_bool trace; // insert profiling hooks + d_bool tracegc; // instrument calls to 'new' + d_bool verbose; // verbose compile + d_bool vcg_ast; // write-out codegen-ast + d_bool showColumns; // print character (column) numbers in diagnostics + d_bool vtls; // identify thread local variables + d_bool vtemplates; // collect and list statistics on template instantiations + d_bool vtemplatesListInstances; // collect and list statistics on template instantiations origins + d_bool vgc; // identify gc usage + d_bool vfield; // identify non-mutable field variables + d_bool vcomplex; // identify complex/imaginary type usage + d_bool vin; // identify 'in' parameters Diagnostic useDeprecated; - bool useUnitTests; // generate unittest code - bool useInline; // inline expand functions - bool release; // build release version - bool preservePaths; // true means don't strip path from source file + d_bool useUnitTests; // generate unittest code + 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; - bool color; // use ANSI colors in console output - bool cov; // generate code coverage data + d_bool color; // use ANSI colors in console output + d_bool cov; // generate code coverage data unsigned char covPercent; // 0..100 code coverage percentage required - bool ctfe_cov; // generate coverage data for ctfe - bool ignoreUnsupportedPragmas; // rather than error on them - bool useModuleInfo; // generate runtime module information - bool useTypeInfo; // generate runtime type information - bool useExceptions; // support exception handling - bool betterC; // be a "better C" compiler; no dependency on D runtime - bool addMain; // add a default main() function - bool allInst; // generate code for all template instantiations - bool bitfields; // support C style bit fields + d_bool ctfe_cov; // generate coverage data for ctfe + d_bool ignoreUnsupportedPragmas; // rather than error on them + d_bool useModuleInfo; // generate runtime module information + d_bool useTypeInfo; // generate runtime type information + d_bool useExceptions; // support exception handling + d_bool betterC; // be a "better C" compiler; no dependency on D runtime + d_bool addMain; // add a default main() function + d_bool allInst; // generate code for all template instantiations + d_bool bitfields; // support C style bit fields CppStdRevision cplusplus; // version of C++ name mangling to support - bool showGaggedErrors; // print gagged errors anyway - bool printErrorContext; // print errors with the error context (the error line in the source file) - bool manual; // open browser on compiler manual - bool usage; // print usage and exit - bool mcpuUsage; // print help on -mcpu switch - bool transitionUsage; // print help on -transition switch - bool checkUsage; // print help on -check switch - bool checkActionUsage; // print help on -checkaction switch - bool revertUsage; // print help on -revert switch - bool previewUsage; // print help on -preview switch - bool externStdUsage; // print help on -extern-std switch - bool hcUsage; // print help on -HC switch - bool logo; // print logo; + 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 manual; // open browser on compiler manual + d_bool usage; // print usage and exit + d_bool mcpuUsage; // print help on -mcpu switch + d_bool transitionUsage; // print help on -transition switch + d_bool checkUsage; // print help on -check switch + d_bool checkActionUsage; // print help on -checkaction switch + d_bool revertUsage; // print help on -revert switch + d_bool previewUsage; // print help on -preview switch + d_bool externStdUsage; // print help on -extern-std switch + d_bool hcUsage; // print help on -HC switch + d_bool logo; // print logo; // Options for `-preview=/-revert=` FeatureState useDIP25; // implement https://wiki.dlang.org/DIP25 FeatureState useDIP1000; // implement https://dlang.org/spec/memory-safe-d.html#scope-return-params - bool ehnogc; // use @nogc exception handling - bool useDIP1021; // implement https://github.com/dlang/DIPs/blob/master/DIPs/accepted/DIP1021.md - bool fieldwise; // do struct equality testing field-wise rather than by memcmp() - bool fixAliasThis; // if the current scope has an alias this, check it before searching upper scopes + d_bool ehnogc; // use @nogc exception handling + d_bool useDIP1021; // implement https://github.com/dlang/DIPs/blob/master/DIPs/accepted/DIP1021.md + FeatureState fieldwise; // do struct equality testing field-wise rather than by memcmp() + d_bool fixAliasThis; // if the current scope has an alias this, check it before searching upper scopes FeatureState rvalueRefParam; // allow rvalues to be arguments to ref parameters // https://dconf.org/2019/talks/alexandrescu.html // https://gist.github.com/andralex/e5405a5d773f07f73196c05f8339435a // https://digitalmars.com/d/archives/digitalmars/D/Binding_rvalues_to_ref_parameters_redux_325087.html // Implementation: https://github.com/dlang/dmd/pull/9817 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 - bool shortenedMethods; // allow => in normal function declarations - bool fixImmutableConv; // error on unsound immutable conversion - https://github.com/dlang/dmd/pull/14070 - bool fix16997; // fix integral promotions for unary + - ~ operators + 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 + d_bool shortenedMethods; // allow => in normal function declarations + d_bool fixImmutableConv; // error on unsound immutable conversion - https://github.com/dlang/dmd/pull/14070 + d_bool fix16997; // fix integral promotions for unary + - ~ operators // https://issues.dlang.org/show_bug.cgi?id=16997 FeatureState dtorFields; // destruct fields of partially constructed objects // https://issues.dlang.org/show_bug.cgi?id=14246 @@ -235,10 +235,11 @@ struct Param MessageStyle messageStyle; // style of file/line annotations on messages - bool run; // run resulting executable + d_bool run; // run resulting executable Strings runargs; // arguments for executable Array cppswitches; // preprocessor switches + const char *cpp; // if not null, then this specifies the C preprocessor // Linker stuff Array objfiles; @@ -303,7 +304,7 @@ struct Param struct structalign_t { unsigned short value; - bool pack; + d_bool pack; bool isDefault() const; void setDefault(); @@ -335,6 +336,18 @@ const DString bc_ext = "bc"; const DString s_ext = "s"; #endif +struct CompileEnv +{ + uint32_t versionNumber; + DString date; + DString time; + DString vendor; + DString timestamp; + bool previewIn; + bool ddocOutput; + bool shortenedMethods; +}; + struct Global { DString inifilename; @@ -344,7 +357,7 @@ struct Global 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 - DString vendor; // Compiler backend name + CompileEnv compileEnv; Param params; unsigned errors; // number of errors reported so far @@ -358,7 +371,7 @@ struct Global Array* versionids; // command line versions and predefined versions Array* debugids; // command line debug versions and predefined versions - bool hasMainFunction; + d_bool hasMainFunction; unsigned varSequenceNumber; FileManager* fileManager; diff --git a/dmd/hdrgen.d b/dmd/hdrgen.d index d63b4ff3689..94e0eda4df1 100644 --- a/dmd/hdrgen.d +++ b/dmd/hdrgen.d @@ -139,37 +139,19 @@ void moduleToBuffer2(Module m, OutBuffer* buf, HdrGenState* hgs) private void statementToBuffer(Statement s, OutBuffer* buf, HdrGenState* hgs) { - scope v = new StatementPrettyPrintVisitor(buf, hgs); - s.accept(v); -} - -private extern (C++) final class StatementPrettyPrintVisitor : Visitor -{ - alias visit = Visitor.visit; -public: - OutBuffer* buf; - HdrGenState* hgs; - - extern (D) this(OutBuffer* buf, HdrGenState* hgs) scope + void visitDefaultCase(Statement s) { - this.buf = buf; - this.hgs = hgs; + printf("Statement::toCBuffer() %d\n", s.stmt); + assert(0, "unrecognized statement in statementToBuffer()"); } - override void visit(Statement s) - { - buf.writestring("Statement::toCBuffer()"); - buf.writenl(); - assert(0); - } - - override void visit(ErrorStatement s) + void visitError(ErrorStatement s) { buf.writestring("__error__"); buf.writenl(); } - override void visit(ExpStatement s) + void visitExp(ExpStatement s) { if (s.exp && s.exp.op == EXP.declaration && (cast(DeclarationExp)s.exp).declaration) @@ -185,7 +167,12 @@ public: buf.writenl(); } - override void visit(CompileStatement s) + void visitDtorExp(DtorExpStatement s) + { + visitExp(s); + } + + void visitMixin(MixinStatement s) { buf.writestring("mixin("); argsToBuffer(s.exps, buf, hgs, null); @@ -194,25 +181,29 @@ public: buf.writenl(); } - override void visit(CompoundStatement s) + void visitCompound(CompoundStatement s) { foreach (sx; *s.statements) { if (sx) - sx.accept(this); + sx.statementToBuffer(buf, hgs); } } - override void visit(CompoundDeclarationStatement s) + void visitCompoundAsm(CompoundAsmStatement s) + { + visitCompound(s); + } + + void visitCompoundDeclaration(CompoundDeclarationStatement s) { bool anywritten = false; foreach (sx; *s.statements) { auto ds = sx ? sx.isExpStatement() : null; - if (ds && ds.exp.op == EXP.declaration) + if (ds && ds.exp.isDeclarationExp()) { - auto d = (cast(DeclarationExp)ds.exp).declaration; - assert(d.isDeclaration()); + auto d = ds.exp.isDeclarationExp().declaration; if (auto v = d.isVarDeclaration()) { scope ppv = new DsymbolPrettyPrintVisitor(buf, hgs); @@ -228,7 +219,7 @@ public: buf.writenl(); } - override void visit(UnrolledLoopStatement s) + void visitUnrolledLoop(UnrolledLoopStatement s) { buf.writestring("/*unrolled*/ {"); buf.writenl(); @@ -236,26 +227,26 @@ public: foreach (sx; *s.statements) { if (sx) - sx.accept(this); + sx.statementToBuffer(buf, hgs); } buf.level--; buf.writeByte('}'); buf.writenl(); } - override void visit(ScopeStatement s) + void visitScope(ScopeStatement s) { buf.writeByte('{'); buf.writenl(); buf.level++; if (s.statement) - s.statement.accept(this); + s.statement.statementToBuffer(buf, hgs); buf.level--; buf.writeByte('}'); buf.writenl(); } - override void visit(WhileStatement s) + void visitWhile(WhileStatement s) { buf.writestring("while ("); if (auto p = s.param) @@ -276,28 +267,28 @@ public: buf.writeByte(')'); buf.writenl(); if (s._body) - s._body.accept(this); + s._body.statementToBuffer(buf, hgs); } - override void visit(DoStatement s) + void visitDo(DoStatement s) { buf.writestring("do"); buf.writenl(); if (s._body) - s._body.accept(this); + s._body.statementToBuffer(buf, hgs); buf.writestring("while ("); s.condition.expressionToBuffer(buf, hgs); buf.writestring(");"); buf.writenl(); } - override void visit(ForStatement s) + void visitFor(ForStatement s) { buf.writestring("for ("); if (s._init) { hgs.forStmtInit++; - s._init.accept(this); + s._init.statementToBuffer(buf, hgs); hgs.forStmtInit--; } else @@ -319,13 +310,13 @@ public: buf.writenl(); buf.level++; if (s._body) - s._body.accept(this); + s._body.statementToBuffer(buf, hgs); buf.level--; buf.writeByte('}'); buf.writenl(); } - private void foreachWithoutBody(ForeachStatement s) + void foreachWithoutBody(ForeachStatement s) { buf.writestring(Token.toString(s.op)); buf.writestring(" ("); @@ -346,20 +337,20 @@ public: buf.writenl(); } - override void visit(ForeachStatement s) + void visitForeach(ForeachStatement s) { foreachWithoutBody(s); buf.writeByte('{'); buf.writenl(); buf.level++; if (s._body) - s._body.accept(this); + s._body.statementToBuffer(buf, hgs); buf.level--; buf.writeByte('}'); buf.writenl(); } - private void foreachRangeWithoutBody(ForeachRangeStatement s) + void foreachRangeWithoutBody(ForeachRangeStatement s) { buf.writestring(Token.toString(s.op)); buf.writestring(" ("); @@ -375,39 +366,39 @@ public: buf.writenl(); } - override void visit(ForeachRangeStatement s) + void visitForeachRange(ForeachRangeStatement s) { foreachRangeWithoutBody(s); buf.writeByte('{'); buf.writenl(); buf.level++; if (s._body) - s._body.accept(this); + s._body.statementToBuffer(buf, hgs); buf.level--; buf.writeByte('}'); buf.writenl(); } - override void visit(StaticForeachStatement s) + void visitStaticForeach(StaticForeachStatement s) { buf.writestring("static "); if (s.sfe.aggrfe) { - visit(s.sfe.aggrfe); + visitForeach(s.sfe.aggrfe); } else { assert(s.sfe.rangefe); - visit(s.sfe.rangefe); + visitForeachRange(s.sfe.rangefe); } } - override void visit(ForwardingStatement s) + void visitForwarding(ForwardingStatement s) { - s.statement.accept(this); + s.statement.statementToBuffer(buf, hgs); } - override void visit(IfStatement s) + void visitIf(IfStatement s) { buf.writestring("if ("); if (Parameter p = s.prm) @@ -428,12 +419,12 @@ public: buf.writenl(); if (s.ifbody.isScopeStatement()) { - s.ifbody.accept(this); + s.ifbody.statementToBuffer(buf, hgs); } else { buf.level++; - s.ifbody.accept(this); + s.ifbody.statementToBuffer(buf, hgs); buf.level--; } if (s.elsebody) @@ -449,18 +440,18 @@ public: } if (s.elsebody.isScopeStatement() || s.elsebody.isIfStatement()) { - s.elsebody.accept(this); + s.elsebody.statementToBuffer(buf, hgs); } else { buf.level++; - s.elsebody.accept(this); + s.elsebody.statementToBuffer(buf, hgs); buf.level--; } } } - override void visit(ConditionalStatement s) + void visitConditional(ConditionalStatement s) { s.condition.conditionToBuffer(buf, hgs); buf.writenl(); @@ -468,7 +459,7 @@ public: buf.writenl(); buf.level++; if (s.ifbody) - s.ifbody.accept(this); + s.ifbody.statementToBuffer(buf, hgs); buf.level--; buf.writeByte('}'); buf.writenl(); @@ -479,14 +470,14 @@ public: buf.writeByte('{'); buf.level++; buf.writenl(); - s.elsebody.accept(this); + s.elsebody.statementToBuffer(buf, hgs); buf.level--; buf.writeByte('}'); } buf.writenl(); } - override void visit(PragmaStatement s) + void visitPragma(PragmaStatement s) { buf.writestring("pragma ("); buf.writestring(s.ident.toString()); @@ -502,7 +493,7 @@ public: buf.writeByte('{'); buf.writenl(); buf.level++; - s._body.accept(this); + s._body.statementToBuffer(buf, hgs); buf.level--; buf.writeByte('}'); buf.writenl(); @@ -514,12 +505,12 @@ public: } } - override void visit(StaticAssertStatement s) + void visitStaticAssert(StaticAssertStatement s) { s.sa.dsymbolToBuffer(buf, hgs); } - override void visit(SwitchStatement s) + void visitSwitch(SwitchStatement s) { buf.writestring(s.isFinal ? "final switch (" : "switch ("); s.condition.expressionToBuffer(buf, hgs); @@ -532,28 +523,28 @@ public: buf.writeByte('{'); buf.writenl(); buf.level++; - s._body.accept(this); + s._body.statementToBuffer(buf, hgs); buf.level--; buf.writeByte('}'); buf.writenl(); } else { - s._body.accept(this); + s._body.statementToBuffer(buf, hgs); } } } - override void visit(CaseStatement s) + void visitCase(CaseStatement s) { buf.writestring("case "); s.exp.expressionToBuffer(buf, hgs); buf.writeByte(':'); buf.writenl(); - s.statement.accept(this); + s.statement.statementToBuffer(buf, hgs); } - override void visit(CaseRangeStatement s) + void visitCaseRange(CaseRangeStatement s) { buf.writestring("case "); s.first.expressionToBuffer(buf, hgs); @@ -561,23 +552,23 @@ public: s.last.expressionToBuffer(buf, hgs); buf.writeByte(':'); buf.writenl(); - s.statement.accept(this); + s.statement.statementToBuffer(buf, hgs); } - override void visit(DefaultStatement s) + void visitDefault(DefaultStatement s) { buf.writestring("default:"); buf.writenl(); - s.statement.accept(this); + s.statement.statementToBuffer(buf, hgs); } - override void visit(GotoDefaultStatement s) + void visitGotoDefault(GotoDefaultStatement s) { buf.writestring("goto default;"); buf.writenl(); } - override void visit(GotoCaseStatement s) + void visitGotoCase(GotoCaseStatement s) { buf.writestring("goto case"); if (s.exp) @@ -589,13 +580,13 @@ public: buf.writenl(); } - override void visit(SwitchErrorStatement s) + void visitSwitchError(SwitchErrorStatement s) { buf.writestring("SwitchErrorStatement::toCBuffer()"); buf.writenl(); } - override void visit(ReturnStatement s) + void visitReturn(ReturnStatement s) { buf.writestring("return "); if (s.exp) @@ -604,7 +595,7 @@ public: buf.writenl(); } - override void visit(BreakStatement s) + void visitBreak(BreakStatement s) { buf.writestring("break"); if (s.ident) @@ -616,7 +607,7 @@ public: buf.writenl(); } - override void visit(ContinueStatement s) + void visitContinue(ContinueStatement s) { buf.writestring("continue"); if (s.ident) @@ -628,7 +619,7 @@ public: buf.writenl(); } - override void visit(SynchronizedStatement s) + void visitSynchronized(SynchronizedStatement s) { buf.writestring("synchronized"); if (s.exp) @@ -640,21 +631,21 @@ public: if (s._body) { buf.writeByte(' '); - s._body.accept(this); + s._body.statementToBuffer(buf, hgs); } } - override void visit(WithStatement s) + void visitWith(WithStatement s) { buf.writestring("with ("); s.exp.expressionToBuffer(buf, hgs); buf.writestring(")"); buf.writenl(); if (s._body) - s._body.accept(this); + s._body.statementToBuffer(buf, hgs); } - override void visit(TryCatchStatement s) + void visitTryCatch(TryCatchStatement s) { buf.writestring("try"); buf.writenl(); @@ -662,29 +653,44 @@ public: { if (s._body.isScopeStatement()) { - s._body.accept(this); + s._body.statementToBuffer(buf, hgs); } else { buf.level++; - s._body.accept(this); + s._body.statementToBuffer(buf, hgs); buf.level--; } } foreach (c; *s.catches) { - visit(c); + buf.writestring("catch"); + if (c.type) + { + buf.writeByte('('); + typeToBuffer(c.type, c.ident, buf, hgs); + buf.writeByte(')'); + } + buf.writenl(); + buf.writeByte('{'); + buf.writenl(); + buf.level++; + if (c.handler) + c.handler.statementToBuffer(buf, hgs); + buf.level--; + buf.writeByte('}'); + buf.writenl(); } } - override void visit(TryFinallyStatement s) + void visitTryFinally(TryFinallyStatement s) { buf.writestring("try"); buf.writenl(); buf.writeByte('{'); buf.writenl(); buf.level++; - s._body.accept(this); + s._body.statementToBuffer(buf, hgs); buf.level--; buf.writeByte('}'); buf.writenl(); @@ -692,25 +698,25 @@ public: buf.writenl(); if (s.finalbody.isScopeStatement()) { - s.finalbody.accept(this); + s.finalbody.statementToBuffer(buf, hgs); } else { buf.level++; - s.finalbody.accept(this); + s.finalbody.statementToBuffer(buf, hgs); buf.level--; } } - override void visit(ScopeGuardStatement s) + void visitScopeGuard(ScopeGuardStatement s) { buf.writestring(Token.toString(s.tok)); buf.writeByte(' '); if (s.statement) - s.statement.accept(this); + s.statement.statementToBuffer(buf, hgs); } - override void visit(ThrowStatement s) + void visitThrow(ThrowStatement s) { buf.writestring("throw "); s.exp.expressionToBuffer(buf, hgs); @@ -718,15 +724,15 @@ public: buf.writenl(); } - override void visit(DebugStatement s) + void visitDebug(DebugStatement s) { if (s.statement) { - s.statement.accept(this); + s.statement.statementToBuffer(buf, hgs); } } - override void visit(GotoStatement s) + void visitGoto(GotoStatement s) { buf.writestring("goto "); buf.writestring(s.ident.toString()); @@ -734,16 +740,16 @@ public: buf.writenl(); } - override void visit(LabelStatement s) + void visitLabel(LabelStatement s) { buf.writestring(s.ident.toString()); buf.writeByte(':'); buf.writenl(); if (s.statement) - s.statement.accept(this); + s.statement.statementToBuffer(buf, hgs); } - override void visit(AsmStatement s) + void visitAsm(AsmStatement s) { buf.writestring("asm { "); Token* t = s.tokens; @@ -769,33 +775,26 @@ public: buf.writenl(); } - override void visit(ImportStatement s) + void visitInlineAsm(InlineAsmStatement s) { - foreach (imp; *s.imports) - { - imp.dsymbolToBuffer(buf, hgs); - } + visitAsm(s); } - void visit(Catch c) + void visitGccAsm(GccAsmStatement s) { - buf.writestring("catch"); - if (c.type) + visitAsm(s); + } + + void visitImport(ImportStatement s) + { + foreach (imp; *s.imports) { - buf.writeByte('('); - typeToBuffer(c.type, c.ident, buf, hgs); - buf.writeByte(')'); + imp.dsymbolToBuffer(buf, hgs); } - buf.writenl(); - buf.writeByte('{'); - buf.writenl(); - buf.level++; - if (c.handler) - c.handler.accept(this); - buf.level--; - buf.writeByte('}'); - buf.writenl(); } + + mixin VisitStatement!void visit; + visit.VisitStatement(s); } private void dsymbolToBuffer(Dsymbol s, OutBuffer* buf, HdrGenState* hgs) @@ -1165,7 +1164,7 @@ public: } - override void visit(CompileDeclaration d) + override void visit(MixinDeclaration d) { buf.writestring("mixin("); argsToBuffer(d.exps, buf, hgs, null); @@ -2326,6 +2325,16 @@ private void expressionPrettyPrint(Expression e, OutBuffer* buf, HdrGenState* hg expToBuffer(e.e1, precedence[e.op], buf, hgs); } + void visitLoweredAssignExp(LoweredAssignExp e) + { + if (global.params.vcg_ast) + { + expressionToBuffer(e.lowering, buf, hgs); + return; + } + + visit(cast(BinExp)e); + } void visitBin(BinExp e) { expToBuffer(e.e1, precedence[e.op], buf, hgs); @@ -2699,6 +2708,7 @@ private void expressionPrettyPrint(Expression e, OutBuffer* buf, HdrGenState* hg case EXP.remove: return visitRemove(e.isRemoveExp()); case EXP.question: return visitCond(e.isCondExp()); case EXP.classReference: return visitClassReference(e.isClassReferenceExp()); + case EXP.loweredAssignExp: return visitLoweredAssignExp(e.isLoweredAssignExp()); } } @@ -2887,8 +2897,7 @@ public: void toCBuffer(const Statement s, OutBuffer* buf, HdrGenState* hgs) { - scope v = new StatementPrettyPrintVisitor(buf, hgs); - (cast() s).accept(v); + (cast()s).statementToBuffer(buf, hgs); } void toCBuffer(const Type t, OutBuffer* buf, const Identifier ident, HdrGenState* hgs) @@ -3228,6 +3237,7 @@ private void parametersToBuffer(ParameterList pl, OutBuffer* buf, HdrGenState* h final switch (pl.varargs) { case VarArg.none: + case VarArg.KRvariadic: break; case VarArg.variadic: @@ -3822,15 +3832,8 @@ private void initializerToBuffer(Initializer inx, OutBuffer* buf, HdrGenState* h buf.writeByte('}'); } - final switch (inx.kind) - { - case InitKind.error: return visitError (inx.isErrorInitializer ()); - case InitKind.void_: return visitVoid (inx.isVoidInitializer ()); - case InitKind.struct_: return visitStruct(inx.isStructInitializer()); - case InitKind.array: return visitArray (inx.isArrayInitializer ()); - case InitKind.exp: return visitExp (inx.isExpInitializer ()); - case InitKind.C_: return visitC (inx.isCInitializer ()); - } + mixin VisitInitializer!void visit; + visit.VisitInitializer(inx); } @@ -4106,7 +4109,6 @@ string EXPtoString(EXP op) EXP.error : "error", EXP.objcClassReference : "class", - EXP.typeof_ : "typeof", EXP.mixin_ : "mixin", EXP.import_ : "import", @@ -4146,7 +4148,6 @@ string EXPtoString(EXP op) EXP.remove : "remove", EXP.tuple : "tuple", EXP.traits : "__traits", - EXP.default_ : "default", EXP.overloadSet : "__overloadset", EXP.void_ : "void", EXP.vectorArray : "vectorarray", @@ -4237,6 +4238,7 @@ string EXPtoString(EXP op) EXP.declaration : "declaration", EXP.interval : "interval", + EXP.loweredAssignExp : "=" ]; const p = strings[op]; if (!p) diff --git a/dmd/iasmgcc.d b/dmd/iasmgcc.d index f8c88ab536e..1d4dea47b81 100644 --- a/dmd/iasmgcc.d +++ b/dmd/iasmgcc.d @@ -302,7 +302,8 @@ Ldone: extern (C++) public Statement gccAsmSemantic(GccAsmStatement s, Scope *sc) { //printf("GccAsmStatement.semantic()\n"); - scope p = new Parser!ASTCodegen(sc._module, ";", false, global.errorSink); + const bool doUnittests = global.params.useUnitTests || global.params.ddoc.doOutput || global.params.dihdr.doOutput; + 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; @@ -410,7 +411,8 @@ unittest { const errors = global.errors; scope gas = new GccAsmStatement(Loc.initial, tokens); - scope p = new Parser!ASTCodegen(null, ";", false, global.errorSink); + const bool doUnittests = false; + scope p = new Parser!ASTCodegen(null, ";", false, global.errorSink, &global.compileEnv, doUnittests); p.token = *tokens; p.parseGccAsm(gas); return global.errors - errors; @@ -420,7 +422,8 @@ unittest static void parseAsm(string input, bool expectError) { // Generate tokens from input test. - scope p = new Parser!ASTCodegen(null, input, false, global.errorSink); + const bool doUnittests = false; + scope p = new Parser!ASTCodegen(null, input, false, global.errorSink, &global.compileEnv, doUnittests); p.nextToken(); Token* toklist = null; diff --git a/dmd/id.d b/dmd/id.d index c280b657d34..3614d9275cb 100644 --- a/dmd/id.d +++ b/dmd/id.d @@ -362,6 +362,8 @@ immutable Msgtable[] msgtable = { "_d_arrayappendcTXImpl" }, { "_d_arrayappendcTX" }, { "_d_arrayappendcTXTrace" }, + { "_d_arraycatnTX" }, + { "_d_arraycatnTXTrace" }, // varargs implementation { "stdc" }, @@ -371,6 +373,10 @@ immutable Msgtable[] msgtable = // Builtin functions { "std" }, { "core" }, + { "config" }, + { "c_complex_float" }, + { "c_complex_double" }, + { "c_complex_real" }, { "etc" }, { "attribute" }, { "atomic" }, @@ -520,9 +526,17 @@ immutable Msgtable[] msgtable = { "__tag" }, { "dllimport" }, { "dllexport" }, + { "naked" }, + { "thread" }, { "vector_size" }, { "__func__" }, + { "always_inline" }, + { "noinline" }, { "noreturn" }, + { "_nothrow", "nothrow" }, + { "_deprecated", "deprecated" }, + { "_align", "align" }, + { "aligned" }, { "__pragma", "pragma" }, { "builtins", "__builtins" }, { "builtinsModuleName", "builtins" }, @@ -534,6 +548,7 @@ immutable Msgtable[] msgtable = { "show" }, { "push" }, { "pop" }, + { "_pure", "pure" }, { "define" }, { "undef" }, diff --git a/dmd/identifier.h b/dmd/identifier.h index c12c3554c1b..e7b3ba60b0f 100644 --- a/dmd/identifier.h +++ b/dmd/identifier.h @@ -17,7 +17,7 @@ class Identifier final : public RootObject { private: int value; - bool isAnonymous_; + d_bool isAnonymous_; DString string; public: diff --git a/dmd/init.d b/dmd/init.d index f646d0382eb..6f20a3c7a45 100644 --- a/dmd/init.d +++ b/dmd/init.d @@ -269,7 +269,22 @@ extern (C++) final class CInitializer : Initializer */ Initializer syntaxCopy(Initializer inx) { - static Initializer copyStruct(StructInitializer vi) + static Initializer visitVoid(VoidInitializer vi) + { + return new VoidInitializer(vi.loc); + } + + static Initializer visitError(ErrorInitializer vi) + { + return vi; + } + + static Initializer visitExp(ExpInitializer vi) + { + return new ExpInitializer(vi.loc, vi.exp.syntaxCopy()); + } + + static Initializer visitStruct(StructInitializer vi) { auto si = new StructInitializer(vi.loc); assert(vi.field.length == vi.value.length); @@ -283,7 +298,7 @@ Initializer syntaxCopy(Initializer inx) return si; } - static Initializer copyArray(ArrayInitializer vi) + static Initializer visitArray(ArrayInitializer vi) { auto ai = new ArrayInitializer(vi.loc); assert(vi.index.length == vi.value.length); @@ -297,7 +312,7 @@ Initializer syntaxCopy(Initializer inx) return ai; } - static Initializer copyC(CInitializer vi) + static Initializer visitC(CInitializer vi) { auto ci = new CInitializer(vi.loc); ci.initializerList.setDim(vi.initializerList.length); @@ -322,13 +337,62 @@ Initializer syntaxCopy(Initializer inx) return ci; } - final switch (inx.kind) + mixin VisitInitializer!Initializer visit; + return visit.VisitInitializer(inx); +} + +/*********************************************************** + * Visit each Initializer in init. Call a function visit%s(init) for + * each node, where %s is the op of the node. Otherwise call visitDefault(init) + * for that node. If the visit function returns R.init, continue + * visiting each node, otherwise return the value of R. + * Params: + * Result = return type + * init = Initializer tree to traverse + * Returns: + * Result.init for continue, value of type Result for early exit + */ + +mixin template VisitInitializer(Result) +{ + Result VisitInitializer(Initializer init) + { + final switch (init.kind) + { + case InitKind.void_: mixin(visitCase("Void")); break; + case InitKind.error: mixin(visitCase("Error")); break; + case InitKind.struct_: mixin(visitCase("Struct")); break; + case InitKind.array: mixin(visitCase("Array")); break; + case InitKind.exp: mixin(visitCase("Exp")); break; + case InitKind.C_: mixin(visitCase("C")); break; + } + static if (is(Result == void)) { } else + return Result.init; + } +} + +/**************************************** + * CTFE-only helper function for VisitInitializer. + * Params: + * handler = string for the name of the visit handler + * Returns: boilerplate code for a case + */ +pure string visitCase(string handler) +{ + if (__ctfe) { - case InitKind.void_: return new VoidInitializer(inx.loc); - case InitKind.error: return inx; - case InitKind.struct_: return copyStruct(cast(StructInitializer)inx); - case InitKind.array: return copyArray(cast(ArrayInitializer)inx); - case InitKind.exp: return new ExpInitializer(inx.loc, (cast(ExpInitializer)inx).exp.syntaxCopy()); - case InitKind.C_: return copyC(cast(CInitializer)inx); + return + " + auto ix = init.is"~handler~"Initializer(); + static if (is(Result == void)) + visit"~handler~"(ix); + else + { + Result r = visit"~handler~"(ix); + if (r !is Result.init) + return r; + } + "; } + assert(0); } diff --git a/dmd/init.h b/dmd/init.h index 66b874c91b5..9a6a56b68bb 100644 --- a/dmd/init.h +++ b/dmd/init.h @@ -77,8 +77,8 @@ 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 - bool sem; // true if semantic() is run - bool isCarray; // C array semantics + d_bool sem; // true if semantic() is run + d_bool isCarray; // C array semantics bool isAssociativeArray() const; Expression *toAssocArrayLiteral(); @@ -89,7 +89,7 @@ class ArrayInitializer final : public Initializer class ExpInitializer final : public Initializer { public: - bool expandTuples; + d_bool expandTuples; Expression *exp; void accept(Visitor *v) override { v->visit(this); } @@ -112,7 +112,7 @@ class CInitializer final : public Initializer public: DesigInits initializerList; Type *type; // type that array will be used to initialize - bool sem; // true if semantic() is run + 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 18b10b41a2d..054ca766868 100644 --- a/dmd/initsem.d +++ b/dmd/initsem.d @@ -582,7 +582,7 @@ extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ Initializer visitC(CInitializer ci) { - //printf("CInitializer::semantic() (%s) %s\n", t.toChars(), ci.toChars()); + //printf("CInitializer::semantic() tx: %s t: %s ci: %s\n", (tx ? tx.toChars() : "".ptr), t.toChars(), ci.toChars()); /* Rewrite CInitializer into ExpInitializer, ArrayInitializer, or StructInitializer */ t = t.toBasetype(); @@ -767,13 +767,15 @@ extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ return err(); } const nfields = sd.fields.length; - size_t fieldi = 0; + Loop1: for (size_t index = 0; index < ci.initializerList.length; ) { - auto di = ci.initializerList[index]; - auto dlist = di.designatorList; + CInitializer cprev; + L1: + DesigInit di = ci.initializerList[index]; + Designators* dlist = di.designatorList; if (dlist) { const length = (*dlist).length; @@ -796,14 +798,34 @@ extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ si.addInit(id, di.initializer); ++fieldi; ++index; - break; + continue Loop1; } } + if (cprev) + { + /* The peeling didn't work, so unpeel it + */ + ci = cprev; + di = ci.initializerList[index]; + goto L2; + } + error(ci.loc, "`.%s` is not a field of `%s`\n", id.toChars(), sd.toChars()); + return err(); } else { if (fieldi == nfields) break; + if (index == 0 && ci.initializerList.length == 1 && di.initializer.isCInitializer()) + { + /* Try peeling off this set of { } and see if it works + */ + cprev = ci; + ci = di.initializer.isCInitializer(); + goto L1; + } + + L2: VarDeclaration field; while (1) // skip field if it overlaps with previously seen fields { @@ -951,22 +973,19 @@ extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ return initializerSemantic(ai, sc, tx, needInterpret); } else if (ExpInitializer ei = isBraceExpression()) + { return visitExp(ei); + } else { - assert(0); + error(ci.loc, "unrecognized C initializer `%s`", ci.toChars()); + return err(); } } - final switch (init.kind) - { - case InitKind.void_: return visitVoid (init.isVoidInitializer()); - case InitKind.error: return visitError (init.isErrorInitializer()); - case InitKind.struct_: return visitStruct(init.isStructInitializer()); - case InitKind.array: return visitArray (init.isArrayInitializer()); - case InitKind.exp: return visitExp (init.isExpInitializer()); - case InitKind.C_: return visitC (init.isCInitializer()); - } + mixin VisitInitializer!Initializer visit; + auto result = visit.VisitInitializer(init); + return (result !is null) ? result : new ErrorInitializer(); } /*********************** @@ -1120,15 +1139,9 @@ Initializer inferType(Initializer init, Scope* sc) return new ErrorInitializer(); } - final switch (init.kind) - { - case InitKind.void_: return visitVoid (init.isVoidInitializer()); - case InitKind.error: return visitError (init.isErrorInitializer()); - case InitKind.struct_: return visitStruct(init.isStructInitializer()); - case InitKind.array: return visitArray (init.isArrayInitializer()); - case InitKind.exp: return visitExp (init.isExpInitializer()); - case InitKind.C_: return visitC (init.isCInitializer()); - } + mixin VisitInitializer!Initializer visit; + auto result = visit.VisitInitializer(init); + return (result !is null) ? result : new ErrorInitializer(); } /*********************** @@ -1333,15 +1346,8 @@ extern (C++) Expression initializerToExpression(Initializer init, Type itype = n return null; } - final switch (init.kind) - { - case InitKind.void_: return visitVoid (init.isVoidInitializer()); - case InitKind.error: return visitError (init.isErrorInitializer()); - case InitKind.struct_: return visitStruct(init.isStructInitializer()); - case InitKind.array: return visitArray (init.isArrayInitializer()); - case InitKind.exp: return visitExp (init.isExpInitializer()); - case InitKind.C_: return visitC (init.isCInitializer()); - } + mixin VisitInitializer!Expression visit; + return visit.VisitInitializer(init); } diff --git a/dmd/inline.d b/dmd/inline.d index 0a94d4669fe..4c5836e7226 100644 --- a/dmd/inline.d +++ b/dmd/inline.d @@ -69,12 +69,17 @@ public void inlineScanModule(Module m) Dsymbol s = (*m.members)[i]; //if (global.params.verbose) // message("inline scan symbol %s", s.toChars()); - scope InlineScanVisitor v = new InlineScanVisitor(); - s.accept(v); + inlineScanDsymbol(s); } m.semanticRun = PASS.inlinedone; } +private void inlineScanDsymbol(Dsymbol s) +{ + scope InlineScanVisitorDsymbol v = new InlineScanVisitorDsymbol(); + s.accept(v); +} + /*********************************************************** * Perform the "inline copying" of a default argument for a function parameter. * @@ -745,6 +750,21 @@ version (IN_LLVM) {} else result = ae; } + override void visit(CatExp e) + { + auto ce = e.copy().isCatExp(); + + if (auto lowering = ce.lowering) + ce.lowering = doInlineAs!Expression(lowering, ids); + else + { + ce.e1 = doInlineAs!Expression(e.e1, ids); + ce.e2 = doInlineAs!Expression(e.e2, ids); + } + + result = ce; + } + override void visit(BinExp e) { auto be = cast(BinExp)e.copy(); @@ -764,12 +784,11 @@ version (IN_LLVM) {} else override void visit(AssignExp e) { visit(cast(BinExp)e); + } - if (auto ale = e.e1.isArrayLengthExp()) - { - Type tn = ale.e1.type.toBasetype().nextOf(); - semanticTypeInfo(null, tn); - } + override void visit(LoweredAssignExp e) + { + result = doInlineAs!Expression(e.lowering, ids); } override void visit(EqualExp e) @@ -1225,7 +1244,7 @@ public: } else { - s.accept(this); + inlineScanDsymbol(s); } } @@ -1246,6 +1265,18 @@ public: inlineScan(e.msg); } + override void visit(CatExp e) + { + if (auto lowering = e.lowering) + { + inlineScan(lowering); + return; + } + + inlineScan(e.e1); + inlineScan(e.e2); + } + override void visit(BinExp e) { inlineScan(e.e1); @@ -1290,6 +1321,11 @@ public: visit(cast(BinExp)e); } + override void visit(LoweredAssignExp e) + { + inlineScan(e.lowering); + } + override void visit(CallExp e) { //printf("CallExp.inlineScan() %s\n", e.toChars()); @@ -1497,7 +1533,7 @@ public: //printf("StructLiteralExp.inlineScan()\n"); if (e.stageflags & stageInlineScan) return; - int old = e.stageflags; + const old = e.stageflags; e.stageflags |= stageInlineScan; arrayInlineScan(e.elements); e.stageflags = old; @@ -1535,6 +1571,20 @@ public: eresult = null; } } +} + +/*********************************************************** + * Walk the trees, looking for functions to inline. + * Inline any that can be. + */ +private extern (C++) final class InlineScanVisitorDsymbol : Visitor +{ + alias visit = Visitor.visit; +public: + + extern (D) this() scope + { + } /************************************* * Look for function inlining possibilities. @@ -1556,20 +1606,20 @@ public: return; if (fd.fbody && !fd.isNaked()) { - auto againsave = again; - auto parentsave = parent; - parent = fd; - do + while (1) { - again = false; fd.inlineNest++; fd.inlineScanned = true; - inlineScan(fd.fbody); + + scope InlineScanVisitor v = new InlineScanVisitor(); + v.parent = fd; + v.inlineScan(fd.fbody); + bool again = v.again; + fd.inlineNest--; + if (!again) + break; } - while (again); - again = againsave; - parent = parentsave; } } @@ -1708,7 +1758,8 @@ private bool canInline(FuncDeclaration fd, bool hasthis, bool hdrscan, bool stat TypeFunction tf = fd.type.isTypeFunction(); // no variadic parameter lists - if (tf.parameterList.varargs == VarArg.variadic) + if (tf.parameterList.varargs == VarArg.variadic || + tf.parameterList.varargs == VarArg.KRvariadic) goto Lno; /* No lazy parameters when inlining by statement, as the inliner tries to @@ -1815,8 +1866,7 @@ private bool canInline(FuncDeclaration fd, bool hasthis, bool hdrscan, bool stat else fd.inlineStatusExp = ILS.yes; - scope InlineScanVisitor v = new InlineScanVisitor(); - fd.accept(v); // Don't scan recursively for header content scan + inlineScanDsymbol(fd); // Don't scan recursively for header content scan if (fd.inlineStatusExp == ILS.uninitialized) { diff --git a/dmd/json.d b/dmd/json.d index 2af7faec354..dcf53b8f547 100644 --- a/dmd/json.d +++ b/dmd/json.d @@ -833,7 +833,7 @@ public: { import dmd.target : target; objectStart(); - requiredProperty("vendor", global.vendor); + requiredProperty("vendor", global.compileEnv.vendor); requiredProperty("version", global.versionString()); property("__VERSION__", global.versionNumber()); requiredProperty("interface", determineCompilerInterface()); @@ -1070,13 +1070,13 @@ Determines and returns the compiler interface which is one of `dmd`, `ldc`, */ private extern(D) string determineCompilerInterface() { - if (global.vendor == "Digital Mars D") + if (global.compileEnv.vendor == "Digital Mars D") return "dmd"; - if (global.vendor == "LDC") + if (global.compileEnv.vendor == "LDC") return "ldc"; - if (global.vendor == "GNU D") + if (global.compileEnv.vendor == "GNU D") return "gdc"; - if (global.vendor == "SDC") + if (global.compileEnv.vendor == "SDC") return "sdc"; return null; } diff --git a/dmd/ldcbindings.d b/dmd/ldcbindings.d index 018f9874304..e68aee435dd 100644 --- a/dmd/ldcbindings.d +++ b/dmd/ldcbindings.d @@ -80,6 +80,6 @@ Expression createExpressionForIntOp(const ref Loc loc, TOK op, Expression e1, Ex } } -Expression createExpression(const ref Loc loc, EXP op) { return new Expression(loc, op, __traits(classInstanceSize, Expression)); } +Expression createExpression(const ref Loc loc, EXP op) { return new Expression(loc, op); } DsymbolExp createDsymbolExp(const ref Loc loc, Dsymbol s) { return new DsymbolExp(loc, s, /*hasOverloads=*/false); } AddrExp createAddrExp(const ref Loc loc, Expression e) { return new AddrExp(loc, e); } diff --git a/dmd/lexer.d b/dmd/lexer.d index f0f7872c2b2..b5f6617b99a 100644 --- a/dmd/lexer.d +++ b/dmd/lexer.d @@ -14,12 +14,8 @@ module dmd.lexer; import core.stdc.ctype; -import core.stdc.errno; -import core.stdc.stdarg; import core.stdc.stdio; -import core.stdc.stdlib : getenv; import core.stdc.string; -import core.stdc.time; import dmd.entity; import dmd.errorsink; @@ -31,10 +27,8 @@ import dmd.root.ctfloat; import dmd.common.outbuffer; import dmd.root.port; import dmd.root.rmem; -import dmd.root.string; import dmd.root.utf; import dmd.tokens; -import dmd.utils; nothrow: @@ -43,6 +37,22 @@ version (DMDLIB) version = LocOffset; } +/*********************************************************** + * Values to use for various magic identifiers + */ +struct CompileEnv +{ + uint versionNumber; /// __VERSION__ + const(char)[] date; /// __DATE__ + const(char)[] time; /// __TIME__ + const(char)[] vendor; /// __VENDOR__ + const(char)[] timestamp; /// __TIMESTAMP__ + + bool previewIn; /// `in` means `[ref] scope const`, accepts rvalues + bool ddocOutput; /// collect embedded documentation comments + bool shortenedMethods = true; /// allow => in normal function declarations +} + /*********************************************************** */ class Lexer @@ -69,6 +79,7 @@ class Lexer ubyte wchar_tsize; /// size of C wchar_t, 2 or 4 ErrorSink eSink; /// send error messages through this interface + CompileEnv compileEnv; /// environment private { @@ -87,8 +98,6 @@ class Lexer int lastDocLine; // last line of previous doc comment Token* tokenFreelist; - uint versionNumber; - const(char)[] vendor; } nothrow: @@ -105,13 +114,12 @@ class Lexer * doDocComment = handle documentation comments * commentToken = comments become TOK.comment's * errorSink = where error messages go, must not be null - * vendor = name of the vendor - * versionNumber = version of the caller + * compileEnv = version, vendor, date, time, etc. */ this(const(char)* filename, const(char)* base, size_t begoffset, size_t endoffset, bool doDocComment, bool commentToken, ErrorSink errorSink, - const(char)[] vendor = "DLF", uint versionNumber = 1) pure scope + const CompileEnv* compileEnv) pure scope { scanloc = Loc(filename, 1, 1); // debug printf("Lexer::Lexer(%p)\n", base); @@ -128,8 +136,13 @@ class Lexer this.lastDocLine = 0; this.eSink = errorSink; assert(errorSink); - this.versionNumber = versionNumber; - this.vendor = vendor; + if (compileEnv) + this.compileEnv = *compileEnv; + else + { + this.compileEnv.versionNumber = 1; + this.compileEnv.vendor = "DLF"; + } //initKeywords(); /* If first line starts with '#!', ignore the line */ @@ -169,10 +182,10 @@ class Lexer */ this(const(char)* filename, const(char)* base, size_t begoffset, size_t endoffset, bool doDocComment, bool commentToken, bool whitespaceToken, - ErrorSink errorSink + ErrorSink errorSink, const CompileEnv* compileEnv = null ) { - this(filename, base, begoffset, endoffset, doDocComment, commentToken, errorSink); + this(filename, base, begoffset, endoffset, doDocComment, commentToken, errorSink, compileEnv); this.whitespaceToken = whitespaceToken; } @@ -571,36 +584,26 @@ class Lexer else if (*t.ptr == '_') // if special identifier token { - // Lazy initialization - TimeStampInfo.initialize(t.loc, eSink); - - if (id == Id.DATE) + void toToken(const(char)[] s) { - t.ustring = TimeStampInfo.date.ptr; - goto Lstr; + t.value = TOK.string_; + t.ustring = s.ptr; + t.len = cast(uint)s.length; + t.postfix = 0; } + + if (id == Id.DATE) + toToken(compileEnv.date); else if (id == Id.TIME) - { - t.ustring = TimeStampInfo.time.ptr; - goto Lstr; - } + toToken(compileEnv.time); else if (id == Id.VENDOR) - { - t.ustring = vendor.xarraydup.ptr; - goto Lstr; - } + toToken(compileEnv.vendor); else if (id == Id.TIMESTAMP) - { - t.ustring = TimeStampInfo.timestamp.ptr; - Lstr: - t.value = TOK.string_; - t.postfix = 0; - t.len = cast(uint)strlen(t.ustring); - } + toToken(compileEnv.timestamp); else if (id == Id.VERSIONX) { t.value = TOK.int64Literal; - t.unsvalue = versionNumber; + t.unsvalue = compileEnv.versionNumber; } else if (id == Id.EOFX) { @@ -2570,6 +2573,14 @@ class Lexer TOK result; bool isOutOfRange = false; t.floatvalue = (isWellformedString ? CTFloat.parse(sbufptr, isOutOfRange) : CTFloat.zero); + + bool imaginary = false; + if (*p == 'i' && Ccompile) + { + ++p; + imaginary = true; + } + switch (*p) { case 'F': @@ -2590,16 +2601,25 @@ class Lexer goto case 'L'; case 'L': ++p; +version (IN_LLVM) { /* *always* map C `long double` literals to D `real` ones */ } else +{ if (Ccompile && long_doublesize == 8) goto default; +} result = TOK.float80Literal; break; } + if ((*p == 'i' || *p == 'I') && !Ccompile) { if (*p == 'I') error("use 'i' suffix instead of 'I'"); p++; + imaginary = true; + } + + if (imaginary) + { switch (result) { case TOK.float32Literal: @@ -3033,7 +3053,10 @@ class Lexer auto dc = (lineComment && anyToken) ? &t.lineComment : &t.blockComment; // Combine with previous doc comment, if any if (*dc) - *dc = combineComments(*dc, buf[], newParagraph).toDString(); + { + auto p = combineComments(*dc, buf[], newParagraph); + *dc = p ? p[0 .. strlen(p)] : null; + } else *dc = buf.extractSlice(true); } @@ -3081,42 +3104,6 @@ class Lexer private: -/// Support for `__DATE__`, `__TIME__`, and `__TIMESTAMP__` -private struct TimeStampInfo -{ - private __gshared bool initdone = false; - - // Note: Those properties need to be guarded by a call to `init` - // The API isn't safe, and quite brittle, but it was left this way - // over performance concerns. - // This is currently only called once, from the lexer. - __gshared char[11 + 1] date; - __gshared char[8 + 1] time; - __gshared char[24 + 1] timestamp; - - public static void initialize(const ref Loc loc, ErrorSink eSink) nothrow - { - if (initdone) - return; - - initdone = true; - time_t ct; - // https://issues.dlang.org/show_bug.cgi?id=20444 - if (auto p = getenv("SOURCE_DATE_EPOCH")) - { - if (!ct.parseDigits(p.toDString())) - eSink.error(loc, "value of environment variable `SOURCE_DATE_EPOCH` should be a valid UNIX timestamp, not: `%s`", p); - } - else - .time(&ct); - const p = ctime(&ct); - assert(p); - snprintf(&date[0], date.length, "%.6s %.4s", p + 4, p + 20); - snprintf(&time[0], time.length, "%.8s", p + 11); - snprintf(×tamp[0], timestamp.length, "%.24s", p); - } -} - private enum LS = 0x2028; // UTF line separator private enum PS = 0x2029; // UTF paragraph separator @@ -3366,7 +3353,7 @@ unittest */ string text = "int"; // We rely on the implicit null-terminator ErrorSink errorSink = new ErrorSinkStderr; - scope Lexer lex1 = new Lexer(null, text.ptr, 0, text.length, false, false, errorSink); + scope Lexer lex1 = new Lexer(null, text.ptr, 0, text.length, false, false, errorSink, null); TOK tok; tok = lex1.nextToken(); //printf("tok == %s, %d, %d\n", Token::toChars(tok), tok, TOK.int32); @@ -3402,7 +3389,7 @@ unittest foreach (testcase; testcases) { - scope Lexer lex2 = new Lexer(null, testcase.ptr, 0, testcase.length-1, false, false, errorSink); + scope Lexer lex2 = new Lexer(null, testcase.ptr, 0, testcase.length-1, false, false, errorSink, null); TOK tok = lex2.nextToken(); size_t iterations = 1; while ((tok != TOK.endOfFile) && (iterations++ < testcase.length)) diff --git a/dmd/mars.d b/dmd/mars.d index 49561188675..dd34292d086 100644 --- a/dmd/mars.d +++ b/dmd/mars.d @@ -178,6 +178,10 @@ private int tryMain(size_t argc, const(char)** argv, ref Param params) if (parseCommandlineAndConfig(argc, argv, params, files)) return EXIT_FAILURE; + global.compileEnv.previewIn = global.params.previewIn; + global.compileEnv.ddocOutput = global.params.ddoc.doOutput; + global.compileEnv.shortenedMethods = global.params.shortenedMethods; + if (params.usage) { usage(); @@ -1338,187 +1342,6 @@ private void setDefaultLibrary(ref Param params, const ref Target target) driverParams.debuglibname = driverParams.defaultlibname; } -/** - * Add default `version` identifier for dmd, and set the - * target platform in `params`. - * https://dlang.org/spec/version.html#predefined-versions - * - * Needs to be run after all arguments parsing (command line, DFLAGS environment - * variable and config file) in order to add final flags (such as `X86_64` or - * the `CRuntime` used). - * - * Params: - * params = which target to compile for (set by `setTarget()`) - * tgt = target - */ -public -void addDefaultVersionIdentifiers(const ref Param params, const ref Target tgt) -{ - VersionCondition.addPredefinedGlobalIdent("DigitalMars"); - VersionCondition.addPredefinedGlobalIdent("LittleEndian"); - VersionCondition.addPredefinedGlobalIdent("D_Version2"); - VersionCondition.addPredefinedGlobalIdent("all"); - - addPredefinedGlobalIdentifiers(tgt); - - if (params.ddoc.doOutput) - VersionCondition.addPredefinedGlobalIdent("D_Ddoc"); - if (params.cov) - VersionCondition.addPredefinedGlobalIdent("D_Coverage"); - if (driverParams.pic != PIC.fixed) - VersionCondition.addPredefinedGlobalIdent(driverParams.pic == PIC.pic ? "D_PIC" : "D_PIE"); - if (params.useUnitTests) - VersionCondition.addPredefinedGlobalIdent("unittest"); - if (params.useAssert == CHECKENABLE.on) - VersionCondition.addPredefinedGlobalIdent("assert"); - if (params.useIn == CHECKENABLE.on) - VersionCondition.addPredefinedGlobalIdent("D_PreConditions"); - if (params.useOut == CHECKENABLE.on) - VersionCondition.addPredefinedGlobalIdent("D_PostConditions"); - if (params.useInvariants == CHECKENABLE.on) - VersionCondition.addPredefinedGlobalIdent("D_Invariants"); - if (params.useArrayBounds == CHECKENABLE.off) - VersionCondition.addPredefinedGlobalIdent("D_NoBoundsChecks"); - if (params.betterC) - { - VersionCondition.addPredefinedGlobalIdent("D_BetterC"); - } - else - { - VersionCondition.addPredefinedGlobalIdent("D_ModuleInfo"); - VersionCondition.addPredefinedGlobalIdent("D_Exceptions"); - VersionCondition.addPredefinedGlobalIdent("D_TypeInfo"); - } - - VersionCondition.addPredefinedGlobalIdent("D_HardFloat"); - - if (params.tracegc) - VersionCondition.addPredefinedGlobalIdent("D_ProfileGC"); - - if (driverParams.optimize) - VersionCondition.addPredefinedGlobalIdent("D_Optimized"); -} - -/** - * Add predefined global identifiers that are determied by the target - */ -private -void addPredefinedGlobalIdentifiers(const ref Target tgt) -{ - import dmd.cond : VersionCondition; - - alias predef = VersionCondition.addPredefinedGlobalIdent; - if (tgt.cpu >= CPU.sse2) - { - predef("D_SIMD"); - if (tgt.cpu >= CPU.avx) - predef("D_AVX"); - if (tgt.cpu >= CPU.avx2) - predef("D_AVX2"); - } - - with (Target) - { - if (tgt.os & OS.Posix) - predef("Posix"); - if (tgt.os & (OS.linux | OS.FreeBSD | OS.OpenBSD | OS.DragonFlyBSD | OS.Solaris)) - predef("ELFv1"); - switch (tgt.os) - { - case OS.none: { predef("FreeStanding"); break; } - case OS.linux: { predef("linux"); break; } - case OS.OpenBSD: { predef("OpenBSD"); break; } - case OS.DragonFlyBSD: { predef("DragonFlyBSD"); break; } - case OS.Solaris: { predef("Solaris"); break; } - case OS.Windows: - { - predef("Windows"); - VersionCondition.addPredefinedGlobalIdent(tgt.is64bit ? "Win64" : "Win32"); - break; - } - case OS.OSX: - { - predef("OSX"); - // For legacy compatibility - predef("darwin"); - break; - } - case OS.FreeBSD: - { - predef("FreeBSD"); - switch (tgt.osMajor) - { - case 10: predef("FreeBSD_10"); break; - case 11: predef("FreeBSD_11"); break; - case 12: predef("FreeBSD_12"); break; - case 13: predef("FreeBSD_13"); break; - default: predef("FreeBSD_11"); break; - } - break; - } - default: assert(0); - } - } - - addCRuntimePredefinedGlobalIdent(tgt.c); - addCppRuntimePredefinedGlobalIdent(tgt.cpp); - - if (tgt.is64bit) - { - VersionCondition.addPredefinedGlobalIdent("D_InlineAsm_X86_64"); - VersionCondition.addPredefinedGlobalIdent("X86_64"); - } - else - { - VersionCondition.addPredefinedGlobalIdent("D_InlineAsm"); //legacy - VersionCondition.addPredefinedGlobalIdent("D_InlineAsm_X86"); - VersionCondition.addPredefinedGlobalIdent("X86"); - } - if (tgt.isLP64) - VersionCondition.addPredefinedGlobalIdent("D_LP64"); - else if (tgt.is64bit) - VersionCondition.addPredefinedGlobalIdent("X32"); -} - -private -void addCRuntimePredefinedGlobalIdent(const ref TargetC c) -{ - import dmd.cond : VersionCondition; - - alias predef = VersionCondition.addPredefinedGlobalIdent; - with (TargetC.Runtime) switch (c.runtime) - { - default: - case Unspecified: return; - case Bionic: return predef("CRuntime_Bionic"); - case DigitalMars: return predef("CRuntime_DigitalMars"); - case Glibc: return predef("CRuntime_Glibc"); - case Microsoft: return predef("CRuntime_Microsoft"); - case Musl: return predef("CRuntime_Musl"); - case Newlib: return predef("CRuntime_Newlib"); - case UClibc: return predef("CRuntime_UClibc"); - case WASI: return predef("CRuntime_WASI"); - } -} - -private -void addCppRuntimePredefinedGlobalIdent(const ref TargetCPP cpp) -{ - import dmd.cond : VersionCondition; - - alias predef = VersionCondition.addPredefinedGlobalIdent; - with (TargetCPP.Runtime) switch (cpp.runtime) - { - default: - case Unspecified: return; - case Clang: return predef("CppRuntime_Clang"); - case DigitalMars: return predef("CppRuntime_DigitalMars"); - case Gcc: return predef("CppRuntime_Gcc"); - case Microsoft: return predef("CppRuntime_Microsoft"); - case Sun: return predef("CppRuntime_Sun"); - } -} - } // !IN_LLVM private void printPredefinedVersions(FILE* stream) @@ -1857,6 +1680,18 @@ bool parseCommandLine(const ref Strings arguments, const size_t argc, ref Param if (arg == "-allinst") // https://dlang.org/dmd.html#switch-allinst params.allInst = true; + else if (startsWith(p + 1, "cpp=")) // https://dlang.org/dmd.html#switch-cpp + { + if (p[5]) + { + params.cpp = p + 5; + } + else + { + errorInvalidSwitch(p, "it must be followed by the filename of the desired C preprocessor"); + return false; + } + } else if (arg == "-de") // https://dlang.org/dmd.html#switch-de params.useDeprecated = DiagnosticReporting.error; else if (arg == "-d") // https://dlang.org/dmd.html#switch-d diff --git a/dmd/module.h b/dmd/module.h index 6dbab15933f..874a1994d91 100644 --- a/dmd/module.h +++ b/dmd/module.h @@ -82,8 +82,8 @@ class Module final : public Package unsigned errors; // if any errors in file unsigned numlines; // number of lines in source file FileType filetype; // source file type - bool hasAlwaysInlines; // contains references to functions that must be inlined - bool isPackageFile; // if it is a package.d + d_bool hasAlwaysInlines; // contains references to functions that must be inlined + d_bool isPackageFile; // if it is a package.d Package *pkg; // if isPackageFile is true, the Package that contains this package.d Strings contentImportedFiles; // array of files whose content was imported int needmoduleinfo; @@ -98,7 +98,7 @@ class Module final : public Package Identifier *searchCacheIdent; Dsymbol *searchCacheSymbol; // cached value of search int searchCacheFlags; // cached flags - bool insearch; + d_bool insearch; // module from command line we're imported from, // i.e. a module that will be taken all the @@ -189,7 +189,7 @@ struct ModuleDeclaration Loc loc; Identifier *id; DArray packages; // array of Identifier's representing packages - bool isdeprecated; // if it is a deprecated module + d_bool isdeprecated; // if it is a deprecated module Expression *msg; const char *toChars() const; diff --git a/dmd/mtype.d b/dmd/mtype.d index c02ab8a9cef..cb190910d3a 100644 --- a/dmd/mtype.d +++ b/dmd/mtype.d @@ -313,6 +313,7 @@ int mutabilityOfType(bool isref, Type t) */ enum DotExpFlag { + none = 0, gag = 1, // don't report "not a property" error and just return null noDeref = 2, // the use of the expression will not attempt a dereference noAliasThis = 4, // don't do 'alias this' resolution @@ -2048,9 +2049,11 @@ version (IN_LLVM) t = t.addMod(this.mod); return t; } - if (auto fd = s.isFuncDeclaration()) + Dsymbol callable = s.isFuncDeclaration(); + callable = callable ? callable : s.isTemplateDeclaration(); + if (callable) { - fd = resolveFuncCall(Loc.initial, null, fd, null, this, ArgumentList(), FuncResolveFlag.quiet); + auto fd = resolveFuncCall(Loc.initial, null, callable, null, this, ArgumentList(), FuncResolveFlag.quiet); if (!fd || fd.errors || !fd.functionSemantic()) return Type.terror; @@ -2069,24 +2072,17 @@ version (IN_LLVM) { return ed.type; } - if (auto td = s.isTemplateDeclaration()) - { - assert(td._scope); - auto fd = resolveFuncCall(Loc.initial, null, td, null, this, ArgumentList(), FuncResolveFlag.quiet); - if (!fd || fd.errors || !fd.functionSemantic()) - return Type.terror; - - auto t = fd.type.nextOf(); - if (!t) - return Type.terror; - t = t.substWildTo(mod == 0 ? MODFlags.mutable : mod); - return t; - } //printf("%s\n", s.kind()); return null; } + /** + * Check whether this type has endless `alias this` recursion. + * Returns: + * `true` if this type has an `alias this` that can be implicitly + * converted back to this type itself. + */ extern (D) final bool checkAliasThisRec() { Type tb = toBasetype(); @@ -2669,6 +2665,8 @@ version (IN_LLVM) if (t.isimaginary() || t.iscomplex()) { + if (sc.flags & SCOPE.Cfile) + return true; // complex/imaginary not deprecated in C code Type rt; switch (t.ty) { @@ -4294,7 +4292,7 @@ extern (C++) final class TypeFunction : TypeNext super(Tfunction, treturn); //if (!treturn) *(char*)0=0; // assert(treturn); - assert(VarArg.none <= pl.varargs && pl.varargs <= VarArg.typesafe); + assert(VarArg.none <= pl.varargs && pl.varargs <= VarArg.max); this.parameterList = pl; this.linkage = linkage; @@ -6537,6 +6535,7 @@ extern (C++) final class TypeTag : Type { Loc loc; /// location of declaration TOK tok; /// TOK.struct_, TOK.union_, TOK.enum_ + structalign_t packalign; /// alignment of struct/union fields Identifier id; /// tag name identifier Type base; /// base type for enums otherwise null Dsymbols* members; /// members of struct, null if none @@ -6546,13 +6545,14 @@ 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, Type base, Dsymbols* members) + extern (D) this(const ref Loc loc, TOK tok, Identifier id, structalign_t packalign, Type base, Dsymbols* members) { //printf("TypeTag ctor %s %p\n", id ? id.toChars() : "null".ptr, this); super(Ttag); this.loc = loc; this.tok = tok; this.id = id; + this.packalign = packalign; this.base = base; this.members = members; this.mod = 0; @@ -7276,7 +7276,7 @@ private extern(D) bool isCopyConstructorCallable (StructDeclaration argStruct, s ~= "pure "; if (!f.isSafe() && !f.isTrusted() && sc.setUnsafe()) s ~= "@safe "; - if (!f.isNogc && sc.func.setGC()) + if (!f.isNogc && sc.func.setGC(arg.loc, null)) s ~= "nogc "; if (s) { @@ -7603,3 +7603,113 @@ TypeVector toBooleanVector(TypeVector tv) return new TypeVector(new TypeSArray(telem, tsa.dim)); } + +/************************************************* + * Dispatch to function based on static type of Type. + */ +mixin template VisitType(Result) +{ + Result VisitType(Type t) + { + final switch (t.ty) + { + case TY.Tvoid: + case TY.Tint8: + case TY.Tuns8: + case TY.Tint16: + case TY.Tuns16: + case TY.Tint32: + case TY.Tuns32: + case TY.Tint64: + case TY.Tuns64: + case TY.Tfloat32: + case TY.Tfloat64: + case TY.Tfloat80: + case TY.Timaginary32: + case TY.Timaginary64: + case TY.Timaginary80: + case TY.Tcomplex32: + case TY.Tcomplex64: + case TY.Tcomplex80: + case TY.Tbool: + case TY.Tchar: + case TY.Twchar: + case TY.Tdchar: + case TY.Tint128: + case TY.Tuns128: mixin(visitTYCase("Basic")); + case TY.Tarray: mixin(visitTYCase("DArray")); + case TY.Tsarray: mixin(visitTYCase("SArray")); + case TY.Taarray: mixin(visitTYCase("AArray")); + case TY.Tpointer: mixin(visitTYCase("Pointer")); + case TY.Treference: mixin(visitTYCase("Reference")); + case TY.Tfunction: mixin(visitTYCase("Function")); + case TY.Tident: mixin(visitTYCase("Identifier")); + case TY.Tclass: mixin(visitTYCase("Class")); + case TY.Tstruct: mixin(visitTYCase("Struct")); + case TY.Tenum: mixin(visitTYCase("Enum")); + case TY.Tdelegate: mixin(visitTYCase("Delegate")); + case TY.Terror: mixin(visitTYCase("Error")); + case TY.Tinstance: mixin(visitTYCase("Instance")); + case TY.Ttypeof: mixin(visitTYCase("Typeof")); + case TY.Ttuple: mixin(visitTYCase("Tuple")); + case TY.Tslice: mixin(visitTYCase("Slice")); + case TY.Treturn: mixin(visitTYCase("Return")); + case TY.Tnull: mixin(visitTYCase("Null")); + case TY.Tvector: mixin(visitTYCase("Vector")); + case TY.Ttraits: mixin(visitTYCase("Traits")); + case TY.Tmixin: mixin(visitTYCase("Mixin")); + case TY.Tnoreturn: mixin(visitTYCase("Noreturn")); + case TY.Ttag: mixin(visitTYCase("Tag")); + case TY.Tnone: assert(0); + } + } +} + +/**************************************** + * CTFE-only helper function for VisitInitializer. + * Params: + * handler = string for the name of the visit handler + * Returns: boilerplate code for a case + */ +pure string visitTYCase(string handler) +{ + if (__ctfe) + { + return + " + enum isVoid = is(Result == void); + auto tx = t.isType"~handler~"(); + static if (__traits(compiles, visit"~handler~"(tx))) + { + static if (isVoid) + { + visit"~handler~"(tx); + return; + } + else + { + if (Result r = visit"~handler~"(tx)) + return r; + return Result.init; + } + } + else static if (__traits(compiles, visitDefaultCase(t))) + { + static if (isVoid) + { + visitDefaultCase(tx); + return; + } + else + { + if (Result r = visitDefaultCase(t)) + return r; + return Result.init; + } + } + else + static assert(0, "~handler~"); + "; + } + assert(0); +} diff --git a/dmd/mtype.h b/dmd/mtype.h index 5d22bf64b4d..d48e7388231 100644 --- a/dmd/mtype.h +++ b/dmd/mtype.h @@ -125,8 +125,9 @@ enum VarArgValues { VARARGnone = 0, /// fixed number of arguments VARARGvariadic = 1, /// T t, ...) can be C-style (core.stdc.stdarg) or D-style (core.vararg) - VARARGtypesafe = 2 /// T t ...) typesafe https://dlang.org/spec/function.html#typesafe_variadic_functions + VARARGtypesafe = 2, /// T t ...) typesafe https://dlang.org/spec/function.html#typesafe_variadic_functions /// or https://dlang.org/spec/function.html#typesafe_variadic_functions + VARARGKRvariadic = 3 /// K+R C style variadics (no function prototype) }; typedef unsigned char VarArg; @@ -594,7 +595,7 @@ struct ParameterList Parameters* parameters; StorageClass stc; VarArg varargs; - bool hasIdentifierList; // true if C identifier-list style + d_bool hasIdentifierList; // true if C identifier-list style size_t length(); Parameter *operator[](size_t i) { return Parameter::getNth(parameters, i); } @@ -784,7 +785,7 @@ class TypeStruct final : public Type public: StructDeclaration *sym; AliasThisRec att; - bool inuse; + d_bool inuse; static TypeStruct *create(StructDeclaration *sym); const char *kind() override; diff --git a/dmd/nogc.d b/dmd/nogc.d index 201f168527c..a0f3e60861b 100644 --- a/dmd/nogc.d +++ b/dmd/nogc.d @@ -83,7 +83,7 @@ public: err = true; return true; } - if (f.setGC()) + if (f.setGC(e.loc, format)) { e.error(format, f.kind(), f.toPrettyChars()); err = true; @@ -135,7 +135,7 @@ public: override void visit(NewExp e) { - if (e.member && !e.member.isNogc() && f.setGC()) + if (e.member && !e.member.isNogc() && f.setGC(e.loc, null)) { // @nogc-ness is already checked in NewExp::semantic return; @@ -195,7 +195,7 @@ public: err = true; return; } - if (f.setGC()) + if (f.setGC(e.loc, null)) { err = true; return; diff --git a/dmd/ob.d b/dmd/ob.d index 9cff76b84aa..89728b64486 100644 --- a/dmd/ob.d +++ b/dmd/ob.d @@ -844,7 +844,7 @@ void toObNodes(ref ObNodes obnodes, Statement s) case STMT.Conditional: case STMT.While: case STMT.Forwarding: - case STMT.Compile: + case STMT.Mixin: case STMT.Peel: case STMT.Synchronized: debug printf("s: %s\n", s.toChars()); diff --git a/dmd/objc.h b/dmd/objc.h index 305ce812487..a5cc6f1b089 100644 --- a/dmd/objc.h +++ b/dmd/objc.h @@ -37,8 +37,8 @@ struct ObjcSelector struct ObjcClassDeclaration { - bool isMeta; - bool isExtern; + d_bool isMeta; + d_bool isExtern; Identifier* identifier; ClassDeclaration* classDeclaration; @@ -52,7 +52,7 @@ struct ObjcFuncDeclaration { ObjcSelector* selector; VarDeclaration* selectorParameter; - bool isOptional; + d_bool isOptional; }; class Objc diff --git a/dmd/opover.d b/dmd/opover.d index 3c80e5e1d0e..771287691ad 100644 --- a/dmd/opover.d +++ b/dmd/opover.d @@ -293,6 +293,7 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null) { ie = (*ae.arguments)[0].isIntervalExp(); } + Type att = null; // first cyclic `alias this` type while (true) { if (ae.e1.op == EXP.error) @@ -354,7 +355,7 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null) return result; } // Didn't find it. Forward to aliasthis - if (ad.aliasthis && !isRecursiveAliasThis(ae.att1, ae.e1.type)) + if (ad.aliasthis && !isRecursiveAliasThis(att, ae.e1.type)) { /* Rewrite op(a[arguments]) as: * op(a.aliasthis[arguments]) @@ -370,13 +371,18 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null) } e.e1 = e.e1.expressionSemantic(sc); e.e1 = resolveProperties(sc, e.e1); - if (e.e1.op == EXP.error) - { - return e.e1; - } - AggregateDeclaration ad = isAggregate(e.e1.type); - if (ad) + Type att = null; // first cyclic `alias this` type + while (1) { + if (e.e1.op == EXP.error) + { + return e.e1; + } + + AggregateDeclaration ad = isAggregate(e.e1.type); + if (!ad) + break; + Dsymbol fd = null; /* Rewrite as: * e1.opUnary!(op)() @@ -404,18 +410,20 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null) } } // Didn't find it. Forward to aliasthis - if (ad.aliasthis && !isRecursiveAliasThis(e.att1, e.e1.type)) + 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()); - Expression e1 = new DotIdExp(e.loc, e.e1, ad.aliasthis.ident); - UnaExp ue = cast(UnaExp)e.copy(); - ue.e1 = e1; - result = ue.trySemantic(sc); - return result; + if (auto e1 = resolveAliasThis(sc, e.e1, true)) + { + e.e1 = e1; + continue; + } + break; } + break; } return result; } @@ -433,6 +441,7 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null) ie = (*ae.arguments)[0].isIntervalExp(); } Expression result; + Type att = null; // first cyclic `alias this` type while (true) { if (ae.e1.op == EXP.error) @@ -526,7 +535,7 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null) return result; } // Didn't find it. Forward to aliasthis - if (ad.aliasthis && !isRecursiveAliasThis(ae.att1, ae.e1.type)) + if (ad.aliasthis && !isRecursiveAliasThis(att, ae.e1.type)) { //printf("att arr e1 = %s\n", this.e1.type.toChars()); /* Rewrite op(a[arguments]) as: @@ -547,7 +556,7 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null) * This is mostly the same as UnaryExp::op_overload(), but has * a different rewrite. */ - Expression visitCast(CastExp e) + Expression visitCast(CastExp e, Type att = null) { //printf("CastExp::op_overload() (%s)\n", e.toChars()); Expression result; @@ -578,7 +587,7 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null) return result; } // Didn't find it. Forward to aliasthis - if (ad.aliasthis && !isRecursiveAliasThis(e.att1, e.e1.type)) + if (ad.aliasthis && !isRecursiveAliasThis(att, e.e1.type)) { /* Rewrite op(e1) as: * op(e1.aliasthis) @@ -587,7 +596,7 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null) { result = e.copy(); (cast(UnaExp)result).e1 = e1; - result = result.op_overload(sc); + result = visitCast(result.isCastExp(), att); return result; } } @@ -997,7 +1006,7 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null) return null; import dmd.clone : needOpEquals; - if (!global.params.fieldwise && !needOpEquals(sd)) + if (global.params.fieldwise != FeatureState.enabled && !needOpEquals(sd)) { // Use bitwise equality. auto op2 = e.op == EXP.equal ? EXP.identity : EXP.notIdentity; @@ -1016,12 +1025,7 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null) * also compare the parent class's equality. Otherwise, compares * the identity of parent context through void*. */ - if (e.att1 && t1.equivalent(e.att1)) return null; - if (e.att2 && t2.equivalent(e.att2)) return null; - e = e.copy().isEqualExp(); - if (!e.att1) e.att1 = t1; - if (!e.att2) e.att2 = t2; e.e1 = new DotIdExp(e.loc, e.e1, Id._tupleof); e.e2 = new DotIdExp(e.loc, e.e2, Id._tupleof); @@ -1029,18 +1033,6 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null) sc2.flags |= SCOPE.noaccesscheck; Expression r = e.expressionSemantic(sc2); sc2.pop(); - - /* https://issues.dlang.org/show_bug.cgi?id=15292 - * if the rewrite result is same with the original, - * the equality is unresolvable because it has recursive definition. - */ - if (r.op == e.op && - r.isEqualExp().e1.type.toBasetype() == t1) - { - e.error("cannot compare `%s` because its auto generated member-wise equality has recursive definition", - t1.toChars()); - return ErrorExp.get(); - } return r; } @@ -1071,8 +1063,6 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null) auto ex1 = (*tup1.exps)[i]; auto ex2 = (*tup2.exps)[i]; auto eeq = new EqualExp(e.op, e.loc, ex1, ex2); - eeq.att1 = e.att1; - eeq.att2 = e.att2; if (!result) result = eeq; @@ -1114,6 +1104,7 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null) { ie = (*ae.arguments)[0].isIntervalExp(); } + Type att = null; // first cyclic `alias this` type while (true) { if (ae.e1.op == EXP.error) @@ -1185,7 +1176,7 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null) return result; } // Didn't find it. Forward to aliasthis - if (ad.aliasthis && !isRecursiveAliasThis(ae.att1, ae.e1.type)) + if (ad.aliasthis && !isRecursiveAliasThis(att, ae.e1.type)) { /* Rewrite (a[arguments] op= e2) as: * a.aliasthis[arguments] op= e2 diff --git a/dmd/optimize.d b/dmd/optimize.d index b5d32b2932d..61c385fc061 100644 --- a/dmd/optimize.d +++ b/dmd/optimize.d @@ -371,7 +371,7 @@ Expression Expression_optimize(Expression e, int result, bool keepLvalue) { if (e.stageflags & stageOptimize) return; - int old = e.stageflags; + const old = e.stageflags; e.stageflags |= stageOptimize; if (e.elements) { diff --git a/dmd/parse.d b/dmd/parse.d index 36a76f50da2..68a25064fc0 100644 --- a/dmd/parse.d +++ b/dmd/parse.d @@ -15,14 +15,13 @@ module dmd.parse; import core.stdc.stdio; import core.stdc.string; + import dmd.astenums; import dmd.errorsink; -import dmd.globals; import dmd.id; import dmd.identifier; import dmd.lexer; import dmd.location; -import dmd.errors; import dmd.root.filename; import dmd.common.outbuffer; import dmd.root.rmem; @@ -30,6 +29,8 @@ import dmd.root.rootobject; import dmd.root.string; import dmd.tokens; +alias CompileEnv = dmd.lexer.CompileEnv; + /*********************************************************** */ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer @@ -45,49 +46,38 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer Loc endloc; // set to location of last right curly int inBrackets; // inside [] of array index or slice Loc lookingForElse; // location of lonely if looking for an else + 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 + * loc = location in source file of mixin */ extern (D) this(const ref Loc loc, AST.Module _module, const(char)[] input, bool doDocComment, - ErrorSink errorSink) scope + ErrorSink errorSink, const CompileEnv* compileEnv, const bool doUnittests) scope { - super(_module ? _module.srcfile.toChars() : null, input.ptr, 0, input.length, doDocComment, false, - errorSink, - global.vendor, global.versionNumber()); - - //printf("Parser::Parser()\n"); + //printf("Parser::Parser()1 %d\n", doUnittests); + this(_module, input, doDocComment, errorSink, compileEnv, doUnittests); scanloc = loc; - - if (!writeMixin(input, scanloc) && loc.filename) - { - /* Create a pseudo-filename for the mixin string, as it may not even exist - * in the source file. - */ - 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); - scanloc.filename = filename; - } - - mod = _module; - linkage = LINK.d; - //nextToken(); // start up the scanner } - extern (D) this(AST.Module _module, const(char)[] input, bool doDocComment, ErrorSink errorSink) scope + /************************************************** + * Main Parser constructor. + */ + extern (D) this(AST.Module _module, const(char)[] input, bool doDocComment, ErrorSink errorSink, + const CompileEnv* compileEnv, const bool doUnittests) scope { super(_module ? _module.srcfile.toChars() : null, input.ptr, 0, input.length, doDocComment, false, errorSink, - global.vendor, global.versionNumber()); + compileEnv); - //printf("Parser::Parser()\n"); - mod = _module; - linkage = LINK.d; - //nextToken(); // start up the scanner + //printf("Parser::Parser()2 %d\n", doUnittests); + this.mod = _module; + this.linkage = LINK.d; + this.doUnittests = doUnittests; } /++ @@ -335,6 +325,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer linkage = linksave; Loc startloc; + Loc scdLoc; switch (token.value) { @@ -381,7 +372,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer nextToken(); auto exps = parseArguments(); check(TOK.semicolon); - s = new AST.CompileDeclaration(loc, exps); + s = new AST.MixinDeclaration(loc, exps); break; } case TOK.template_: @@ -497,7 +488,8 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer * template instantiations in these unittests as candidates for * further codegen culling. */ - if (mod.isRoot() && (global.params.useUnitTests || global.params.ddoc.doOutput || global.params.dihdr.doOutput)) + // The isRoot check is here because it can change after parsing begins (see dmodule.d) + if (doUnittests && mod.isRoot()) { linkage = LINK.d; // unittests have D linkage s = parseUnitTest(pAttrs); @@ -696,6 +688,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer } Lstc: pAttrs.storageClass = appendStorageClass(pAttrs.storageClass, stc); + scdLoc = token.loc; nextToken(); Lautodecl: @@ -748,7 +741,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer auto stc2 = getStorageClass!AST(pAttrs); if (stc2 != STC.undefined_) { - s = new AST.StorageClassDeclaration(stc2, a); + s = new AST.StorageClassDeclaration(scdLoc, stc2, a); } if (pAttrs.udas) { @@ -1219,19 +1212,20 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer return orig | added; } - const Redundant = (STC.const_ | STC.scope_ | - (global.params.previewIn ? STC.ref_ : 0)); + const Redundant = (STC.const_ | STC.scope_ | STC.ref_); orig |= added; if ((orig & STC.in_) && (added & Redundant)) { if (added & STC.const_) error("attribute `const` is redundant with previously-applied `in`"); - else if (global.params.previewIn) + else if (compileEnv.previewIn) { error("attribute `%s` is redundant with previously-applied `in`", (orig & STC.scope_) ? "scope".ptr : "ref".ptr); } + else if (added & STC.ref_) + deprecation("using `in ref` is deprecated, use `-preview=in` and `in` instead"); else error("attribute `scope` cannot be applied with `in`, use `-preview=in` instead"); return orig; @@ -1241,13 +1235,15 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer { if (orig & STC.const_) error("attribute `in` cannot be added after `const`: remove `const`"); - else if (global.params.previewIn) + else if (compileEnv.previewIn) { // Windows `printf` does not support `%1$s` const(char*) stc_str = (orig & STC.scope_) ? "scope".ptr : "ref".ptr; error(token.loc, "attribute `in` cannot be added after `%s`: remove `%s`", stc_str, stc_str); } + else if (orig & STC.ref_) + deprecation("using `ref in` is deprecated, use `-preview=in` and `in` instead"); else error("attribute `in` cannot be added after `scope`: remove `scope` and use `-preview=in`"); return orig; @@ -1302,12 +1298,38 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer return 0; } + AST.Expression templateArgToExp(RootObject o, const ref Loc loc) + { + switch (o.dyncast) + { + case DYNCAST.expression: + return cast(AST.Expression) o; + case DYNCAST.type: + return new AST.TypeExp(loc, cast(AST.Type)o); + default: + assert(0); + } + } + if (token.value == TOK.leftParenthesis) { // Multi-UDAs ( `@( ArgumentList )`) form, concatenate with existing if (peekNext() == TOK.rightParenthesis) error("empty attribute list is not allowed"); - udas = AST.UserAttributeDeclaration.concat(udas, parseArguments()); + + if (udas is null) + udas = new AST.Expressions(); + auto args = parseTemplateArgumentList(); + foreach (arg; *args) + udas.push(templateArgToExp(arg, token.loc)); + return 0; + } + + if (auto o = parseTemplateSingleArgument()) + { + if (udas is null) + udas = new AST.Expressions(); + udas.push(templateArgToExp(o, token.loc)); return 0; } @@ -1764,7 +1786,16 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer else { // ident!template_argument - tiargs = parseTemplateSingleArgument(); + RootObject o = parseTemplateSingleArgument(); + if (!o) + { + error("template argument expected following `!`"); + } + else + { + tiargs = new AST.Objects(); + tiargs.push(o); + } } if (token.value == TOK.not) { @@ -1830,11 +1861,11 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer * foo!arg * Input: * current token is the arg + * Returns: An AST.Type, AST.Expression, or `null` on error */ - private AST.Objects* parseTemplateSingleArgument() + private RootObject parseTemplateSingleArgument() { //printf("parseTemplateSingleArgument()\n"); - auto tiargs = new AST.Objects(); AST.Type ta; switch (token.value) { @@ -1942,9 +1973,8 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer ta = AST.Type.tdchar; goto LabelX; LabelX: - tiargs.push(ta); nextToken(); - break; + return ta; case TOK.int32Literal: case TOK.uns32Literal: @@ -1974,15 +2004,11 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer case TOK.this_: { // Template argument is an expression - AST.Expression ea = parsePrimaryExp(); - tiargs.push(ea); - break; + return parsePrimaryExp(); } default: - error("template argument expected following `!`"); - break; + return null; } - return tiargs; } /********************************** @@ -2694,7 +2720,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer /** Extract unittest body as a string. Must be done eagerly since memory will be released by the lexer before doc gen. */ char* docline = null; - if (global.params.ddoc.doOutput && endPtr > begPtr) + if (compileEnv.ddocOutput && endPtr > begPtr) { /* Remove trailing whitespaces */ for (const(char)* p = endPtr - 1; begPtr <= p && (*p == ' ' || *p == '\r' || *p == '\n' || *p == '\t'); --p) @@ -2849,8 +2875,8 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer // Don't call nextToken again. } case TOK.in_: - if (global.params.vin) - message(scanloc, "Usage of 'in' on parameter"); + if (transitionIn) + eSink.message(scanloc, "Usage of 'in' on parameter"); stc = STC.in_; goto L2; @@ -4610,8 +4636,6 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer AST.Dsymbol s; if (width) { - if (!global.params.bitfields) - error("use -preview=bitfields for bitfield support"); if (_init) error("initializer not allowed for bit-field declaration"); if (storage_class) @@ -5144,7 +5168,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer case TOK.goesTo: if (requireDo) error("missing `do { ... }` after `in` or `out`"); - if (!global.params.shortenedMethods) + if (!compileEnv.shortenedMethods) error("=> shortened method not enabled, compile with compiler switch `-preview=shortenedMethods`"); const returnloc = token.loc; nextToken(); @@ -5156,7 +5180,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer case TOK.leftCurly: if (requireDo) error("missing `do { ... }` after `in` or `out`"); - f.fbody = parseStatement(ParseStatementFlags.semi); + f.fbody = parseStatement(0); f.endloc = endloc; break; @@ -5801,7 +5825,11 @@ LagainStc: if (token.value != TOK.semicolon && peek(&token).value == TOK.semicolon) error("found `%s` when expecting `;` following statement", token.toChars()); else - check(TOK.semicolon, "statement"); + { + if (token.value != TOK.semicolon) + error("found `%s` when expecting `;` following statement `%s` on line %s", token.toChars(), exp.toChars(), exp.loc.toChars()); + nextToken(); + } } s = new AST.ExpStatement(loc, exp); break; @@ -5959,7 +5987,7 @@ LagainStc: if (e.op == EXP.mixin_) { AST.MixinExp cpe = cast(AST.MixinExp)e; - s = new AST.CompileStatement(loc, cpe.exps); + s = new AST.MixinStatement(loc, cpe.exps); } else { @@ -5992,7 +6020,7 @@ LagainStc: auto statements = new AST.Statements(); while (token.value != TOK.rightCurly && token.value != TOK.endOfFile) { - statements.push(parseStatement(ParseStatementFlags.semi | ParseStatementFlags.curlyScope)); + statements.push(parseStatement(ParseStatementFlags.curlyScope)); } if (endPtr) *endPtr = token.ptr; @@ -6025,10 +6053,7 @@ LagainStc: case TOK.semicolon: if (!(flags & ParseStatementFlags.semiOk)) { - if (flags & ParseStatementFlags.semi) - deprecation("use `{ }` for an empty statement, not `;`"); - else - error("use `{ }` for an empty statement, not `;`"); + error("use `{ }` for an empty statement, not `;`"); } nextToken(); s = new AST.ExpStatement(loc, cast(AST.Expression)null); @@ -6245,7 +6270,7 @@ LagainStc: _body = null; } else - _body = parseStatement(ParseStatementFlags.semi); + _body = parseStatement(0); s = new AST.PragmaStatement(loc, ident, args, _body); break; } @@ -6298,7 +6323,7 @@ LagainStc: auto statements = new AST.Statements(); while (token.value != TOK.case_ && token.value != TOK.default_ && token.value != TOK.endOfFile && token.value != TOK.rightCurly) { - auto cur = parseStatement(ParseStatementFlags.semi | ParseStatementFlags.curlyScope); + auto cur = parseStatement(ParseStatementFlags.curlyScope); statements.push(cur); // https://issues.dlang.org/show_bug.cgi?id=21739 @@ -6313,7 +6338,7 @@ LagainStc: } else { - s = parseStatement(ParseStatementFlags.semi); + s = parseStatement(0); } s = new AST.ScopeStatement(loc, s, token.loc); @@ -6342,12 +6367,12 @@ LagainStc: auto statements = new AST.Statements(); while (token.value != TOK.case_ && token.value != TOK.default_ && token.value != TOK.endOfFile && token.value != TOK.rightCurly) { - statements.push(parseStatement(ParseStatementFlags.semi | ParseStatementFlags.curlyScope)); + statements.push(parseStatement(ParseStatementFlags.curlyScope)); } s = new AST.CompoundStatement(loc, statements); } else - s = parseStatement(ParseStatementFlags.semi); + s = parseStatement(0); s = new AST.ScopeStatement(loc, s, token.loc); s = new AST.DefaultStatement(loc, s); break; @@ -6891,6 +6916,15 @@ LagainStc: /******************** * Parse inline assembler block. + * Enters with token on the `asm`. + * https://dlang.org/spec/iasm.html + * + * AsmStatement: + * asm FunctionAttributes(opt) { AsmInstructionListopt } + * AsmInstructionList: + * AsmInstruction ; + * AsmInstruction ; AsmInstruction + * * Returns: * inline assembler block as a Statement */ @@ -6905,7 +6939,7 @@ LagainStc: Loc labelloc; nextToken(); - StorageClass stc = parsePostfix(STC.undefined_, null); + StorageClass stc = parsePostfix(STC.undefined_, 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"); @@ -9181,7 +9215,7 @@ LagainStc: void checkRequiredParens() { if (e.op == EXP.question && !e.parens) - dmd.errors.error(e.loc, "`%s` must be surrounded by parentheses when next to operator `%s`", + eSink.error(e.loc, "`%s` must be surrounded by parentheses when next to operator `%s`", e.toChars(), Token.toChars(token.value)); } @@ -9498,7 +9532,6 @@ immutable PREC[EXP.max + 1] precedence = EXP.error : PREC.expr, EXP.objcClassReference : PREC.expr, // Objective-C class reference, same as EXP.type - EXP.typeof_ : PREC.primary, EXP.mixin_ : PREC.primary, EXP.import_ : PREC.primary, @@ -9538,7 +9571,6 @@ immutable PREC[EXP.max + 1] precedence = EXP.remove : PREC.primary, EXP.tuple : PREC.primary, EXP.traits : PREC.primary, - EXP.default_ : PREC.primary, EXP.overloadSet : PREC.primary, EXP.void_ : PREC.primary, EXP.vectorArray : PREC.primary, @@ -9637,7 +9669,6 @@ immutable PREC[EXP.max + 1] precedence = enum ParseStatementFlags : int { - semi = 1, // empty ';' statements are allowed, but deprecated scope_ = 2, // start a new scope curly = 4, // { } statement is required curlyScope = 8, // { } starts a new scope @@ -9708,52 +9739,3 @@ private StorageClass getStorageClass(AST)(PrefixAttributes!(AST)* pAttrs) } return stc; } - -/************************************** - * dump mixin expansion to file for better debugging - */ -private bool writeMixin(const(char)[] s, ref Loc loc) -{ - if (!global.params.mixinOut.doOutput) - return false; - - OutBuffer* ob = global.params.mixinOut.buffer; - - ob.writestring("// expansion at "); - ob.writestring(loc.toChars()); - ob.writenl(); - - global.params.mixinOut.bufferLines++; - - loc = Loc(global.params.mixinOut.name.ptr, global.params.mixinOut.bufferLines + 1, loc.charnum); - - // write by line to create consistent line endings - size_t lastpos = 0; - for (size_t i = 0; i < s.length; ++i) - { - // detect LF and CRLF - const c = s[i]; - if (c == '\n' || (c == '\r' && i+1 < s.length && s[i+1] == '\n')) - { - ob.writestring(s[lastpos .. i]); - ob.writenl(); - global.params.mixinOut.bufferLines++; - if (c == '\r') - ++i; - lastpos = i + 1; - } - } - - if(lastpos < s.length) - ob.writestring(s[lastpos .. $]); - - if (s.length == 0 || s[$-1] != '\n') - { - ob.writenl(); // ensure empty line after expansion - global.params.mixinOut.bufferLines++; - } - ob.writenl(); - global.params.mixinOut.bufferLines++; - - return true; -} diff --git a/dmd/parsetimevisitor.d b/dmd/parsetimevisitor.d index 387b28c1532..a4a9434334e 100644 --- a/dmd/parsetimevisitor.d +++ b/dmd/parsetimevisitor.d @@ -66,7 +66,7 @@ public: void visit(AST.SharedStaticDtorDeclaration s) { visit(cast(AST.StaticDtorDeclaration)s); } // AttribDeclarations - void visit(AST.CompileDeclaration s) { visit(cast(AST.AttribDeclaration)s); } + void visit(AST.MixinDeclaration s) { visit(cast(AST.AttribDeclaration)s); } void visit(AST.UserAttributeDeclaration s) { visit(cast(AST.AttribDeclaration)s); } void visit(AST.LinkDeclaration s) { visit(cast(AST.AttribDeclaration)s); } void visit(AST.AnonDeclaration s) { visit(cast(AST.AttribDeclaration)s); } @@ -99,7 +99,7 @@ public: void visit(AST.ReturnStatement s) { visit(cast(AST.Statement)s); } void visit(AST.LabelStatement s) { visit(cast(AST.Statement)s); } void visit(AST.StaticAssertStatement s) { visit(cast(AST.Statement)s); } - void visit(AST.CompileStatement s) { visit(cast(AST.Statement)s); } + void visit(AST.MixinStatement s) { visit(cast(AST.Statement)s); } void visit(AST.WhileStatement s) { visit(cast(AST.Statement)s); } void visit(AST.ForStatement s) { visit(cast(AST.Statement)s); } void visit(AST.DoStatement s) { visit(cast(AST.Statement)s); } diff --git a/dmd/printast.d b/dmd/printast.d index d85105d6f20..8c0109524f7 100644 --- a/dmd/printast.d +++ b/dmd/printast.d @@ -219,6 +219,25 @@ extern (C++) final class PrintASTVisitor : Visitor printAST(e.value, indent + 2); } + override void visit(ArrayLiteralExp e) + { + visit(cast(Expression)e); + printIndent(indent + 2); + printf(".basis : %s\n", e.basis ? e.basis.toChars() : ""); + if (e.elements) + { + printIndent(indent + 2); + printf("["); + foreach (i, element; (*e.elements)[]) + { + if (i) + printf(", "); + printf("%s", element.toChars()); + } + printf("]\n"); + } + } + static void printIndent(int indent) { foreach (i; 0 .. indent) diff --git a/dmd/root/dcompat.h b/dmd/root/dcompat.h index 552922020b0..fa51370e3fb 100644 --- a/dmd/root/dcompat.h +++ b/dmd/root/dcompat.h @@ -36,7 +36,7 @@ struct DString : public DArray }; /// Corresponding C++ type that maps to D size_t -#if __APPLE__ && __i386__ +#if __APPLE__ && (__i386__ || __ppc__) // size_t is 'unsigned long', which makes it mangle differently than D's 'uint' typedef unsigned d_size_t; #elif MARS && DMD_VERSION >= 2079 && DMD_VERSION <= 2081 && \ @@ -51,3 +51,11 @@ typedef unsigned d_size_t; #else typedef size_t d_size_t; #endif + +/// Corresponding C++ type that maps to D bool +#if __APPLE__ && __ppc__ +// bool is defined as an 'int', which does not match same size as D +typedef uint8_t d_bool; +#else +typedef bool d_bool; +#endif diff --git a/dmd/root/file.d b/dmd/root/file.d index 1fb105682ea..df9cf8ccdd0 100644 --- a/dmd/root/file.d +++ b/dmd/root/file.d @@ -201,6 +201,25 @@ nothrow: } } +version (IN_LLVM) +{ + extern (C++) static void removeDirectory(const(char)* name) + { + version (Posix) + { + .remove(name); + } + else version (Windows) + { + name.toDString.extendedPathThen!(p => RemoveDirectoryW(p.ptr)); + } + else + { + static assert(0); + } + } +} + /*************************************************** * Update file * diff --git a/dmd/root/filename.d b/dmd/root/filename.d index 14cffd265e2..d7842e2463f 100644 --- a/dmd/root/filename.d +++ b/dmd/root/filename.d @@ -44,7 +44,7 @@ version (Windows) version (IN_LLVM) { - private enum CodePage = CP_UTF8; + enum CodePage = CP_UTF8; } else { diff --git a/dmd/root/optional.h b/dmd/root/optional.h index cc2ee79edeb..353332c2199 100644 --- a/dmd/root/optional.h +++ b/dmd/root/optional.h @@ -11,6 +11,8 @@ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/root/optional.h */ +#include "dcompat.h" // for d_bool + /// Optional type that is either `empty` or contains a value of type `T` template struct Optional final @@ -20,7 +22,7 @@ struct Optional final T value; /** whether `value` is set **/ - bool present; + d_bool present; public: /** Creates an `Optional` with the given value **/ diff --git a/dmd/root/port.d b/dmd/root/port.d index 9d16e3db1af..a80d751aee2 100644 --- a/dmd/root/port.d +++ b/dmd/root/port.d @@ -70,7 +70,21 @@ extern (C++) struct Port return t; } - + private extern (D) static bool resultOutOfRange(FloatingType)(const FloatingType x, const int errnoValue) + { + import core.stdc.math : HUGE_VAL, HUGE_VALF; + static if (is(FloatingType == double)) + const FloatingType hugeVal = HUGE_VAL; + else static if (is(FloatingType == float)) + const FloatingType hugeVal = HUGE_VALF; + else static assert(0, "This function does not support " ~ FloatingType); + + if (errnoValue == ERANGE) + { + return x == hugeVal || x == 0.0f; + } + return false; + } static bool isFloat32LiteralOutOfRange(scope const(char)* s) { version (IN_LLVM) @@ -85,6 +99,8 @@ extern (C++) struct Port { auto save = __locale_decpoint; __locale_decpoint = "."; + scope(exit) + __locale_decpoint = save; } version (CRuntime_Microsoft) { @@ -92,13 +108,13 @@ extern (C++) struct Port int res = _atoflt(&r, s); if (res == _UNDERFLOW || res == _OVERFLOW) errno = ERANGE; + return errno == ERANGE; } else { - strtof(s, null); + const result = strtof(s, null); + return resultOutOfRange(result, errno); } - version (CRuntime_DigitalMars) __locale_decpoint = save; - return errno == ERANGE; } } @@ -116,6 +132,8 @@ extern (C++) struct Port { auto save = __locale_decpoint; __locale_decpoint = "."; + scope(exit) + __locale_decpoint = save; } version (CRuntime_Microsoft) { @@ -123,13 +141,13 @@ extern (C++) struct Port int res = _atodbl(&r, s); if (res == _UNDERFLOW || res == _OVERFLOW) errno = ERANGE; + return errno == ERANGE; } else { - strtod(s, null); + const result = strtod(s, null); + return resultOutOfRange(result, errno); } - version (CRuntime_DigitalMars) __locale_decpoint = save; - return errno == ERANGE; } } diff --git a/dmd/scope.h b/dmd/scope.h index 1b6ee3584ee..a04239b89db 100644 --- a/dmd/scope.h +++ b/dmd/scope.h @@ -81,8 +81,8 @@ struct Scope ForeachStatement *fes; // if nested function for ForeachStatement, this is it Scope *callsc; // used for __FUNCTION__, __PRETTY_FUNCTION__ and __MODULE__ Dsymbol *inunion; // !=null if processing members of a union - bool nofree; // true if shouldn't free it - bool inLoop; // true if inside a loop (where constructor calls aren't allowed) + d_bool nofree; // true if shouldn't free it + d_bool inLoop; // true if inside a loop (where constructor calls aren't allowed) int intypeof; // in typeof(exp) VarDeclaration *lastVar; // Previous symbol used to prevent goto-skips-init diff --git a/dmd/semantic2.d b/dmd/semantic2.d index c55674b3d73..7994048856c 100644 --- a/dmd/semantic2.d +++ b/dmd/semantic2.d @@ -669,6 +669,13 @@ private extern(C++) final class Semantic2Visitor : Visitor { foreach (base; cd.interfaces) { + // https://issues.dlang.org/show_bug.cgi?id=22729 + // interfaces that have errors or that + // inherit from interfaces that have errors + // might have an uninitialized vtable + if (!base.sym.vtbl.length) + continue; + // first entry is ClassInfo reference auto methods = base.sym.vtbl[base.sym.vtblOffset .. $]; diff --git a/dmd/semantic3.d b/dmd/semantic3.d index 19d53507db2..2025c9c7ab6 100644 --- a/dmd/semantic3.d +++ b/dmd/semantic3.d @@ -477,7 +477,7 @@ private extern(C++) final class Semantic3Visitor : Visitor /* Generate identifier for un-named parameter, * because we need it later on. */ - fparam.ident = id = Identifier.generateId("_param_", i); + fparam.ident = id = Identifier.generateId("__param_", i); stc |= STC.temp; } Type vtype = fparam.type; diff --git a/dmd/sideeffect.d b/dmd/sideeffect.d index 60a74cc2812..3f3e7e6377a 100644 --- a/dmd/sideeffect.d +++ b/dmd/sideeffect.d @@ -185,6 +185,7 @@ private bool lambdaHasSideEffect(Expression e, bool assumeImpureCalls = false) case EXP.delete_: case EXP.new_: case EXP.newAnonymousClass: + case EXP.loweredAssignExp: return true; case EXP.call: { diff --git a/dmd/statement.d b/dmd/statement.d index ba0959692e1..50f1a9a745f 100644 --- a/dmd/statement.d +++ b/dmd/statement.d @@ -379,11 +379,10 @@ version (IN_LLVM) * the downcast statement if it can be downcasted, otherwise `null` */ inout(ErrorStatement) isErrorStatement() { return stmt == STMT.Error ? cast(typeof(return))this : null; } + inout(PeelStatement) isPeelStatement() { return stmt == STMT.Peel ? cast(typeof(return))this : null; } inout(ScopeStatement) isScopeStatement() { return stmt == STMT.Scope ? cast(typeof(return))this : null; } inout(ExpStatement) isExpStatement() { return stmt == STMT.Exp ? cast(typeof(return))this : null; } inout(CompoundStatement) isCompoundStatement() { return stmt == STMT.Compound ? cast(typeof(return))this : null; } - version (IN_LLVM) - inout(CompoundAsmStatement) isCompoundAsmStatement() { return stmt == STMT.CompoundAsm ? cast(typeof(return))this : null; } inout(ReturnStatement) isReturnStatement() { return stmt == STMT.Return ? cast(typeof(return))this : null; } inout(IfStatement) isIfStatement() { return stmt == STMT.If ? cast(typeof(return))this : null; } inout(ConditionalStatement) isConditionalStatement() { return stmt == STMT.Conditional ? cast(typeof(return))this : null; } @@ -396,7 +395,7 @@ version (IN_LLVM) inout(GotoCaseStatement) isGotoCaseStatement() { return stmt == STMT.GotoCase ? cast(typeof(return))this : null; } inout(BreakStatement) isBreakStatement() { return stmt == STMT.Break ? cast(typeof(return))this : null; } inout(DtorExpStatement) isDtorExpStatement() { return stmt == STMT.DtorExp ? cast(typeof(return))this : null; } - inout(CompileStatement) isCompileStatement() { return stmt == STMT.Compile ? cast(typeof(return))this : null; } + inout(MixinStatement) isMixinStatement() { return stmt == STMT.Mixin ? cast(typeof(return))this : null; } inout(ForwardingStatement) isForwardingStatement() { return stmt == STMT.Forwarding ? cast(typeof(return))this : null; } inout(DoStatement) isDoStatement() { return stmt == STMT.Do ? cast(typeof(return))this : null; } inout(WhileStatement) isWhileStatement() { return stmt == STMT.While ? cast(typeof(return))this : null; } @@ -414,6 +413,15 @@ version (IN_LLVM) inout(UnrolledLoopStatement) isUnrolledLoopStatement() { return stmt == STMT.UnrolledLoop ? cast(typeof(return))this : null; } inout(ForeachRangeStatement) isForeachRangeStatement() { return stmt == STMT.ForeachRange ? cast(typeof(return))this : null; } inout(CompoundDeclarationStatement) isCompoundDeclarationStatement() { return stmt == STMT.CompoundDeclaration ? cast(typeof(return))this : null; } + inout(CompoundAsmStatement) isCompoundAsmStatement() { return stmt == STMT.CompoundAsm ? cast(typeof(return))this : null; } + inout(PragmaStatement) isPragmaStatement() { return stmt == STMT.Pragma ? cast(typeof(return))this : null; } + inout(StaticAssertStatement) isStaticAssertStatement() { return stmt == STMT.StaticAssert ? cast(typeof(return))this : null; } + inout(CaseRangeStatement) isCaseRangeStatement() { return stmt == STMT.CaseRange ? cast(typeof(return))this : null; } + inout(SynchronizedStatement) isSynchronizedStatement() { return stmt == STMT.Synchronized ? cast(typeof(return))this : null; } + inout(AsmStatement) isAsmStatement() { return stmt == STMT.Asm ? cast(typeof(return))this : null; } + inout(InlineAsmStatement) isInlineAsmStatement() { return stmt == STMT.InlineAsm ? cast(typeof(return))this : null; } + inout(GccAsmStatement) isGccAsmStatement() { return stmt == STMT.GccAsm ? cast(typeof(return))this : null; } + inout(ImportStatement) isImportStatement() { return stmt == STMT.Import ? cast(typeof(return))this : null; } } /*********************************************************** @@ -526,7 +534,8 @@ extern (C++) final class DtorExpStatement : ExpStatement /*********************************************************** * https://dlang.org/spec/statement.html#mixin-statement */ -extern (C++) final class CompileStatement : Statement +// Note: was called CompileStatement +extern (C++) final class MixinStatement : Statement { Expressions* exps; @@ -539,13 +548,13 @@ extern (C++) final class CompileStatement : Statement extern (D) this(const ref Loc loc, Expressions* exps) { - super(loc, STMT.Compile); + super(loc, STMT.Mixin); this.exps = exps; } - override CompileStatement syntaxCopy() + override MixinStatement syntaxCopy() { - return new CompileStatement(loc, Expression.arraySyntaxCopy(exps)); + return new MixinStatement(loc, Expression.arraySyntaxCopy(exps)); } override void accept(Visitor v) @@ -2168,3 +2177,107 @@ extern (C++) final class ImportStatement : Statement v.visit(this); } } + + +mixin template VisitStatement(Result) +{ + Result VisitStatement(Statement s) + { + final switch (s.stmt) + { + case STMT.Error: mixin(visitStmtCase("Error")); + case STMT.Scope: mixin(visitStmtCase("Scope")); + case STMT.Exp: mixin(visitStmtCase("Exp")); + case STMT.Compound: mixin(visitStmtCase("Compound")); + case STMT.Return: mixin(visitStmtCase("Return")); + case STMT.If: mixin(visitStmtCase("If")); + case STMT.Conditional: mixin(visitStmtCase("Conditional")); + case STMT.StaticForeach: mixin(visitStmtCase("StaticForeach")); + case STMT.Case: mixin(visitStmtCase("Case")); + case STMT.Default: mixin(visitStmtCase("Default")); + case STMT.Label: mixin(visitStmtCase("Label")); + case STMT.Goto: mixin(visitStmtCase("Goto")); + case STMT.GotoDefault: mixin(visitStmtCase("GotoDefault")); + case STMT.GotoCase: mixin(visitStmtCase("GotoCase")); + case STMT.Break: mixin(visitStmtCase("Break")); + case STMT.DtorExp: mixin(visitStmtCase("DtorExp")); + case STMT.Mixin: mixin(visitStmtCase("Mixin")); + case STMT.Forwarding: mixin(visitStmtCase("Forwarding")); + case STMT.Do: mixin(visitStmtCase("Do")); + case STMT.While: mixin(visitStmtCase("While")); + case STMT.For: mixin(visitStmtCase("For")); + case STMT.Foreach: mixin(visitStmtCase("Foreach")); + case STMT.Switch: mixin(visitStmtCase("Switch")); + case STMT.Continue: mixin(visitStmtCase("Continue")); + case STMT.With: mixin(visitStmtCase("With")); + case STMT.TryCatch: mixin(visitStmtCase("TryCatch")); + case STMT.Throw: mixin(visitStmtCase("Throw")); + case STMT.Debug: mixin(visitStmtCase("Debug")); + case STMT.TryFinally: mixin(visitStmtCase("TryFinally")); + case STMT.ScopeGuard: mixin(visitStmtCase("ScopeGuard")); + case STMT.SwitchError: mixin(visitStmtCase("SwitchError")); + case STMT.UnrolledLoop: mixin(visitStmtCase("UnrolledLoop")); + case STMT.ForeachRange: mixin(visitStmtCase("ForeachRange")); + case STMT.CompoundDeclaration: mixin(visitStmtCase("CompoundDeclaration")); + case STMT.Peel: mixin(visitStmtCase("Peel")); + case STMT.CompoundAsm: mixin(visitStmtCase("CompoundAsm")); + case STMT.Pragma: mixin(visitStmtCase("Pragma")); + case STMT.StaticAssert: mixin(visitStmtCase("StaticAssert")); + case STMT.CaseRange: mixin(visitStmtCase("CaseRange")); + case STMT.Synchronized: mixin(visitStmtCase("Synchronized")); + case STMT.Asm: mixin(visitStmtCase("Asm")); + case STMT.InlineAsm: mixin(visitStmtCase("InlineAsm")); + case STMT.GccAsm: mixin(visitStmtCase("GccAsm")); + case STMT.Import: mixin(visitStmtCase("Import")); + } + } +} + +/**************************************** + * CTFE-only helper function for VisitInitializer. + * Params: + * handler = string for the name of the visit handler + * Returns: boilerplate code for a case + */ +pure string visitStmtCase(string handler) +{ + if (__ctfe) + { + return + " + enum isVoid = is(Result == void); + auto sx = s.is"~handler~"Statement(); + static if (__traits(compiles, visit"~handler~"(sx))) + { + static if (isVoid) + { + visit"~handler~"(sx); + return; + } + else + { + if (Result r = visit"~handler~"(sx)) + return r; + return Result.init; + } + } + else static if (__traits(compiles, visitDefaultCase(s))) + { + static if (isVoid) + { + visitDefaultCase(sx); + return; + } + else + { + if (Result r = visitDefaultCase(s)) + return r; + return Result.init; + } + } + else + static assert(0, "~handler~"); + "; + } + assert(0); +} diff --git a/dmd/statement.h b/dmd/statement.h index e844636f9ab..1c96fb5aefd 100644 --- a/dmd/statement.h +++ b/dmd/statement.h @@ -70,7 +70,7 @@ enum STMTerror, STMTpeel, STMTexp, STMTdtorExp, - STMTcompile, + STMTmixin, STMTcompound, STMTcompoundDeclaration, STMTcompoundAsm, STMTunrolledLoop, STMTscope, @@ -156,7 +156,7 @@ class Statement : public ASTNode GotoCaseStatement *isGotoCaseStatement() { return stmt == STMTgotoCase ? (GotoCaseStatement*)this : NULL; } BreakStatement *isBreakStatement() { return stmt == STMTbreak ? (BreakStatement*)this : NULL; } DtorExpStatement *isDtorExpStatement() { return stmt == STMTdtorExp ? (DtorExpStatement*)this : NULL; } - CompileStatement *isCompileStatement() { return stmt == STMTcompile ? (CompileStatement*)this : NULL; } + MixinStatement *isMixinStatement() { return stmt == STMTmixin ? (MixinStatement*)this : NULL; } ForwardingStatement *isForwardingStatement() { return stmt == STMTforwarding ? (ForwardingStatement*)this : NULL; } DoStatement *isDoStatement() { return stmt == STMTdo ? (DoStatement*)this : NULL; } ForStatement *isForStatement() { return stmt == STMTfor ? (ForStatement*)this : NULL; } @@ -219,12 +219,12 @@ class DtorExpStatement final : public ExpStatement void accept(Visitor *v) override { v->visit(this); } }; -class CompileStatement final : public Statement +class MixinStatement final : public Statement { public: Expressions *exps; - CompileStatement *syntaxCopy() override; + MixinStatement *syntaxCopy() override; void accept(Visitor *v) override { v->visit(this); } }; @@ -450,7 +450,7 @@ class SwitchStatement final : public Statement public: Expression *condition; Statement *_body; - bool isFinal; + d_bool isFinal; DefaultStatement *sdefault; Statement *tryBody; // set to TryCatchStatement or TryFinallyStatement if in _body portion @@ -643,11 +643,11 @@ class Catch final : public RootObject VarDeclaration *var; // set if semantic processing errors - bool errors; + d_bool errors; // was generated by the compiler, // wasn't present in source code - bool internalCatch; + d_bool internalCatch; Catch *syntaxCopy(); }; @@ -659,7 +659,7 @@ class TryFinallyStatement final : public Statement Statement *finalbody; Statement *tryBody; // set to enclosing TryCatchStatement or TryFinallyStatement if in _body portion - bool bodyFallsThru; // true if _body falls through to finally + d_bool bodyFallsThru; // true if _body falls through to finally static TryFinallyStatement *create(const Loc &loc, Statement *body, Statement *finalbody); TryFinallyStatement *syntaxCopy() override; @@ -686,7 +686,7 @@ class ThrowStatement final : public Statement Expression *exp; // was generated by the compiler, // wasn't present in source code - bool internalThrow; + d_bool internalThrow; ThrowStatement *syntaxCopy() override; @@ -711,7 +711,7 @@ class GotoStatement final : public Statement TryFinallyStatement *tf; ScopeGuardStatement *os; VarDeclaration *lastVar; - bool inCtfeBlock; + d_bool inCtfeBlock; GotoStatement *syntaxCopy() override; void accept(Visitor *v) override { v->visit(this); } @@ -728,8 +728,8 @@ class LabelStatement final : public Statement VarDeclaration *lastVar; Statement *gotoTarget; // interpret void* extra; // used by Statement_toIR() - bool breaks; // someone did a 'break ident' - bool inCtfeBlock; + d_bool breaks; // someone did a 'break ident' + d_bool inCtfeBlock; LabelStatement *syntaxCopy() override; void accept(Visitor *v) override { v->visit(this); } @@ -740,8 +740,8 @@ class LabelDsymbol final : public Dsymbol public: LabelStatement *statement; - bool deleted; // set if rewritten to return in foreach delegate - bool iasm; // set if used by inline assembler + d_bool deleted; // set if rewritten to return in foreach delegate + d_bool iasm; // set if used by inline assembler static LabelDsymbol *create(Identifier *ident); LabelDsymbol *isLabel() override; @@ -765,8 +765,8 @@ class InlineAsmStatement final : public AsmStatement code *asmcode; unsigned asmalign; // alignment of this statement unsigned regs; // mask of registers modified (must match regm_t in back end) - bool refparam; // true if function parameter is referenced - bool naked; // true if function is to be naked + d_bool refparam; // true if function parameter is referenced + d_bool naked; // true if function is to be naked #if IN_LLVM // non-zero if this is a branch, contains the target label diff --git a/dmd/statementsem.d b/dmd/statementsem.d index 213b7891678..24fb02457fd 100644 --- a/dmd/statementsem.d +++ b/dmd/statementsem.d @@ -145,43 +145,35 @@ extern(C++) Statement statementSemantic(Statement s, Scope* sc) version (CallbackAPI) Compiler.onStatementSemanticStart(s, sc); - scope v = new StatementSemanticVisitor(sc); - s.accept(v); + Statement result = statementSemanticVisit(s, sc); version (CallbackAPI) Compiler.onStatementSemanticDone(s, sc); - return v.result; + return result; } -package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor +package (dmd) +Statement statementSemanticVisit(Statement s, Scope* sc) { - alias visit = Visitor.visit; - Statement result; - Scope* sc; - - this(Scope* sc) scope - { - this.sc = sc; - } - private void setError() + void setError() { result = new ErrorStatement(); } - override void visit(Statement s) + void visitDefaultCase(Statement s) { result = s; } - override void visit(ErrorStatement s) + void visitError(ErrorStatement s) { result = s; } - override void visit(PeelStatement s) + void visitPeel(PeelStatement s) { /* "peel" off this wrapper, and don't run semantic() * on the result. @@ -189,7 +181,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor result = s.s; } - override void visit(ExpStatement s) + void visitExp(ExpStatement s) { /* https://dlang.org/spec/statement.html#expression-statement */ @@ -226,12 +218,17 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor result = s; } - override void visit(CompileStatement cs) + void visitDtorExp(DtorExpStatement s) + { + visitExp(s); + } + + void visitMixin(MixinStatement cs) { /* https://dlang.org/spec/statement.html#mixin-statement */ - //printf("CompileStatement::semantic() %s\n", exp.toChars()); + //printf("MixinStatement::semantic() %s\n", exp.toChars()); Statements* a = cs.flatten(sc); if (!a) return; @@ -239,7 +236,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor result = s.statementSemantic(sc); } - override void visit(CompoundStatement cs) + void visitCompound(CompoundStatement cs) { //printf("CompoundStatement::semantic(this = %p, sc = %p)\n", cs, sc); version (none) @@ -431,7 +428,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor result = cs; } - override void visit(UnrolledLoopStatement uls) + void visitUnrolledLoop(UnrolledLoopStatement uls) { //printf("UnrolledLoopStatement::semantic(this = %p, sc = %p)\n", uls, sc); Scope* scd = sc.push(); @@ -454,7 +451,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor result = serror ? serror : uls; } - override void visit(ScopeStatement ss) + void visitScope(ScopeStatement ss) { //printf("ScopeStatement::semantic(sc = %p)\n", sc); if (!ss.statement) @@ -501,7 +498,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor result = ss; } - override void visit(ForwardingStatement ss) + void visitForwarding(ForwardingStatement ss) { assert(ss.sym); for (Scope* csc = sc; !ss.sym.parent; csc = csc.enclosing) @@ -517,7 +514,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor result = ss.statement; } - override void visit(WhileStatement ws) + void visitWhile(WhileStatement ws) { /* Rewrite as a for(;condition;) loop * https://dlang.org/spec/statement.html#while-statement @@ -544,7 +541,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor result = s; } - override void visit(DoStatement ds) + void visitDo(DoStatement ds) { /* https://dlang.org/spec/statement.html#do-statement */ @@ -580,7 +577,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor result = ds; } - override void visit(ForStatement fs) + void visitFor(ForStatement fs) { /* https://dlang.org/spec/statement.html#for-statement */ @@ -674,7 +671,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor result = fs; } - override void visit(ForeachStatement fs) + void visitForeach(ForeachStatement fs) { /* https://dlang.org/spec/statement.html#foreach-statement */ @@ -1350,7 +1347,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor auto exp = (*exps)[i]; version (none) { - printf("[%d] p = %s %s, exp = %s %s\n", i, + printf("[%lu] p = %s %s, exp = %s %s\n", i, p.type ? p.type.toChars() : "?", p.ident.toChars(), exp.type.toChars(), exp.toChars()); } @@ -1361,7 +1358,11 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor if (ignoreRef) sc &= ~STC.ref_; p.type = p.type.addStorageClass(sc).typeSemantic(loc, sc2); if (!exp.implicitConvTo(p.type)) - return rangeError(); + { + fs.error("cannot implicilty convert range element of type `%s` to variable `%s` of type `%s`", + exp.type.toChars(), p.toChars(), p.type.toChars()); + return retError(); + } auto var = new VarDeclaration(loc, p.type, p.ident, new ExpInitializer(loc, exp)); var.storage_class |= STC.ctfe | STC.ref_ | STC.foreach_; @@ -1396,342 +1397,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor } } - private static extern(D) Expression applyOpApply(ForeachStatement fs, Expression flde, - Type tab, Scope* sc2, Dsymbol sapply) - { - version (none) - { - if (global.params.useDIP1000 == FeatureState.enabled) - { - message(loc, "To enforce `@safe`, the compiler allocates a closure unless `opApply()` uses `scope`"); - } - (cast(FuncExp)flde).fd.tookAddressOf = 1; - } - else - { - if (global.params.useDIP1000 == FeatureState.enabled) - ++(cast(FuncExp)flde).fd.tookAddressOf; // allocate a closure unless the opApply() uses 'scope' - } - assert(tab.ty == Tstruct || tab.ty == Tclass); - assert(sapply); - /* Call: - * aggr.apply(flde) - */ - Expression ec; - ec = new DotIdExp(fs.loc, fs.aggr, sapply.ident); - ec = new CallExp(fs.loc, ec, flde); - ec = ec.expressionSemantic(sc2); - if (ec.op == EXP.error) - return null; - if (ec.type != Type.tint32) - { - fs.error("`opApply()` function for `%s` must return an `int`", tab.toChars()); - return null; - } - return ec; - } - - private static extern(D) Expression applyDelegate(ForeachStatement fs, Expression flde, - Type tab, Scope* sc2) - { - Expression ec; - /* Call: - * aggr(flde) - */ - if (fs.aggr.op == EXP.delegate_ && (cast(DelegateExp)fs.aggr).func.isNested() && - !(cast(DelegateExp)fs.aggr).func.needThis()) - { - // https://issues.dlang.org/show_bug.cgi?id=3560 - fs.aggr = (cast(DelegateExp)fs.aggr).e1; - } - ec = new CallExp(fs.loc, fs.aggr, flde); - ec = ec.expressionSemantic(sc2); - if (ec.op == EXP.error) - return null; - if (ec.type != Type.tint32) - { - fs.error("`opApply()` function for `%s` must return an `int`", tab.toChars()); - return null; - } - return ec; - } - - private static extern(D) Expression applyArray(ForeachStatement fs, Expression flde, - Type tab, Scope* sc2, Type tn, Type tnv) - { - Expression ec; - const dim = fs.parameters.length; - const loc = fs.loc; - /* Call: - * _aApply(aggr, flde) - */ - static immutable fntab = - [ - "cc", "cw", "cd", - "wc", "cc", "wd", - "dc", "dw", "dd" - ]; - - const(size_t) BUFFER_LEN = 7 + 1 + 2 + dim.sizeof * 3 + 1; - char[BUFFER_LEN] fdname; - int flag; - - switch (tn.ty) - { - case Tchar: flag = 0; break; - case Twchar: flag = 3; break; - case Tdchar: flag = 6; break; - default: - assert(0); - } - switch (tnv.ty) - { - case Tchar: flag += 0; break; - case Twchar: flag += 1; break; - case Tdchar: flag += 2; break; - default: - assert(0); - } - const(char)* r = (fs.op == TOK.foreach_reverse_) ? "R" : ""; - int j = snprintf(fdname.ptr, BUFFER_LEN, "_aApply%s%.*s%llu", r, 2, fntab[flag].ptr, cast(ulong)dim); - assert(j < BUFFER_LEN); - - FuncDeclaration fdapply; - TypeDelegate dgty; - auto params = new Parameters(); - params.push(new Parameter(STC.in_, tn.arrayOf(), null, null, null)); - auto dgparams = new Parameters(); - dgparams.push(new Parameter(0, Type.tvoidptr, null, null, null)); - if (dim == 2) - dgparams.push(new Parameter(0, Type.tvoidptr, null, null, null)); - dgty = new TypeDelegate(new TypeFunction(ParameterList(dgparams), Type.tint32, LINK.d)); - params.push(new Parameter(0, dgty, null, null, null)); - fdapply = FuncDeclaration.genCfunc(params, Type.tint32, fdname.ptr); - - if (tab.isTypeSArray()) - fs.aggr = fs.aggr.castTo(sc2, tn.arrayOf()); - // paint delegate argument to the type runtime expects - Expression fexp = flde; - if (!dgty.equals(flde.type)) - { - fexp = new CastExp(loc, flde, flde.type); - fexp.type = dgty; - } - ec = new VarExp(Loc.initial, fdapply, false); - ec = new CallExp(loc, ec, fs.aggr, fexp); - ec.type = Type.tint32; // don't run semantic() on ec - return ec; - } - - private static extern(D) Expression applyAssocArray(ForeachStatement fs, Expression flde, Type tab) - { - auto taa = tab.isTypeAArray(); - Expression ec; - const dim = fs.parameters.length; - // Check types - Parameter p = (*fs.parameters)[0]; - bool isRef = (p.storageClass & STC.ref_) != 0; - Type ta = p.type; - if (dim == 2) - { - Type ti = (isRef ? taa.index.addMod(MODFlags.const_) : taa.index); - if (isRef ? !ti.constConv(ta) : !ti.implicitConvTo(ta)) - { - fs.error("`foreach`: index must be type `%s`, not `%s`", - ti.toChars(), ta.toChars()); - return null; - } - p = (*fs.parameters)[1]; - isRef = (p.storageClass & STC.ref_) != 0; - ta = p.type; - } - Type taav = taa.nextOf(); - if (isRef ? !taav.constConv(ta) : !taav.implicitConvTo(ta)) - { - fs.error("`foreach`: value must be type `%s`, not `%s`", - taav.toChars(), ta.toChars()); - return null; - } - - /* Call: - * extern(C) int _aaApply(void*, in size_t, int delegate(void*)) - * _aaApply(aggr, keysize, flde) - * - * extern(C) int _aaApply2(void*, in size_t, int delegate(void*, void*)) - * _aaApply2(aggr, keysize, flde) - */ - __gshared FuncDeclaration* fdapply = [null, null]; - __gshared TypeDelegate* fldeTy = [null, null]; - ubyte i = (dim == 2 ? 1 : 0); - if (!fdapply[i]) - { - auto params = new Parameters(); - params.push(new Parameter(0, Type.tvoid.pointerTo(), null, null, null)); - params.push(new Parameter(STC.const_, Type.tsize_t, null, null, null)); - auto dgparams = new Parameters(); - dgparams.push(new Parameter(0, Type.tvoidptr, null, null, null)); - if (dim == 2) - dgparams.push(new Parameter(0, Type.tvoidptr, null, null, null)); - fldeTy[i] = new TypeDelegate(new TypeFunction(ParameterList(dgparams), Type.tint32, LINK.d)); - params.push(new Parameter(0, fldeTy[i], null, null, null)); - fdapply[i] = FuncDeclaration.genCfunc(params, Type.tint32, i ? Id._aaApply2 : Id._aaApply); - } - - auto exps = new Expressions(); - exps.push(fs.aggr); - auto keysize = taa.index.size(); - if (keysize == SIZE_INVALID) - return null; - assert(keysize < keysize.max - target.ptrsize); - keysize = (keysize + (target.ptrsize - 1)) & ~(target.ptrsize - 1); - // paint delegate argument to the type runtime expects - Expression fexp = flde; - if (!fldeTy[i].equals(flde.type)) - { - fexp = new CastExp(fs.loc, flde, flde.type); - fexp.type = fldeTy[i]; - } - exps.push(new IntegerExp(Loc.initial, keysize, Type.tsize_t)); - exps.push(fexp); - ec = new VarExp(Loc.initial, fdapply[i], false); - ec = new CallExp(fs.loc, ec, exps); - ec.type = Type.tint32; // don't run semantic() on ec - return ec; - } - - private static extern(D) Statement loopReturn(Expression e, Statements* cases, const ref Loc loc) - { - if (!cases.length) - { - // Easy case, a clean exit from the loop - e = new CastExp(loc, e, Type.tvoid); // https://issues.dlang.org/show_bug.cgi?id=13899 - return new ExpStatement(loc, e); - } - // Construct a switch statement around the return value - // of the apply function. - Statement s; - auto a = new Statements(); - - // default: break; takes care of cases 0 and 1 - s = new BreakStatement(Loc.initial, null); - s = new DefaultStatement(Loc.initial, s); - a.push(s); - - // cases 2... - foreach (i, c; *cases) - { - s = new CaseStatement(Loc.initial, new IntegerExp(i + 2), c); - a.push(s); - } - - s = new CompoundStatement(loc, a); - return new SwitchStatement(loc, e, s, false); - } - /************************************* - * Turn foreach body into the function literal: - * int delegate(ref T param) { body } - * Params: - * sc = context - * fs = ForeachStatement - * tfld = type of function literal to be created (type of opApply() function if any), can be null - * Returns: - * Function literal created, as an expression - * null if error. - */ - static FuncExp foreachBodyToFunction(Scope* sc, ForeachStatement fs, TypeFunction tfld, - /*IN_LLVM*/ bool enforceSizeTIndex) - { - auto params = new Parameters(); - foreach (i, p; *fs.parameters) - { - StorageClass stc = STC.ref_ | (p.storageClass & STC.scope_); - Identifier id; - - p.type = p.type.typeSemantic(fs.loc, sc); - p.type = p.type.addStorageClass(p.storageClass); -version (IN_LLVM) -{ - // Type of parameter may be different; see below - auto para_type = p.type; -} - 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_)) - { - if (!(prm.storageClass & STC.ref_)) - { - fs.error("`foreach`: cannot make `%s` `ref`", p.ident.toChars()); - return null; - } - goto LcopyArg; - } - id = p.ident; // argument copy is not need. - } - else if (p.storageClass & STC.ref_) - { - // default delegate parameters are marked as ref, then - // argument copy is not need. - id = p.ident; - } - else - { - // Make a copy of the ref argument so it isn't - // a reference. - LcopyArg: - id = Identifier.generateId("__applyArg", cast(int)i); - -version (IN_LLVM) -{ - // In case of a foreach loop on an array the index passed - // to the delegate is always of type size_t. The type of - // the parameter must be changed to size_t and a cast to - // the type used must be inserted. Otherwise the index is - // always 0 on a big endian architecture. This fixes - // issue #326. - Initializer ie; - if (fs.parameters.length == 2 && i == 0 && enforceSizeTIndex) - { - para_type = Type.tsize_t; - ie = new ExpInitializer(fs.loc, - new CastExp(fs.loc, - new IdentifierExp(fs.loc, id), p.type)); - } - else - { - ie = new ExpInitializer(fs.loc, new IdentifierExp(fs.loc, id)); - } -} -else -{ - Initializer ie = new ExpInitializer(fs.loc, new IdentifierExp(fs.loc, id)); -} - auto v = new VarDeclaration(fs.loc, p.type, p.ident, ie); - v.storage_class |= STC.temp | (stc & STC.scope_); - Statement s = new ExpStatement(fs.loc, v); - fs._body = new CompoundStatement(fs.loc, s, fs._body); - } - params.push(new Parameter(stc, IN_LLVM ? para_type : p.type, id, null, null)); - } - // https://issues.dlang.org/show_bug.cgi?id=13840 - // Throwable nested function inside nothrow function is acceptable. - StorageClass stc = mergeFuncAttrs(STC.safe | STC.pure_ | STC.nogc, fs.func); - auto tf = new TypeFunction(ParameterList(params), Type.tint32, LINK.d, stc); - fs.cases = new Statements(); - fs.gotos = new ScopeStatements(); - auto fld = new FuncLiteralDeclaration(fs.loc, fs.endloc, tf, TOK.delegate_, fs); - fld.fbody = fs._body; - Expression flde = new FuncExp(fs.loc, fld); - flde = flde.expressionSemantic(sc); - fld.tookAddressOf = 0; - if (flde.op == EXP.error) - return null; - return cast(FuncExp)flde; - } - - override void visit(ForeachRangeStatement fs) + void visitForeachRange(ForeachRangeStatement fs) { /* https://dlang.org/spec/statement.html#foreach-range-statement */ @@ -1917,7 +1583,7 @@ else result = s.statementSemantic(sc); } - override void visit(IfStatement ifs) + void visitIf(IfStatement ifs) { /* https://dlang.org/spec/statement.html#IfStatement */ @@ -1990,6 +1656,20 @@ else // Save 'root' of two branches (then and else) at the point where it forks CtorFlow ctorflow_root = scd.ctorflow.clone(); + /* Rewrite `if (!__ctfe) A else B` as `if (__ctfe) B else A` + */ + NotExp notExp; + if (ifs.elsebody && + (notExp = ifs.condition.isNotExp()) !is null && + notExp.e1.isVarExp() && + notExp.e1.isVarExp().var.ident == Id.ctfe) + { + ifs.condition = notExp.e1; + auto sbody = ifs.ifbody; + ifs.ifbody = ifs.elsebody; + ifs.elsebody = sbody; + } + /* Detect `if (__ctfe)` */ if (ifs.isIfCtfeBlock()) @@ -2022,7 +1702,7 @@ else result = ifs; } - override void visit(ConditionalStatement cs) + void visitConditional(ConditionalStatement cs) { //printf("ConditionalStatement::semantic()\n"); @@ -2051,7 +1731,7 @@ else } } - override void visit(PragmaStatement ps) + void visitPragma(PragmaStatement ps) { /* https://dlang.org/spec/statement.html#pragma-statement */ @@ -2167,14 +1847,14 @@ else result = ps._body; } - override void visit(StaticAssertStatement s) + void visitStaticAssert(StaticAssertStatement s) { s.sa.semantic2(sc); if (s.sa.errors) return setError(); } - override void visit(SwitchStatement ss) + void visitSwitch(SwitchStatement ss) { /* https://dlang.org/spec/statement.html#switch-statement */ @@ -2355,6 +2035,11 @@ version (IN_LLVM) { s = new BreakStatement(ss.loc, null); // default for C is `default: break;` } + else if (!sc.needsCodegen()) + { + // something for the interpreter to deal with + s = new ExpStatement(ss.loc, new AssertExp(ss.loc, IntegerExp.literal!0)); + } else if (global.params.useSwitchError == CHECKENABLE.on && global.params.checkAction != CHECKACTION.halt) { @@ -2416,7 +2101,7 @@ version (IN_LLVM) } } - if (!ss.condition.type.isString()) + if (!(ss.condition.type.isString() && sc.needsCodegen())) { sc.pop(); result = ss; @@ -2499,7 +2184,7 @@ version (IN_LLVM) result = ss; } - override void visit(CaseStatement cs) + void visitCase(CaseStatement cs) { SwitchStatement sw = sc.sw; bool errors = false; @@ -2645,7 +2330,7 @@ version (IN_LLVM) result = cs; } - override void visit(CaseRangeStatement crs) + void visitCaseRange(CaseRangeStatement crs) { SwitchStatement sw = sc.sw; if (sw is null) @@ -2728,7 +2413,7 @@ version (IN_LLVM) result = s; } - override void visit(DefaultStatement ds) + void visitDefault(DefaultStatement ds) { //printf("DefaultStatement::semantic()\n"); bool errors = false; @@ -2772,7 +2457,7 @@ version (IN_LLVM) result = ds; } - override void visit(GotoDefaultStatement gds) + void visitGotoDefault(GotoDefaultStatement gds) { /* https://dlang.org/spec/statement.html#goto-statement */ @@ -2797,7 +2482,7 @@ version (IN_LLVM) result = gds; } - override void visit(GotoCaseStatement gcs) + void visitGotoCase(GotoCaseStatement gcs) { /* https://dlang.org/spec/statement.html#goto-statement */ @@ -2826,7 +2511,7 @@ version (IN_LLVM) result = gcs; } - override void visit(ReturnStatement rs) + void visitReturn(ReturnStatement rs) { /* https://dlang.org/spec/statement.html#return-statement */ @@ -3219,7 +2904,7 @@ version (IN_LLVM) result = rs; } - override void visit(BreakStatement bs) + void visitBreak(BreakStatement bs) { /* https://dlang.org/spec/statement.html#break-statement */ @@ -3301,7 +2986,7 @@ version (IN_LLVM) result = bs; } - override void visit(ContinueStatement cs) + void visitContinue(ContinueStatement cs) { /* https://dlang.org/spec/statement.html#continue-statement */ @@ -3392,7 +3077,7 @@ version (IN_LLVM) result = cs; } - override void visit(SynchronizedStatement ss) + void visitSynchronized(SynchronizedStatement ss) { /* https://dlang.org/spec/statement.html#synchronized-statement */ @@ -3514,7 +3199,7 @@ version (IN_LLVM) } } - override void visit(WithStatement ws) + void visitWith(WithStatement ws) { /* https://dlang.org/spec/statement.html#with-statement */ @@ -3549,14 +3234,16 @@ version (IN_LLVM) } else { - Type t = ws.exp.type.toBasetype(); + Type texp = ws.exp.type; + Type t = texp.toBasetype(); Expression olde = ws.exp; if (t.ty == Tpointer) { ws.exp = new PtrExp(ws.loc, ws.exp); ws.exp = ws.exp.expressionSemantic(sc); - t = ws.exp.type.toBasetype(); + texp = ws.exp.type; + t = texp.toBasetype(); } assert(t); @@ -3604,9 +3291,16 @@ version (IN_LLVM) sym.parent = sc.scopesym; sym.endlinnum = ws.endloc.linnum; } + else if (auto tenum = texp.isTypeEnum()) + { + ws.exp = new TypeExp(ws.exp.loc, tenum); + sym = new WithScopeSymbol(ws); + sym.parent = sc.scopesym; + sym.endlinnum = ws.endloc.linnum; + } else { - ws.error("`with` expressions must be aggregate types or pointers to them, not `%s`", olde.type.toChars()); + ws.error("`with` expression types must be enums or aggregates or pointers to them, not `%s`", olde.type.toChars()); return setError(); } } @@ -3629,7 +3323,7 @@ version (IN_LLVM) } // https://dlang.org/spec/statement.html#TryStatement - override void visit(TryCatchStatement tcs) + void visitTryCatch(TryCatchStatement tcs) { //printf("TryCatchStatement.semantic()\n"); @@ -3733,7 +3427,7 @@ version (IN_LLVM) result = tcs; } - override void visit(TryFinallyStatement tfs) + void visitTryFinally(TryFinallyStatement tfs) { //printf("TryFinallyStatement::semantic()\n"); tfs.tryBody = sc.tryBody; // chain on in-flight tryBody @@ -3773,7 +3467,7 @@ version (IN_LLVM) result = tfs; } - override void visit(ScopeGuardStatement oss) + void visitScopeGuard(ScopeGuardStatement oss) { /* https://dlang.org/spec/statement.html#scope-guard-statement */ @@ -3823,7 +3517,7 @@ version (IN_LLVM) result = oss; } - override void visit(ThrowStatement ts) + void visitThrow(ThrowStatement ts) { /* https://dlang.org/spec/statement.html#throw-statement */ @@ -3836,57 +3530,7 @@ version (IN_LLVM) } - /** - * Run semantic on `throw `. - * - * Params: - * loc = location of the `throw` - * exp = value to be thrown - * sc = enclosing scope - * - * Returns: true if the `throw` is valid, or false if an error was found - */ - extern(D) static bool throwSemantic(const ref Loc loc, ref Expression exp, Scope* sc) - { - if (!global.params.useExceptions) - { - loc.error("cannot use `throw` statements with -betterC"); - return false; - } - - if (!ClassDeclaration.throwable) - { - loc.error("cannot use `throw` statements because `object.Throwable` was not declared"); - return false; - } - - if (FuncDeclaration fd = sc.parent.isFuncDeclaration()) - fd.hasReturnExp |= 2; - - if (exp.op == EXP.new_) - { - NewExp ne = cast(NewExp) exp; - ne.thrownew = true; - } - - exp = exp.expressionSemantic(sc); - exp = resolveProperties(sc, exp); - exp = checkGC(sc, exp); - if (exp.op == EXP.error) - return false; - - checkThrowEscape(sc, exp, false); - - ClassDeclaration cd = exp.type.toBasetype().isClassHandle(); - if (!cd || ((cd != ClassDeclaration.throwable) && !ClassDeclaration.throwable.isBaseOf(cd, null))) - { - loc.error("can only throw class objects derived from `Throwable`, not type `%s`", exp.type.toChars()); - return false; - } - return true; - } - - override void visit(DebugStatement ds) + void visitDebug(DebugStatement ds) { if (ds.statement) { @@ -3898,7 +3542,7 @@ version (IN_LLVM) result = ds.statement; } - override void visit(GotoStatement gs) + void visitGoto(GotoStatement gs) { /* https://dlang.org/spec/statement.html#goto-statement */ @@ -3942,7 +3586,7 @@ version (IN_LLVM) result = gs; } - override void visit(LabelStatement ls) + void visitLabel(LabelStatement ls) { //printf("LabelStatement::semantic()\n"); FuncDeclaration fd = sc.parent.isFuncDeclaration(); @@ -3976,7 +3620,7 @@ version (IN_LLVM) result = ls; } - override void visit(AsmStatement s) + void visitAsm(AsmStatement s) { /* https://dlang.org/spec/statement.html#asm */ @@ -3985,7 +3629,7 @@ version (IN_LLVM) result = asmSemantic(s, sc); } - override void visit(CompoundAsmStatement cas) + void visitCompoundAsm(CompoundAsmStatement cas) { //printf("CompoundAsmStatement()::semantic()\n"); // Apply postfix attributes of the asm block to each statement. @@ -4013,9 +3657,9 @@ version (IN_LLVM) } assert(sc.func); - if (!(cas.stc & STC.pure_) && sc.func.setImpure()) + 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")) cas.error("`asm` statement is assumed to be impure - mark it with `pure` if it is not"); - if (!(cas.stc & STC.nogc) && sc.func.setGC()) + 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")) cas.error("`asm` statement is assumed to use the GC - mark it with `@nogc` if it does not"); if (!(cas.stc & (STC.trusted | STC.safe))) { @@ -4026,7 +3670,7 @@ version (IN_LLVM) result = cas; } - override void visit(ImportStatement imps) + void visitImport(ImportStatement imps) { /* https://dlang.org/spec/module.html#ImportDeclaration */ @@ -4065,8 +3709,405 @@ version (IN_LLVM) } result = imps; } + + mixin VisitStatement!void visit; + visit.VisitStatement(s); + return result; +} + +/** + * Run semantic on `throw `. + * + * Params: + * loc = location of the `throw` + * exp = value to be thrown + * sc = enclosing scope + * + * 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) +{ + if (!global.params.useExceptions) + { + loc.error("cannot use `throw` statements with -betterC"); + return false; + } + + if (!ClassDeclaration.throwable) + { + loc.error("cannot use `throw` statements because `object.Throwable` was not declared"); + return false; + } + + if (FuncDeclaration fd = sc.parent.isFuncDeclaration()) + fd.hasReturnExp |= 2; + + if (exp.op == EXP.new_) + { + NewExp ne = cast(NewExp) exp; + ne.thrownew = true; + } + + exp = exp.expressionSemantic(sc); + exp = resolveProperties(sc, exp); + exp = checkGC(sc, exp); + if (exp.op == EXP.error) + return false; + if (!exp.type.isNaked()) + { + // @@@DEPRECATED_2.112@@@ + // Deprecated in 2.102, change into an error & return false in 2.112 + exp.loc.deprecation("cannot throw object of qualified type `%s`", exp.type.toChars()); + //return false; + } + checkThrowEscape(sc, exp, false); + + ClassDeclaration cd = exp.type.toBasetype().isClassHandle(); + if (!cd || ((cd != ClassDeclaration.throwable) && !ClassDeclaration.throwable.isBaseOf(cd, null))) + { + loc.error("can only throw class objects derived from `Throwable`, not type `%s`", exp.type.toChars()); + return false; + } + return true; +} + +private extern(D) Expression applyOpApply(ForeachStatement fs, Expression flde, + Type tab, Scope* sc2, Dsymbol sapply) +{ + version (none) + { + if (global.params.useDIP1000 == FeatureState.enabled) + { + message(loc, "To enforce `@safe`, the compiler allocates a closure unless `opApply()` uses `scope`"); + } + (cast(FuncExp)flde).fd.tookAddressOf = 1; + } + else + { + if (global.params.useDIP1000 == FeatureState.enabled) + ++(cast(FuncExp)flde).fd.tookAddressOf; // allocate a closure unless the opApply() uses 'scope' + } + assert(tab.ty == Tstruct || tab.ty == Tclass); + assert(sapply); + /* Call: + * aggr.apply(flde) + */ + Expression ec; + ec = new DotIdExp(fs.loc, fs.aggr, sapply.ident); + ec = new CallExp(fs.loc, ec, flde); + ec = ec.expressionSemantic(sc2); + if (ec.op == EXP.error) + return null; + if (ec.type != Type.tint32) + { + fs.error("`opApply()` function for `%s` must return an `int`", tab.toChars()); + return null; + } + return ec; } +private extern(D) Expression applyDelegate(ForeachStatement fs, Expression flde, + Type tab, Scope* sc2) +{ + Expression ec; + /* Call: + * aggr(flde) + */ + if (fs.aggr.op == EXP.delegate_ && (cast(DelegateExp)fs.aggr).func.isNested() && + !(cast(DelegateExp)fs.aggr).func.needThis()) + { + // https://issues.dlang.org/show_bug.cgi?id=3560 + fs.aggr = (cast(DelegateExp)fs.aggr).e1; + } + ec = new CallExp(fs.loc, fs.aggr, flde); + ec = ec.expressionSemantic(sc2); + if (ec.op == EXP.error) + return null; + if (ec.type != Type.tint32) + { + fs.error("`opApply()` function for `%s` must return an `int`", tab.toChars()); + return null; + } + return ec; +} + +private extern(D) Expression applyArray(ForeachStatement fs, Expression flde, + Type tab, Scope* sc2, Type tn, Type tnv) +{ + Expression ec; + const dim = fs.parameters.length; + const loc = fs.loc; + /* Call: + * _aApply(aggr, flde) + */ + static immutable fntab = + [ + "cc", "cw", "cd", + "wc", "cc", "wd", + "dc", "dw", "dd" + ]; + + const(size_t) BUFFER_LEN = 7 + 1 + 2 + dim.sizeof * 3 + 1; + char[BUFFER_LEN] fdname; + int flag; + + switch (tn.ty) + { + case Tchar: flag = 0; break; + case Twchar: flag = 3; break; + case Tdchar: flag = 6; break; + default: + assert(0); + } + switch (tnv.ty) + { + case Tchar: flag += 0; break; + case Twchar: flag += 1; break; + case Tdchar: flag += 2; break; + default: + assert(0); + } + const(char)* r = (fs.op == TOK.foreach_reverse_) ? "R" : ""; + int j = snprintf(fdname.ptr, BUFFER_LEN, "_aApply%s%.*s%llu", r, 2, fntab[flag].ptr, cast(ulong)dim); + assert(j < BUFFER_LEN); + + FuncDeclaration fdapply; + TypeDelegate dgty; + auto params = new Parameters(); + params.push(new Parameter(STC.in_, tn.arrayOf(), null, null, null)); + auto dgparams = new Parameters(); + dgparams.push(new Parameter(0, Type.tvoidptr, null, null, null)); + if (dim == 2) + dgparams.push(new Parameter(0, Type.tvoidptr, null, null, null)); + dgty = new TypeDelegate(new TypeFunction(ParameterList(dgparams), Type.tint32, LINK.d)); + params.push(new Parameter(0, dgty, null, null, null)); + fdapply = FuncDeclaration.genCfunc(params, Type.tint32, fdname.ptr); + + if (tab.isTypeSArray()) + fs.aggr = fs.aggr.castTo(sc2, tn.arrayOf()); + // paint delegate argument to the type runtime expects + Expression fexp = flde; + if (!dgty.equals(flde.type)) + { + fexp = new CastExp(loc, flde, flde.type); + fexp.type = dgty; + } + ec = new VarExp(Loc.initial, fdapply, false); + ec = new CallExp(loc, ec, fs.aggr, fexp); + ec.type = Type.tint32; // don't run semantic() on ec + return ec; +} + +private extern(D) Expression applyAssocArray(ForeachStatement fs, Expression flde, Type tab) +{ + auto taa = tab.isTypeAArray(); + Expression ec; + const dim = fs.parameters.length; + // Check types + Parameter p = (*fs.parameters)[0]; + bool isRef = (p.storageClass & STC.ref_) != 0; + Type ta = p.type; + if (dim == 2) + { + Type ti = (isRef ? taa.index.addMod(MODFlags.const_) : taa.index); + if (isRef ? !ti.constConv(ta) : !ti.implicitConvTo(ta)) + { + fs.error("`foreach`: index must be type `%s`, not `%s`", + ti.toChars(), ta.toChars()); + return null; + } + p = (*fs.parameters)[1]; + isRef = (p.storageClass & STC.ref_) != 0; + ta = p.type; + } + Type taav = taa.nextOf(); + if (isRef ? !taav.constConv(ta) : !taav.implicitConvTo(ta)) + { + fs.error("`foreach`: value must be type `%s`, not `%s`", + taav.toChars(), ta.toChars()); + return null; + } + + /* Call: + * extern(C) int _aaApply(void*, in size_t, int delegate(void*)) + * _aaApply(aggr, keysize, flde) + * + * extern(C) int _aaApply2(void*, in size_t, int delegate(void*, void*)) + * _aaApply2(aggr, keysize, flde) + */ + __gshared FuncDeclaration* fdapply = [null, null]; + __gshared TypeDelegate* fldeTy = [null, null]; + ubyte i = (dim == 2 ? 1 : 0); + if (!fdapply[i]) + { + auto params = new Parameters(); + params.push(new Parameter(0, Type.tvoid.pointerTo(), null, null, null)); + params.push(new Parameter(STC.const_, Type.tsize_t, null, null, null)); + auto dgparams = new Parameters(); + dgparams.push(new Parameter(0, Type.tvoidptr, null, null, null)); + if (dim == 2) + dgparams.push(new Parameter(0, Type.tvoidptr, null, null, null)); + fldeTy[i] = new TypeDelegate(new TypeFunction(ParameterList(dgparams), Type.tint32, LINK.d)); + params.push(new Parameter(0, fldeTy[i], null, null, null)); + fdapply[i] = FuncDeclaration.genCfunc(params, Type.tint32, i ? Id._aaApply2 : Id._aaApply); + } + + auto exps = new Expressions(); + exps.push(fs.aggr); + auto keysize = taa.index.size(); + if (keysize == SIZE_INVALID) + return null; + assert(keysize < keysize.max - target.ptrsize); + keysize = (keysize + (target.ptrsize - 1)) & ~(target.ptrsize - 1); + // paint delegate argument to the type runtime expects + Expression fexp = flde; + if (!fldeTy[i].equals(flde.type)) + { + fexp = new CastExp(fs.loc, flde, flde.type); + fexp.type = fldeTy[i]; + } + exps.push(new IntegerExp(Loc.initial, keysize, Type.tsize_t)); + exps.push(fexp); + ec = new VarExp(Loc.initial, fdapply[i], false); + ec = new CallExp(fs.loc, ec, exps); + ec.type = Type.tint32; // don't run semantic() on ec + return ec; +} + +private extern(D) Statement loopReturn(Expression e, Statements* cases, const ref Loc loc) +{ + if (!cases.length) + { + // Easy case, a clean exit from the loop + e = new CastExp(loc, e, Type.tvoid); // https://issues.dlang.org/show_bug.cgi?id=13899 + return new ExpStatement(loc, e); + } + // Construct a switch statement around the return value + // of the apply function. + Statement s; + auto a = new Statements(); + + // default: break; takes care of cases 0 and 1 + s = new BreakStatement(Loc.initial, null); + s = new DefaultStatement(Loc.initial, s); + a.push(s); + + // cases 2... + foreach (i, c; *cases) + { + s = new CaseStatement(Loc.initial, new IntegerExp(i + 2), c); + a.push(s); + } + + s = new CompoundStatement(loc, a); + return new SwitchStatement(loc, e, s, false); +} + +/************************************* + * Turn foreach body into the function literal: + * int delegate(ref T param) { body } + * Params: + * sc = context + * fs = ForeachStatement + * tfld = type of function literal to be created (type of opApply() function if any), can be null + * Returns: + * Function literal created, as an expression + * null if error. + */ +private FuncExp foreachBodyToFunction(Scope* sc, ForeachStatement fs, TypeFunction tfld, + /*IN_LLVM*/ bool enforceSizeTIndex) +{ + auto params = new Parameters(); + foreach (i, p; *fs.parameters) + { + StorageClass stc = STC.ref_ | (p.storageClass & STC.scope_); + Identifier id; + + p.type = p.type.typeSemantic(fs.loc, sc); + p.type = p.type.addStorageClass(p.storageClass); +version (IN_LLVM) +{ + // Type of parameter may be different; see below + auto para_type = p.type; +} + 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_)) + { + if (!(prm.storageClass & STC.ref_)) + { + fs.error("`foreach`: cannot make `%s` `ref`", p.ident.toChars()); + return null; + } + goto LcopyArg; + } + id = p.ident; // argument copy is not need. + } + else if (p.storageClass & STC.ref_) + { + // default delegate parameters are marked as ref, then + // argument copy is not need. + id = p.ident; + } + else + { + // Make a copy of the ref argument so it isn't + // a reference. + LcopyArg: + id = Identifier.generateId("__applyArg", cast(int)i); + +version (IN_LLVM) +{ + // In case of a foreach loop on an array the index passed + // to the delegate is always of type size_t. The type of + // the parameter must be changed to size_t and a cast to + // the type used must be inserted. Otherwise the index is + // always 0 on a big endian architecture. This fixes + // issue #326. + Initializer ie; + if (fs.parameters.length == 2 && i == 0 && enforceSizeTIndex) + { + para_type = Type.tsize_t; + ie = new ExpInitializer(fs.loc, + new CastExp(fs.loc, + new IdentifierExp(fs.loc, id), p.type)); + } + else + { + ie = new ExpInitializer(fs.loc, new IdentifierExp(fs.loc, id)); + } +} +else +{ + Initializer ie = new ExpInitializer(fs.loc, new IdentifierExp(fs.loc, id)); +} + auto v = new VarDeclaration(fs.loc, p.type, p.ident, ie); + v.storage_class |= STC.temp | (stc & STC.scope_); + Statement s = new ExpStatement(fs.loc, v); + fs._body = new CompoundStatement(fs.loc, s, fs._body); + } + params.push(new Parameter(stc, IN_LLVM ? para_type : p.type, id, null, null)); + } + // https://issues.dlang.org/show_bug.cgi?id=13840 + // Throwable nested function inside nothrow function is acceptable. + StorageClass stc = mergeFuncAttrs(STC.safe | STC.pure_ | STC.nogc, fs.func); + auto tf = new TypeFunction(ParameterList(params), Type.tint32, LINK.d, stc); + fs.cases = new Statements(); + fs.gotos = new ScopeStatements(); + auto fld = new FuncLiteralDeclaration(fs.loc, fs.endloc, tf, TOK.delegate_, fs); + fld.fbody = fs._body; + Expression flde = new FuncExp(fs.loc, fld); + flde = flde.expressionSemantic(sc); + fld.tookAddressOf = 0; + if (flde.op == EXP.error) + return null; + return cast(FuncExp)flde; +} + + void catchSemantic(Catch c, Scope* sc) { //printf("Catch::semantic(%s)\n", ident.toChars()); @@ -4833,8 +4874,8 @@ private Statements* flatten(Statement statement, Scope* sc) (*a)[0] = ls; return a; - case STMT.Compile: - auto cs = statement.isCompileStatement(); + case STMT.Mixin: + auto cs = statement.isMixinStatement(); OutBuffer buf; @@ -4845,13 +4886,16 @@ private Statements* flatten(Statement statement, Scope* sc) const len = buf.length; buf.writeByte(0); const str = buf.extractSlice()[0 .. len]; - scope p = new Parser!ASTCodegen(cs.loc, sc._module, str, false, global.errorSink); + const bool doUnittests = global.params.useUnitTests || global.params.ddoc.doOutput || global.params.dihdr.doOutput; + 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.vin; p.nextToken(); auto a = new Statements(); while (p.token.value != TOK.endOfFile) { - Statement s = p.parseStatement(ParseStatementFlags.semi | ParseStatementFlags.curlyScope); + Statement s = p.parseStatement(ParseStatementFlags.curlyScope); if (!s || global.errors != errors) return errorStatements(); a.push(s); diff --git a/dmd/strictvisitor.d b/dmd/strictvisitor.d index 7c2f6ae89e4..82e91c6aa76 100644 --- a/dmd/strictvisitor.d +++ b/dmd/strictvisitor.d @@ -45,7 +45,7 @@ extern(C++) class StrictVisitor(AST) : ParseTimeVisitor!AST override void visit(AST.TemplateDeclaration) { assert(0); } override void visit(AST.TemplateInstance) { assert(0); } override void visit(AST.Nspace) { assert(0); } - override void visit(AST.CompileDeclaration) { assert(0); } + override void visit(AST.MixinDeclaration) { assert(0); } override void visit(AST.UserAttributeDeclaration) { assert(0); } override void visit(AST.LinkDeclaration) { assert(0); } override void visit(AST.AnonDeclaration) { assert(0); } @@ -71,7 +71,7 @@ extern(C++) class StrictVisitor(AST) : ParseTimeVisitor!AST override void visit(AST.ReturnStatement) { assert(0); } override void visit(AST.LabelStatement) { assert(0); } override void visit(AST.StaticAssertStatement) { assert(0); } - override void visit(AST.CompileStatement) { assert(0); } + override void visit(AST.MixinStatement) { assert(0); } override void visit(AST.WhileStatement) { assert(0); } override void visit(AST.ForStatement) { assert(0); } override void visit(AST.DoStatement) { assert(0); } diff --git a/dmd/target.d b/dmd/target.d index e08cfd27a81..bcbe11fd0cd 100644 --- a/dmd/target.d +++ b/dmd/target.d @@ -25,7 +25,7 @@ module dmd.target; -import dmd.globals : Param; +import dmd.globals : Param, CHECKENABLE; version (IN_LLVM) { @@ -91,6 +91,195 @@ ubyte defaultTargetOSMajor() return 0; } +version (IN_LLVM) {} else +{ + +/** + * Add default `version` identifier for dmd, and set the + * target platform in `params`. + * https://dlang.org/spec/version.html#predefined-versions + * + * Needs to be run after all arguments parsing (command line, DFLAGS environment + * variable and config file) in order to add final flags (such as `X86_64` or + * the `CRuntime` used). + * + * Params: + * params = which target to compile for (set by `setTarget()`) + * tgt = target + */ +public +void addDefaultVersionIdentifiers(const ref Param params, const ref Target tgt) +{ + import dmd.cond : VersionCondition; + import dmd.dmdparams : driverParams, PIC; + + VersionCondition.addPredefinedGlobalIdent("DigitalMars"); + VersionCondition.addPredefinedGlobalIdent("LittleEndian"); + VersionCondition.addPredefinedGlobalIdent("D_Version2"); + VersionCondition.addPredefinedGlobalIdent("all"); + + addPredefinedGlobalIdentifiers(tgt); + + if (params.ddoc.doOutput) + VersionCondition.addPredefinedGlobalIdent("D_Ddoc"); + if (params.cov) + VersionCondition.addPredefinedGlobalIdent("D_Coverage"); + if (driverParams.pic != PIC.fixed) + VersionCondition.addPredefinedGlobalIdent(driverParams.pic == PIC.pic ? "D_PIC" : "D_PIE"); + if (params.useUnitTests) + VersionCondition.addPredefinedGlobalIdent("unittest"); + if (params.useAssert == CHECKENABLE.on) + VersionCondition.addPredefinedGlobalIdent("assert"); + if (params.useIn == CHECKENABLE.on) + VersionCondition.addPredefinedGlobalIdent("D_PreConditions"); + if (params.useOut == CHECKENABLE.on) + VersionCondition.addPredefinedGlobalIdent("D_PostConditions"); + if (params.useInvariants == CHECKENABLE.on) + VersionCondition.addPredefinedGlobalIdent("D_Invariants"); + if (params.useArrayBounds == CHECKENABLE.off) + VersionCondition.addPredefinedGlobalIdent("D_NoBoundsChecks"); + if (params.betterC) + { + VersionCondition.addPredefinedGlobalIdent("D_BetterC"); + } + else + { + VersionCondition.addPredefinedGlobalIdent("D_ModuleInfo"); + VersionCondition.addPredefinedGlobalIdent("D_Exceptions"); + VersionCondition.addPredefinedGlobalIdent("D_TypeInfo"); + } + + VersionCondition.addPredefinedGlobalIdent("D_HardFloat"); + + if (params.tracegc) + VersionCondition.addPredefinedGlobalIdent("D_ProfileGC"); + + if (driverParams.optimize) + VersionCondition.addPredefinedGlobalIdent("D_Optimized"); +} + +// /** +// * Add predefined global identifiers that are determied by the target +// */ +private +void addPredefinedGlobalIdentifiers(const ref Target tgt) +{ + import dmd.cond : VersionCondition; + + alias predef = VersionCondition.addPredefinedGlobalIdent; + if (tgt.cpu >= CPU.sse2) + { + predef("D_SIMD"); + if (tgt.cpu >= CPU.avx) + predef("D_AVX"); + if (tgt.cpu >= CPU.avx2) + predef("D_AVX2"); + } + + with (Target) + { + if (tgt.os & OS.Posix) + predef("Posix"); + if (tgt.os & (OS.linux | OS.FreeBSD | OS.OpenBSD | OS.DragonFlyBSD | OS.Solaris)) + predef("ELFv1"); + switch (tgt.os) + { + case OS.none: { predef("FreeStanding"); break; } + case OS.linux: { predef("linux"); break; } + case OS.OpenBSD: { predef("OpenBSD"); break; } + case OS.DragonFlyBSD: { predef("DragonFlyBSD"); break; } + case OS.Solaris: { predef("Solaris"); break; } + case OS.Windows: + { + predef("Windows"); + VersionCondition.addPredefinedGlobalIdent(tgt.is64bit ? "Win64" : "Win32"); + break; + } + case OS.OSX: + { + predef("OSX"); + // For legacy compatibility + predef("darwin"); + break; + } + case OS.FreeBSD: + { + predef("FreeBSD"); + switch (tgt.osMajor) + { + case 10: predef("FreeBSD_10"); break; + case 11: predef("FreeBSD_11"); break; + case 12: predef("FreeBSD_12"); break; + case 13: predef("FreeBSD_13"); break; + default: predef("FreeBSD_11"); break; + } + break; + } + default: assert(0); + } + } + + addCRuntimePredefinedGlobalIdent(tgt.c); + addCppRuntimePredefinedGlobalIdent(tgt.cpp); + + if (tgt.is64bit) + { + VersionCondition.addPredefinedGlobalIdent("D_InlineAsm_X86_64"); + VersionCondition.addPredefinedGlobalIdent("X86_64"); + } + else + { + VersionCondition.addPredefinedGlobalIdent("D_InlineAsm"); //legacy + VersionCondition.addPredefinedGlobalIdent("D_InlineAsm_X86"); + VersionCondition.addPredefinedGlobalIdent("X86"); + } + if (tgt.isLP64) + VersionCondition.addPredefinedGlobalIdent("D_LP64"); + else if (tgt.is64bit) + VersionCondition.addPredefinedGlobalIdent("X32"); +} + +private +void addCRuntimePredefinedGlobalIdent(const ref TargetC c) +{ + import dmd.cond : VersionCondition; + + alias predef = VersionCondition.addPredefinedGlobalIdent; + with (TargetC.Runtime) switch (c.runtime) + { + default: + case Unspecified: return; + case Bionic: return predef("CRuntime_Bionic"); + case DigitalMars: return predef("CRuntime_DigitalMars"); + case Glibc: return predef("CRuntime_Glibc"); + case Microsoft: return predef("CRuntime_Microsoft"); + case Musl: return predef("CRuntime_Musl"); + case Newlib: return predef("CRuntime_Newlib"); + case UClibc: return predef("CRuntime_UClibc"); + case WASI: return predef("CRuntime_WASI"); + } +} + +private +void addCppRuntimePredefinedGlobalIdent(const ref TargetCPP cpp) +{ + import dmd.cond : VersionCondition; + + alias predef = VersionCondition.addPredefinedGlobalIdent; + with (TargetCPP.Runtime) switch (cpp.runtime) + { + default: + case Unspecified: return; + case Clang: return predef("CppRuntime_Clang"); + case DigitalMars: return predef("CppRuntime_DigitalMars"); + case Gcc: return predef("CppRuntime_Gcc"); + case Microsoft: return predef("CppRuntime_Microsoft"); + case Sun: return predef("CppRuntime_Sun"); + } +} + +} // !IN_LLVM + //////////////////////////////////////////////////////////////////////////////// /** * Describes a back-end target. At present it is incomplete, but in the future diff --git a/dmd/target.h b/dmd/target.h index 15d90658e6e..09236391931 100644 --- a/dmd/target.h +++ b/dmd/target.h @@ -98,11 +98,11 @@ struct TargetCPP Microsoft, Sun }; - bool reverseOverloads; // with dmc and cl, overloaded functions are grouped and in reverse order - bool exceptions; // set if catching C++ exceptions is supported - bool twoDtorInVtable; // target C++ ABI puts deleting and non-deleting destructor into vtable - bool splitVBasetable; // set if C++ ABI uses separate tables for virtual functions and virtual bases - bool wrapDtorInExternD; // set if C++ dtors require a D wrapper to be callable from runtime + d_bool reverseOverloads; // with dmc and cl, overloaded functions are grouped and in reverse order + d_bool exceptions; // set if catching C++ exceptions is supported + d_bool twoDtorInVtable; // target C++ ABI puts deleting and non-deleting destructor into vtable + d_bool splitVBasetable; // set if C++ ABI uses separate tables for virtual functions and virtual bases + d_bool wrapDtorInExternD; // set if C++ dtors require a D wrapper to be callable from runtime #if !IN_LLVM Runtime runtime; #endif @@ -118,7 +118,7 @@ struct TargetCPP struct TargetObjC { - bool supported; // set if compiler can interface with Objective-C + d_bool supported; // set if compiler can interface with Objective-C }; struct Target @@ -167,15 +167,15 @@ struct Target DString architectureName; // name of the platform architecture (e.g. X86_64) CPU cpu; // CPU instruction set to target - bool is64bit; // generate 64 bit code for x86_64; true by default for 64 bit dmd - bool isLP64; // pointers are 64 bits + d_bool is64bit; // generate 64 bit code for x86_64; true by default for 64 bit dmd + d_bool isLP64; // pointers are 64 bits // Environmental DString obj_ext; /// extension for object files DString lib_ext; /// extension for static library files DString dll_ext; /// extension for dynamic library files - bool run_noext; /// allow -run sources without extensions - bool omfobj; /// for Win32: write OMF object files instead of COFF + d_bool run_noext; /// allow -run sources without extensions + d_bool omfobj; /// for Win32: write OMF object files instead of COFF template struct FPTypeProperties diff --git a/dmd/template.h b/dmd/template.h index 09db40fd126..2150233ea3b 100644 --- a/dmd/template.h +++ b/dmd/template.h @@ -78,12 +78,12 @@ class TemplateDeclaration final : public ScopeDsymbol Dsymbol *onemember; // if !=NULL then one member of this template - bool literal; // this template declaration is a literal - bool ismixin; // template declaration is only to be used as a mixin - bool isstatic; // this is static template declaration - bool isTrivialAliasSeq; // matches `template AliasSeq(T...) { alias AliasSeq = T; } - bool isTrivialAlias; // matches pattern `template Alias(T) { alias Alias = qualifiers(T); }` - bool deprecated_; // this template declaration is deprecated + d_bool literal; // this template declaration is a literal + d_bool ismixin; // template declaration is only to be used as a mixin + d_bool isstatic; // this is static template declaration + d_bool isTrivialAliasSeq; // matches `template AliasSeq(T...) { alias AliasSeq = T; } + d_bool isTrivialAlias; // matches pattern `template Alias(T) { alias Alias = qualifiers(T); }` + d_bool deprecated_; // this template declaration is deprecated Visibility visibility; TemplatePrevious *previous; // threaded list of previous instantiation attempts on stack @@ -137,7 +137,7 @@ class TemplateParameter : public ASTNode * A dependent template parameter should return MATCHexact in matchArg() * to respect the match level of the corresponding precedent parameter. */ - bool dependent; + d_bool dependent; virtual TemplateTypeParameter *isTemplateTypeParameter(); virtual TemplateValueParameter *isTemplateValueParameter(); diff --git a/dmd/tokens.d b/dmd/tokens.d index aec3a77dee8..352c89ef164 100644 --- a/dmd/tokens.d +++ b/dmd/tokens.d @@ -274,6 +274,7 @@ enum TOK : ubyte __cdecl, __declspec, __stdcall, + __thread, __pragma, __int128, __attribute__, @@ -289,8 +290,6 @@ enum EXP : ubyte cast_, null_, assert_, - true_, - false_, array, call, address, @@ -307,13 +306,10 @@ enum EXP : ubyte dotType, slice, arrayLength, - version_, dollar, template_, dotTemplateDeclaration, declaration, - typeof_, - pragma_, dSymbol, typeid_, uadd, @@ -394,13 +390,11 @@ enum EXP : ubyte int64, float64, complex80, - char_, import_, delegate_, function_, mixin_, in_, - default_, break_, continue_, goto_, @@ -414,7 +408,6 @@ enum EXP : ubyte moduleString, // __MODULE__ functionString, // __FUNCTION__ prettyFunction, // __PRETTY_FUNCTION__ - shared_, pow, powAssign, vector, @@ -424,10 +417,11 @@ enum EXP : ubyte showCtfeContext, objcClassReference, vectorArray, - arrow, // -> compoundLiteral, // ( type-name ) { initializer-list } _Generic, interval, + + loweredAssignExp, } enum FirstCKeyword = TOK.inline; @@ -586,6 +580,7 @@ private immutable TOK[] keywords = TOK.__cdecl, TOK.__declspec, TOK.__stdcall, + TOK.__thread, TOK.__pragma, TOK.__int128, TOK.__attribute__, @@ -617,7 +612,7 @@ static immutable TOK[TOK.max + 1] Ckeywords = union_, unsigned, void_, volatile, while_, asm_, typeof_, _Alignas, _Alignof, _Atomic, _Bool, _Complex, _Generic, _Imaginary, _Noreturn, _Static_assert, _Thread_local, - _import, __cdecl, __declspec, __stdcall, __pragma, __int128, __attribute__, + _import, __cdecl, __declspec, __stdcall, __thread, __pragma, __int128, __attribute__, _assert ]; foreach (kw; Ckwds) @@ -889,6 +884,7 @@ extern (C++) struct Token TOK.__cdecl : "__cdecl", TOK.__declspec : "__declspec", TOK.__stdcall : "__stdcall", + TOK.__thread : "__thread", TOK.__pragma : "__pragma", TOK.__int128 : "__int128", TOK.__attribute__ : "__attribute__", diff --git a/dmd/tokens.h b/dmd/tokens.h index 87361f327a4..6c1b9792dbb 100644 --- a/dmd/tokens.h +++ b/dmd/tokens.h @@ -283,6 +283,7 @@ enum class TOK : unsigned char cdecl_, declspec, stdcall, + thread, pragma, int128_, attribute__, @@ -299,8 +300,6 @@ enum class EXP : unsigned char cast_, null_, assert_, - true_, - false_, array, call, address, @@ -317,13 +316,10 @@ enum class EXP : unsigned char dotType, slice, arrayLength, - version_, dollar, template_, dotTemplateDeclaration, declaration, - typeof_, - pragma_, dSymbol, typeid_, uadd, @@ -404,13 +400,11 @@ enum class EXP : unsigned char int64, float64, complex80, - char_, import_, delegate_, function_, mixin_, in_, - default_, break_, continue_, goto_, @@ -424,7 +418,6 @@ enum class EXP : unsigned char moduleString, // __MODULE__ functionString, // __FUNCTION__ prettyFunction, // __PRETTY_FUNCTION__ - shared_, pow, powAssign, vector, @@ -434,7 +427,6 @@ enum class EXP : unsigned char showCtfeContext, objcClassReference, vectorArray, - arrow, // -> compoundLiteral, // ( type-name ) { initializer-list } _Generic_, interval, diff --git a/dmd/traits.d b/dmd/traits.d index 989e978b8e9..f5f6283f604 100644 --- a/dmd/traits.d +++ b/dmd/traits.d @@ -118,55 +118,40 @@ ulong getTypePointerBitmap(Loc loc, Type t, Array!(ulong)* data) data.setDim(cast(size_t)cntdata); data.zero(); - extern (C++) final class PointerBitmapVisitor : Visitor - { - alias visit = Visitor.visit; - public: - extern (D) this(Array!(ulong)* _data, ulong _sz_size_t) scope - { - this.data = _data; - this.sz_size_t = _sz_size_t; - } + ulong offset; + bool error; + void visit(Type t) + { void setpointer(ulong off) { ulong ptroff = off / sz_size_t; (*data)[cast(size_t)(ptroff / (8 * sz_size_t))] |= 1L << (ptroff % (8 * sz_size_t)); } - override void visit(Type t) + void visitType(Type t) { Type tb = t.toBasetype(); if (tb != t) - tb.accept(this); - } - - override void visit(TypeError t) - { - visit(cast(Type)t); + visit(tb); } - override void visit(TypeNext t) + void visitError(TypeError t) { - assert(0); + visitType(t); } - override void visit(TypeBasic t) + void visitBasic(TypeBasic t) { if (t.ty == Tvoid) setpointer(offset); } - override void visit(TypeVector t) + void visitVector(TypeVector t) { } - override void visit(TypeArray t) - { - assert(0); - } - - override void visit(TypeSArray t) + void visitSArray(TypeSArray t) { ulong arrayoff = offset; ulong nextsize = t.next.size(); @@ -176,95 +161,67 @@ ulong getTypePointerBitmap(Loc loc, Type t, Array!(ulong)* data) for (ulong i = 0; i < dim; i++) { offset = arrayoff + i * nextsize; - t.next.accept(this); + visit(t.next); } offset = arrayoff; } - override void visit(TypeDArray t) + void visitDArray(TypeDArray t) { setpointer(offset + sz_size_t); } // dynamic array is {length,ptr} - override void visit(TypeAArray t) + void visitAArray(TypeAArray t) { setpointer(offset); } - override void visit(TypePointer t) + void visitPointer(TypePointer t) { if (t.nextOf().ty != Tfunction) // don't mark function pointers setpointer(offset); } - override void visit(TypeReference t) + void visitReference(TypeReference t) { setpointer(offset); } - override void visit(TypeClass t) + void visitClass(TypeClass t) { setpointer(offset); } - override void visit(TypeFunction t) + void visitFunction(TypeFunction t) { } - override void visit(TypeDelegate t) + void visitDelegate(TypeDelegate t) { setpointer(offset); } - // delegate is {context, function} - override void visit(TypeQualified t) + void visitEnum(TypeEnum t) { - assert(0); + visitType(t); } - // assume resolved - override void visit(TypeIdentifier t) + void visitTuple(TypeTuple t) { - assert(0); - } - - override void visit(TypeInstance t) - { - assert(0); + visitType(t); } - override void visit(TypeTypeof t) + void visitNull(TypeNull t) { - assert(0); - } - - override void visit(TypeReturn t) - { - assert(0); - } - - override void visit(TypeEnum t) - { - visit(cast(Type)t); - } - - override void visit(TypeTuple t) - { - visit(cast(Type)t); - } - - override void visit(TypeSlice t) - { - assert(0); + // always a null pointer } - override void visit(TypeNull t) + void visitNoreturn(TypeNoreturn t) { - // always a null pointer } - override void visit(TypeStruct t) + void visitStruct(TypeStruct t) { ulong structoff = offset; foreach (v; t.sym.fields) @@ -273,38 +230,43 @@ ulong getTypePointerBitmap(Loc loc, Type t, Array!(ulong)* data) if (v.type.ty == Tclass) setpointer(offset); else - v.type.accept(this); + visit(v.type); } offset = structoff; } + void visitDefaultCase(Type t) + { + //printf("ty = %d\n", t.ty); + assert(0); + } + + mixin VisitType!void visit; + visit.VisitType(t); + } + + if (auto tc = t.isTypeClass()) + { // a "toplevel" class is treated as an instance, while TypeClass fields are treated as references - void visitClass(TypeClass t) + void visitTopLevelClass(TypeClass t) { ulong classoff = offset; // skip vtable-ptr and monitor if (t.sym.baseClass) - visitClass(cast(TypeClass)t.sym.baseClass.type); + visitTopLevelClass(t.sym.baseClass.type.isTypeClass()); foreach (v; t.sym.fields) { offset = classoff + v.offset; - v.type.accept(this); + visit(v.type); } offset = classoff; } - Array!(ulong)* data; - ulong offset; - ulong sz_size_t; - bool error; + visitTopLevelClass(tc); } - - scope PointerBitmapVisitor pbv = new PointerBitmapVisitor(data, sz_size_t); - if (t.ty == Tclass) - pbv.visitClass(cast(TypeClass)t); else - t.accept(pbv); - return pbv.error ? ulong.max : sz; + visit(t); + return error ? ulong.max : sz; } /** @@ -985,15 +947,24 @@ Expression semanticTraits(TraitsExp e, Scope* sc) */ Dsymbol sym = getDsymbol(o); + + if (sym && e.ident == Id.hasMember) + { + if (auto sm = sym.search(e.loc, id)) + return True(); + + // https://issues.dlang.org/show_bug.cgi?id=23951 + if (auto decl = sym.isDeclaration()) + { + ex = typeDotIdExp(e.loc, decl.type, id); + goto doSemantic; + } + } + if (auto t = isType(o)) ex = typeDotIdExp(e.loc, t, id); else if (sym) { - if (e.ident == Id.hasMember) - { - if (auto sm = sym.search(e.loc, id)) - return True(); - } ex = new DsymbolExp(e.loc, sym); ex = new DotIdExp(e.loc, ex, id); } @@ -1004,7 +975,7 @@ Expression semanticTraits(TraitsExp e, Scope* sc) e.error("invalid first argument"); return ErrorExp.get(); } - + doSemantic: // ignore symbol visibility and disable access checks for these traits Scope* scx = sc.push(); scx.flags |= SCOPE.ignoresymbolvisibility | SCOPE.noaccesscheck; @@ -1261,7 +1232,7 @@ Expression semanticTraits(TraitsExp e, Scope* sc) // @@@DEPRECATION 2.100.2 if (auto td = s.isTemplateDeclaration()) { - if (td.overnext || td.funcroot) + if (td.overnext || td.overroot) { deprecation(e.loc, "`__traits(getAttributes)` may only be used for individual functions, not overload sets such as: `%s`", td.ident.toChars()); deprecationSupplemental(e.loc, "the result of `__traits(getOverloads)` may be used to select the desired function to extract attributes from"); @@ -1314,6 +1285,19 @@ Expression semanticTraits(TraitsExp e, Scope* sc) return ErrorExp.get(); } + // https://issues.dlang.org/show_bug.cgi?id=19706 + // When getting the attributes of the instance of a + // templated member function semantic tiargs does + // not perform semantic3 on the instance. + // For more information see FuncDeclaration.functionSemantic. + // For getFunctionAttributes it is mandatory to do + // attribute inference. + if (fd && fd.parent && fd.parent.isTemplateInstance) + { + fd.functionSemantic3(); + tf = cast(TypeFunction)fd.type; + } + auto mods = new Expressions(); void addToMods(string str) @@ -1354,6 +1338,7 @@ Expression semanticTraits(TraitsExp e, Scope* sc) * "argptr" extern(D) void dstyle(...), use `__argptr` and `__arguments` * "stdarg" extern(C) void cstyle(int, ...), use core.stdc.stdarg * "typesafe" void typesafe(T[] ...) + * "KR" old K+R style */ // get symbol linkage as a string if (dim != 1) @@ -1388,6 +1373,7 @@ Expression semanticTraits(TraitsExp e, Scope* sc) case VarArg.variadic: style = (link == LINK.d) ? "argptr" : "stdarg"; break; + case VarArg.KRvariadic: style = "KR"; break; case VarArg.typesafe: style = "typesafe"; break; } auto se = new StringExp(e.loc, style); @@ -2254,65 +2240,65 @@ private void traitNotFound(TraitsExp e) // All possible traits __gshared Identifier*[59] idents = [ + &Id.allMembers, + &Id.child, + &Id.classInstanceAlignment, + &Id.classInstanceSize, + &Id.compiles, + &Id.derivedMembers, + &Id.fullyQualifiedName, + &Id.getAliasThis, + &Id.getAttributes, + &Id.getFunctionAttributes, + &Id.getFunctionVariadicStyle, + &Id.getLinkage, + &Id.getLocation, + &Id.getMember, + &Id.getOverloads, + &Id.getParameterStorageClasses, + &Id.getPointerBitmap, + &Id.getProtection, + &Id.getTargetInfo, + &Id.getUnitTests, + &Id.getVirtualFunctions, + &Id.getVirtualIndex, + &Id.getVirtualMethods, + &Id.getVisibility, + &Id.hasCopyConstructor, + &Id.hasMember, + &Id.hasPostblit, + &Id.identifier, &Id.isAbstractClass, + &Id.isAbstractFunction, &Id.isArithmetic, &Id.isAssociativeArray, - &Id.isDisabled, + &Id.isCopyable, &Id.isDeprecated, - &Id.isFuture, + &Id.isDisabled, &Id.isFinalClass, - &Id.isPOD, - &Id.isNested, + &Id.isFinalFunction, &Id.isFloating, + &Id.isFuture, &Id.isIntegral, - &Id.isScalar, - &Id.isStaticArray, - &Id.isUnsigned, - &Id.isVirtualFunction, - &Id.isVirtualMethod, - &Id.isAbstractFunction, - &Id.isFinalFunction, - &Id.isOverrideFunction, - &Id.isStaticFunction, + &Id.isLazy, &Id.isModule, + &Id.isNested, + &Id.isOut, + &Id.isOverrideFunction, &Id.isPackage, + &Id.isPOD, &Id.isRef, - &Id.isOut, - &Id.isLazy, &Id.isReturnOnStack, - &Id.hasMember, - &Id.identifier, - &Id.fullyQualifiedName, - &Id.getProtection, - &Id.getVisibility, - &Id.parent, - &Id.child, - &Id.getLinkage, - &Id.getMember, - &Id.getOverloads, - &Id.getVirtualFunctions, - &Id.getVirtualMethods, - &Id.classInstanceSize, - &Id.classInstanceAlignment, - &Id.allMembers, - &Id.derivedMembers, &Id.isSame, - &Id.compiles, - &Id.getAliasThis, - &Id.getAttributes, - &Id.getFunctionAttributes, - &Id.getFunctionVariadicStyle, - &Id.getParameterStorageClasses, - &Id.getUnitTests, - &Id.getVirtualIndex, - &Id.getPointerBitmap, + &Id.isScalar, + &Id.isStaticArray, + &Id.isStaticFunction, + &Id.isUnsigned, + &Id.isVirtualFunction, + &Id.isVirtualMethod, &Id.isZeroInit, - &Id.getTargetInfo, - &Id.getLocation, - &Id.hasPostblit, - &Id.hasCopyConstructor, - &Id.isCopyable, &Id.parameters, + &Id.parent, ]; StringTable!(bool)* stringTable = cast(StringTable!(bool)*) &traitsStringTable; diff --git a/dmd/transitivevisitor.d b/dmd/transitivevisitor.d index 5844911bc6a..c58827063d2 100644 --- a/dmd/transitivevisitor.d +++ b/dmd/transitivevisitor.d @@ -44,9 +44,9 @@ package mixin template ParseVisitMethods(AST) } } - override void visit(AST.CompileStatement s) + override void visit(AST.MixinStatement s) { - //printf("Visiting CompileStatement\n"); + //printf("Visiting MixinStatement\n"); visitArgs(s.exps.peekSlice()); } @@ -579,7 +579,7 @@ package mixin template ParseVisitMethods(AST) de.accept(this); } - override void visit(AST.CompileDeclaration d) + override void visit(AST.MixinDeclaration d) { //printf("Visiting compileDeclaration\n"); visitArgs(d.exps.peekSlice()); diff --git a/dmd/typesem.d b/dmd/typesem.d index c668199e5f7..f0decf2a48d 100644 --- a/dmd/typesem.d +++ b/dmd/typesem.d @@ -290,6 +290,8 @@ private void resolveHelper(TypeQualified mt, const ref Loc loc, Scope* sc, Dsymb if (!sm) return helper3(); + if (sm.isAliasDeclaration) + sm.checkDeprecated(loc, sc); s = sm.toAlias(); } @@ -456,6 +458,17 @@ extern(C++) Type typeSemantic(Type type, const ref Loc loc, Scope* sc) return t.merge(); } + Type visitComplex(TypeBasic t) + { + if (!(sc.flags & SCOPE.Cfile)) + return visitType(t); + + auto tc = getComplexLibraryType(loc, sc, t.ty); + if (tc.ty == Terror) + return tc; + return tc.addMod(t.mod).merge(); + } + Type visitVector(TypeVector mtype) { const errors = global.errors; @@ -1185,19 +1198,31 @@ extern(C++) Type typeSemantic(Type type, const ref Loc loc, Scope* sc) // -preview=in: Always add `ref` when used with `extern(C++)` functions // Done here to allow passing opaque types with `in` - if (global.params.previewIn && (fparam.storageClass & (STC.in_ | STC.ref_)) == STC.in_) + if ((fparam.storageClass & (STC.in_ | STC.ref_)) == STC.in_) { switch (tf.linkage) { case LINK.cpp: - fparam.storageClass |= STC.ref_; + if (global.params.previewIn) + fparam.storageClass |= STC.ref_; break; case LINK.default_, LINK.d: break; default: - .error(loc, "cannot use `in` parameters with `extern(%s)` functions", - linkageToChars(tf.linkage)); - .errorSupplemental(loc, "parameter `%s` declared as `in` here", fparam.toChars()); + if (global.params.previewIn) + { + .error(loc, "cannot use `in` parameters with `extern(%s)` functions", + linkageToChars(tf.linkage)); + .errorSupplemental(loc, "parameter `%s` declared as `in` here", fparam.toChars()); + } + else + { + // Note that this deprecation will not trigger on `in ref` / `ref in` + // parameters, however the parser will trigger a deprecation on them. + .deprecation(loc, "using `in` parameters with `extern(%s)` functions is deprecated", + linkageToChars(tf.linkage)); + .deprecationSupplemental(loc, "parameter `%s` declared as `in` here", fparam.toChars()); + } break; } } @@ -1292,28 +1317,6 @@ extern(C++) Type typeSemantic(Type type, const ref Loc loc, Scope* sc) // error(loc, "inout on parameter means inout must be on return type as well (if from D1 code, replace with `ref`)"); } - /* Scope attribute is not necessary if the parameter type does not have pointers - */ - const sr = buildScopeRef(fparam.storageClass); - switch (sr) - { - case ScopeRef.Scope: - case ScopeRef.RefScope: - case ScopeRef.ReturnRef_Scope: - if (!fparam.type.hasPointers()) - fparam.storageClass &= ~STC.scope_; - break; - - case ScopeRef.ReturnScope: - case ScopeRef.Ref_ReturnScope: - if (!fparam.type.hasPointers()) - fparam.storageClass &= ~(STC.return_ | STC.scope_ | STC.returnScope); - break; - - default: - break; - } - // Remove redundant storage classes for type, they are already applied fparam.storageClass &= ~(STC.TYPECTOR); @@ -1786,12 +1789,14 @@ extern(C++) Type typeSemantic(Type type, const ref Loc loc, Scope* sc) case TOK.struct_: auto sd = new StructDeclaration(mtype.loc, mtype.id, false); + sd.alignment = mtype.packalign; declare(sd); mtype.resolved = visitStruct(new TypeStruct(sd)); break; case TOK.union_: auto ud = new UnionDeclaration(mtype.loc, mtype.id); + ud.alignment = mtype.packalign; declare(ud); mtype.resolved = visitStruct(new TypeStruct(ud)); break; @@ -1928,6 +1933,9 @@ extern(C++) Type typeSemantic(Type type, const ref Loc loc, Scope* sc) switch (type.ty) { default: return visitType(type); + case Tcomplex32: + case Tcomplex64: + case Tcomplex80: return visitComplex(type.isTypeBasic()); case Tvector: return visitVector(type.isTypeVector()); case Tsarray: return visitSArray(type.isTypeSArray()); case Tarray: return visitDArray(type.isTypeDArray()); @@ -2697,7 +2705,7 @@ void resolve(Type mt, const ref Loc loc, Scope* sc, out Expression pe, out Type { void semanticOnMixin(Dsymbol member) { - if (auto compileDecl = member.isCompileDeclaration()) + if (auto compileDecl = member.isMixinDeclaration()) compileDecl.dsymbolSemantic(sc); else if (auto mixinTempl = member.isTemplateMixin()) mixinTempl.dsymbolSemantic(sc); @@ -2796,8 +2804,10 @@ void resolve(Type mt, const ref Loc loc, Scope* sc, out Expression pe, out Type } mt.exp = exp2; - if (mt.exp.op == EXP.type || - mt.exp.op == EXP.scope_) + if ((mt.exp.op == EXP.type || mt.exp.op == EXP.scope_) && + // https://issues.dlang.org/show_bug.cgi?id=23863 + // 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 mt.exp.checkType()) @@ -3126,7 +3136,7 @@ void resolve(Type mt, const ref Loc loc, Scope* sc, out Expression pe, out Type * Returns: * resulting expression with e.ident resolved */ -Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, int flag) +Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag flag) { Expression visitType(Type mt) { @@ -3624,7 +3634,7 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, int flag) * template opDispatch(name) if (isValid!name) { ... } */ uint errors = gagError ? global.startGagging() : 0; - e = dti.dotTemplateSemanticProp(sc, 0); + e = dti.dotTemplateSemanticProp(sc, DotExpFlag.none); if (gagError && global.endGagging(errors)) e = null; return returnExp(e); @@ -3730,6 +3740,9 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, int flag) { return noMember(mt, sc, e, ident, flag); } + // check before alias resolution; the alias itself might be deprecated! + if (s.isAliasDeclaration) + e.checkDeprecated(sc, s); s = s.toAlias(); if (auto em = s.isEnumMember()) @@ -3917,7 +3930,7 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, int flag) return mt.getProperty(sc, e.loc, ident, flag & 1); } - Expression res = mt.sym.getMemtype(Loc.initial).dotExp(sc, e, ident, 1); + Expression res = mt.sym.getMemtype(Loc.initial).dotExp(sc, e, ident, DotExpFlag.gag); if (!(flag & 1) && !res) { if (auto ns = mt.sym.search_correct(ident)) @@ -4599,6 +4612,74 @@ extern (C++) Expression defaultInit(Type mt, const ref Loc loc, const bool isCfi } } + +/********************************************** + * Extract complex type from core.stdc.config + * Params: + * loc = for error messages + * sc = context + * ty = a complex or imaginary type + * Returns: + * Complex!float, Complex!double, Complex!real or null for error + */ + +Type getComplexLibraryType(const ref Loc loc, Scope* sc, TY ty) +{ + // singleton + __gshared Type complex_float; + __gshared Type complex_double; + __gshared Type complex_real; + + Type* pt; + Identifier id; + switch (ty) + { + case Timaginary32: + case Tcomplex32: id = Id.c_complex_float; pt = &complex_float; break; + case Timaginary64: + case Tcomplex64: id = Id.c_complex_double; pt = &complex_double; break; + case Timaginary80: + case Tcomplex80: id = Id.c_complex_real; pt = &complex_real; break; + default: + return Type.terror; + } + + if (*pt) + return *pt; + *pt = Type.terror; + + Module mConfig = Module.loadCoreStdcConfig(); + if (!mConfig) + { + error(loc, "`core.stdc.config` is required for complex numbers"); + return *pt; + } + + Dsymbol s = mConfig.searchX(Loc.initial, sc, id, IgnorePrivateImports); + if (!s) + { + error(loc, "`%s` not found in core.stdc.config", id.toChars()); + return *pt; + } + s = s.toAlias(); + if (auto t = s.getType()) + { + if (auto ts = t.toBasetype().isTypeStruct()) + { + *pt = ts; + return ts; + } + } + if (auto sd = s.isStructDeclaration()) + { + *pt = sd.type; + return sd.type; + } + + error(loc, "`%s` must be an alias for a complex struct", s.toChars()); + return *pt; +} + /******************************* Private *****************************************/ private: @@ -4909,7 +4990,7 @@ Expression getMaxMinValue(EnumDeclaration ed, const ref Loc loc, Identifier id) * Return: * null if error, else RootObject AST as parsed */ -RootObject compileTypeMixin(TypeMixin tm, Loc loc, Scope* sc) +RootObject compileTypeMixin(TypeMixin tm, ref const Loc loc, Scope* sc) { OutBuffer buf; if (expressionsToString(buf, sc, tm.exps)) @@ -4919,7 +5000,10 @@ RootObject compileTypeMixin(TypeMixin tm, Loc loc, Scope* sc) const len = buf.length; buf.writeByte(0); const str = buf.extractSlice()[0 .. len]; - scope p = new Parser!ASTCodegen(loc, sc._module, str, false, global.errorSink); + const bool doUnittests = global.params.useUnitTests || global.params.ddoc.doOutput || global.params.dihdr.doOutput; + 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.vin; p.nextToken(); //printf("p.loc.linnum = %d\n", p.loc.linnum); diff --git a/dmd/typinf.d b/dmd/typinf.d index f993262e607..805eb585e5d 100644 --- a/dmd/typinf.d +++ b/dmd/typinf.d @@ -47,6 +47,7 @@ extern (C++) void genTypeInfo(Expression e, const ref Loc loc, Type torig, Scope { if (!global.params.useTypeInfo) { + global.gag = 0; if (e) .error(loc, "expression `%s` uses the GC and cannot be used with switch `-betterC`", e.toChars()); else diff --git a/dmd/visitor.d b/dmd/visitor.d index 8990ce49a2a..7b059a061fd 100644 --- a/dmd/visitor.d +++ b/dmd/visitor.d @@ -89,6 +89,7 @@ public: void visit(ASTCodegen.ClassReferenceExp e) { visit(cast(ASTCodegen.Expression)e); } void visit(ASTCodegen.VoidInitExp e) { visit(cast(ASTCodegen.Expression)e); } void visit(ASTCodegen.ThrownExceptionExp e) { visit(cast(ASTCodegen.Expression)e); } + void visit(ASTCodegen.LoweredAssignExp e) { visit(cast(ASTCodegen.AssignExp)e); } } /** @@ -152,7 +153,7 @@ extern (C++) class SemanticTimeTransitiveVisitor : SemanticTimePermissiveVisitor // need to avoid infinite recursion. if (!(e.stageflags & stageToCBuffer)) { - int old = e.stageflags; + const old = e.stageflags; e.stageflags |= stageToCBuffer; foreach (el; *e.elements) if (el) @@ -240,6 +241,12 @@ extern (C++) class SemanticTimeTransitiveVisitor : SemanticTimePermissiveVisitor e.e1.accept(this); e.e2.accept(this); } + + override void visit(ASTCodegen.LoweredAssignExp e) + { + e.lowering.accept(this); + visit(cast(AssignExp)e); + } } extern (C++) class StoppableVisitor : Visitor diff --git a/dmd/visitor.h b/dmd/visitor.h index f8cbdb48c92..3d8c3e60220 100644 --- a/dmd/visitor.h +++ b/dmd/visitor.h @@ -10,13 +10,14 @@ #pragma once #include "root/dsystem.h" +#include "root/dcompat.h" // for d_bool class Statement; class ErrorStatement; class PeelStatement; class ExpStatement; class DtorExpStatement; -class CompileStatement; +class MixinStatement; class CompoundStatement; class CompoundDeclarationStatement; class UnrolledLoopStatement; @@ -109,7 +110,7 @@ class AnonDeclaration; class PragmaDeclaration; class ConditionalDeclaration; class StaticIfDeclaration; -class CompileDeclaration; +class MixinDeclaration; class StaticForeachDeclaration; class UserAttributeDeclaration; class ForwardingAttribDeclaration; @@ -267,6 +268,7 @@ class UshrAssignExp; class CatAssignExp; class CatElemAssignExp; class CatDcharAssignExp; +class LoweredAssignExp; class AddExp; class MinExp; class CatExp; @@ -364,7 +366,7 @@ class ParseTimeVisitor virtual void visit(SharedStaticDtorDeclaration *s) { visit((StaticDtorDeclaration *)s); } // AttribDeclarations - virtual void visit(CompileDeclaration *s) { visit((AttribDeclaration *)s); } + virtual void visit(MixinDeclaration *s) { visit((AttribDeclaration *)s); } virtual void visit(UserAttributeDeclaration *s) { visit((AttribDeclaration *)s); } virtual void visit(LinkDeclaration *s) { visit((AttribDeclaration *)s); } virtual void visit(AnonDeclaration *s) { visit((AttribDeclaration *)s); } @@ -395,7 +397,7 @@ class ParseTimeVisitor virtual void visit(ReturnStatement *s) { visit((Statement *)s); } virtual void visit(LabelStatement *s) { visit((Statement *)s); } virtual void visit(StaticAssertStatement *s) { visit((Statement *)s); } - virtual void visit(CompileStatement *s) { visit((Statement *)s); } + virtual void visit(MixinStatement *s) { visit((Statement *)s); } virtual void visit(WhileStatement *s) { visit((Statement *)s); } virtual void visit(ForStatement *s) { visit((Statement *)s); } virtual void visit(DoStatement *s) { visit((Statement *)s); } @@ -658,11 +660,12 @@ class Visitor : public ParseTimeVisitor virtual void visit(ClassReferenceExp *e) { visit((Expression *)e); } virtual void visit(VoidInitExp *e) { visit((Expression *)e); } virtual void visit(ThrownExceptionExp *e) { visit((Expression *)e); } + virtual void visit(LoweredAssignExp *e) { visit((AssignExp *)e); } }; class StoppableVisitor : public Visitor { public: - bool stop; + d_bool stop; StoppableVisitor() : stop(false) {} }; diff --git a/dmd/vsoptions.d b/dmd/vsoptions.d index 2a363ceeed9..afb235ffa48 100644 --- a/dmd/vsoptions.d +++ b/dmd/vsoptions.d @@ -486,6 +486,21 @@ public: return FileName.exists(proposed) ? proposed : null; } +version (IN_LLVM) +{ + const(char)* getVCIncludeDir() const + { + const(char)* proposed; + + if (VCToolsInstallDir !is null) + proposed = FileName.combine(VCToolsInstallDir, "include"); + else if (VCInstallDir !is null) + proposed = FileName.combine(VCInstallDir, "include"); + + return FileName.exists(proposed) ? proposed : null; + } +} + /** * get the path to the universal CRT libraries * Params: @@ -546,6 +561,33 @@ version (IN_LLVM) {} else return null; } +version (IN_LLVM) +{ + const(char)* getSDKIncludePath() const + { + if (WindowsSdkDir) + { + alias umExists = returnDirIfContainsFile!"um"; // subdir in this case + + const sdk = FileName.combine(WindowsSdkDir.toDString, "include"); + if (WindowsSdkVersion) + { + if (auto p = umExists(sdk, WindowsSdkVersion.toDString)) // SDK 10.0 + return p; + } + // purely speculative + if (auto p = umExists(sdk, "win8")) // SDK 8.0 + return p; + if (auto p = umExists(sdk, "winv6.3")) // SDK 8.1 + return p; + if (auto p = umExists(sdk)) // SDK 7.1 or earlier + return p; + } + + return null; + } +} + private: extern(D): diff --git a/dmd/vsoptions.h b/dmd/vsoptions.h index fc63e1c1b9b..b39601bf92c 100644 --- a/dmd/vsoptions.h +++ b/dmd/vsoptions.h @@ -25,8 +25,14 @@ struct VSOptions void initialize(); const char *getVCBinDir(bool x64, const char *&addpath) const; const char *getVCLibDir(bool x64) const; +#if IN_LLVM + const char *getVCIncludeDir() const; +#endif const char *getUCRTLibPath(bool x64) const; const char *getSDKLibPath(bool x64) const; +#if IN_LLVM + const char *getSDKIncludePath() const; +#endif }; #endif // _WIN32 diff --git a/driver/archiver.cpp b/driver/archiver.cpp index 95ba1dc131b..6ff14f22a8d 100644 --- a/driver/archiver.cpp +++ b/driver/archiver.cpp @@ -20,9 +20,7 @@ #include "llvm/Object/MachO.h" #include "llvm/Object/ObjectFile.h" #include "llvm/Support/Errc.h" -#if LDC_LLVM_VER >= 1100 #include "llvm/Support/Host.h" -#endif #include "llvm/Support/Path.h" #include "llvm/Support/raw_ostream.h" #include "llvm/ToolDrivers/llvm-lib/LibDriver.h" @@ -352,7 +350,7 @@ int createStaticLibrary() { } // invoke external archiver - return executeToolAndWait(tool, args, global.params.verbose); + return executeToolAndWait(Loc(), tool, args, global.params.verbose); } const char *getPathToProducedStaticLibrary() { diff --git a/driver/args.cpp b/driver/args.cpp index da291202d18..bad8e45dee5 100644 --- a/driver/args.cpp +++ b/driver/args.cpp @@ -175,7 +175,11 @@ int executeAndWait(std::vector fullArgs, } const std::vector argv = toRefsVector(fullArgs); +#if LDC_LLVM_VER < 1600 auto envVars = llvm::None; +#else + auto envVars = std::nullopt; +#endif return llvm::sys::ExecuteAndWait(argv[0], argv, envVars, {}, 0, 0, errorMsg); } diff --git a/driver/cache.cpp b/driver/cache.cpp index bb5af90b1a1..9e9278847da 100644 --- a/driver/cache.cpp +++ b/driver/cache.cpp @@ -73,20 +73,12 @@ static std::error_code createSymLink(const char *to, const char *from) { #include namespace llvm { namespace sys { -#if LDC_LLVM_VER >= 1100 namespace windows { // Fwd declaration to an internal LLVM function. std::error_code widenPath(const llvm::Twine &Path8, llvm::SmallVectorImpl &Path16, size_t MaxPathLen = MAX_PATH); } -#else -namespace path { -// Fwd declaration to an internal LLVM function. -std::error_code widenPath(const llvm::Twine &Path8, - llvm::SmallVectorImpl &Path16); -} -#endif // LDC_LLVM_VER < 1100 } // namespace sys } // namespace llvm @@ -100,11 +92,7 @@ std::error_code createLink(FType f, const char *to, const char *from) { // //===----------------------------------------------------------------------===// -#if LDC_LLVM_VER >= 1100 using llvm::sys::windows::widenPath; -#else - using llvm::sys::path::widenPath; -#endif llvm::SmallVector wide_from; llvm::SmallVector wide_to; diff --git a/driver/cl_options-llvm.cpp b/driver/cl_options-llvm.cpp index dd2e18f5268..657029f81fd 100644 --- a/driver/cl_options-llvm.cpp +++ b/driver/cl_options-llvm.cpp @@ -15,51 +15,28 @@ // Pull in command-line options and helper functions from special LLVM header // shared by multiple LLVM tools. -#if LDC_LLVM_VER >= 1100 #include "llvm/CodeGen/CommandFlags.h" static llvm::codegen::RegisterCodeGenFlags CGF; using namespace llvm; -#else -#include "llvm/CodeGen/CommandFlags.inc" -#endif static cl::opt DisableRedZone("disable-red-zone", cl::ZeroOrMore, cl::desc("Do not emit code that uses the red zone.")); -#if LDC_LLVM_VER < 1100 -// legacy option -static cl::opt - disableFPElim("disable-fp-elim", cl::ZeroOrMore, cl::ReallyHidden, - cl::desc("Disable frame pointer elimination optimization")); -#endif - // Now expose the helper functions (with static linkage) via external wrappers // in the opts namespace, including some additional helper functions. namespace opts { std::string getArchStr() { -#if LDC_LLVM_VER >= 1100 return codegen::getMArch(); -#else - return ::MArch; -#endif } Optional getRelocModel() { -#if LDC_LLVM_VER >= 1100 return codegen::getExplicitRelocModel(); -#else - return ::getRelocModel(); -#endif } Optional getCodeModel() { -#if LDC_LLVM_VER >= 1100 return codegen::getExplicitCodeModel(); -#else - return ::getCodeModel(); -#endif } #if LDC_LLVM_VER >= 1300 @@ -69,26 +46,16 @@ using FPK = llvm::FramePointer::FP; #endif llvm::Optional framePointerUsage() { -#if LDC_LLVM_VER >= 1100 // Defaults to `FP::None`; no way to check if set explicitly by user except // indirectly via setFunctionAttributes()... return codegen::getFramePointerUsage(); -#else - if (::FramePointerUsage.getNumOccurrences() > 0) - return ::FramePointerUsage.getValue(); - if (disableFPElim.getNumOccurrences() > 0) - return disableFPElim ? llvm::FramePointer::All : llvm::FramePointer::None; - return llvm::None; -#endif } bool disableRedZone() { return ::DisableRedZone; } bool printTargetFeaturesHelp() { -#if LDC_LLVM_VER >= 1100 const auto MCPU = codegen::getMCPU(); const auto MAttrs = codegen::getMAttrs(); -#endif if (MCPU == "help") return true; return std::any_of(MAttrs.begin(), MAttrs.end(), @@ -98,39 +65,23 @@ bool printTargetFeaturesHelp() { TargetOptions InitTargetOptionsFromCodeGenFlags(const llvm::Triple &triple) { #if LDC_LLVM_VER >= 1200 return codegen::InitTargetOptionsFromCodeGenFlags(triple); -#elif LDC_LLVM_VER >= 1100 - return codegen::InitTargetOptionsFromCodeGenFlags(); #else - return ::InitTargetOptionsFromCodeGenFlags(); + return codegen::InitTargetOptionsFromCodeGenFlags(); #endif } std::string getCPUStr() { -#if LDC_LLVM_VER >= 1100 return codegen::getCPUStr(); -#else - return ::getCPUStr(); -#endif } std::string getFeaturesStr() { -#if LDC_LLVM_VER >= 1100 return codegen::getFeaturesStr(); -#else - return ::getFeaturesStr(); -#endif } -#if LDC_LLVM_VER >= 1000 void setFunctionAttributes(StringRef cpu, StringRef features, Function &function) { -#if LDC_LLVM_VER >= 1100 return codegen::setFunctionAttributes(cpu, features, function); -#else - return ::setFunctionAttributes(cpu, features, function); -#endif } -#endif } // namespace opts #if LDC_WITH_LLD @@ -142,11 +93,9 @@ TargetOptions initTargetOptionsFromCodeGenFlags() { return ::opts::InitTargetOptionsFromCodeGenFlags(llvm::Triple()); } -#if LDC_LLVM_VER >= 1000 Optional getRelocModelFromCMModel() { return ::opts::getRelocModel(); } -#endif Optional getCodeModelFromCMModel() { return ::opts::getCodeModel(); @@ -154,10 +103,6 @@ Optional getCodeModelFromCMModel() { std::string getCPUStr() { return ::opts::getCPUStr(); } -#if LDC_LLVM_VER >= 1100 std::vector getMAttrs() { return codegen::getMAttrs(); } -#else -std::vector getMAttrs() { return ::MAttrs; } -#endif } // namespace lld #endif // LDC_WITH_LLD diff --git a/driver/cl_options-llvm.h b/driver/cl_options-llvm.h index ba6379f144f..a0ed68613d4 100644 --- a/driver/cl_options-llvm.h +++ b/driver/cl_options-llvm.h @@ -38,8 +38,6 @@ InitTargetOptionsFromCodeGenFlags(const llvm::Triple &triple); std::string getCPUStr(); std::string getFeaturesStr(); -#if LDC_LLVM_VER >= 1000 void setFunctionAttributes(llvm::StringRef cpu, llvm::StringRef features, llvm::Function &function); -#endif } diff --git a/driver/cl_options.cpp b/driver/cl_options.cpp index 0b7ab7225d4..c1183676852 100644 --- a/driver/cl_options.cpp +++ b/driver/cl_options.cpp @@ -400,16 +400,18 @@ cl::list cl::value_desc("linkerflag"), cl::cat(linkingCategory), cl::Prefix); -cl::list - ccSwitches("Xcc", cl::desc("Pass to GCC/Clang for linking"), - cl::value_desc("ccflag"), cl::cat(linkingCategory)); +cl::list ccSwitches( + "Xcc", cl::value_desc("ccflag"), cl::cat(linkingCategory), + cl::desc("Pass to GCC/Clang for linking/preprocessing")); -cl::opt - moduleDeps("deps", cl::ValueOptional, cl::ZeroOrMore, - cl::value_desc("filename"), - cl::desc("Write module dependencies to (only imports). " - "'-deps' alone prints module dependencies " - "(imports/file/version/debug/lib)")); +cl::list cppSwitches("P", cl::value_desc("cppflag"), cl::Prefix, + cl::desc("Pass to C preprocessor")); + +cl::opt moduleDeps( + "deps", cl::ValueOptional, cl::ZeroOrMore, cl::value_desc("filename"), + cl::desc("Write module dependencies to (only imports). " + "'-deps' alone prints module dependencies " + "(imports/file/version/debug/lib)")); cl::opt makeDeps("makedeps", cl::ValueOptional, cl::ZeroOrMore, @@ -860,7 +862,8 @@ void hideLLVMOptions() { "internalize-public-api-list", "iterative-counter-promotion", "join-liveintervals", "jump-table-type", "limit-float-precision", "lower-global-dtors-via-cxa-atexit", - "lto-embed-bitcode", "matrix-default-layout", "matrix-propagate-shape", + "lto-embed-bitcode", "matrix-default-layout", + "matrix-print-after-transpose-opt", "matrix-propagate-shape", "max-counter-promotions", "max-counter-promotions-per-loop", "mc-relax-all", "mc-x86-disable-arith-relaxation", "mcabac", "meabi", "memop-size-large", "memop-size-range", "merror-missing-parenthesis", @@ -892,9 +895,9 @@ void hideLLVMOptions() { "speculative-counter-promotion-to-loop", "spiller", "spirv-debug", "spirv-erase-cl-md", "spirv-lower-const-expr", "spirv-mem2reg", "spirv-no-deref-attr", "spirv-text", "spirv-verify-regularize-passes", - "split-machine-functions", "spv-lower-saddwithoverflow-validate", - "spvbool-validate", "spvmemmove-validate", - "stack-alignment", "stack-protector-guard", + "split-machine-functions", "spv-dump-deps", + "spv-lower-saddwithoverflow-validate", "spvbool-validate", + "spvmemmove-validate", "stack-alignment", "stack-protector-guard", "stack-protector-guard-offset", "stack-protector-guard-reg", "stack-size-section", "stack-symbol-ordering", "stackmap-version", "static-func-full-module-prefix", diff --git a/driver/cl_options.h b/driver/cl_options.h index 8bf0866619a..b78a715e161 100644 --- a/driver/cl_options.h +++ b/driver/cl_options.h @@ -75,6 +75,7 @@ extern cl::opt makeDeps; extern cl::opt cacheDir; extern cl::list linkerSwitches; extern cl::list ccSwitches; +extern cl::list cppSwitches; extern cl::list includeModulePatterns; extern cl::opt m32bits; diff --git a/driver/cl_options_instrumentation.cpp b/driver/cl_options_instrumentation.cpp index 00fe6031d4c..ea00e7d130c 100644 --- a/driver/cl_options_instrumentation.cpp +++ b/driver/cl_options_instrumentation.cpp @@ -84,6 +84,19 @@ llvm::StringRef getXRayInstructionThresholdString() { return thresholdString; } +cl::opt fCFProtection( + "fcf-protection", + cl::desc("Instrument control-flow architecture protection"), cl::ZeroOrMore, + cl::ValueOptional, + cl::values(clEnumValN(CFProtectionType::None, "none", ""), + clEnumValN(CFProtectionType::Branch, "branch", ""), + clEnumValN(CFProtectionType::Return, "return", ""), + clEnumValN(CFProtectionType::Full, "full", ""), + clEnumValN(CFProtectionType::Full, "", + "") // default to "full" if no argument specified + ), + cl::init(CFProtectionType::None)); + void initializeInstrumentationOptionsFromCmdline(const llvm::Triple &triple) { if (ASTPGOInstrGenFile.getNumOccurrences() > 0) { pgoMode = PGO_ASTBasedInstr; @@ -110,6 +123,14 @@ void initializeInstrumentationOptionsFromCmdline(const llvm::Triple &triple) { if (dmdFunctionTrace) global.params.trace = true; + + // fcf-protection is only valid for X86 + if (fCFProtection != CFProtectionType::None && + !(triple.getArch() == llvm::Triple::x86 || + triple.getArch() == llvm::Triple::x86_64)) { + error(Loc(), "option '--fcf-protection' cannot be specified on this target " + "architecture"); + } } } // namespace opts diff --git a/driver/cl_options_instrumentation.h b/driver/cl_options_instrumentation.h index 50a249c09aa..9b06789b341 100644 --- a/driver/cl_options_instrumentation.h +++ b/driver/cl_options_instrumentation.h @@ -29,6 +29,9 @@ extern cl::opt instrumentFunctions; extern cl::opt fXRayInstrument; llvm::StringRef getXRayInstructionThresholdString(); +enum class CFProtectionType { None = 0, Branch = 1, Return = 2, Full = 3 }; +extern cl::opt fCFProtection; + /// This initializes the instrumentation options, and checks the validity of the /// commandline flags. targetTriple should be initialized before calling this. /// It should be called only once. diff --git a/driver/cl_options_sanitizers.cpp b/driver/cl_options_sanitizers.cpp index 416568b209c..4994e8b162a 100644 --- a/driver/cl_options_sanitizers.cpp +++ b/driver/cl_options_sanitizers.cpp @@ -206,12 +206,8 @@ void initializeSanitizerOptionsFromCmdline() if (isAnySanitizerEnabled() && !fSanitizeBlacklist.empty()) { std::string loadError; - sanitizerBlacklist = - llvm::SpecialCaseList::create(fSanitizeBlacklist, -#if LDC_LLVM_VER >= 1000 - *llvm::vfs::getRealFileSystem(), -#endif - loadError); + sanitizerBlacklist = llvm::SpecialCaseList::create( + fSanitizeBlacklist, *llvm::vfs::getRealFileSystem(), loadError); if (!sanitizerBlacklist) error(Loc(), "-fsanitize-blacklist error: %s", loadError.c_str()); } diff --git a/driver/codegenerator.cpp b/driver/codegenerator.cpp index e277d00f75e..af3b19ce075 100644 --- a/driver/codegenerator.cpp +++ b/driver/codegenerator.cpp @@ -28,11 +28,7 @@ #if LDC_LLVM_VER >= 1400 #include "llvm/IR/DiagnosticInfo.h" #endif -#if LDC_LLVM_VER >= 1100 #include "llvm/IR/LLVMRemarkStreamer.h" -#else -#include "llvm/IR/RemarkStreamer.h" -#endif #include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" #include "llvm/Support/ToolOutputFile.h" @@ -66,13 +62,8 @@ createAndSetDiagnosticsOutputFile(IRState &irs, llvm::LLVMContext &ctx, // If there is instrumentation data available, also output function hotness const bool withHotness = opts::isUsingPGOProfile(); - auto remarksFileOrError = -#if LDC_LLVM_VER >= 1100 - llvm::setupLLVMOptimizationRemarks( -#else - llvm::setupOptimizationRemarks( -#endif - ctx, diagnosticsFilename, "", "", withHotness); + auto remarksFileOrError = llvm::setupLLVMOptimizationRemarks( + ctx, diagnosticsFilename, "", "", withHotness); if (llvm::Error e = remarksFileOrError.takeError()) { irs.dmodule->error("Could not create file %s: %s", diagnosticsFilename.c_str(), @@ -377,15 +368,24 @@ void CodeGenerator::writeMLIRModule(mlir::OwningModuleRef *module, const auto llpath = replaceExtensionWith(mlir_ext, filename); Logger::println("Writting MLIR to %s\n", llpath.c_str()); std::error_code errinfo; - llvm::raw_fd_ostream aos(llpath, errinfo, llvm::sys::fs::OF_None); + llvm::ToolOutputFile aos(llpath, errinfo, llvm::sys::fs::OF_None); - if (aos.has_error()) { + if (aos.os().has_error()) { error(Loc(), "Cannot write MLIR file '%s': %s", llpath.c_str(), errinfo.message().c_str()); fatal(); } // module->print(aos); + + // Terminate upon errors during the LLVM passes. + if (global.errors || global.warnings) { + Logger::println( + "Aborting because of errors/warnings during bitcode LLVM passes"); + fatal(); + } + + aos.keep(); } } diff --git a/driver/cpreprocessor.cpp b/driver/cpreprocessor.cpp new file mode 100644 index 00000000000..25947fd1266 --- /dev/null +++ b/driver/cpreprocessor.cpp @@ -0,0 +1,155 @@ +#include "driver/cpreprocessor.h" + +#include "dmd/errors.h" +#include "driver/cl_options.h" +#include "driver/timetrace.h" +#include "driver/tool.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/Program.h" + +namespace { +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); + if (!cached) { + error(loc, "cannot find \"importc.h\" along import path"); + fatal(); + } + } + return cached; +} + +const std::string &getCC(bool isMSVC) { + static std::string cached; + if (cached.empty()) { + std::string fallback = "cc"; + if (isMSVC) { +#ifdef _WIN32 + // by default, prefer clang-cl.exe (if in PATH) over cl.exe + // (e.g., no echoing of source filename being preprocessed to stderr) + auto found = llvm::sys::findProgramByName("clang-cl.exe"); + if (found) { + fallback = found.get(); + } else { + fallback = "cl.exe"; + } +#else + fallback = "clang-cl"; +#endif + } + cached = getGcc(fallback.c_str()); + } + return cached; +} + +FileName getOutputPath(const Loc &loc, const char *csrcfile) { + llvm::SmallString<64> buffer; + + // 1) create a new temporary directory (e.g., `/tmp/itmp-ldc-10ecec`) + auto ec = llvm::sys::fs::createUniqueDirectory("itmp-ldc", buffer); + if (ec) { + error(loc, + "failed to create temporary directory for preprocessed .i file: %s\n%s", + buffer.c_str(), ec.message().c_str()); + fatal(); + } + + // 2) append the .c file name, replacing the extension with .i + llvm::sys::path::append(buffer, FileName::name(csrcfile)); + llvm::sys::path::replace_extension(buffer, i_ext.ptr); + + // the directory is removed (after the file) in Module.read() + + return FileName::create(buffer.c_str()); // allocates a copy +} +} // anonymous namespace + +FileName runCPreprocessor(FileName csrcfile, const Loc &loc, bool &ifile, + OutBuffer &defines) { + TimeTraceScope timeScope("Preprocess C file", csrcfile.toChars()); + + const char *importc_h = getPathToImportc_h(loc); + + const auto &triple = *global.params.targetTriple; + const bool isMSVC = triple.isWindowsMSVCEnvironment(); + +#ifdef _WIN32 + windows::MsvcEnvironmentScope msvcEnv; + if (isMSVC) + msvcEnv.setup(/*forPreprocessingOnly=*/true); +#endif + + FileName ipath = getOutputPath(loc, csrcfile.toChars()); + + const std::string &cc = getCC(isMSVC); + std::vector args; + + if (!isMSVC) + appendTargetArgsForGcc(args); + + if (triple.isOSDarwin()) + args.push_back("-fno-blocks"); // disable blocks extension + + for (const auto &ccSwitch : opts::ccSwitches) { + args.push_back(ccSwitch); + } + for (const auto &cppSwitch : opts::cppSwitches) { + args.push_back(cppSwitch); + } + + if (isMSVC) { + args.push_back("/nologo"); + args.push_back("/P"); // preprocess only + + const bool isClangCl = llvm::StringRef(cc) +#if LDC_LLVM_VER >= 1300 + .contains_insensitive("clang-cl"); +#else + .contains_lower("clang-cl"); +#endif + + if (!isClangCl) { + args.push_back("/PD"); // print all macro definitions + args.push_back("/Zc:preprocessor"); // use the new conforming preprocessor + } else { + // print macro definitions (clang-cl doesn't support /PD - use clang's + // -dD) + args.push_back("-Xclang"); + args.push_back("-dD"); + + // need to redefine some macros in importc.h + args.push_back("-Wno-builtin-macro-redefined"); + } + + args.push_back(csrcfile.toChars()); + args.push_back((llvm::Twine("/FI") + importc_h).str()); + // preprocessed output file + args.push_back((llvm::Twine("/Fi") + ipath.toChars()).str()); + } else { // Posix + // merge #define's with output: + // https://gcc.gnu.org/onlinedocs/cpp/Invocation.html#index-dD + args.push_back("-dD"); + + // need to redefine some macros in importc.h + args.push_back("-Wno-builtin-macro-redefined"); + + args.push_back("-E"); // run preprocessor only + args.push_back("-include"); + args.push_back(importc_h); + args.push_back(csrcfile.toChars()); + args.push_back("-o"); + args.push_back(ipath.toChars()); + } + + const int status = executeToolAndWait(loc, cc, args, global.params.verbose); + if (status) { + errorSupplemental(loc, "C preprocessor failed for file '%s'", csrcfile.toChars()); + fatal(); + } + + ifile = true; + return ipath; +} diff --git a/driver/cpreprocessor.h b/driver/cpreprocessor.h new file mode 100644 index 00000000000..ae0a2c588bc --- /dev/null +++ b/driver/cpreprocessor.h @@ -0,0 +1,8 @@ +#pragma once + +#include "dmd/common/outbuffer.h" +#include "dmd/globals.h" +#include "dmd/root/filename.h" + +FileName runCPreprocessor(FileName csrcfile, const Loc &loc, bool &ifile, + OutBuffer &defines); diff --git a/driver/ldmd.cpp b/driver/ldmd.cpp index 4a9aaf63694..c25938a1c51 100644 --- a/driver/ldmd.cpp +++ b/driver/ldmd.cpp @@ -234,7 +234,9 @@ Where:\n\ #if 0 " -os= sets target operating system to \n" #endif -" -preview= enable an upcoming language change identified by 'name'\n\ +" -P=\n\ + pass preprocessorflag to C preprocessor\n\ + -preview= enable an upcoming language change identified by 'name'\n\ -preview=[h|help|?]\n\ list all upcoming language changes\n\ -profile profile runtime performance of generated code\n" @@ -510,11 +512,7 @@ void translateArgs(const llvm::SmallVectorImpl &ldmdArgs, } else if (strcmp(p + 1, "gf") == 0) { ldcArgs.push_back("-g"); } else if (strcmp(p + 1, "gs") == 0) { -#if LDC_LLVM_VER >= 1100 ldcArgs.push_back("-frame-pointer=all"); -#else - ldcArgs.push_back("-disable-fp-elim"); -#endif } else if (strcmp(p + 1, "gx") == 0) { goto Lnot_in_ldc; } else if (strcmp(p + 1, "gt") == 0) { @@ -707,6 +705,7 @@ void translateArgs(const llvm::SmallVectorImpl &ldmdArgs, exit(EXIT_SUCCESS); } /* -L + * -P * -defaultlib * -debuglib * -deps diff --git a/driver/linker-gcc.cpp b/driver/linker-gcc.cpp index 85bd2761891..2aef6402578 100644 --- a/driver/linker-gcc.cpp +++ b/driver/linker-gcc.cpp @@ -149,6 +149,11 @@ void ArgsBuilder::addLTOGoldPluginFlags(bool requirePlugin) { addLdFlag("-plugin-opt=-function-sections"); if (TO.DataSections) addLdFlag("-plugin-opt=-data-sections"); + +#if LDC_LLVM_VER >= 1600 && LDC_LLVM_VER < 1700 + // LLVM 16: disable function specializations by default + addLdFlag("-plugin-opt=-func-specialization-size-threshold=1000000000"); +#endif } // Returns an empty string when libLTO.dylib was not specified nor found. @@ -179,6 +184,11 @@ void ArgsBuilder::addDarwinLTOFlags() { std::string dylibPath = getLTOdylibPath(); if (!dylibPath.empty()) { addLdFlag("-lto_library", dylibPath); + +#if LDC_LLVM_VER >= 1600 && LDC_LLVM_VER < 1700 + // LLVM 16: disable function specializations by default + addLdFlag("-mllvm", "-func-specialization-size-threshold=1000000000"); +#endif } } @@ -759,10 +769,8 @@ int linkObjToBinaryGcc(llvm::StringRef outputPath, , CanExitEarly #endif -#if LDC_LLVM_VER >= 1000 , llvm::outs(), llvm::errs() -#endif #if LDC_LLVM_VER >= 1400 , CanExitEarly, false @@ -778,10 +786,8 @@ int linkObjToBinaryGcc(llvm::StringRef outputPath, , CanExitEarly #endif -#if LDC_LLVM_VER >= 1000 , llvm::outs(), llvm::errs() -#endif #if LDC_LLVM_VER >= 1400 , CanExitEarly, false @@ -789,14 +795,12 @@ int linkObjToBinaryGcc(llvm::StringRef outputPath, ); } else if (global.params.targetTriple->isOSBinFormatCOFF()) { success = lld::mingw::link(fullArgs -#if LDC_LLVM_VER >= 1000 && LDC_LLVM_VER < 1400 +#if LDC_LLVM_VER < 1400 , CanExitEarly #endif -#if LDC_LLVM_VER >= 1000 , llvm::outs(), llvm::errs() -#endif #if LDC_LLVM_VER >= 1400 , CanExitEarly, false @@ -813,10 +817,8 @@ int linkObjToBinaryGcc(llvm::StringRef outputPath, , CanExitEarly #endif -#if LDC_LLVM_VER >= 1000 , llvm::outs(), llvm::errs() -#endif #if LDC_LLVM_VER >= 1400 , CanExitEarly, false @@ -839,10 +841,10 @@ int linkObjToBinaryGcc(llvm::StringRef outputPath, std::unique_ptr argsBuilder; if (global.params.targetTriple->isOSBinFormatWasm()) { tool = getProgram("wasm-ld", &opts::linker); - argsBuilder = llvm::make_unique(); + argsBuilder = std::make_unique(); } else { tool = getGcc(); - argsBuilder = llvm::make_unique(); + argsBuilder = std::make_unique(); } // build arguments @@ -858,5 +860,6 @@ int linkObjToBinaryGcc(llvm::StringRef outputPath, logstr << "\n"; // FIXME where's flush ? // try to call linker - return executeToolAndWait(tool, argsBuilder->args, global.params.verbose); + return executeToolAndWait(Loc(), tool, argsBuilder->args, + global.params.verbose); } diff --git a/driver/linker-msvc.cpp b/driver/linker-msvc.cpp index 5a07f885b18..15830d5182a 100644 --- a/driver/linker-msvc.cpp +++ b/driver/linker-msvc.cpp @@ -88,11 +88,6 @@ void addSanitizerLibs(std::vector &args) { int linkObjToBinaryMSVC(llvm::StringRef outputPath, const std::vector &defaultLibNames) { - if (!opts::ccSwitches.empty()) { - error(Loc(), "-Xcc is not supported for MSVC"); - fatal(); - } - #ifdef _WIN32 windows::MsvcEnvironmentScope msvcEnv; @@ -281,10 +276,8 @@ int linkObjToBinaryMSVC(llvm::StringRef outputPath, , canExitEarly #endif -#if LDC_LLVM_VER >= 1000 , llvm::outs(), llvm::errs() -#endif #if LDC_LLVM_VER >= 1400 , canExitEarly, false @@ -309,5 +302,5 @@ int linkObjToBinaryMSVC(llvm::StringRef outputPath, #endif } - return executeToolAndWait(linker, args, global.params.verbose); + return executeToolAndWait(Loc(), linker, args, global.params.verbose); } diff --git a/driver/linker.cpp b/driver/linker.cpp index 76266379d29..f061d77bf95 100644 --- a/driver/linker.cpp +++ b/driver/linker.cpp @@ -195,7 +195,11 @@ static std::vector getDefaultLibNames() { llvm::Optional> getExplicitPlatformLibs() { if (platformLib.getNumOccurrences() > 0) return parseLibNames(platformLib); +#if LDC_LLVM_VER < 1600 return llvm::None; +#else + return std::nullopt; +#endif } ////////////////////////////////////////////////////////////////////////////// @@ -323,7 +327,7 @@ int runProgram() { // Run executable int status = - executeToolAndWait(gExePath, opts::runargs, global.params.verbose); + executeToolAndWait(Loc(), gExePath, opts::runargs, global.params.verbose); if (status < 0) { #if defined(_MSC_VER) || defined(__MINGW32__) error(Loc(), "program received signal %d", -status); diff --git a/driver/linker.h b/driver/linker.h index f962054efaf..28362d86526 100644 --- a/driver/linker.h +++ b/driver/linker.h @@ -42,7 +42,11 @@ bool linkAgainstSharedDefaultLibs(); /** * Returns the -platformlib library names, if specified. */ +#if LDC_LLVM_VER < 1600 llvm::Optional> getExplicitPlatformLibs(); +#else +std::optional> getExplicitPlatformLibs(); +#endif /** * Returns the value of -mscrtlib. diff --git a/driver/main.cpp b/driver/main.cpp index 161fd3c86ec..81f34ff84e3 100644 --- a/driver/main.cpp +++ b/driver/main.cpp @@ -29,6 +29,7 @@ #include "driver/cl_options_sanitizers.h" #include "driver/codegenerator.h" #include "driver/configfile.h" +#include "driver/cpreprocessor.h" #include "driver/dcomputecodegenerator.h" #include "driver/exe_path.h" #include "driver/ldc-version.h" @@ -582,7 +583,9 @@ void initializePasses() { #endif initializeVectorization(Registry); initializeInstCombine(Registry); +#if LDC_LLVM_VER < 1600 initializeAggressiveInstCombine(Registry); +#endif initializeIPO(Registry); #if LDC_LLVM_VER < 1600 initializeInstrumentation(Registry); @@ -1117,6 +1120,10 @@ int cppmain() { fatal(); } + global.compileEnv.previewIn = global.params.previewIn; + global.compileEnv.ddocOutput = global.params.ddoc.doOutput; + global.compileEnv.shortenedMethods = global.params.shortenedMethods; + if (opts::fTimeTrace) { initializeTimeTrace(opts::fTimeTraceGranularity, 0, opts::allArguments[0]); } @@ -1189,6 +1196,8 @@ int cppmain() { global.params.dllimport = DLLImport::none; } + global.preprocess = &runCPreprocessor; + // allocate the target abi gABI = TargetABI::getTarget(); diff --git a/driver/plugins.cpp b/driver/plugins.cpp index 6820da5ea41..713d8bee806 100644 --- a/driver/plugins.cpp +++ b/driver/plugins.cpp @@ -9,6 +9,9 @@ // // Implements functionality related to plugins (`-plugin=...`). // +// Note: plugins can be LLVM-plugins (to be registered with the pass manager) +// or dlang-plugins for semantic analysis. +// //===----------------------------------------------------------------------===// #include "driver/plugins.h" @@ -17,6 +20,7 @@ #include "dmd/errors.h" #include "dmd/globals.h" +#include "dmd/module.h" #include "llvm/Passes/PassBuilder.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/DynamicLibrary.h" @@ -36,63 +40,120 @@ cl::list pluginFiles("plugin", cl::CommaSeparated, cl::desc("Pass plugins to load."), cl::value_desc("dynamic_library.so,lib2.so")); +struct SemaPlugin { + llvm::sys::DynamicLibrary library; + void (*runSemanticAnalysis)(Module *); + + SemaPlugin(const llvm::sys::DynamicLibrary &library, + void (*runSemanticAnalysis)(Module *)) + : library(library), runSemanticAnalysis(runSemanticAnalysis) {} +}; + +llvm::SmallVector sema_plugins; + } // anonymous namespace -#if LDC_LLVM_VER >= 1400 +// Tries to load plugin as SemanticAnalysis. Returns true on 'success', i.e. no +// further attempts needed. +bool loadSemanticAnalysisPlugin(const std::string &filename) { + std::string errorString; + auto library = llvm::sys::DynamicLibrary::getPermanentLibrary( + filename.c_str(), &errorString); + if (!library.isValid()) { + error(Loc(), "Error loading plugin '%s': %s", filename.c_str(), + errorString.c_str()); + return true; // No success, but no need to try loading again as LLVM plugin. + } -namespace { -llvm::SmallVector plugins; + // SemanticAnalysis plugins need to export the `runSemanticAnalysis` function. + void *runSemanticAnalysisFnPtr = + library.getAddressOfSymbol("runSemanticAnalysis"); + + // If the symbol isn't found, this is probably an LLVM plugin. + if (!runSemanticAnalysisFnPtr) + return false; + + sema_plugins.emplace_back( + library, reinterpret_cast(runSemanticAnalysisFnPtr)); + return true; } -/// Loads all plugins for the new pass manager. These plugins will need to be -/// added When building the optimization pipeline. -void loadAllPluginsNewPM() { - for (auto &filename : pluginFiles) { - auto plugin = llvm::PassPlugin::Load(filename); - if (!plugin) { - error(Loc(), "Error loading plugin '%s': %s", filename.c_str(), - llvm::toString(plugin.takeError()).c_str()); - continue; - } - plugins.emplace_back(plugin.get()); + +/// Loads plugin for the legacy pass manager. The static constructor of +/// the plugin should take care of the plugins registering themself with the +/// rest of LDC/LLVM. +void loadLLVMPluginLegacyPM(const std::string &filename) { + std::string errorString; + if (llvm::sys::DynamicLibrary::LoadLibraryPermanently(filename.c_str(), + &errorString)) { + error(Loc(), "Error loading plugin '%s': %s", filename.c_str(), + errorString.c_str()); } } -void registerAllPluginsWithPassBuilder(llvm::PassBuilder &PB) { - for (auto &plugin : plugins) { - plugin.registerPassBuilderCallbacks(PB); + +#if LDC_LLVM_VER >= 1400 + +namespace { +llvm::SmallVector llvm_plugins; + +/// Loads plugin for the new pass manager. The plugin will need to be +/// added explicitly when building the optimization pipeline. +void loadLLVMPluginNewPM(const std::string &filename) { + + auto plugin = llvm::PassPlugin::Load(filename); + if (!plugin) { + error(Loc(), "Error loading plugin '%s': %s", filename.c_str(), + llvm::toString(plugin.takeError()).c_str()); + return; } + llvm_plugins.emplace_back(plugin.get()); } +} // anonymous namespace + #endif // LDC_LLVM_VER >= 1400 -/// Loads all plugins for the legacy pass manaager. The static constructor of -/// each plugin should take care of the plugins registering themself with the -/// rest of LDC/LLVM. -void loadAllPluginsLegacyPM() { +void loadLLVMPlugin(const std::string &filename) { +#if LDC_LLVM_VER >= 1400 + if (opts::isUsingLegacyPassManager()) + loadLLVMPluginLegacyPM(filename); + else + loadLLVMPluginNewPM(filename); +#else + loadLLVMPluginLegacyPM(filename); +#endif +} + +void loadAllPlugins() { for (auto &filename : pluginFiles) { - std::string errorString; - if (llvm::sys::DynamicLibrary::LoadLibraryPermanently(filename.c_str(), - &errorString)) { - error(Loc(), "Error loading plugin '%s': %s", filename.c_str(), - errorString.c_str()); - } + // First attempt to load plugin as SemanticAnalysis plugin. If unsuccesfull, + // load as LLVM plugin. + auto success = loadSemanticAnalysisPlugin(filename); + if (!success) + loadLLVMPlugin(filename); } } +void registerAllPluginsWithPassBuilder(llvm::PassBuilder &PB) { #if LDC_LLVM_VER >= 1400 -void loadAllPlugins() { - if (opts::isUsingLegacyPassManager()) - loadAllPluginsLegacyPM(); - else - loadAllPluginsNewPM(); -} -#else -void loadAllPlugins() { loadAllPluginsLegacyPM(); } -void registerAllPluginsWithPassBuilder(llvm::PassBuilder &) {} + for (auto &plugin : llvm_plugins) { + plugin.registerPassBuilderCallbacks(PB); + } #endif +} + +void runAllSemanticAnalysisPlugins(Module *m) { + for (auto &plugin : sema_plugins) { + assert(plugin.runSemanticAnalysis); + plugin.runSemanticAnalysis(m); + } +} #else // #if LDC_ENABLE_PLUGINS +class Module; + void loadAllPlugins() {} void registerAllPluginsWithPassBuilder(llvm::PassBuilder &) {} +void runAllSemanticAnalysisPlugins(Module *m) {} #endif // LDC_ENABLE_PLUGINS diff --git a/driver/targetmachine.cpp b/driver/targetmachine.cpp index be47a1dc931..b454fa2c8e1 100644 --- a/driver/targetmachine.cpp +++ b/driver/targetmachine.cpp @@ -57,7 +57,20 @@ static llvm::cl::opt preserveDwarfLineSection( llvm::cl::init(false)); #endif -const char *getABI(const llvm::Triple &triple) { +// Returns true if 'feature' is enabled and false otherwise. Handles the +// case where the feature is specified multiple times ('+m,-m'), and +// takes the last occurrence. +bool isFeatureEnabled(const llvm::SmallVectorImpl &features, + llvm::StringRef feature) { + for (auto it = features.rbegin(), end = features.rend(); it != end; ++it) { + if (it->substr(1) == feature) { + return (*it)[0] == '+'; + } + } + return false; +}; + +const char *getABI(const llvm::Triple &triple, const llvm::SmallVectorImpl &features) { llvm::StringRef ABIName(opts::mABI); if (ABIName != "") { switch (triple.getArch()) { @@ -120,6 +133,10 @@ const char *getABI(const llvm::Triple &triple) { case llvm::Triple::ppc64le: return "elfv2"; case llvm::Triple::riscv64: + if (isFeatureEnabled(features, "d")) + return "lp64d"; + if (isFeatureEnabled(features, "f")) + return "lp64f"; return "lp64"; case llvm::Triple::riscv32: return "ilp32"; @@ -203,9 +220,11 @@ static std::string getARMTargetCPU(const llvm::Triple &triple) { } static std::string getAArch64TargetCPU(const llvm::Triple &triple) { +#if LDC_LLVM_VER < 1600 auto defaultCPU = llvm::AArch64::getDefaultCPU(triple.getArchName()); if (!defaultCPU.empty()) return std::string(defaultCPU); +#endif return "generic"; } @@ -439,6 +458,16 @@ createTargetMachine(const std::string targetTriple, const std::string arch, features.push_back("+cx16"); } + // For a hosted RISC-V 64-bit target default to rv64gc if nothing has + // been selected + if (triple.getArch() == llvm::Triple::riscv64 && features.empty()) { + const llvm::StringRef os = triple.getOSName(); + const bool isFreeStanding = os.empty() || os == "unknown" || os == "none"; + if (!isFreeStanding) { + features = {"+m", "+a", "+f", "+d", "+c"}; + } + } + // Handle cases where LLVM picks wrong default relocModel #if LDC_LLVM_VER >= 1600 if (relocModel.has_value()) {} @@ -476,7 +505,7 @@ createTargetMachine(const std::string targetTriple, const std::string arch, opts::InitTargetOptionsFromCodeGenFlags(triple); if (targetOptions.MCOptions.ABIName.empty()) - targetOptions.MCOptions.ABIName = getABI(triple); + targetOptions.MCOptions.ABIName = getABI(triple, features); if (floatABI == FloatABI::Default) { switch (triple.getArch()) { diff --git a/driver/targetmachine.h b/driver/targetmachine.h index 17e664a2bf8..66e1a20be8e 100644 --- a/driver/targetmachine.h +++ b/driver/targetmachine.h @@ -15,6 +15,8 @@ #pragma once #include "llvm/ADT/Optional.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" #include "llvm/Support/CodeGen.h" #include #include @@ -73,4 +75,8 @@ MipsABI::Type getMipsABI(); const llvm::Target *lookupTarget(const std::string &arch, llvm::Triple &triple, std::string &errorMsg); -const char *getABI(const llvm::Triple &triple); +const char *getABI(const llvm::Triple &triple, + const llvm::SmallVectorImpl &features); + +bool isFeatureEnabled(const llvm::SmallVectorImpl &features, + llvm::StringRef feature); diff --git a/driver/timetrace_sema.d b/driver/timetrace_sema.d index 11be682c34b..ab7e10bbd7a 100644 --- a/driver/timetrace_sema.d +++ b/driver/timetrace_sema.d @@ -131,7 +131,7 @@ extern(C++) final class SemanticTimeTraceVisitor(SemaVisitor) : Visitor override void visit(StaticForeachDeclaration sfd) { semavisitor.visit(sfd); } - override void visit(CompileDeclaration cd) { semavisitor.visit(cd); } + override void visit(MixinDeclaration md) { semavisitor.visit(md); } override void visit(CPPNamespaceDeclaration ns) { semavisitor.visit(ns); } diff --git a/driver/toobj.cpp b/driver/toobj.cpp index 8e1ff6832f1..48c8d6407ad 100644 --- a/driver/toobj.cpp +++ b/driver/toobj.cpp @@ -30,6 +30,7 @@ #include "llvm/Support/FormattedStream.h" #include "llvm/Support/Program.h" #include "llvm/Support/Path.h" +#include "llvm/Support/ToolOutputFile.h" #include "llvm/Target/TargetMachine.h" #include "llvm/Analysis/TargetLibraryInfo.h" #include "llvm/Analysis/TargetTransformInfo.h" @@ -44,13 +45,7 @@ #include #include -#if LDC_LLVM_VER < 1000 -using CodeGenFileType = llvm::TargetMachine::CodeGenFileType; -constexpr CodeGenFileType CGFT_AssemblyFile = llvm::TargetMachine::CGFT_AssemblyFile; -constexpr CodeGenFileType CGFT_ObjectFile = llvm::TargetMachine::CGFT_ObjectFile; -#else using CodeGenFileType = llvm::CodeGenFileType; -#endif static llvm::cl::opt NoIntegratedAssembler("no-integrated-as", llvm::cl::ZeroOrMore, @@ -93,7 +88,7 @@ void codegenModule(llvm::TargetMachine &Target, llvm::Module &m, } std::error_code errinfo; - llvm::raw_fd_ostream out(filename, errinfo, llvm::sys::fs::OF_None); + llvm::ToolOutputFile out(filename, errinfo, llvm::sys::fs::OF_None); if (errinfo) { error(Loc(), "cannot write file '%s': %s", filename, errinfo.message().c_str()); @@ -119,8 +114,8 @@ void codegenModule(llvm::TargetMachine &Target, llvm::Module &m, if (Target.addPassesToEmitFile( Passes, - out, // Output file - nullptr, // DWO output file + out.os(), // Output file + nullptr, // DWO output file // Always generate assembly for ptx as it is an assembly format // The PTX backend fails if we pass anything else. (cb == ComputeBackend::NVPTX) ? CGFT_AssemblyFile : fileType, @@ -129,6 +124,14 @@ void codegenModule(llvm::TargetMachine &Target, llvm::Module &m, } Passes.run(m); + + // Terminate upon errors during the LLVM passes. + if (global.errors || global.warnings) { + Logger::println("Aborting because of errors/warnings during LLVM passes"); + fatal(); + } + + out.keep(); } } @@ -145,7 +148,7 @@ static void assemble(const std::string &asmpath, const std::string &objpath) { appendTargetArgsForGcc(args); // Run the compiler to assembly the program. - int R = executeToolAndWait(getGcc(), args, global.params.verbose); + int R = executeToolAndWait(Loc(), getGcc(), args, global.params.verbose); if (R) { error(Loc(), "Error while invoking external assembler."); fatal(); @@ -349,6 +352,15 @@ void writeModule(llvm::Module *m, const char *filename) { runDLLImportRelocationPass(*gTargetMachine, *m); } + // Check if there are any errors before writing files. + // Note: LLVM passes can add new warnings/errors (warnings become errors with + // `-w`) such that we reach here with errors that did not trigger earlier + // termination of the compiler. + if (global.errors) { + Logger::println("Aborting because of errors"); + fatal(); + } + // Everything beyond this point is writing file(s) to disk. ::TimeTraceScope timeScope("Write file(s)", filename); @@ -371,8 +383,8 @@ void writeModule(llvm::Module *m, const char *filename) { : replaceExtensionWith(bc_ext, filename); Logger::println("Writing LLVM bitcode to: %s\n", bcpath.c_str()); std::error_code errinfo; - llvm::raw_fd_ostream bos(bcpath.c_str(), errinfo, llvm::sys::fs::OF_None); - if (bos.has_error()) { + llvm::ToolOutputFile bos(bcpath.c_str(), errinfo, llvm::sys::fs::OF_None); + if (bos.os().has_error()) { error(Loc(), "cannot write LLVM bitcode file '%s': %s", bcpath.c_str(), errinfo.message().c_str()); fatal(); @@ -390,11 +402,20 @@ void writeModule(llvm::Module *m, const char *filename) { auto moduleSummaryIndex = buildModuleSummaryIndex( *m, /* function freq callback */ nullptr, &PSI); - llvm::WriteBitcodeToFile(M, bos, true, &moduleSummaryIndex, + llvm::WriteBitcodeToFile(M, bos.os(), true, &moduleSummaryIndex, /* generate ThinLTO hash */ true); } else { - llvm::WriteBitcodeToFile(M, bos); + llvm::WriteBitcodeToFile(M, bos.os()); } + + // Terminate upon errors during the LLVM passes. + if (global.errors || global.warnings) { + Logger::println( + "Aborting because of errors/warnings during bitcode LLVM passes"); + fatal(); + } + + bos.keep(); } // write LLVM IR @@ -402,14 +423,22 @@ void writeModule(llvm::Module *m, const char *filename) { const auto llpath = replaceExtensionWith(ll_ext, filename); Logger::println("Writing LLVM IR to: %s\n", llpath.c_str()); std::error_code errinfo; - llvm::raw_fd_ostream aos(llpath.c_str(), errinfo, llvm::sys::fs::OF_None); - if (aos.has_error()) { + llvm::ToolOutputFile aos(llpath.c_str(), errinfo, llvm::sys::fs::OF_None); + if (aos.os().has_error()) { error(Loc(), "cannot write LLVM IR file '%s': %s", llpath.c_str(), errinfo.message().c_str()); fatal(); } AssemblyAnnotator annotator(m->getDataLayout()); - m->print(aos, &annotator); + m->print(aos.os(), &annotator); + + // Terminate upon errors during the LLVM passes. + if (global.errors || global.warnings) { + Logger::println("Aborting because of errors/warnings during LLVM passes"); + fatal(); + } + + aos.keep(); } const bool writeObj = outputObj && !emitBitcodeAsObjectFile; diff --git a/driver/tool.cpp b/driver/tool.cpp index b5ceb226a9e..dea42d05a58 100644 --- a/driver/tool.cpp +++ b/driver/tool.cpp @@ -76,7 +76,7 @@ std::string getProgram(const char *fallbackName, //////////////////////////////////////////////////////////////////////////////// -std::string getGcc() { return getProgram("cc", &gcc, "CC"); } +std::string getGcc(const char *fallback) { return getProgram(fallback, &gcc, "CC"); } //////////////////////////////////////////////////////////////////////////////// @@ -121,62 +121,45 @@ void appendTargetArgsForGcc(std::vector &args) { } return; - case Triple::riscv64: - { - std::string mabi = getABI(triple); - args.push_back("-mabi=" + mabi); - - extern llvm::TargetMachine* gTargetMachine; - auto featuresStr = gTargetMachine->getTargetFeatureString(); - llvm::SmallVector features; - featuresStr.split(features, ",", -1, false); - - // Returns true if 'feature' is enabled and false otherwise. Handles the - // case where the feature is specified multiple times ('+m,-m'), and - // takes the last occurrence. - auto hasFeature = [&features](llvm::StringRef feature) { - for (int i = features.size() - 1; i >= 0; i--) { - auto f = features[i]; - if (f.substr(1) == feature) { - return f[0] == '+'; - } - } - return false; - }; - - std::string march; - if (triple.isArch64Bit()) - march = "rv64"; - else - march = "rv32"; - bool m = hasFeature("m"); - bool a = hasFeature("a"); - bool f = hasFeature("f"); - bool d = hasFeature("d"); - bool c = hasFeature("c"); - bool g = false; - - if (m && a && f && d) { - march += "g"; - g = true; - } else { - march += "i"; - if (m) - march += "m"; - if (a) - march += "a"; - if (f) - march += "f"; - if (d) - march += "d"; - } - if (c) - march += "c"; - if (!g) - march += "_zicsr_zifencei"; - args.push_back("-march=" + march); + case Triple::riscv64: { + extern llvm::TargetMachine* gTargetMachine; + const auto featuresStr = gTargetMachine->getTargetFeatureString(); + llvm::SmallVector features; + featuresStr.split(features, ",", -1, false); + + const std::string mabi = getABI(triple, features); + args.push_back("-mabi=" + mabi); + + std::string march = triple.isArch64Bit() ? "rv64" : "rv32"; + const bool m = isFeatureEnabled(features, "m"); + const bool a = isFeatureEnabled(features, "a"); + const bool f = isFeatureEnabled(features, "f"); + const bool d = isFeatureEnabled(features, "d"); + const bool c = isFeatureEnabled(features, "c"); + bool g = false; + + if (m && a && f && d) { + march += "g"; + g = true; + } else { + march += "i"; + if (m) + march += "m"; + if (a) + march += "a"; + if (f) + march += "f"; + if (d) + march += "d"; } + if (c) + march += "c"; + if (!g) + march += "_zicsr_zifencei"; + args.push_back("-march=" + march); return; + } + default: break; } @@ -223,11 +206,11 @@ std::vector getFullArgs(const char *tool, //////////////////////////////////////////////////////////////////////////////// -int executeToolAndWait(const std::string &tool_, +int executeToolAndWait(const Loc &loc, const std::string &tool_, const std::vector &args, bool verbose) { const auto tool = findProgramByName(tool_); if (tool.empty()) { - error(Loc(), "cannot find program `%s`", tool_.c_str()); + error(loc, "cannot find program `%s`", tool_.c_str()); return -1; } @@ -249,9 +232,9 @@ int executeToolAndWait(const std::string &tool_, args::executeAndWait(std::move(fullArgs), rspEncoding, &errorMsg); if (status) { - error(Loc(), "%s failed with status: %d", tool.c_str(), status); + error(loc, "%s failed with status: %d", tool.c_str(), status); if (!errorMsg.empty()) { - errorSupplemental(Loc(), "message: %s", errorMsg.c_str()); + errorSupplemental(loc, "message: %s", errorMsg.c_str()); } } @@ -266,6 +249,7 @@ namespace windows { namespace { bool setupMsvcEnvironmentImpl( + bool forPreprocessingOnly, std::vector> *rollback) { const bool x64 = global.params.targetTriple->isArch64Bit(); @@ -280,30 +264,63 @@ bool setupMsvcEnvironmentImpl( const auto begin = std::chrono::steady_clock::now(); - VSOptions vsOptions; - vsOptions.initialize(); - if (!vsOptions.VSInstallDir) - return false; - - llvm::SmallVector libPaths; - if (auto vclibdir = vsOptions.getVCLibDir(x64)) - libPaths.push_back(vclibdir); - if (auto ucrtlibdir = vsOptions.getUCRTLibPath(x64)) - libPaths.push_back(ucrtlibdir); - if (auto sdklibdir = vsOptions.getSDKLibPath(x64)) - libPaths.push_back(sdklibdir); - - llvm::SmallVector binPaths; - const char *secondaryBindir = nullptr; - if (auto bindir = vsOptions.getVCBinDir(x64, secondaryBindir)) { - binPaths.push_back(bindir); - if (secondaryBindir) - binPaths.push_back(secondaryBindir); + static VSOptions vsOptions; // cache, as this can be expensive + if (!vsOptions.VSInstallDir) { + vsOptions.initialize(); + if (!vsOptions.VSInstallDir) + return false; } - const bool success = libPaths.size() == 3 && !binPaths.empty(); - if (!success) - return false; + // cache the environment variable prefixes too + static llvm::SmallVector binPaths; + static llvm::SmallVector includePaths; + static llvm::SmallVector libPaths; + + if (binPaths.empty()) { + // PATH + const char *secondaryBindir = nullptr; + if (auto bindir = vsOptions.getVCBinDir(x64, secondaryBindir)) { + binPaths.push_back(bindir); + if (secondaryBindir) + binPaths.push_back(secondaryBindir); + } else { + return false; + } + } + + if (forPreprocessingOnly && includePaths.empty()) { + // INCLUDE + if (auto vcincludedir = vsOptions.getVCIncludeDir()) { + includePaths.push_back(vcincludedir); + } else { + return false; + } + if (auto sdkincludedir = vsOptions.getSDKIncludePath()) { + includePaths.push_back(FileName::combine(sdkincludedir, "ucrt")); + includePaths.push_back(FileName::combine(sdkincludedir, "shared")); + includePaths.push_back(FileName::combine(sdkincludedir, "um")); + includePaths.push_back(FileName::combine(sdkincludedir, "winrt")); + includePaths.push_back(FileName::combine(sdkincludedir, "cppwinrt")); + } else { + includePaths.clear(); + return false; + } + } + + if (!forPreprocessingOnly && libPaths.empty()) { + // LIB + if (auto vclibdir = vsOptions.getVCLibDir(x64)) + libPaths.push_back(vclibdir); + if (auto ucrtlibdir = vsOptions.getUCRTLibPath(x64)) + libPaths.push_back(ucrtlibdir); + if (auto sdklibdir = vsOptions.getSDKLibPath(x64)) + libPaths.push_back(sdklibdir); + + if (libPaths.size() != 3) { + libPaths.clear(); + return false; + } + } if (!rollback) // check for availability only return true; @@ -311,9 +328,12 @@ bool setupMsvcEnvironmentImpl( if (global.params.verbose) message("Prepending to environment variables:"); - const auto preprendToEnvVar = + const auto prependToEnvVar = [rollback](const char *key, const wchar_t *wkey, const llvm::SmallVectorImpl &entries) { + if (entries.empty()) + return; + wchar_t *originalValue = _wgetenv(wkey); llvm::SmallString<256> head; @@ -343,8 +363,9 @@ bool setupMsvcEnvironmentImpl( }; rollback->reserve(2); - preprendToEnvVar("LIB", L"LIB", libPaths); - preprendToEnvVar("PATH", L"PATH", binPaths); + prependToEnvVar("INCLUDE", L"INCLUDE", includePaths); + prependToEnvVar("LIB", L"LIB", libPaths); + prependToEnvVar("PATH", L"PATH", binPaths); if (global.params.verbose) { const auto end = std::chrono::steady_clock::now(); @@ -357,11 +378,11 @@ bool setupMsvcEnvironmentImpl( } } // anonymous namespace -bool isMsvcAvailable() { return setupMsvcEnvironmentImpl(nullptr); } +bool isMsvcAvailable() { return setupMsvcEnvironmentImpl(false, nullptr); } -bool MsvcEnvironmentScope::setup() { +bool MsvcEnvironmentScope::setup(bool forPreprocessingOnly) { rollback.clear(); - return setupMsvcEnvironmentImpl(&rollback); + return setupMsvcEnvironmentImpl(forPreprocessingOnly, &rollback); } MsvcEnvironmentScope::~MsvcEnvironmentScope() { diff --git a/driver/tool.h b/driver/tool.h index d65d11ea79c..c6f0cc35a17 100644 --- a/driver/tool.h +++ b/driver/tool.h @@ -19,11 +19,13 @@ #include "llvm/Support/CommandLine.h" +struct Loc; + namespace opts { extern llvm::cl::opt linker; } -std::string getGcc(); +std::string getGcc(const char *fallback = "cc"); void appendTargetArgsForGcc(std::vector &args); std::string getProgram(const char *fallbackName, @@ -37,7 +39,7 @@ std::vector getFullArgs(const char *tool, const std::vector &args, bool printVerbose); -int executeToolAndWait(const std::string &tool, +int executeToolAndWait(const Loc &loc, const std::string &tool, const std::vector &args, bool verbose = false); @@ -51,7 +53,7 @@ struct MsvcEnvironmentScope { // Tries to set up the MSVC environment variables for the current process and // returns true if successful. The original environment is restored on // destruction. - bool setup(); + bool setup(bool forPreprocessingOnly = false); ~MsvcEnvironmentScope(); diff --git a/gen/abi/generic.h b/gen/abi/generic.h index e1e6f452f44..19b7aee0362 100644 --- a/gen/abi/generic.h +++ b/gen/abi/generic.h @@ -36,12 +36,8 @@ struct LLTypeMemoryLayout { const size_t sizeInBits = getTypeBitSize(type); assert(sizeInBits % 8 == 0); return llvm::VectorType::get(LLIntegerType::get(gIR->context(), 8), - sizeInBits / 8 -#if LDC_LLVM_VER >= 1100 - , - /*Scalable=*/false -#endif - ); + sizeInBits / 8, + /*Scalable=*/false); } if (LLStructType *structType = isaStruct(type)) { diff --git a/gen/abi/x86-64.cpp b/gen/abi/x86-64.cpp index df2491f4a27..5bb4534e955 100644 --- a/gen/abi/x86-64.cpp +++ b/gen/abi/x86-64.cpp @@ -322,7 +322,7 @@ void X86_64TargetABI::rewriteVarargs(IrFuncTy &fty, /** * The System V AMD64 ABI uses a special native va_list type - a 24-bytes struct * passed by reference. - * In druntime, the struct is aliased as object.__va_list; the actually used + * In druntime, the struct is aliased as object.__va_list_tag; the actually used * core.stdc.stdarg.va_list type is a __va_list_tag* pointer though to achieve * byref semantics. * This requires a little bit of compiler magic in the following @@ -375,7 +375,7 @@ Type *X86_64TargetABI::vaListType() { // using TypeIdentifier here is a bit wonky but works, as long as the name // is actually available in the scope (this is what DMD does, so if a better // solution is found there, this should be adapted). - return TypeIdentifier::create(Loc(), Identifier::idPool("__va_list")) + return TypeIdentifier::create(Loc(), Identifier::idPool("__va_list_tag")) ->pointerTo(); } diff --git a/gen/abi/x86.cpp b/gen/abi/x86.cpp index 1f5357e6074..366bfad1423 100644 --- a/gen/abi/x86.cpp +++ b/gen/abi/x86.cpp @@ -266,7 +266,7 @@ struct X86TargetABI : TargetABI { #else // Keep alignment for LLVM 13+, to prevent invalid `movaps` etc., // but limit to 4 (required according to runnable/ldc_cabi1.d). - auto align4 = LLAlign(4); + auto align4 = llvm::Align(4); if (arg->attrs.getAlignment(). #if LDC_LLVM_VER >= 1500 value_or diff --git a/gen/arrays.cpp b/gen/arrays.cpp index 14b1b26614d..da319d5e3a7 100644 --- a/gen/arrays.cpp +++ b/gen/arrays.cpp @@ -624,11 +624,10 @@ void initializeArrayLiteral(IRState *p, ArrayLiteralExp *ale, //////////////////////////////////////////////////////////////////////////////// LLConstant *DtoConstSlice(LLConstant *dim, LLConstant *ptr, Type *type) { LLConstant *values[2] = {dim, ptr}; - llvm::ArrayRef valuesRef = llvm::makeArrayRef(values, 2); LLStructType *lltype = type ? isaStruct(DtoType(type)) - : LLConstantStruct::getTypeForElements(gIR->context(), valuesRef); - return LLConstantStruct::get(lltype, valuesRef); + : LLConstantStruct::getTypeForElements(gIR->context(), values); + return LLConstantStruct::get(lltype, values); } //////////////////////////////////////////////////////////////////////////////// @@ -748,133 +747,6 @@ DSliceValue *DtoNewMulDimDynArray(const Loc &loc, Type *arrayType, return getSlice(arrayType, newptr); } -//////////////////////////////////////////////////////////////////////////////// -DSliceValue *DtoResizeDynArray(const Loc &loc, Type *arrayType, DValue *array, - LLValue *newdim) { - IF_LOG Logger::println("DtoResizeDynArray : %s", arrayType->toChars()); - LOG_SCOPE; - - assert(array); - assert(newdim); - assert(arrayType); - assert(arrayType->toBasetype()->ty == TY::Tarray); - - // decide on what runtime function to call based on whether the type is zero - // initialized - bool zeroInit = arrayType->toBasetype()->nextOf()->isZeroInit(); - - // call runtime - LLFunction *fn = - getRuntimeFunction(loc, gIR->module, zeroInit ? "_d_arraysetlengthT" - : "_d_arraysetlengthiT"); - - LLValue *newArray = gIR->CreateCallOrInvoke( - fn, DtoTypeInfoOf(loc, arrayType), newdim, - DtoBitCast(DtoLVal(array), fn->getFunctionType()->getParamType(2)), - ".gc_mem"); - - return getSlice(arrayType, newArray); -} - -//////////////////////////////////////////////////////////////////////////////// - -static LLValue *DtoSlicePtr(DValue *dval) { - Loc loc; - Type *vt = dval->type->toBasetype(); - if (vt->ty == TY::Tarray) { - return makeLValue(loc, dval); - } - - bool isStaticArray = vt->ty == TY::Tsarray; - LLValue *val = isStaticArray ? DtoLVal(dval) : makeLValue(loc, dval); - LLStructType *i8arrty = DtoArrayType(LLType::getInt8Ty(gIR->context())); - LLValue *array = DtoRawAlloca(i8arrty, 0, ".array"); - LLValue *len = isStaticArray ? DtoArrayLen(dval) : DtoConstSize_t(1); - DtoStore(len, DtoGEP(i8arrty, array, 0u, 0)); - DtoStore(DtoBitCast(val, getVoidPtrType()), DtoGEP(i8arrty, array, 0, 1)); - return array; -} - -static llvm::StructType *DtoSlicePtrType(DValue *dval) { - if(dval->type->toBasetype()->ty == TY::Tarray) - return isaStruct(DtoType(dval->type->toBasetype())); - else - return DtoArrayType(LLType::getInt8Ty(gIR->context())); -} - -static LLValue *DtoSlicePtr(Expression *e) { - return DtoSlicePtr(toElem(e)); -} - -DSliceValue *DtoCatArrays(const Loc &loc, Type *arrayType, Expression *exp1, - Expression *exp2) { - IF_LOG Logger::println("DtoCatArrays"); - LOG_SCOPE; - - llvm::SmallVector args; - LLFunction *fn = nullptr; - - if (auto ce = exp1->isCatExp()) { // handle multiple concat - fn = getRuntimeFunction(loc, gIR->module, "_d_arraycatnTX"); - - // Create array of slices - typedef llvm::SmallVector ArgVector; - ArgVector arrs; - DValue * dval = toElem(exp2); - arrs.push_back(DtoSlicePtr(dval)); - do { - arrs.push_back(DtoSlicePtr(ce->e2)); - ce = static_cast(ce->e1); - } while (ce->op == EXP::concatenate); - arrs.push_back(DtoSlicePtr(ce)); - - // Create static array from slices - LLPointerType *ptrarraytype = isaPointer(arrs[0]); - assert(ptrarraytype && "Expected pointer type"); - LLStructType *arraytype = DtoSlicePtrType(dval); - assert(arraytype && "Expected struct type"); - LLArrayType *type = LLArrayType::get(arraytype, arrs.size()); - LLValue *array = DtoRawAlloca(type, 0, ".slicearray"); - unsigned int i = 0; - for (ArgVector::reverse_iterator I = arrs.rbegin(), E = arrs.rend(); I != E; - ++I) { - LLValue *v = DtoLoad(arraytype, DtoBitCast(*I, ptrarraytype)); - DtoStore(v, DtoGEP(type, array, 0, i++, ".slice")); - } - - LLStructType *type2 = DtoArrayType(arraytype); - LLValue *array2 = DtoRawAlloca(type2, 0, ".array"); - DtoStore(DtoConstSize_t(arrs.size()), DtoGEP(type2, array2, 0u, 0, ".len")); - DtoStore(DtoBitCast(array, ptrarraytype), DtoGEP(type2, array2, 0, 1, ".ptr")); - LLType *bytearrarr = DtoArrayType(DtoArrayType(LLType::getInt8Ty(gIR->context()))); - LLType *pbytearrarr = getPtrToType(bytearrarr); - LLValue *val = DtoLoad(bytearrarr, DtoBitCast(array2, pbytearrarr)); - - // TypeInfo ti - args.push_back(DtoTypeInfoOf(loc, arrayType)); - // byte[][] arrs - args.push_back(val); - } else { - fn = getRuntimeFunction(loc, gIR->module, "_d_arraycatT"); - - // TypeInfo ti - args.push_back(DtoTypeInfoOf(loc, arrayType)); - - auto loadArray = [fn](Expression* e, int paramTypeIdx) { - DValue * dval = toElem(e); - LLValue *val = DtoLoad(DtoSlicePtrType(dval), DtoSlicePtr(dval)); - return DtoSlicePaint(val, fn->getFunctionType()->getParamType(paramTypeIdx)); - }; - // byte[] x - args.push_back(loadArray(exp1,1)); - // byte[] y - args.push_back(loadArray(exp2,2)); - } - - auto newArray = gIR->CreateCallOrInvoke(fn, args, ".appendedArray"); - return getSlice(arrayType, newArray); -} - //////////////////////////////////////////////////////////////////////////////// DSliceValue *DtoAppendDChar(const Loc &loc, DValue *arr, Expression *exp, diff --git a/gen/arrays.h b/gen/arrays.h index 8ea6e5ccfbc..3e893bbdc79 100644 --- a/gen/arrays.h +++ b/gen/arrays.h @@ -64,8 +64,6 @@ DSliceValue *DtoNewDynArray(const Loc &loc, Type *arrayType, DValue *dim, bool defaultInit = true); DSliceValue *DtoNewMulDimDynArray(const Loc &loc, Type *arrayType, DValue **dims, size_t ndims); -DSliceValue *DtoResizeDynArray(const Loc &loc, Type *arrayType, DValue *array, - llvm::Value *newdim); DSliceValue *DtoCatArrays(const Loc &loc, Type *type, Expression *e1, Expression *e2); diff --git a/gen/asmstmt.cpp b/gen/asmstmt.cpp index 48d8342bf9c..7ca35acc0ea 100644 --- a/gen/asmstmt.cpp +++ b/gen/asmstmt.cpp @@ -501,9 +501,7 @@ void CompoundAsmStatement_toIR(CompoundAsmStatement *stmt, IRState *p) { { FuncDeclaration *fd = gIR->func()->decl; - OutBuffer mangleBuf; - mangleToBuffer(fd, &mangleBuf); - const char *fdmangle = mangleBuf.peekChars(); + const char *fdmangle = mangleExact(fd); // we use a simple static counter to make sure the new end labels are // unique diff --git a/gen/coverage.cpp b/gen/coverage.cpp index 2d1a0b6745c..89db3f1758b 100644 --- a/gen/coverage.cpp +++ b/gen/coverage.cpp @@ -44,16 +44,17 @@ void emitCoverageLinecountInc(const Loc &loc) { // Do an atomic increment, so this works when multiple threads are executed. gIR->ir->CreateAtomicRMW(llvm::AtomicRMWInst::Add, ptr, DtoConstUint(1), #if LDC_LLVM_VER >= 1300 - LLAlign(4), + llvm::Align(4), #endif llvm::AtomicOrdering::Monotonic); break; case opts::CoverageIncrement::nonatomic: { // Do a non-atomic increment, user is responsible for correct results with // multithreaded execution - llvm::LoadInst *load = gIR->ir->CreateAlignedLoad(i32Type, ptr, LLAlign(4)); + llvm::LoadInst *load = + gIR->ir->CreateAlignedLoad(i32Type, ptr, llvm::Align(4)); llvm::StoreInst *store = gIR->ir->CreateAlignedStore( - gIR->ir->CreateAdd(load, DtoConstUint(1)), ptr, LLAlign(4)); + gIR->ir->CreateAdd(load, DtoConstUint(1)), ptr, llvm::Align(4)); // add !nontemporal attribute, to inform the optimizer that caching is not // needed llvm::MDNode *node = llvm::MDNode::get( @@ -66,7 +67,7 @@ void emitCoverageLinecountInc(const Loc &loc) { // Do a boolean set, avoiding a memory read (blocking) and threading issues // at the cost of not "counting" llvm::StoreInst *store = - gIR->ir->CreateAlignedStore(DtoConstUint(1), ptr, LLAlign(4)); + gIR->ir->CreateAlignedStore(DtoConstUint(1), ptr, llvm::Align(4)); // add !nontemporal attribute, to inform the optimizer that caching is not // needed llvm::MDNode *node = llvm::MDNode::get( diff --git a/gen/ctfloat.cpp b/gen/ctfloat.cpp index 0ba8a5e838c..ef033c018df 100644 --- a/gen/ctfloat.cpp +++ b/gen/ctfloat.cpp @@ -26,11 +26,8 @@ union CTFloatUnion { APFloat parseLiteral(const llvm::fltSemantics &semantics, const char *literal, bool *isOutOfRange = nullptr) { APFloat ap(semantics, APFloat::uninitialized); - auto r = -#if LDC_LLVM_VER >= 1000 - llvm::cantFail -#endif - (ap.convertFromString(literal, APFloat::rmNearestTiesToEven)); + auto r = llvm::cantFail( + ap.convertFromString(literal, APFloat::rmNearestTiesToEven)); if (isOutOfRange) { *isOutOfRange = (r & (APFloat::opOverflow | APFloat::opUnderflow)) != 0; } diff --git a/gen/dcompute/druntime.cpp b/gen/dcompute/druntime.cpp index 6867765466e..aee93f4c532 100644 --- a/gen/dcompute/druntime.cpp +++ b/gen/dcompute/druntime.cpp @@ -42,8 +42,13 @@ bool isFromLDC_OpenCL(Dsymbol *sym) { } llvm::Optional toDcomputePointer(StructDeclaration *sd) { - if (sd->ident != Id::dcPointer || !isFromLDC_DCompute(sd)) + if (sd->ident != Id::dcPointer || !isFromLDC_DCompute(sd)) { +#if LDC_LLVM_VER < 1600 return llvm::Optional(llvm::None); +#else + return std::optional(std::nullopt); +#endif + } TemplateInstance *ti = sd->isInstantiated(); int addrspace = isExpression((*ti->tiargs)[0])->toInteger(); diff --git a/gen/declarations.cpp b/gen/declarations.cpp index 040bb862e58..d98652a2ebb 100644 --- a/gen/declarations.cpp +++ b/gen/declarations.cpp @@ -284,7 +284,9 @@ class CodegenVisitor : public Visitor { void visit(FuncDeclaration *decl) override { // don't touch function aliases, they don't contribute any new symbols - if (!decl->skipCodegen() && !decl->isFuncAliasDeclaration()) { + if (!decl->skipCodegen() && !decl->isFuncAliasDeclaration() && + // skip fwd declarations (IR-declared lazily) + decl->fbody) { DtoDefineFunction(decl); } } @@ -417,12 +419,23 @@ class CodegenVisitor : public Visitor { std::string arg = ("/DEFAULTLIB:\"" + name + "\"").str(); gIR->addLinkerOption(llvm::StringRef(arg)); } else { - size_t const n = name.size() + 3; - char *arg = static_cast(mem.xmalloc(n)); - arg[0] = '-'; - arg[1] = 'l'; - memcpy(arg + 2, name.data(), name.size()); - arg[n - 1] = 0; + const bool isStaticLib = name.endswith(".a"); + const size_t nameLen = name.size(); + + char *arg = nullptr; + if (!isStaticLib) { // name => -lname + const size_t n = nameLen + 3; + arg = static_cast(mem.xmalloc(n)); + arg[0] = '-'; + arg[1] = 'l'; + memcpy(arg + 2, name.data(), nameLen); + arg[n - 1] = 0; + } else { + arg = static_cast((mem.xmalloc(nameLen + 1))); + memcpy(arg, name.data(), nameLen); + arg[nameLen] = 0; + } + global.params.linkswitches.push(arg); if (triple.isOSBinFormatMachO()) { diff --git a/gen/dibuilder.cpp b/gen/dibuilder.cpp index 1e817ee3f4a..72e8b949950 100644 --- a/gen/dibuilder.cpp +++ b/gen/dibuilder.cpp @@ -44,6 +44,13 @@ namespace cl = llvm::cl; using LLMetadata = llvm::Metadata; +#if LDC_LLVM_VER >= 1600 +namespace llvm { + template using Optional = std::optional; + inline constexpr std::nullopt_t None = std::nullopt; +} +#endif + static cl::opt emitColumnInfo( "gcolumn-info", cl::ZeroOrMore, cl::Hidden, cl::desc("Include column numbers in line debug infos. Defaults to " @@ -385,12 +392,8 @@ DIType DIBuilder::CreateVectorType(TypeVector *type) { LLType *T = DtoType(type); const auto dim = type->basetype->isTypeSArray()->dim->toInteger(); -#if LDC_LLVM_VER >= 1100 const auto Dim = llvm::ConstantAsMetadata::get(DtoConstSize_t(dim)); auto subscript = DBuilder.getOrCreateSubrange(Dim, nullptr, nullptr, nullptr); -#else - auto subscript = DBuilder.getOrCreateSubrange(0, dim); -#endif return DBuilder.createVectorType( getTypeAllocSize(T) * 8, // size (bits) @@ -669,13 +672,9 @@ DIType DIBuilder::CreateSArrayType(TypeSArray *type) { for (; te->ty == TY::Tsarray; te = te->nextOf()) { TypeSArray *tsa = static_cast(te); const auto count = tsa->dim->toInteger(); -#if LDC_LLVM_VER >= 1100 const auto Count = llvm::ConstantAsMetadata::get(DtoConstSize_t(count)); const auto subscript = DBuilder.getOrCreateSubrange(Count, nullptr, nullptr, nullptr); -#else - const auto subscript = DBuilder.getOrCreateSubrange(0, count); -#endif subscripts.push_back(subscript); } @@ -1292,9 +1291,7 @@ void DIBuilder::EmitGlobalVariable(llvm::GlobalVariable *llVar, vd->loc.linnum, // line num CreateTypeDescription(vd->type), // type vd->visibility.kind == Visibility::private_, // is local to unit -#if LDC_LLVM_VER >= 1000 !(vd->storage_class & STCextern), // bool isDefined -#endif nullptr, // DIExpression *Expr Decl // declaration ); diff --git a/gen/dvalue.cpp b/gen/dvalue.cpp index 02b4166dd06..8ef2c3911fd 100644 --- a/gen/dvalue.cpp +++ b/gen/dvalue.cpp @@ -180,7 +180,7 @@ DBitFieldLValue::DBitFieldLValue(Type *t, LLValue *ptr, BitFieldDeclaration *bf) DRValue *DBitFieldLValue::getRVal() { const auto sizeInBits = intType->getBitWidth(); const auto ptr = DtoBitCast(val, getPtrToType(intType)); - LLValue *v = gIR->ir->CreateAlignedLoad(intType, ptr, LLMaybeAlign(1)); + LLValue *v = gIR->ir->CreateAlignedLoad(intType, ptr, llvm::MaybeAlign(1)); if (bf->type->isunsigned()) { if (auto n = bf->bitOffset) @@ -208,7 +208,8 @@ void DBitFieldLValue::store(LLValue *value) { const auto mask = llvm::APInt::getLowBitsSet(intType->getBitWidth(), bf->fieldWidth); - const auto oldVal = gIR->ir->CreateAlignedLoad(intType, ptr, LLMaybeAlign(1)); + const auto oldVal = + gIR->ir->CreateAlignedLoad(intType, ptr, llvm::MaybeAlign(1)); const auto maskedOldVal = gIR->ir->CreateAnd(oldVal, ~(mask << bf->bitOffset)); @@ -218,7 +219,7 @@ void DBitFieldLValue::store(LLValue *value) { bfVal = gIR->ir->CreateShl(bfVal, n); const auto newVal = gIR->ir->CreateOr(maskedOldVal, bfVal); - gIR->ir->CreateAlignedStore(newVal, ptr, LLMaybeAlign(1)); + gIR->ir->CreateAlignedStore(newVal, ptr, llvm::MaybeAlign(1)); } DDcomputeLValue::DDcomputeLValue(Type *t, llvm::Type * llt, LLValue *v) : DLValue(t, v) { diff --git a/gen/dynamiccompile.cpp b/gen/dynamiccompile.cpp index f0a0906c3f9..b982013c4f3 100644 --- a/gen/dynamiccompile.cpp +++ b/gen/dynamiccompile.cpp @@ -766,12 +766,8 @@ void createThunkFunc(llvm::Module &module, const llvm::Function *src, for (auto &arg : dst->args()) { args.push_back(&arg); } -#if LDC_LLVM_VER >= 1100 auto ret = builder.CreateCall( llvm::FunctionCallee(dst->getFunctionType(), thunkPtr), args); -#else - auto ret = builder.CreateCall(thunkPtr, args); -#endif ret->setCallingConv(src->getCallingConv()); ret->setAttributes(src->getAttributes()); if (dst->getReturnType()->isVoidTy()) { diff --git a/gen/funcgenstate.cpp b/gen/funcgenstate.cpp index fbdb9a34625..879e7cf7a02 100644 --- a/gen/funcgenstate.cpp +++ b/gen/funcgenstate.cpp @@ -100,13 +100,13 @@ llvm::BasicBlock *SwitchCaseTargets::getOrCreate(Statement *stmt, } FuncGenState::FuncGenState(IrFunction &irFunc, IRState &irs) - : irFunc(irFunc), scopes(irs), jumpTargets(scopes), switchTargets(), - irs(irs) {} + : irFunc(irFunc), scopes(irs), localVariableLifetimeAnnotator(irs), + jumpTargets(scopes), switchTargets(), irs(irs) {} -LLCallBasePtr FuncGenState::callOrInvoke(llvm::Value *callee, - llvm::FunctionType *calleeType, - llvm::ArrayRef args, - const char *name, bool isNothrow) { +llvm::CallBase *FuncGenState::callOrInvoke(llvm::Value *callee, + llvm::FunctionType *calleeType, + llvm::ArrayRef args, + const char *name, bool isNothrow) { // If this is a direct call, we might be able to use the callee attributes // to our advantage. llvm::Function *calleeFn = llvm::dyn_cast(callee); @@ -124,11 +124,7 @@ LLCallBasePtr FuncGenState::callOrInvoke(llvm::Value *callee, // calls inside a funclet must be annotated with its value llvm::SmallVector BundleList; -#if LDC_LLVM_VER >= 1100 llvm::FunctionCallee calleeArg(calleeType, callee); -#else - auto calleeArg = callee; -#endif if (doesNotThrow || scopes.empty()) { auto call = irs.ir->CreateCall(calleeArg, args, BundleList, name); diff --git a/gen/funcgenstate.h b/gen/funcgenstate.h index af719c74b8a..634286c5668 100644 --- a/gen/funcgenstate.h +++ b/gen/funcgenstate.h @@ -17,6 +17,7 @@ #include "gen/irstate.h" #include "gen/pgo_ASTbased.h" #include "gen/trycatchfinally.h" +#include "gen/variable_lifetime.h" #include "llvm/ADT/DenseMap.h" #include @@ -176,6 +177,8 @@ class FuncGenState { TryCatchFinallyScopes scopes; + LocalVariableLifetimeAnnotator localVariableLifetimeAnnotator; + JumpTargets jumpTargets; // PGO information @@ -199,10 +202,10 @@ class FuncGenState { /// Emits a call or invoke to the given callee, depending on whether there /// are catches/cleanups active or not. - LLCallBasePtr callOrInvoke(llvm::Value *callee, - llvm::FunctionType *calleeType, - llvm::ArrayRef args, - const char *name = "", bool isNothrow = false); + llvm::CallBase *callOrInvoke(llvm::Value *callee, + llvm::FunctionType *calleeType, + llvm::ArrayRef args, + const char *name = "", bool isNothrow = false); private: IRState &irs; diff --git a/gen/functions.cpp b/gen/functions.cpp index d3f4e8d7baf..0d6da623d88 100644 --- a/gen/functions.cpp +++ b/gen/functions.cpp @@ -173,7 +173,8 @@ llvm::FunctionType *DtoFunctionType(Type *type, IrFuncTy &irFty, Type *thistype, // Non-typesafe variadics (both C and D styles) are also variadics on the LLVM // level. - const bool isLLVMVariadic = (f->parameterList.varargs == VARARGvariadic); + const bool isLLVMVariadic = (f->parameterList.varargs == VARARGvariadic || + f->parameterList.varargs == VARARGKRvariadic); if (isLLVMVariadic && f->linkage == LINK::d) { // Add extra `_arguments` parameter for D-style variadic functions. newIrFty.arg_arguments = @@ -216,9 +217,7 @@ llvm::FunctionType *DtoFunctionType(Type *type, IrFuncTy &irFty, Type *thistype, // opaque struct if (!opts::fNullPointerIsValid) attrs.addAttribute(LLAttribute::NonNull); -#if LDC_LLVM_VER >= 1100 attrs.addAttribute(LLAttribute::NoUndef); -#endif } else { attrs.addDereferenceableAttr(loweredDType->size()); } @@ -497,42 +496,11 @@ void applyTargetMachineAttributes(llvm::Function &func, const auto cpu = dcompute ? "" : target.getTargetCPU(); const auto features = dcompute ? "" : target.getTargetFeatureString(); -#if LDC_LLVM_VER >= 1000 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"); -#else - if (!cpu.empty()) - func.addFnAttr("target-cpu", cpu); - if (!features.empty()) - func.addFnAttr("target-features", features); - - // Floating point settings - const auto &TO = target.Options; - func.addFnAttr("unsafe-fp-math", TO.UnsafeFPMath ? "true" : "false"); - // This option was removed from llvm::TargetOptions in LLVM 5.0. - // Clang sets this to true when `-cl-mad-enable` is passed (OpenCL only). - // TODO: implement interface for this option. - const bool lessPreciseFPMADOption = false; - func.addFnAttr("less-precise-fpmad", - lessPreciseFPMADOption ? "true" : "false"); - func.addFnAttr("no-infs-fp-math", TO.NoInfsFPMath ? "true" : "false"); - func.addFnAttr("no-nans-fp-math", TO.NoNaNsFPMath ? "true" : "false"); - - switch (whichFramePointersToEmit()) { - case llvm::FramePointer::None: - func.addFnAttr("frame-pointer", "none"); - break; - case llvm::FramePointer::NonLeaf: - func.addFnAttr("frame-pointer", "non-leaf"); - break; - case llvm::FramePointer::All: - func.addFnAttr("frame-pointer", "all"); - break; - } -#endif // LDC_LLVM_VER < 1000 } void applyXRayAttributes(FuncDeclaration &fdecl, llvm::Function &func) { @@ -1260,11 +1228,7 @@ void DtoDefineFunction(FuncDeclaration *fd, bool linkageAvailableExternally) { } applyXRayAttributes(*fd, *func); if (opts::fNullPointerIsValid) { -#if LDC_LLVM_VER >= 1100 func->addFnAttr(LLAttribute::NullPointerIsValid); -#else - func->addFnAttr("null-pointer-is-valid", "true"); -#endif } if (opts::fSplitStack && !hasNoSplitStackUDA(fd)) { func->addFnAttr("split-stack"); diff --git a/gen/irstate.cpp b/gen/irstate.cpp index 5894088dbba..5cbd2d685ae 100644 --- a/gen/irstate.cpp +++ b/gen/irstate.cpp @@ -48,14 +48,14 @@ llvm::Function *IRState::topfunc() { return func()->getLLVMFunc(); } llvm::Instruction *IRState::topallocapoint() { return funcGen().allocapoint; } std::unique_ptr IRState::setInsertPoint(llvm::BasicBlock *bb) { - auto savedScope = llvm::make_unique(builder); + auto savedScope = std::make_unique(builder); builder.SetInsertPoint(bb); return savedScope; } std::unique_ptr IRState::saveInsertPoint() { - return llvm::make_unique(builder); + return std::make_unique(builder); } bool IRState::scopereturned() { @@ -147,7 +147,7 @@ IRState::setGlobalVarInitializer(LLGlobalVariable *&globalVar, module, initializer->getType(), globalVar->isConstant(), globalVar->getLinkage(), nullptr, "", nullptr, globalVar->getThreadLocalMode()); - globalHelperVar->setAlignment(LLMaybeAlign(globalVar->getAlignment())); + globalHelperVar->setAlignment(llvm::MaybeAlign(globalVar->getAlignment())); globalHelperVar->setComdat(globalVar->getComdat()); globalHelperVar->setDLLStorageClass(globalVar->getDLLStorageClass()); globalHelperVar->setSection(globalVar->getSection()); diff --git a/gen/llvm.h b/gen/llvm.h index daebd1f2e7c..6cd420340bc 100644 --- a/gen/llvm.h +++ b/gen/llvm.h @@ -31,31 +31,10 @@ #include "llvm/IR/IRBuilder.h" #include "llvm/IR/DebugInfo.h" -#if LDC_LLVM_VER >= 1000 -// LLVM >= 10 requires C++14 and no longer has llvm::make_unique. Add it back -// and point to std::make_unique. -#include -namespace llvm { -using std::make_unique; -} -#endif - using llvm::APFloat; using llvm::APInt; using llvm::IRBuilder; -#if LDC_LLVM_VER >= 1000 -#if LDC_LLVM_VER >= 1100 -#define LLAlign llvm::Align -#else -#define LLAlign llvm::MaybeAlign -#endif -#define LLMaybeAlign llvm::MaybeAlign -#else -#define LLAlign -#define LLMaybeAlign -#endif - #define GET_INTRINSIC_DECL(_X) \ (llvm::Intrinsic::getDeclaration(&gIR->module, llvm::Intrinsic::_X)) @@ -81,5 +60,3 @@ using llvm::IRBuilder; #define LLConstantFP llvm::ConstantFP #define LLSmallVector llvm::SmallVector - -using LLCallBasePtr = llvm::CallBase *; diff --git a/gen/llvmhelpers.cpp b/gen/llvmhelpers.cpp index 2a4a550ee4a..6e517591702 100644 --- a/gen/llvmhelpers.cpp +++ b/gen/llvmhelpers.cpp @@ -191,7 +191,7 @@ llvm::AllocaInst *DtoArrayAlloca(Type *type, unsigned arraysize, lltype, gIR->module.getDataLayout().getAllocaAddrSpace(), DtoConstUint(arraysize), name, gIR->topallocapoint()); if (auto alignment = DtoAlignment(type)) { - ai->setAlignment(LLAlign(alignment)); + ai->setAlignment(llvm::Align(alignment)); } return ai; } @@ -202,7 +202,7 @@ llvm::AllocaInst *DtoRawAlloca(LLType *lltype, size_t alignment, lltype, gIR->module.getDataLayout().getAllocaAddrSpace(), name, gIR->topallocapoint()); if (alignment) { - ai->setAlignment(LLAlign(alignment)); + ai->setAlignment(llvm::Align(alignment)); } return ai; } @@ -919,19 +919,29 @@ void DtoVarDeclaration(VarDeclaration *vd) { Type *type = isSpecialRefVar(vd) ? vd->type->pointerTo() : vd->type; llvm::Value *allocainst; + bool isRealAlloca = false; LLType *lltype = DtoType(type); // void for noreturn if (lltype->isVoidTy() || gDataLayout->getTypeSizeInBits(lltype) == 0) { allocainst = llvm::ConstantPointerNull::get(getPtrToType(lltype)); } else if (type != vd->type) { allocainst = DtoAlloca(type, vd->toChars()); + isRealAlloca = true; } else { allocainst = DtoAlloca(vd, vd->toChars()); + isRealAlloca = true; } irLocal->value = allocainst; if (!lltype->isVoidTy()) gIR->DBuilder.EmitLocalVariable(allocainst, vd); + + // Lifetime annotation is only valid on alloca. + if (isRealAlloca) { + // The lifetime of a stack variable starts from the point it is declared + gIR->funcGen().localVariableLifetimeAnnotator.addLocalVariable( + allocainst, DtoConstUlong(type->size())); + } } IF_LOG Logger::cout() << "llvm value for decl: " << *getIrLocal(vd)->value @@ -1178,7 +1188,7 @@ LLConstant *DtoConstExpInit(const Loc &loc, Type *targetType, Expression *exp) { val = llvm::ConstantArray::get(at, elements); } - (void)numTotalVals; + (void)numTotalVals; (void) product; // Silence unused variable warning when assert is disabled. assert(product == numTotalVals); return val; } @@ -1192,10 +1202,8 @@ LLConstant *DtoConstExpInit(const Loc &loc, Type *targetType, Expression *exp) { static_cast(tv->basetype)->dim->toInteger(); #if LDC_LLVM_VER >= 1200 const auto elementCount = llvm::ElementCount::getFixed(elemCount); -#elif LDC_LLVM_VER >= 1100 - const auto elementCount = llvm::ElementCount(elemCount, false); #else - const auto elementCount = elemCount; + const auto elementCount = llvm::ElementCount(elemCount, false); #endif return llvm::ConstantVector::getSplat(elementCount, val); } @@ -1271,11 +1279,7 @@ static char *DtoOverloadedIntrinsicName(TemplateInstance *ti, if (dtype->isPPC_FP128Ty()) { // special case replacement = "ppcf128"; } else if (dtype->isVectorTy()) { -#if LDC_LLVM_VER >= 1100 auto vectorType = llvm::cast(dtype); -#else - auto vectorType = llvm::cast(dtype); -#endif llvm::raw_string_ostream stream(replacement); stream << 'v' << vectorType->getNumElements() << prefix << gDataLayout->getTypeSizeInBits(vectorType->getElementType()); diff --git a/gen/modules.cpp b/gen/modules.cpp index 3372e5c3190..6ad3f6e9c01 100644 --- a/gen/modules.cpp +++ b/gen/modules.cpp @@ -393,8 +393,27 @@ void registerModuleInfo(Module *m) { emitModuleRefToSection(mangle, moduleInfoSym); } } + +void addModuleFlags(llvm::Module &m) { +#if LDC_LLVM_VER >= 1500 + const auto ModuleMinFlag = llvm::Module::Min; +#else + const auto ModuleMinFlag = llvm::Module::Warning; // Fallback value +#endif + + if (opts::fCFProtection == opts::CFProtectionType::Return || + opts::fCFProtection == opts::CFProtectionType::Full) { + m.addModuleFlag(ModuleMinFlag, "cf-protection-return", 1); + } + + if (opts::fCFProtection == opts::CFProtectionType::Branch || + opts::fCFProtection == opts::CFProtectionType::Full) { + m.addModuleFlag(ModuleMinFlag, "cf-protection-branch", 1); + } } +} // anonymous namespace + void codegenModule(IRState *irs, Module *m) { TimeTraceScope timeScope("Generate IR", m->toChars(), m->loc); @@ -433,9 +452,11 @@ void codegenModule(IRState *irs, Module *m) { // Skip emission of all the additional module metadata if: // a) the -betterC switch is on, - // b) requested explicitly by the user via pragma(LDC_no_moduleinfo), or if - // c) there's no ModuleInfo declaration. - if (global.params.useModuleInfo && !m->noModuleInfo && Module::moduleinfo) { + // b) requested explicitly by the user via pragma(LDC_no_moduleinfo), + // c) there's no ModuleInfo declaration, or if + // d) the module is a C file. + if (global.params.useModuleInfo && !m->noModuleInfo && Module::moduleinfo && + m->filetype != FileType::c) { // generate ModuleInfo registerModuleInfo(m); } @@ -444,6 +465,8 @@ void codegenModule(IRState *irs, Module *m) { addCoverageAnalysisInitializer(m); } + addModuleFlags(irs->module); + gIR = nullptr; irs->dmodule = nullptr; } diff --git a/gen/ms-cxx-helper.cpp b/gen/ms-cxx-helper.cpp index 52814fd5cfc..b66bc0cffd8 100644 --- a/gen/ms-cxx-helper.cpp +++ b/gen/ms-cxx-helper.cpp @@ -114,7 +114,12 @@ void cloneBlocks(const std::vector &srcblocks, if (!newInst) newInst = Inst->clone(); +#if LDC_LLVM_VER < 1600 nbb->getInstList().push_back(newInst); +#else + newInst->insertInto(nbb, nbb->end()); +#endif + VMap[Inst] = newInst; // Add instruction map to value. if (unwindTo) if (auto dest = getUnwindDest(Inst)) diff --git a/gen/optimizer.cpp b/gen/optimizer.cpp index 7d5946e276c..9f831c61139 100644 --- a/gen/optimizer.cpp +++ b/gen/optimizer.cpp @@ -49,9 +49,7 @@ #include "llvm/Transforms/Scalar/LICM.h" #include "llvm/Transforms/Scalar/Reassociate.h" #endif -#if LDC_LLVM_VER >= 1000 #include "llvm/Transforms/Instrumentation/SanitizerCoverage.h" -#endif extern llvm::TargetMachine *gTargetMachine; using namespace llvm; @@ -141,15 +139,6 @@ bool willCrossModuleInline() { return enableCrossModuleInlining == llvm::cl::BOU_TRUE && willInline(); } -#if LDC_LLVM_VER < 1000 -llvm::FramePointer::FP whichFramePointersToEmit() { - if (auto option = opts::framePointerUsage()) - return *option; - return isOptimizationEnabled() ? llvm::FramePointer::None - : llvm::FramePointer::All; -} -#endif - bool isOptimizationEnabled() { return optimizeLevel != 0; } llvm::CodeGenOpt::Level codeGenOptLevel() { @@ -203,8 +192,10 @@ static void legacyAddGarbageCollect2StackPass(const PassManagerBuilder &builder, } static void legacyAddAddressSanitizerPasses(const PassManagerBuilder &Builder, - PassManagerBase &PM) { - PM.add(createAddressSanitizerFunctionPass()); + PassManagerBase &PM) { + PM.add(createAddressSanitizerFunctionPass(/*CompileKernel = */ false, + /*Recover = */ false, + /*UseAfterScope = */ true)); PM.add(createModuleAddressSanitizerLegacyPassPass()); } @@ -236,13 +227,8 @@ static void legacyAddThreadSanitizerPass(const PassManagerBuilder &Builder, static void legacyAddSanitizerCoveragePass(const PassManagerBuilder &Builder, legacy::PassManagerBase &PM) { -#if LDC_LLVM_VER >= 1000 PM.add(createModuleSanitizerCoverageLegacyPassPass( opts::getSanitizerCoverageOptions())); -#else - PM.add( - createSanitizerCoverageModulePass(opts::getSanitizerCoverageOptions())); -#endif } // Adds PGO instrumentation generation and use passes. @@ -572,8 +558,13 @@ static llvm::Optional getPGOOptions() { PGOOptions::CSPGOAction::NoCSAction, debugInfoForProfiling, pseudoProbeForProfiling); } +#if LDC_LLVM_VER < 1600 return None; +#else + return std::nullopt; +#endif } + static PipelineTuningOptions getPipelineTuningOptions(unsigned optLevelVal, unsigned sizeLevelVal) { PipelineTuningOptions pto; @@ -635,7 +626,11 @@ void runOptimizationPasses(llvm::Module *M) { bool debugLogging = false; ppo.Indent = false; ppo.SkipAnalyses = false; +#if LDC_LLVM_VER < 1600 StandardInstrumentations si(debugLogging, /*VerifyEach=*/false, ppo); +#else + StandardInstrumentations si(M->getContext(), debugLogging, /*VerifyEach=*/false, ppo); +#endif si.registerCallbacks(pic, &fam); diff --git a/gen/optimizer.h b/gen/optimizer.h index cbe3adb8fa2..0b3da829fb6 100644 --- a/gen/optimizer.h +++ b/gen/optimizer.h @@ -35,10 +35,6 @@ bool willInline(); bool willCrossModuleInline(); -#if LDC_LLVM_VER < 1000 -llvm::FramePointer::FP whichFramePointersToEmit(); -#endif - unsigned optLevel(); bool isOptimizationEnabled(); diff --git a/gen/passes/GarbageCollect2Stack.cpp b/gen/passes/GarbageCollect2Stack.cpp index 7684906a7de..af710b18f3e 100644 --- a/gen/passes/GarbageCollect2Stack.cpp +++ b/gen/passes/GarbageCollect2Stack.cpp @@ -71,11 +71,7 @@ void EmitMemSet(IRBuilder<> &B, Value *Dst, Value *Val, Value *Len, const G2StackAnalysis &A) { Dst = B.CreateBitCast(Dst, PointerType::getUnqual(B.getInt8Ty())); -#if LDC_LLVM_VER >= 1000 MaybeAlign Align(1); -#else - unsigned Align = 1; -#endif auto CS = B.CreateMemSet(Dst, Val, Len, Align, false /*isVolatile*/); if (A.CGNode) { @@ -95,7 +91,7 @@ static void EmitMemZero(IRBuilder<> &B, Value *Dst, Value *Len, //namespace { -Value* FunctionInfo::promote(LLCallBasePtr CB, IRBuilder<> &B, const G2StackAnalysis &A) { +Value* FunctionInfo::promote(CallBase *CB, IRBuilder<> &B, const G2StackAnalysis &A) { NumGcToStack++; auto &BB = CB->getCaller()->getEntryBlock(); @@ -132,7 +128,7 @@ static bool isKnownLessThan(Value *Val, uint64_t Limit, const G2StackAnalysis &A return true; } -bool TypeInfoFI::analyze(LLCallBasePtr CB, const G2StackAnalysis &A) { +bool TypeInfoFI::analyze(CallBase *CB, const G2StackAnalysis &A) { Value *TypeInfo = CB->getArgOperand(TypeInfoArgNr); Ty = A.getTypeFor(TypeInfo, 0); if (!Ty) { @@ -141,7 +137,7 @@ bool TypeInfoFI::analyze(LLCallBasePtr CB, const G2StackAnalysis &A) { return A.DL.getTypeAllocSize(Ty) < SizeLimit; } -bool ArrayFI::analyze(LLCallBasePtr CB, const G2StackAnalysis &A) { +bool ArrayFI::analyze(CallBase *CB, const G2StackAnalysis &A) { if (!TypeInfoFI::analyze(CB, A)) { return false; } @@ -164,7 +160,7 @@ bool ArrayFI::analyze(LLCallBasePtr CB, const G2StackAnalysis &A) { return true; } -Value* ArrayFI::promote(LLCallBasePtr CB, IRBuilder<> &B, const G2StackAnalysis &A) { +Value* ArrayFI::promote(CallBase *CB, IRBuilder<> &B, const G2StackAnalysis &A) { // If the allocation is of constant size it's best to put it in the // entry block, so do so if we're not already there. // For dynamically-sized allocations it's best to avoid the overhead @@ -207,7 +203,7 @@ Value* ArrayFI::promote(LLCallBasePtr CB, IRBuilder<> &B, const G2StackAnalysis return alloca; } -bool AllocClassFI::analyze(LLCallBasePtr CB, const G2StackAnalysis &A) { +bool AllocClassFI::analyze(CallBase *CB, const G2StackAnalysis &A) { if (CB->arg_size() != 1) { return false; } @@ -242,7 +238,7 @@ bool AllocClassFI::analyze(LLCallBasePtr CB, const G2StackAnalysis &A) { ->getType(); return A.DL.getTypeAllocSize(Ty) < SizeLimit; } -bool UntypedMemoryFI::analyze(LLCallBasePtr CB, const G2StackAnalysis &A) { +bool UntypedMemoryFI::analyze(CallBase *CB, const G2StackAnalysis &A) { if (CB->arg_size() < SizeArgNr + 1) { return false; } @@ -264,7 +260,7 @@ bool UntypedMemoryFI::analyze(LLCallBasePtr CB, const G2StackAnalysis &A) { Ty = llvm::Type::getInt8Ty(CB->getContext()); return true; } -Value* UntypedMemoryFI::promote(LLCallBasePtr CB, IRBuilder<> &B, const G2StackAnalysis &A) { +Value* UntypedMemoryFI::promote(CallBase *CB, IRBuilder<> &B, const G2StackAnalysis &A) { // If the allocation is of constant size it's best to put it in the // entry block, so do so if we're not already there. // For dynamically-sized allocations it's best to avoid the overhead @@ -345,7 +341,7 @@ GarbageCollect2Stack::GarbageCollect2Stack() NewArrayT(ReturnType::Array, 0, 1, true), AllocMemory(0) { } -static void RemoveCall(LLCallBasePtr CB, const G2StackAnalysis &A) { +static void RemoveCall(CallBase *CB, const G2StackAnalysis &A) { // For an invoke instruction, we insert a branch to the normal target BB // immediately before it. Ideally, we would find a way to not invalidate // the dominator tree here. diff --git a/gen/passes/GarbageCollect2Stack.h b/gen/passes/GarbageCollect2Stack.h index 8feef11fc37..0024e23e546 100644 --- a/gen/passes/GarbageCollect2Stack.h +++ b/gen/passes/GarbageCollect2Stack.h @@ -27,11 +27,11 @@ class FunctionInfo { // Analyze the current call, filling in some fields. Returns true if // this is an allocation we can stack-allocate. - virtual bool analyze(LLCallBasePtr CB, const G2StackAnalysis &A) = 0; + virtual bool analyze(llvm::CallBase *CB, const G2StackAnalysis &A) = 0; // Returns the alloca to replace this call. // It will always be inserted before the call. - virtual llvm::Value *promote(LLCallBasePtr CB, IRBuilder<> &B, const G2StackAnalysis &A); + virtual llvm::Value *promote(llvm::CallBase *CB, IRBuilder<> &B, const G2StackAnalysis &A); explicit FunctionInfo(ReturnType::Type returnType) : ReturnType(returnType) {} virtual ~FunctionInfo() = default; @@ -43,7 +43,7 @@ class TypeInfoFI : public FunctionInfo { TypeInfoFI(ReturnType::Type returnType, unsigned tiArgNr) : FunctionInfo(returnType), TypeInfoArgNr(tiArgNr) {} - bool analyze(LLCallBasePtr CB, const G2StackAnalysis &A) override; + bool analyze(llvm::CallBase *CB, const G2StackAnalysis &A) override; }; class ArrayFI : public TypeInfoFI { int ArrSizeArgNr; @@ -56,15 +56,15 @@ class ArrayFI : public TypeInfoFI { : TypeInfoFI(returnType, tiArgNr), ArrSizeArgNr(arrSizeArgNr), Initialized(initialized) {} - bool analyze(LLCallBasePtr CB, const G2StackAnalysis &A) override; + bool analyze(llvm::CallBase *CB, const G2StackAnalysis &A) override; - llvm::Value *promote(LLCallBasePtr CB, IRBuilder<> &B, const G2StackAnalysis &A) override; + llvm::Value *promote(llvm::CallBase *CB, IRBuilder<> &B, const G2StackAnalysis &A) override; }; // FunctionInfo for _d_allocclass class AllocClassFI : public FunctionInfo { public: - bool analyze(LLCallBasePtr CB, const G2StackAnalysis &A) override; + bool analyze(llvm::CallBase *CB, const G2StackAnalysis &A) override; // The default promote() should be fine. @@ -77,9 +77,9 @@ class UntypedMemoryFI : public FunctionInfo { llvm::Value *SizeArg; public: - bool analyze(LLCallBasePtr CB, const G2StackAnalysis &A) override; + bool analyze(llvm::CallBase *CB, const G2StackAnalysis &A) override; - llvm::Value *promote(LLCallBasePtr CB, IRBuilder<> &B, const G2StackAnalysis &A) override; + llvm::Value *promote(llvm::CallBase *CB, IRBuilder<> &B, const G2StackAnalysis &A) override; explicit UntypedMemoryFI(unsigned sizeArgNr) : FunctionInfo(ReturnType::Pointer), SizeArgNr(sizeArgNr) {} diff --git a/gen/passes/SimplifyDRuntimeCalls.cpp b/gen/passes/SimplifyDRuntimeCalls.cpp index 181b925ffbc..998df4ca2f3 100644 --- a/gen/passes/SimplifyDRuntimeCalls.cpp +++ b/gen/passes/SimplifyDRuntimeCalls.cpp @@ -60,7 +60,7 @@ Value *LibCallOptimization::CastToCStr(Value *V, IRBuilder<> &B) { /// expects that the size has type 'intptr_t' and Dst/Src are pointers. Value *LibCallOptimization::EmitMemCpy(Value *Dst, Value *Src, Value *Len, unsigned Align, IRBuilder<> &B) { - auto A = LLMaybeAlign(Align); + auto A = llvm::MaybeAlign(Align); return B.CreateMemCpy(CastToCStr(Dst, B), A, CastToCStr(Src, B), A, Len, false); } diff --git a/gen/pgo_ASTbased.cpp b/gen/pgo_ASTbased.cpp index 7819d9594f1..6f704b896dc 100644 --- a/gen/pgo_ASTbased.cpp +++ b/gen/pgo_ASTbased.cpp @@ -117,7 +117,7 @@ class PGOHash { if (Count && Count % NumTypesPerWord == 0) { using namespace llvm::support; uint64_t Swapped = endian::byte_swap(Working); - MD5.update(llvm::makeArrayRef((uint8_t *)&Swapped, sizeof(Swapped))); + MD5.update(llvm::ArrayRef((uint8_t *)&Swapped, sizeof(Swapped))); Working = 0; } @@ -138,7 +138,7 @@ class PGOHash { if (Working) { using namespace llvm::support; uint64_t Swapped = endian::byte_swap(Working); - MD5.update(llvm::makeArrayRef((uint8_t *)&Swapped, sizeof(Swapped))); + MD5.update(llvm::ArrayRef((uint8_t *)&Swapped, sizeof(Swapped))); } // Finalize the MD5 and return the hash. @@ -190,7 +190,7 @@ struct MapRegionCounters : public StoppableVisitor { void visit(ScopeStatement *) override {} void visit(ReturnStatement *) override {} void visit(StaticAssertStatement *) override {} - void visit(CompileStatement *) override {} + void visit(MixinStatement *) override {} void visit(ScopeGuardStatement *) override {} void visit(ConditionalStatement *) override {} void visit(StaticForeachStatement *) override {} @@ -955,7 +955,7 @@ void CodeGenPGO::loadRegionCounts(llvm::IndexedInstrProfReader *PGOReader, } ProfRecord = - llvm::make_unique(std::move(RecordExpected.get())); + std::make_unique(std::move(RecordExpected.get())); RegionCounts = ProfRecord->Counts; IF_LOG Logger::println("Loaded profile data for function: %s", diff --git a/gen/rttibuilder.cpp b/gen/rttibuilder.cpp index fcdde1bb72b..b8ba7006943 100644 --- a/gen/rttibuilder.cpp +++ b/gen/rttibuilder.cpp @@ -46,7 +46,7 @@ void RTTIBuilder::push(llvm::Constant *C) { // We need to explicitly zero any padding bytes as per TDPL §7.1.1 (and // also match the struct type lowering code here). const uint64_t fieldStart = llvm::alignTo( - prevFieldEnd, gDataLayout->getABITypeAlignment(C->getType())); + prevFieldEnd, gDataLayout->getABITypeAlign(C->getType()).value()); const uint64_t paddingBytes = fieldStart - prevFieldEnd; if (paddingBytes) { @@ -86,7 +86,7 @@ void RTTIBuilder::push_void_array(llvm::Constant *CI, Type *valtype, auto G = new LLGlobalVariable(gIR->module, CI->getType(), true, lwc.first, CI, initname.peekChars()); setLinkage(lwc, G); - G->setAlignment(LLMaybeAlign(DtoAlignment(valtype))); + G->setAlignment(llvm::MaybeAlign(DtoAlignment(valtype))); push_void_array(getTypeAllocSize(CI->getType()), G); } @@ -112,7 +112,7 @@ void RTTIBuilder::push_array(llvm::Constant *CI, uint64_t dim, Type *valtype, auto G = new LLGlobalVariable(gIR->module, CI->getType(), true, lwc.first, CI, initname.peekChars()); setLinkage(lwc, G); - G->setAlignment(LLMaybeAlign(DtoAlignment(valtype))); + G->setAlignment(llvm::MaybeAlign(DtoAlignment(valtype))); push_array(dim, DtoBitCast(G, DtoType(valtype->pointerTo()))); } diff --git a/gen/runtime.cpp b/gen/runtime.cpp index 37cd6dc7419..706acac292b 100644 --- a/gen/runtime.cpp +++ b/gen/runtime.cpp @@ -33,7 +33,7 @@ #include "llvm/Bitcode/BitcodeWriter.h" #include "llvm/IR/Attributes.h" #if LDC_LLVM_VER >= 1600 -#include "llvm/IR/ModRef.h" +#include "llvm/Support/ModRef.h" #endif #include "llvm/IR/Module.h" #include "llvm/Support/CommandLine.h" @@ -73,8 +73,6 @@ static void checkForImplicitGCCall(const Loc &loc, const char *name) { "_d_arrayappendcTX", "_d_arrayappendcd", "_d_arrayappendwd", - "_d_arraycatT", - "_d_arraycatnTX", "_d_arraysetlengthT", "_d_arraysetlengthiT", "_d_assocarrayliteralTX", @@ -606,25 +604,11 @@ static void buildRuntimeModule() { createFwdDecl(LINK::c, voidArrayTy, {"_d_newarraymTX", "_d_newarraymiTX"}, {typeInfoTy, sizeTy->arrayOf()}, {STCconst, 0}); - // void[] _d_arraysetlengthT (const TypeInfo ti, size_t newlength, void[]* p) - // void[] _d_arraysetlengthiT(const TypeInfo ti, size_t newlength, void[]* p) - createFwdDecl(LINK::c, voidArrayTy, - {"_d_arraysetlengthT", "_d_arraysetlengthiT"}, - {typeInfoTy, sizeTy, voidArrayPtrTy}, {STCconst, 0, 0}); - // void[] _d_arrayappendcd(ref byte[] x, dchar c) // void[] _d_arrayappendwd(ref byte[] x, dchar c) createFwdDecl(LINK::c, voidArrayTy, {"_d_arrayappendcd", "_d_arrayappendwd"}, {voidArrayTy, dcharTy}, {STCref, 0}); - // byte[] _d_arraycatT(const TypeInfo ti, byte[] x, byte[] y) - createFwdDecl(LINK::c, voidArrayTy, {"_d_arraycatT"}, - {typeInfoTy, voidArrayTy, voidArrayTy}, {STCconst, 0, 0}); - - // void[] _d_arraycatnTX(const TypeInfo ti, byte[][] arrs) - createFwdDecl(LINK::c, voidArrayTy, {"_d_arraycatnTX"}, - {typeInfoTy, voidArrayTy->arrayOf()}, {STCconst, 0}); - // Object _d_newclass(const ClassInfo ci) // Object _d_allocclass(const ClassInfo ci) createFwdDecl(LINK::c, objectTy, {"_d_newclass", "_d_allocclass"}, diff --git a/gen/semantic.d b/gen/semantic.d index 359f75a943c..c818d7b7e09 100644 --- a/gen/semantic.d +++ b/gen/semantic.d @@ -15,13 +15,20 @@ import dmd.dmodule; extern(C++) void dcomputeSemanticAnalysis(Module m); extern(C) int hasComputeAttr(Dsymbol m); +extern(C++) void runAllSemanticAnalysisPlugins(Module m); extern(C++) void extraLDCSpecificSemanticAnalysis(ref Modules modules) { + // First finish DCompute SemA for all modules, before calling plugins. foreach(m; modules[]) { - if (hasComputeAttr(m)) + if (hasComputeAttr(m)) { dcomputeSemanticAnalysis(m); + } + } + + foreach(m; modules[]) + { + runAllSemanticAnalysisPlugins(m); } - } diff --git a/gen/statements.cpp b/gen/statements.cpp index 042077cc9bd..e80d6173fdb 100644 --- a/gen/statements.cpp +++ b/gen/statements.cpp @@ -397,6 +397,9 @@ class ToIRVisitor : public Visitor { // start a dwarf lexical block irs->DBuilder.EmitBlockStart(stmt->loc); emitCoverageLinecountInc(stmt->loc); + // Open a new scope for the optional condition variable (`if (auto i = ...)`) + irs->funcGen().localVariableLifetimeAnnotator.pushScope(); + // This is a (dirty) hack to get codegen time conditional // compilation, on account of the fact that we are trying @@ -482,6 +485,9 @@ class ToIRVisitor : public Visitor { // rewrite the scope irs->ir->SetInsertPoint(endbb); + // Close the scope for the optional condition variable. This is suboptimal, + // because the condition variable is not in scope in the else block. + irs->funcGen().localVariableLifetimeAnnotator.popScope(); } ////////////////////////////////////////////////////////////////////////// @@ -494,9 +500,11 @@ class ToIRVisitor : public Visitor { PGO.setCurrentStmt(stmt); if (stmt->statement) { + irs->funcGen().localVariableLifetimeAnnotator.pushScope(); irs->DBuilder.EmitBlockStart(stmt->statement->loc); stmt->statement->accept(this); irs->DBuilder.EmitBlockEnd(); + irs->funcGen().localVariableLifetimeAnnotator.popScope(); } } @@ -636,6 +644,7 @@ class ToIRVisitor : public Visitor { // start new dwarf lexical block irs->DBuilder.EmitBlockStart(stmt->loc); + irs->funcGen().localVariableLifetimeAnnotator.pushScope(); // create for blocks llvm::BasicBlock *forbb = irs->insertBB("forcond"); @@ -717,6 +726,7 @@ class ToIRVisitor : public Visitor { irs->ir->SetInsertPoint(endbb); // end the dwarf lexical block + irs->funcGen().localVariableLifetimeAnnotator.popScope(); irs->DBuilder.EmitBlockEnd(); } diff --git a/gen/target.cpp b/gen/target.cpp index 154adb3676b..8b81afe12e8 100644 --- a/gen/target.cpp +++ b/gen/target.cpp @@ -15,6 +15,7 @@ #include "dmd/mtype.h" #include "dmd/target.h" #include "driver/cl_options.h" +#include "driver/cl_options_instrumentation.h" #include "driver/linker.h" #include "gen/abi/abi.h" #include "gen/irstate.h" @@ -101,7 +102,7 @@ void Target::_init(const Param ¶ms) { realType = getRealType(triple); realsize = gDataLayout->getTypeAllocSize(realType); realpad = realsize - gDataLayout->getTypeStoreSize(realType); - realalignsize = gDataLayout->getABITypeAlignment(realType); + realalignsize = gDataLayout->getABITypeAlign(realType).value(); classinfosize = 0; // unused maxStaticDataSize = std::numeric_limits::max(); @@ -210,7 +211,7 @@ unsigned Target::alignsize(Type *type) { if (type->ty == TY::Tvoid) { return 1; } - return gDataLayout->getABITypeAlignment(DtoType(type)); + return gDataLayout->getABITypeAlign(DtoType(type)).value(); } /****************************** @@ -309,6 +310,11 @@ Expression *Target::getTargetInfo(const char *name_, const Loc &loc) { Loc(), static_cast(global.params.cplusplus), Type::tint32); } + if (name == "CET") { + auto cet = opts::fCFProtection.getValue(); + return IntegerExp::create(loc, static_cast(cet), Type::tint32); + } + #if LDC_LLVM_SUPPORTED_TARGET_SPIRV || LDC_LLVM_SUPPORTED_TARGET_NVPTX if (name == "dcomputeTargets") { Expressions* exps = createExpressions(); diff --git a/gen/tocall.cpp b/gen/tocall.cpp index 16da4ba086d..615aa7ba3ea 100644 --- a/gen/tocall.cpp +++ b/gen/tocall.cpp @@ -415,7 +415,7 @@ bool DtoLowerMagicIntrinsic(IRState *p, FuncDeclaration *fndecl, CallExp *e, llvm::StoreInst *ret = p->ir->CreateStore(val, ptr); ret->setAtomic(llvm::AtomicOrdering(atomicOrdering)); if (auto alignment = getTypeAllocSize(val->getType())) { - ret->setAlignment(LLAlign(alignment)); + ret->setAlignment(llvm::Align(alignment)); } return true; } @@ -449,7 +449,7 @@ bool DtoLowerMagicIntrinsic(IRState *p, FuncDeclaration *fndecl, CallExp *e, llvm::LoadInst *load = p->ir->CreateLoad(loadedType, ptr); if (auto alignment = getTypeAllocSize(loadedType)) { - load->setAlignment(LLAlign(alignment)); + load->setAlignment(llvm::Align(alignment)); } load->setAtomic(llvm::AtomicOrdering(atomicOrdering)); llvm::Value *val = load; @@ -819,7 +819,7 @@ class ImplicitArgumentsBuilder { //////////////////////////////////////////////////////////////////////////////// -static LLValue *DtoCallableValue(llvm::FunctionType * ft,DValue *fn) { +static LLValue *DtoCallableValue(DValue *fn) { Type *type = fn->type->toBasetype(); if (type->ty == TY::Tfunction) { return DtoRVal(fn); @@ -829,7 +829,7 @@ static LLValue *DtoCallableValue(llvm::FunctionType * ft,DValue *fn) { LLValue *dg = DtoLVal(fn); llvm::StructType *st = isaStruct(DtoType(fn->type)); LLValue *funcptr = DtoGEP(st, dg, 0, 1); - return DtoLoad(ft->getPointerTo(), funcptr, ".funcptr"); + return DtoLoad(st->getElementType(1), funcptr, ".funcptr"); } LLValue *dg = DtoRVal(fn); assert(isaStruct(dg)); @@ -862,7 +862,7 @@ DValue *DtoCallFunction(const Loc &loc, Type *resulttype, DValue *fnval, } // get callee llvm value - LLValue *callable = DtoCallableValue(irFty.funcType, fnval); + LLValue *callable = DtoCallableValue(fnval); LLFunctionType *callableTy = irFty.funcType; if (dfnval && dfnval->func->isCsymbol()) { // See note in DtoDeclareFunction about K&R foward declared (void) functions @@ -916,8 +916,8 @@ DValue *DtoCallFunction(const Loc &loc, Type *resulttype, DValue *fnval, } // call the function - LLCallBasePtr call = gIR->funcGen().callOrInvoke(callable, callableTy, args, - "", tf->isnothrow()); + llvm::CallBase *call = gIR->funcGen().callOrInvoke(callable, callableTy, args, + "", tf->isnothrow()); // PGO: Insert instrumentation or attach profile metadata at indirect call // sites. diff --git a/gen/toconstelem.cpp b/gen/toconstelem.cpp index 918cd5d91fb..a392b5fcc4e 100644 --- a/gen/toconstelem.cpp +++ b/gen/toconstelem.cpp @@ -443,7 +443,7 @@ class ToConstElemVisitor : public Visitor { auto globalVar = new llvm::GlobalVariable( p->module, DtoType(se->type), false, llvm::GlobalValue::InternalLinkage, nullptr, ".structliteral"); - globalVar->setAlignment(LLMaybeAlign(DtoAlignment(se->type))); + globalVar->setAlignment(llvm::MaybeAlign(DtoAlignment(se->type))); p->setStructLiteralConstant(se, globalVar); llvm::Constant *constValue = toConstElem(se, p); @@ -708,10 +708,8 @@ class ToConstElemVisitor : public Visitor { // constructed. #if LDC_LLVM_VER >= 1200 const auto elementCount = llvm::ElementCount::getFixed(elemCount); -#elif LDC_LLVM_VER >= 1100 - const auto elementCount = llvm::ElementCount(elemCount, false); #else - const auto elementCount = elemCount; + const auto elementCount = llvm::ElementCount(elemCount, false); #endif result = llvm::ConstantVector::getSplat( elementCount, toConstElem(e->e1->optimize(WANTvalue), p)); diff --git a/gen/toir.cpp b/gen/toir.cpp index 265a12a35bf..6ea93a69823 100644 --- a/gen/toir.cpp +++ b/gen/toir.cpp @@ -164,7 +164,7 @@ static void write_struct_literal(Loc loc, LLValue *mem, StructDeclaration *sd, IF_LOG Logger::cout() << "merged IR value: " << *val << '\n'; gIR->ir->CreateAlignedStore(val, DtoBitCast(ptr, getPtrToType(intType)), - LLMaybeAlign(1)); + llvm::MaybeAlign(1)); offset += group.sizeInBytes; i += group.bitFields.size() - 1; // skip the other bit fields of the group @@ -456,6 +456,14 @@ class ToElemVisitor : public Visitor { ////////////////////////////////////////////////////////////////////////////// + void visit(LoweredAssignExp *e) override { + IF_LOG Logger::print("LoweredAssignExp::toElem: %s @ %s\n", e->toChars(), + e->type->toChars()); + LOG_SCOPE; + + result = toElem(e->lowering); + } + void visit(AssignExp *e) override { IF_LOG Logger::print("AssignExp::toElem: %s | (%s)(%s = %s)\n", e->toChars(), e->type->toChars(), @@ -463,17 +471,6 @@ class ToElemVisitor : public Visitor { e->e2->type ? e->e2->type->toChars() : nullptr); LOG_SCOPE; - if (auto ale = e->e1->isArrayLengthExp()) { - Logger::println("performing array.length assignment"); - DLValue arrval(ale->e1->type, DtoLVal(ale->e1)); - DValue *newlen = toElem(e->e2); - DSliceValue *slice = - DtoResizeDynArray(e->loc, arrval.type, &arrval, DtoRVal(newlen)); - DtoStore(DtoRVal(slice), DtoLVal(&arrval)); - result = newlen; - return; - } - // Initialization of ref variable? // Can't just override ConstructExp::toElem because not all EXP::construct // operations are actually instances of ConstructExp... Long live the DMD @@ -1196,8 +1193,8 @@ class ToElemVisitor : public Visitor { p->arrays.pop_back(); const bool hasLength = etype->ty != TY::Tpointer; - const bool needCheckUpper = hasLength && !e->upperIsInBounds; - const bool needCheckLower = !e->lowerIsLessThanUpper; + const bool needCheckUpper = hasLength && !e->upperIsInBounds(); + const bool needCheckLower = !e->lowerIsLessThanUpper(); if (p->emitArrayBoundsChecks() && (needCheckUpper || needCheckLower)) { llvm::BasicBlock *okbb = p->insertBB("bounds.ok"); llvm::BasicBlock *failbb = p->insertBBAfter(okbb, "bounds.fail"); @@ -2161,6 +2158,7 @@ class ToElemVisitor : public Visitor { e->type->toChars()); LOG_SCOPE; + // TODO: still required? if (global.params.betterC) { error( e->loc, @@ -2172,7 +2170,12 @@ class ToElemVisitor : public Visitor { return; } - result = DtoCatArrays(e->loc, e->type, e->e1, e->e2); + if (e->lowering) { + result = toElem(e->lowering); + return; + } + + llvm_unreachable("CatExp should have been lowered"); } ////////////////////////////////////////////////////////////////////////////// @@ -2720,10 +2723,8 @@ class ToElemVisitor : public Visitor { if (auto llConstant = isaConstant(llElement)) { #if LDC_LLVM_VER >= 1200 const auto elementCount = llvm::ElementCount::getFixed(N); -#elif LDC_LLVM_VER >= 1100 - const auto elementCount = llvm::ElementCount(N, false); #else - const auto elementCount = N; + const auto elementCount = llvm::ElementCount(N, false); #endif auto vectorConstant = llvm::ConstantVector::getSplat(elementCount, llConstant); diff --git a/gen/tollvm.cpp b/gen/tollvm.cpp index 72c3cf61f35..cff29e23c05 100644 --- a/gen/tollvm.cpp +++ b/gen/tollvm.cpp @@ -232,9 +232,13 @@ LinkageWithCOMDAT DtoLinkage(Dsymbol *sym) { if (hasWeakUDA(sym)) { linkage = LLGlobalValue::WeakAnyLinkage; } else { - // Function (incl. delegate) literals are emitted into each referencing - // compilation unit, so use linkonce_odr for all lambdas and all global - // variables they define. + /* Function (incl. delegate) literals are emitted into each referencing + * compilation unit, so use internal linkage for all lambdas and all global + * variables they define. + * This makes sure these symbols don't accidentally collide when linking + * object files compiled by different compiler invocations (lambda mangles + * aren't stable - see https://issues.dlang.org/show_bug.cgi?id=23722). + */ auto potentialLambda = sym; if (auto vd = sym->isVarDeclaration()) { if (vd->isDataseg()) @@ -242,7 +246,7 @@ LinkageWithCOMDAT DtoLinkage(Dsymbol *sym) { } if (potentialLambda->isFuncLiteralDeclaration()) { - linkage = LLGlobalValue::LinkOnceODRLinkage; + linkage = LLGlobalValue::InternalLinkage; } else if (sym->isInstantiated()) { linkage = templateLinkage; } @@ -315,8 +319,9 @@ void setVisibility(Dsymbol *sym, llvm::GlobalObject *obj) { } else { if (sym->isExport()) { obj->setVisibility(LLGlobalValue::DefaultVisibility); // overrides @hidden - } else if (!hasHiddenUDA) { + } else if (!obj->hasLocalLinkage() && !hasHiddenUDA) { // Hide with -fvisibility=hidden, or linkonce_odr etc. + // Note that symbols with local linkage cannot be hidden (LLVM assertion). // The Apple linker warns about hidden linkonce_odr symbols from object // files compiled with -linkonce-templates being folded with *public* // weak_odr symbols from non-linkonce-templates code (e.g., Phobos), so @@ -403,7 +408,8 @@ void DtoMemSet(LLValue *dst, LLValue *val, LLValue *nbytes, unsigned align) { dst = DtoBitCast(dst, VoidPtrTy); - gIR->ir->CreateMemSet(dst, val, nbytes, LLMaybeAlign(align), false /*isVolatile*/); + gIR->ir->CreateMemSet(dst, val, nbytes, llvm::MaybeAlign(align), + false /*isVolatile*/); } //////////////////////////////////////////////////////////////////////////////// @@ -425,7 +431,7 @@ void DtoMemCpy(LLValue *dst, LLValue *src, LLValue *nbytes, unsigned align) { dst = DtoBitCast(dst, VoidPtrTy); src = DtoBitCast(src, VoidPtrTy); - auto A = LLMaybeAlign(align); + auto A = llvm::MaybeAlign(align); gIR->ir->CreateMemCpy(dst, A, src, A, nbytes, false /*isVolatile*/); } @@ -532,9 +538,7 @@ LLValue *DtoLoad(DLValue *src, const char *name) { // the type. LLValue *DtoAlignedLoad(LLType *type, LLValue *src, const char *name) { llvm::LoadInst *ld = DtoLoadImpl(type, src, name); - if (auto alignment = getABITypeAlign(ld->getType())) { - ld->setAlignment(LLAlign(alignment)); - } + ld->setAlignment(gDataLayout->getABITypeAlign(ld->getType())); return ld; } @@ -570,9 +574,7 @@ void DtoAlignedStore(LLValue *src, LLValue *dst) { assert(!src->getType()->isIntegerTy(1) && "Should store bools as i8 instead of i1."); llvm::StoreInst *st = gIR->ir->CreateStore(src, dst); - if (auto alignment = getABITypeAlign(src->getType())) { - st->setAlignment(LLAlign(alignment)); - } + st->setAlignment(gDataLayout->getABITypeAlign(src->getType())); } //////////////////////////////////////////////////////////////////////////////// @@ -587,9 +589,7 @@ LLType *stripAddrSpaces(LLType *t) if (!pt) return t; -#if LDC_LLVM_VER >= 1600 - return getVoidPtrType(); -#elif LDC_LLVM_VER >= 1400 +#if LDC_LLVM_VER >= 1400 if (pt->isOpaque()) return getVoidPtrType(); else { @@ -749,7 +749,7 @@ size_t getTypeStoreSize(LLType *t) { return gDataLayout->getTypeStoreSize(t); } size_t getTypeAllocSize(LLType *t) { return gDataLayout->getTypeAllocSize(t); } unsigned int getABITypeAlign(LLType *t) { - return gDataLayout->getABITypeAlignment(t); + return gDataLayout->getABITypeAlign(t).value(); } //////////////////////////////////////////////////////////////////////////////// diff --git a/gen/trycatchfinally.cpp b/gen/trycatchfinally.cpp index f27e42ad656..4b6dfe05963 100644 --- a/gen/trycatchfinally.cpp +++ b/gen/trycatchfinally.cpp @@ -371,11 +371,8 @@ llvm::BasicBlock *CleanupScope::run(IRState &irs, llvm::BasicBlock *sourceBlock, // And convert the BranchInst to the existing branch target to a // SelectInst so we can append the other cases to it. endBlock()->getTerminator()->eraseFromParent(); - llvm::Value *sel = new llvm::LoadInst( -#if LDC_LLVM_VER >= 1100 - branchSelectorType, -#endif - branchSelector, "", endBlock()); + llvm::Value *sel = + new llvm::LoadInst(branchSelectorType, branchSelector, "", endBlock()); llvm::SwitchInst::Create( sel, exitTargets[0].branchTarget, 1, // Expected number of branches, only for pre-allocating. diff --git a/gen/uda.cpp b/gen/uda.cpp index 371b565b38a..de06f110af7 100644 --- a/gen/uda.cpp +++ b/gen/uda.cpp @@ -17,15 +17,6 @@ #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringSwitch.h" -#if LDC_LLVM_VER < 1100 -namespace llvm { -// Auto-generate: -// Attribute::AttrKind getAttrKindFromName(StringRef AttrName) { ... } -#define GET_ATTR_KIND_FROM_NAME -#include "llvm/IR/Attributes.inc" -} -#endif - namespace { /// Checks whether `moduleDecl` is in the ldc package and it's identifier is @@ -219,7 +210,11 @@ void applyAttrAllocSize(StructLiteralExp *sle, IrFunction *irFunc) { if (numArgIdx >= 0) { builder.addAllocSizeAttr(llvmSizeIdx, llvmNumIdx); } else { +#if LDC_LLVM_VER < 1600 builder.addAllocSizeAttr(llvmSizeIdx, llvm::Optional()); +#else + builder.addAllocSizeAttr(llvmSizeIdx, std::optional()); +#endif } llvm::Function *func = irFunc->getLLVMFunc(); @@ -238,11 +233,7 @@ void applyAttrLLVMAttr(StructLiteralExp *sle, llvm::AttrBuilder &attrs) { llvm::StringRef key = getStringElem(sle, 0); llvm::StringRef value = getStringElem(sle, 1); if (value.empty()) { -#if LDC_LLVM_VER >= 1100 const auto kind = llvm::Attribute::getAttrKindFromName(key); -#else - const auto kind = llvm::getAttrKindFromName(key); -#endif if (kind != llvm::Attribute::None) { attrs.addAttribute(kind); } else { @@ -423,9 +414,7 @@ bool parseCallingConvention(llvm::StringRef name, .Case("ccc", llvm::CallingConv::C) .Case("fastcc", llvm::CallingConv::Fast) .Case("coldcc", llvm::CallingConv::Cold) -#if LDC_LLVM_VER >= 1000 .Case("cfguard_checkcc", llvm::CallingConv::CFGuard_Check) -#endif .Case("x86_stdcallcc", llvm::CallingConv::X86_StdCall) .Case("x86_fastcallcc", llvm::CallingConv::X86_FastCall) .Case("x86_regcallcc", llvm::CallingConv::X86_RegCall) @@ -435,10 +424,8 @@ bool parseCallingConvention(llvm::StringRef name, .Case("arm_aapcscc", llvm::CallingConv::ARM_AAPCS) .Case("arm_aapcs_vfpcc", llvm::CallingConv::ARM_AAPCS_VFP) .Case("aarch64_vector_pcs", llvm::CallingConv::AArch64_VectorCall) -#if LDC_LLVM_VER >= 1000 .Case("aarch64_sve_vector_pcs", llvm::CallingConv::AArch64_SVE_VectorCall) -#endif .Case("msp430_intrcc", llvm::CallingConv::MSP430_INTR) .Case("avr_intrcc", llvm::CallingConv::AVR_INTR) .Case("avr_signalcc", llvm::CallingConv::AVR_SIGNAL) @@ -473,9 +460,7 @@ bool parseCallingConvention(llvm::StringRef name, .Case("amdgpu_ps", llvm::CallingConv::AMDGPU_PS) .Case("amdgpu_cs", llvm::CallingConv::AMDGPU_CS) .Case("amdgpu_kernel", llvm::CallingConv::AMDGPU_KERNEL) -#if LDC_LLVM_VER >= 1000 .Case("tailcc", llvm::CallingConv::Tail) -#endif .Case("default", llvm::CallingConv::MaxID - 1) .Default(llvm::CallingConv::MaxID); diff --git a/gen/variable_lifetime.cpp b/gen/variable_lifetime.cpp new file mode 100644 index 00000000000..4accea82960 --- /dev/null +++ b/gen/variable_lifetime.cpp @@ -0,0 +1,97 @@ +//===-- gen/variable_lifetime.cpp - -----------------------------*- C++ -*-===// +// +// LDC – the LLVM D compiler +// +// This file is distributed under the BSD-style LDC license. See the LICENSE +// file for details. +// +//===----------------------------------------------------------------------===// +// +// Codegen for local variable lifetime: llvm.lifetime.start abd +// llvm.lifetime.end. +// +//===----------------------------------------------------------------------===// + +#include "gen/variable_lifetime.h" + +#include "driver/cl_options.h" +#include "gen/irstate.h" + +#include +#include + +// TODO: make this option depend on -O and -fsanitize settings. +static llvm::cl::opt fEmitLocalVarLifetime( + "femit-local-var-lifetime", + llvm::cl::desc( + "Emit local variable lifetime, enabling more optimizations."), + llvm::cl::Hidden, llvm::cl::ZeroOrMore); + +LocalVariableLifetimeAnnotator::LocalVariableLifetimeAnnotator(IRState &irs) + : irs(irs) { + allocaType = + llvm::Type::getInt8Ty(irs.context()) + ->getPointerTo(irs.module.getDataLayout().getAllocaAddrSpace()); +} + +void LocalVariableLifetimeAnnotator::pushScope() { scopes.emplace_back(); } + +void LocalVariableLifetimeAnnotator::addLocalVariable(llvm::Value *address, + llvm::Value *size) { + assert(address); + assert(size); + + if (!fEmitLocalVarLifetime) + return; + + if (scopes.empty()) + return; + + // Push to scopes + scopes.back().variables.emplace_back(size, address); + + // Emit lifetime start + address = irs.ir->CreateBitCast(address, allocaType); + irs.CreateCallOrInvoke(getLLVMLifetimeStartFn(), {size, address}, "", + true /*nothrow*/); +} + +// Emits end-of-lifetime annotation for all variables in current scope. +void LocalVariableLifetimeAnnotator::popScope() { + if (scopes.empty()) + return; + + for (const auto &var : scopes.back().variables) { + auto size = var.first; + auto address = var.second; + + address = irs.ir->CreateBitCast(address, allocaType); + assert(address); + + irs.CreateCallOrInvoke(getLLVMLifetimeEndFn(), {size, address}, "", + true /*nothrow*/); + } + scopes.pop_back(); +} + +/// Lazily declare the @llvm.lifetime.start intrinsic. +llvm::Function *LocalVariableLifetimeAnnotator::getLLVMLifetimeStartFn() { + if (lifetimeStartFunction) + return lifetimeStartFunction; + + lifetimeStartFunction = llvm::Intrinsic::getDeclaration( + &irs.module, llvm::Intrinsic::lifetime_start, allocaType); + assert(lifetimeStartFunction); + return lifetimeStartFunction; +} + +/// Lazily declare the @llvm.lifetime.end intrinsic. +llvm::Function *LocalVariableLifetimeAnnotator::getLLVMLifetimeEndFn() { + if (lifetimeEndFunction) + return lifetimeEndFunction; + + lifetimeEndFunction = llvm::Intrinsic::getDeclaration( + &irs.module, llvm::Intrinsic::lifetime_end, allocaType); + assert(lifetimeEndFunction); + return lifetimeEndFunction; +} diff --git a/gen/variable_lifetime.h b/gen/variable_lifetime.h new file mode 100644 index 00000000000..f482030c9ad --- /dev/null +++ b/gen/variable_lifetime.h @@ -0,0 +1,56 @@ +//===-- gen/variable_lifetime.h - -------------------------------*- C++ -*-===// +// +// LDC – the LLVM D compiler +// +// This file is distributed under the BSD-style LDC license. See the LICENSE +// file for details. +// +//===----------------------------------------------------------------------===// +// +// Codegen for local variable lifetime: llvm.lifetime.start abd +// llvm.lifetime.end. +// +//===----------------------------------------------------------------------===// + +#pragma once + +#include +#include + +namespace llvm { +class Function; +class Type; +class Value; +} +struct IRState; + +struct LocalVariableLifetimeAnnotator { + struct LocalVariableScope { + std::vector> variables; + }; + /// Stack of scopes, each scope can have multiple variables. + std::vector scopes; + + /// Cache the llvm types and intrinsics used for codegen. + llvm::Function *lifetimeStartFunction = nullptr; + llvm::Function *lifetimeEndFunction = nullptr; + llvm::Type *allocaType = nullptr; + + llvm::Function *getLLVMLifetimeStartFn(); + llvm::Function *getLLVMLifetimeEndFn(); + + IRState &irs; + +public: + LocalVariableLifetimeAnnotator(IRState &irs); + + /// Opens a new scope. + void pushScope(); + + /// Closes current scope and emits end-of-lifetime annotation for all + /// variables in current scope. + void popScope(); + + /// Register a new local variable for lifetime annotation. + void addLocalVariable(llvm::Value *address, llvm::Value *size); +}; diff --git a/ir/iraggr.cpp b/ir/iraggr.cpp index e7f428846d0..ab6c572ea7e 100644 --- a/ir/iraggr.cpp +++ b/ir/iraggr.cpp @@ -87,7 +87,7 @@ LLConstant *IrAggr::getInitSymbol(bool define) { irMangle, isConstant, false, useDLLImport()); } - initGlobal->setAlignment(LLMaybeAlign(DtoAlignment(type))); + initGlobal->setAlignment(llvm::MaybeAlign(DtoAlignment(type))); init = initGlobal; diff --git a/ir/irclass.cpp b/ir/irclass.cpp index 88e54d089db..edd3d27c352 100644 --- a/ir/irclass.cpp +++ b/ir/irclass.cpp @@ -121,8 +121,7 @@ LLGlobalVariable *IrClass::getClassInfoSymbol(bool define) { // Construct the metadata and insert it into the module. const auto metaname = getMetadataName(CD_PREFIX, typeInfo); llvm::NamedMDNode *node = gIR->module.getOrInsertNamedMetadata(metaname); - node->addOperand(llvm::MDNode::get( - gIR->context(), llvm::makeArrayRef(mdVals, CD_NumFields))); + node->addOperand(llvm::MDNode::get(gIR->context(), mdVals)); } if (!define) @@ -738,8 +737,7 @@ LLConstant *IrClass::getClassInfoInterfaces() { // create Interface struct LLConstant *inits[3] = {ci, vtb, off}; - LLConstant *entry = - LLConstantStruct::get(interface_type, llvm::makeArrayRef(inits, 3)); + LLConstant *entry = LLConstantStruct::get(interface_type, inits); constants.push_back(entry); } diff --git a/ir/irtype.cpp b/ir/irtype.cpp index 0553fbd7b3f..d00f35c5386 100644 --- a/ir/irtype.cpp +++ b/ir/irtype.cpp @@ -118,10 +118,14 @@ IrTypePointer *IrTypePointer::get(Type *dt) { assert(!ctype); LLType *elemType; + unsigned addressSpace = 0; if (dt->ty == TY::Tnull) { elemType = llvm::Type::getInt8Ty(getGlobalContext()); } else { elemType = DtoMemType(dt->nextOf()); + if (dt->nextOf()->ty == TY::Tfunction) { + addressSpace = gDataLayout->getProgramAddressSpace(); + } // DtoType could have already created the same type, e.g. for // dt == Node* in struct Node { Node* n; }. @@ -130,7 +134,8 @@ IrTypePointer *IrTypePointer::get(Type *dt) { } } - auto t = new IrTypePointer(dt, llvm::PointerType::get(elemType, 0)); + auto t = + new IrTypePointer(dt, llvm::PointerType::get(elemType, addressSpace)); ctype = t; return t; } @@ -199,12 +204,8 @@ IrTypeVector *IrTypeVector::get(Type *dt) { // Could have already built the type as part of a struct forward reference, // just as for pointers and arrays. if (!ctype) { - LLType *lt = llvm::VectorType::get(elemType, tsa->dim->toUInteger() -#if LDC_LLVM_VER >= 1100 - , - /*Scalable=*/false -#endif - ); + LLType *lt = llvm::VectorType::get(elemType, tsa->dim->toUInteger(), + /*Scalable=*/false); ctype = new IrTypeVector(dt, lt); } diff --git a/ir/irtypefunction.cpp b/ir/irtypefunction.cpp index 8a1a4ba9a9d..0e830f187af 100644 --- a/ir/irtypefunction.cpp +++ b/ir/irtypefunction.cpp @@ -53,7 +53,8 @@ IrTypeDelegate *IrTypeDelegate::get(Type *t) { IrFuncTy irFty(tf); llvm::Type *ltf = DtoFunctionType(tf, irFty, nullptr, Type::tvoid->pointerTo()); - llvm::Type *types[] = {getVoidPtrType(), getPtrToType(ltf)}; + llvm::Type *fptr = ltf->getPointerTo(gDataLayout->getProgramAddressSpace()); + llvm::Type *types[] = {getVoidPtrType(), fptr}; LLStructType *lt = LLStructType::get(gIR->context(), types, false); // Could have already built the type as part of a struct forward reference, diff --git a/ir/irvar.cpp b/ir/irvar.cpp index 16343658d64..5f4084f4b59 100644 --- a/ir/irvar.cpp +++ b/ir/irvar.cpp @@ -114,7 +114,7 @@ void IrGlobal::declare() { // Set the alignment (it is important not to use type->alignsize because // VarDeclarations can have an align() attribute independent of the type // as well). - gvar->setAlignment(LLMaybeAlign(DtoAlignment(V))); + gvar->setAlignment(llvm::MaybeAlign(DtoAlignment(V))); applyVarDeclUDAs(V, gvar); diff --git a/packaging/dlang-tools_version b/packaging/dlang-tools_version index 4ac687073e6..9a0ba479e62 100644 --- a/packaging/dlang-tools_version +++ b/packaging/dlang-tools_version @@ -1 +1 @@ -v2.103.1 \ No newline at end of file +v2.104.2 \ No newline at end of file diff --git a/packaging/dub_version b/packaging/dub_version index 4beef39c0b7..3fdd57c402b 100644 --- a/packaging/dub_version +++ b/packaging/dub_version @@ -1 +1 @@ -v1.32.1 \ No newline at end of file +v1.33.1 \ No newline at end of file diff --git a/packaging/mimalloc_version b/packaging/mimalloc_version index 01990b444d6..e1fbb880433 100644 --- a/packaging/mimalloc_version +++ b/packaging/mimalloc_version @@ -1 +1 @@ -v1.7.9 \ No newline at end of file +v1.8.2 \ No newline at end of file diff --git a/packaging/reggae_version b/packaging/reggae_version index ceb52eaf4dd..dafb90e96a6 100644 --- a/packaging/reggae_version +++ b/packaging/reggae_version @@ -1 +1 @@ -fb18f3eb6cb0cd82ea401750f4e200fa82d91e39 \ No newline at end of file +051e7d192155693d309cf09439e395bccb4292cf \ No newline at end of file diff --git a/runtime/CMakeLists.txt b/runtime/CMakeLists.txt index d595144737b..5766d6211fa 100644 --- a/runtime/CMakeLists.txt +++ b/runtime/CMakeLists.txt @@ -38,7 +38,7 @@ set(BUILD_SHARED_LIBS AUTO CACHE STRING "Whet set(D_FLAGS -w;-de;-preview=dip1000;-preview=dtorfields;-preview=fieldwise CACHE STRING "Runtime D compiler flags, separated by ';'") set(D_EXTRA_FLAGS "" CACHE STRING "Runtime extra D compiler flags, separated by ';'") set(D_FLAGS_DEBUG -g;-link-defaultlib-debug;-d-debug CACHE STRING "Runtime D compiler flags (debug libraries), separated by ';'") -set(D_FLAGS_RELEASE -O3;-release CACHE STRING "Runtime D compiler flags (release libraries), separated by ';'") +set(D_FLAGS_RELEASE -O3;-release;-femit-local-var-lifetime CACHE STRING "Runtime D compiler flags (release libraries), separated by ';'") set(COMPILE_ALL_D_FILES_AT_ONCE ON CACHE BOOL "Compile all D files for the runtime libs in a single command line instead of separately. Disabling this is useful for many CPU cores and/or iterative development.") set(RT_ARCHIVE_WITH_LDC ON CACHE STRING "Whether to archive the static runtime libs via LDC instead of CMake archiver") set(RT_CFLAGS "" CACHE STRING "Runtime extra C compiler flags, separated by ' '") @@ -251,6 +251,12 @@ elseif(${BUILD_SHARED_LIBS} STREQUAL "OFF") set(ADDITIONAL_DEFAULT_LDC_SWITCHES "${ADDITIONAL_DEFAULT_LDC_SWITCHES}\n \"-link-defaultlib-shared=false\",") endif() +# LLVM 16: Disable function specializations by default. +# They cause miscompiles of e.g. the frontend for some targets (macOS x86_64 and Windows x64). +if(LDC_LLVM_VER GREATER 1599 AND LDC_LLVM_VER LESS 1700) + set(ADDITIONAL_DEFAULT_LDC_SWITCHES "${ADDITIONAL_DEFAULT_LDC_SWITCHES}\n \"-func-specialization-size-threshold=1000000000\",") +endif() + # Default wasm stack is only 64kb, this is rather small, let's bump it to 1mb set(WASM_DEFAULT_LDC_SWITCHES "${WASM_DEFAULT_LDC_SWITCHES}\n \"-L-z\", \"-Lstack-size=1048576\",") # Protect from stack overflow overwriting global memory @@ -335,6 +341,7 @@ if(TARGET gen_gccbuiltins) add_custom_command( OUTPUT ${module} COMMAND gen_gccbuiltins ${module} "${name}" + DEPENDS gen_gccbuiltins ) endfunction() diff --git a/runtime/druntime/src/__builtins.di b/runtime/druntime/src/__builtins.di index 172c1b96f2a..81c004da7a5 100644 --- a/runtime/druntime/src/__builtins.di +++ b/runtime/druntime/src/__builtins.di @@ -21,12 +21,6 @@ module __builtins; alias va_list = imported!"core.stdc.stdarg".va_list; -version (Posix) -{ - version (X86_64) - alias __va_list_tag = imported!"core.stdc.stdarg".__va_list_tag; -} - version (LDC) { // For some targets, __builtin_va_list resolves to __va_list. @@ -35,13 +29,13 @@ version (LDC) version (ARM) version = ARM_Any; version (AArch64) version = ARM_Any; - // Define a __va_list alias if the platform uses an elaborate type, as it + // Define a __va_list[_tag] alias if the platform uses an elaborate type, as it // is referenced from implicitly generated code for D-style variadics, etc. // LDC does not require people to manually import core.vararg like DMD does. version (X86_64) { version (Win64) {} else - public import core.internal.vararg.sysv_x64 : __va_list; + alias __va_list_tag = imported!"core.internal.vararg.sysv_x64".__va_list_tag; } else version (ARM_Any) { @@ -58,6 +52,11 @@ version (LDC) public import core.internal.vararg.aarch64 : __va_list; } } +else version (Posix) +{ + version (X86_64) + alias __va_list_tag = imported!"core.stdc.stdarg".__va_list_tag; +} alias __builtin_va_start = imported!"core.stdc.stdarg".va_start; @@ -136,8 +135,6 @@ version (DigitalMars) } else version (LDC) { - import ldc.intrinsics; - double __builtin_inf()() { return double.infinity; } float __builtin_inff()() { return float.infinity; } real __builtin_infl()() { return real.infinity; } @@ -146,19 +143,18 @@ else version (LDC) alias __builtin_huge_valf = __builtin_inff; alias __builtin_huge_vall = __builtin_infl; - alias __builtin_fabs = llvm_fabs!double; - alias __builtin_fabsf = llvm_fabs!float; - alias __builtin_fabsl = llvm_fabs!real; + alias __builtin_fabs = imported!"ldc.intrinsics".llvm_fabs!double; + alias __builtin_fabsf = imported!"ldc.intrinsics".llvm_fabs!float; + alias __builtin_fabsl = imported!"ldc.intrinsics".llvm_fabs!real; - alias __builtin_bswap16 = llvm_bswap!ushort; - alias __builtin_bswap32 = llvm_bswap!uint; - alias __builtin_bswap64 = llvm_bswap!ulong; + alias __builtin_bswap16 = imported!"ldc.intrinsics".llvm_bswap!ushort; + alias __builtin_bswap32 = imported!"ldc.intrinsics".llvm_bswap!uint; + alias __builtin_bswap64 = imported!"ldc.intrinsics".llvm_bswap!ulong; int __builtin_constant_p(T)(T exp) { return 0; } - alias __builtin_expect = llvm_expect!long; + alias __builtin_expect = imported!"ldc.intrinsics".llvm_expect!long; void* __builtin_assume_aligned()(const void* p, size_t align_, ...) { return cast(void*)p; } void __builtin_assume(T)(lazy T arg) { } - import core.int128 : Cent; - alias __uint128_t = Cent; + alias __uint128_t = imported!"core.int128".Cent; } diff --git a/runtime/druntime/src/core/atomic.d b/runtime/druntime/src/core/atomic.d index 52e9590a0dd..637a112c2e8 100644 --- a/runtime/druntime/src/core/atomic.d +++ b/runtime/druntime/src/core/atomic.d @@ -2,6 +2,9 @@ * The atomic module provides basic support for lock-free * concurrent programming. * + * $(NOTE Use the `-preview=nosharedaccess` compiler flag to detect + * unsafe individual read or write operations on shared data.) + * * Copyright: Copyright Sean Kelly 2005 - 2016. * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) * Authors: Sean Kelly, Alex Rønne Petersen, Manu Evans @@ -10,6 +13,22 @@ module core.atomic; +/// +@safe unittest +{ + int y = 2; + shared int x = y; // OK + + //x++; // read modify write error + x.atomicOp!"+="(1); // OK + //y = x; // read error with preview flag + y = x.atomicLoad(); // OK + assert(y == 3); + //x = 5; // write error with preview flag + x.atomicStore(5); // OK + assert(x.atomicLoad() == 5); +} + import core.internal.atomic; import core.internal.attributes : betterC; import core.internal.traits : hasUnsharedIndirections; @@ -1186,42 +1205,6 @@ version (CoreUnittest) assert(ptr is null); } - unittest - { - import core.thread; - - // Use heap memory to ensure an optimizing - // compiler doesn't put things in registers. - uint* x = new uint(); - bool* f = new bool(); - uint* r = new uint(); - - auto thr = new Thread(() - { - while (!*f) - { - } - - atomicFence(); - - *r = *x; - }); - - thr.start(); - - *x = 42; - - atomicFence(); - - *f = true; - - atomicFence(); - - thr.join(); - - assert(*r == 42); - } - // === atomicFetchAdd and atomicFetchSub operations ==== @betterC pure nothrow @nogc @safe unittest { diff --git a/runtime/druntime/src/core/demangle.d b/runtime/druntime/src/core/demangle.d index c7ab6a9b489..2eece8b8863 100644 --- a/runtime/druntime/src/core/demangle.d +++ b/runtime/druntime/src/core/demangle.d @@ -69,62 +69,17 @@ pure @safe: { buf = buf_; addType = addType_; - dst = dst_; + dst.dst = dst_; } - - enum size_t minBufSize = 4000; - - const(char)[] buf = null; - char[] dst = null; + Buffer dst; size_t pos = 0; - size_t len = 0; size_t brp = 0; // current back reference pos AddType addType = AddType.yes; bool mute = false; Hooks hooks; - static class ParseException : Exception - { - this(string msg) @safe pure nothrow - { - super( msg ); - } - } - - - static class OverflowException : Exception - { - this(string msg) @safe pure nothrow - { - super( msg ); - } - } - - - static noreturn error( string msg = "Invalid symbol" ) @trusted /* exception only used in module */ - { - pragma(inline, false); // tame dmd inliner - - //throw new ParseException( msg ); - debug(info) printf( "error: %.*s\n", cast(int) msg.length, msg.ptr ); - throw __ctfe ? new ParseException(msg) - : cast(ParseException) __traits(initSymbol, ParseException).ptr; - - } - - - static noreturn overflow( string msg = "Buffer overflow" ) @trusted /* exception only used in module */ - { - pragma(inline, false); // tame dmd inliner - - //throw new OverflowException( msg ); - debug(info) printf( "overflow: %.*s\n", cast(int) msg.length, msg.ptr ); - throw cast(OverflowException) __traits(initSymbol, OverflowException).ptr; - } - - ////////////////////////////////////////////////////////////////////////// // Type Testing and Conversion ////////////////////////////////////////////////////////////////////////// @@ -163,96 +118,16 @@ pure @safe: error(); } - - ////////////////////////////////////////////////////////////////////////// - // Data Output - ////////////////////////////////////////////////////////////////////////// - - - static bool contains( const(char)[] a, const(char)[] b ) @trusted - { - if (a.length && b.length) - { - auto bend = b.ptr + b.length; - auto aend = a.ptr + a.length; - return a.ptr <= b.ptr && bend <= aend; - } - return false; - } - - - // move val to the end of the dst buffer - char[] shift( const(char)[] val ) - { - pragma(inline, false); // tame dmd inliner - - if ( val.length && !mute ) - { - assert( contains( dst[0 .. len], val ) ); - debug(info) printf( "shifting (%.*s)\n", cast(int) val.length, val.ptr ); - - if (len + val.length > dst.length) - overflow(); - size_t v = &val[0] - &dst[0]; - dst[len .. len + val.length] = val[]; - for (size_t p = v; p < len; p++) - dst[p] = dst[p + val.length]; - - return dst[len - val.length .. len]; - } - return null; - } - - // remove val from dst buffer - void remove( const(char)[] val ) + char[] shift(scope const(char)[] val) return scope { - pragma(inline, false); // tame dmd inliner - - if ( val.length ) - { - assert( contains( dst[0 .. len], val ) ); - debug(info) printf( "removing (%.*s)\n", cast(int) val.length, val.ptr ); - size_t v = &val[0] - &dst[0]; - assert( len >= val.length && len <= dst.length ); - len -= val.length; - for (size_t p = v; p < len; p++) - dst[p] = dst[p + val.length]; - } - } - - char[] append( const(char)[] val ) return scope - { - pragma(inline, false); // tame dmd inliner - - if ( val.length && !mute ) - { - if ( !dst.length ) - dst.length = minBufSize; - assert( !contains( dst[0 .. len], val ) ); - debug(info) printf( "appending (%.*s)\n", cast(int) val.length, val.ptr ); - - if ( dst.length - len >= val.length && &dst[len] == &val[0] ) - { - // data is already in place - auto t = dst[len .. len + val.length]; - len += val.length; - return t; - } - if ( dst.length - len >= val.length ) - { - dst[len .. len + val.length] = val[]; - auto t = dst[len .. len + val.length]; - len += val.length; - return t; - } - overflow(); - } - return null; + if (mute) + return null; + return dst.shift(val); } void putComma(size_t n) { - pragma(inline, false); + version (DigitalMars) pragma(inline, false); if (n) put(", "); } @@ -265,14 +140,9 @@ pure @safe: void put(scope const(char)[] val) return scope { - pragma(inline, false); // tame dmd inliner - - if (!val.length) return; - - if (!contains(dst[0 .. len], val)) - append(val); - else - shift(val); + if (mute) + return; + dst.append(val); } @@ -297,7 +167,7 @@ pure @safe: { if ( val.length ) { - append( " " ); + put(" "); put( val ); } } @@ -307,7 +177,7 @@ pure @safe: { debug(trace) printf( "silent+\n" ); debug(trace) scope(success) printf( "silent-\n" ); - auto n = len; dg(); len = n; + auto n = dst.length; dg(); dst.len = n; } @@ -835,7 +705,7 @@ pure @safe: debug(trace) printf( "parseType+\n" ); debug(trace) scope(success) printf( "parseType-\n" ); - auto beg = len; + auto beg = dst.length; auto t = front; char[] parseBackrefType(scope char[] delegate() pure @safe parseDg) pure @safe @@ -867,19 +737,19 @@ pure @safe: put( "shared(" ); parseType(); put( ')' ); - return dst[beg .. len]; + return dst[beg .. $]; case 'x': // Const (x Type) popFront(); put( "const(" ); parseType(); put( ')' ); - return dst[beg .. len]; + return dst[beg .. $]; case 'y': // Immutable (y Type) popFront(); put( "immutable(" ); parseType(); put( ')' ); - return dst[beg .. len]; + return dst[beg .. $]; case 'N': popFront(); switch ( front ) @@ -887,29 +757,28 @@ pure @safe: case 'n': // Noreturn popFront(); put("noreturn"); - return dst[beg .. len]; + return dst[beg .. $]; case 'g': // Wild (Ng Type) popFront(); // TODO: Anything needed here? put( "inout(" ); parseType(); put( ')' ); - return dst[beg .. len]; + return dst[beg .. $]; case 'h': // TypeVector (Nh Type) popFront(); put( "__vector(" ); parseType(); put( ')' ); - return dst[beg .. len]; + return dst[beg .. $]; default: error(); - assert( 0 ); } case 'A': // TypeArray (A Type) popFront(); parseType(); put( "[]" ); - return dst[beg .. len]; + return dst[beg .. $]; case 'G': // TypeStaticArray (G Number Type) popFront(); auto num = sliceNumber(); @@ -917,21 +786,21 @@ pure @safe: put( '[' ); put( num ); put( ']' ); - return dst[beg .. len]; + return dst[beg .. $]; case 'H': // TypeAssocArray (H Type Type) popFront(); // skip t1 auto tx = parseType(); parseType(); put( '[' ); - put( tx ); + shift(tx); put( ']' ); - return dst[beg .. len]; + return dst[beg .. $]; case 'P': // TypePointer (P Type) popFront(); parseType(); put( '*' ); - return dst[beg .. len]; + return dst[beg .. $]; case 'F': case 'U': case 'W': case 'V': case 'R': // TypeFunction return parseTypeFunction(); case 'C': // TypeClass (C LName) @@ -940,7 +809,7 @@ pure @safe: case 'T': // TypeTypedef (T LName) popFront(); parseQualifiedName(); - return dst[beg .. len]; + return dst[beg .. $]; case 'D': // TypeDelegate (D TypeFunction) popFront(); auto modifiers = parseModifier(); @@ -957,15 +826,15 @@ pure @safe: put(str); } } - return dst[beg .. len]; + return dst[beg .. $]; case 'n': // TypeNone (n) popFront(); // TODO: Anything needed here? - return dst[beg .. len]; + return dst[beg .. $]; case 'B': // TypeTuple (B Number Arguments) popFront(); // TODO: Handle this. - return dst[beg .. len]; + return dst[beg .. $]; case 'Z': // Internal symbol // This 'type' is used for untyped internal symbols, i.e.: // __array @@ -975,13 +844,13 @@ pure @safe: // __Interface // __ModuleInfo popFront(); - return dst[beg .. len]; + return dst[beg .. $]; default: if (t >= 'a' && t <= 'w') { popFront(); put( primitives[cast(size_t)(t - 'a')] ); - return dst[beg .. len]; + return dst[beg .. $]; } else if (t == 'z') { @@ -991,14 +860,13 @@ pure @safe: case 'i': popFront(); put( "cent" ); - return dst[beg .. len]; + return dst[beg .. $]; case 'k': popFront(); put( "ucent" ); - return dst[beg .. len]; + return dst[beg .. $]; default: error(); - assert( 0 ); } } error(); @@ -1351,12 +1219,13 @@ pure @safe: { debug(trace) printf( "parseTypeFunction+\n" ); debug(trace) scope(success) printf( "parseTypeFunction-\n" ); - auto beg = len; + auto beg = dst.length; parseCallConvention(); auto attributes = parseFuncAttr(); - auto argbeg = len; + auto argbeg = dst.length; + put(IsDelegate.yes == isdg ? "delegate" : "function"); put( '(' ); parseFuncArguments(); put( ')' ); @@ -1369,17 +1238,18 @@ pure @safe: put(str); } } - auto retbeg = len; - parseType(); - put( ' ' ); - // append delegate/function - if (IsDelegate.yes == isdg) - put( "delegate" ); - else - put( "function" ); - // move arguments and attributes behind name - shift( dst[argbeg .. retbeg] ); - return dst[beg..len]; + + // A function / delegate return type is located at the end of its mangling + // Write it in order, then shift it back to 'code order' + // e.g. `delegate(int) @safedouble ' => 'double delegate(int) @safe' + { + auto retbeg = dst.length; + parseType(); + put(' '); + shift(dst[argbeg .. retbeg]); + } + + return dst[beg .. $]; } static bool isCallConvention( char ch ) @@ -1701,7 +1571,7 @@ pure @safe: if ( mayBeMangledNameArg() ) { - auto l = len; + auto l = dst.length; auto p = pos; auto b = brp; try @@ -1712,7 +1582,7 @@ pure @safe: } catch ( ParseException e ) { - len = l; + dst.len = l; pos = p; brp = b; debug(trace) printf( "not a mangled name arg\n" ); @@ -1724,7 +1594,7 @@ pure @safe: // try all possible pairs of numbers auto qlen = decodeNumber() / 10; // last digit needed for QualifiedName pos--; - auto l = len; + auto l = dst.length; auto p = pos; auto b = brp; while ( qlen > 0 ) @@ -1740,7 +1610,7 @@ pure @safe: } qlen /= 10; // retry with one digit less pos = --p; - len = l; + dst.len = l; brp = b; } } @@ -1860,7 +1730,7 @@ pure @safe: case '0': .. case '9': if ( mayBeTemplateInstanceName() ) { - auto t = len; + auto t = dst.length; try { @@ -1871,7 +1741,7 @@ pure @safe: catch ( ParseException e ) { debug(trace) printf( "not a template instance name\n" ); - len = t; + dst.len = t; } } goto case; @@ -1889,10 +1759,9 @@ pure @safe: { // try to demangle a function, in case we are pointing to some function local auto prevpos = pos; - auto prevlen = len; + auto prevlen = dst.length; auto prevbrp = brp; - char[] attr; try { if ( 'M' == front ) @@ -1908,6 +1777,7 @@ pure @safe: } if ( isCallConvention( front ) ) { + char[] attr; // we don't want calling convention and attributes in the qualified name parseCallConvention(); auto attributes = parseFuncAttr(); @@ -1917,23 +1787,23 @@ pure @safe: put(str); put(' '); } - attr = dst[prevlen .. len]; + attr = dst[prevlen .. $]; } put( '(' ); parseFuncArguments(); put( ')' ); + return attr; } } catch ( ParseException ) { // not part of a qualified name, so back up pos = prevpos; - len = prevlen; + dst.len = prevlen; brp = prevbrp; - attr = null; } - return attr; + return null; } /* @@ -1945,7 +1815,7 @@ pure @safe: { debug(trace) printf( "parseQualifiedName+\n" ); debug(trace) scope(success) printf( "parseQualifiedName-\n" ); - size_t beg = len; + size_t beg = dst.length; size_t n = 0; do @@ -1956,7 +1826,7 @@ pure @safe: parseFunctionTypeNoReturn(); } while ( isSymbolNameFront() ); - return dst[beg .. len]; + return dst[beg .. $]; } @@ -1977,17 +1847,17 @@ pure @safe: match( 'D' ); do { - size_t beg = len; - size_t nameEnd = len; + size_t beg = dst.length; + size_t nameEnd = dst.length; char[] attr; do { if ( attr ) - remove( attr ); // dump attributes of parent symbols - if ( beg != len ) + dst.remove(attr); // dump attributes of parent symbols + if (beg != dst.length) put( '.' ); parseSymbolName(); - nameEnd = len; + nameEnd = dst.length; attr = parseFunctionTypeNoReturn( displayType ); } while ( isSymbolNameFront() ); @@ -1995,7 +1865,7 @@ pure @safe: if ( displayType ) { attr = shift( attr ); - nameEnd = len - attr.length; // name includes function arguments + nameEnd = dst.length - attr.length; // name includes function arguments } name = dst[beg .. nameEnd]; @@ -2003,7 +1873,7 @@ pure @safe: if ( 'M' == front ) popFront(); // has 'this' pointer - auto lastlen = len; + auto lastlen = dst.length; auto type = parseType(); if ( displayType ) { @@ -2016,7 +1886,7 @@ pure @safe: { // remove type assert( attr.length == 0 ); - len = lastlen; + dst.len = lastlen; } if ( pos >= buf.length || (n != 0 && pos >= end) ) return; @@ -2040,15 +1910,6 @@ pure @safe: parseMangledName( AddType.yes == addType ); } - char[] copyInput() return scope - { - if (dst.length < buf.length) - dst.length = buf.length; - char[] r = dst[0 .. buf.length]; - r[] = buf[]; - return r; - } - char[] doDemangle(alias FUNC)() return scope { while ( true ) @@ -2057,17 +1918,17 @@ pure @safe: { debug(info) printf( "demangle(%.*s)\n", cast(int) buf.length, buf.ptr ); FUNC(); - return dst[0 .. len]; + return dst[0 .. $]; } catch ( OverflowException e ) { debug(trace) printf( "overflow... restarting\n" ); - auto a = minBufSize; - auto b = 2 * dst.length; + auto a = Buffer.minSize; + auto b = 2 * dst.dst.length; auto newsz = a < b ? b : a; debug(info) printf( "growing dst to %lu bytes\n", newsz ); - dst.length = newsz; - pos = len = brp = 0; + dst.dst.length = newsz; + pos = dst.len = brp = 0; continue; } catch ( ParseException e ) @@ -2077,7 +1938,7 @@ pure @safe: auto msg = e.toString(); printf( "error: %.*s\n", cast(int) msg.length, msg.ptr ); } - return copyInput(); + return dst.copyInput(buf); } catch ( Exception e ) { @@ -2119,7 +1980,7 @@ char[] demangle(return scope const(char)[] buf, return scope char[] dst = null, // fast path (avoiding throwing & catching exception) for obvious // non-D mangled names if (buf.length < 2 || !(buf[0] == 'D' || buf[0..2] == "_D")) - return d.copyInput(); + return d.dst.copyInput(buf); return d.demangleName(); } @@ -2212,7 +2073,7 @@ char[] reencodeMangled(return scope const(char)[] mangled) nothrow pure @safe d.popFront(); size_t n = d.decodeBackref(); if (!n || n > refpos) - d.error("invalid back reference"); + error("invalid back reference"); auto savepos = d.pos; scope(exit) d.pos = savepos; @@ -2220,11 +2081,11 @@ char[] reencodeMangled(return scope const(char)[] mangled) nothrow pure @safe auto idlen = d.decodeNumber(); if (d.pos + idlen > d.buf.length) - d.error("invalid back reference"); + error("invalid back reference"); auto id = d.buf[d.pos .. d.pos + idlen]; auto pid = id in idpos; if (!pid) - d.error("invalid back reference"); + error("invalid back reference"); npos = positionInResult(*pid); } encodeBackref(reslen - npos); @@ -2235,7 +2096,7 @@ char[] reencodeMangled(return scope const(char)[] mangled) nothrow pure @safe { auto n = d.decodeNumber(); if (!n || n > d.buf.length || n > d.buf.length - d.pos) - d.error("LName too shot or too long"); + error("LName too shot or too long"); auto id = d.buf[d.pos .. d.pos + n]; d.pos += n; if (auto pid = id in idpos) @@ -2267,7 +2128,7 @@ char[] reencodeMangled(return scope const(char)[] mangled) nothrow pure @safe d.popFront(); auto n = d.decodeBackref(); if (n == 0 || n > refPos) - d.error("invalid back reference"); + error("invalid back reference"); size_t npos = positionInResult(refPos - n); size_t reslen = result.length; @@ -2910,6 +2771,7 @@ private shared CXX_DEMANGLER __cxa_demangle; CXX_DEMANGLER getCXXDemangler() nothrow @trusted { + import core.atomic : atomicLoad, atomicStore; if (__cxa_demangle is null) version (Posix) { @@ -2923,17 +2785,21 @@ CXX_DEMANGLER getCXXDemangler() nothrow @trusted version (Solaris) import core.sys.solaris.dlfcn : RTLD_DEFAULT; if (auto found = cast(CXX_DEMANGLER) dlsym(RTLD_DEFAULT, "__cxa_demangle")) - __cxa_demangle = found; + atomicStore(__cxa_demangle, found); } if (__cxa_demangle is null) - __cxa_demangle = (const char* mangled_name, char* output_buffer, - size_t* length, int* status) nothrow pure @trusted { - *status = -1; - return null; - }; + { + static extern(C) char* _(const char* mangled_name, char* output_buffer, + size_t* length, int* status) nothrow pure @trusted + { + *status = -1; + return null; + } + atomicStore(__cxa_demangle, &_); + } - return __cxa_demangle; + return atomicLoad(__cxa_demangle); } /** @@ -2974,3 +2840,163 @@ private char[] demangleCXX(return scope const(char)[] buf, CXX_DEMANGLER __cxa_d dst[] = buf[]; return dst; } + +/** + * Error handling through Exceptions + * + * The following types / functions are only used in this module, + * hence why the functions are `@trusted`. + * To make things `@nogc`, default-initialized instances are thrown. + */ +private class ParseException : Exception +{ + public this(string msg) @safe pure nothrow + { + super(msg); + } +} + +/// Ditto +private class OverflowException : Exception +{ + public this(string msg) @safe pure nothrow + { + super(msg); + } +} + +/// Ditto +private noreturn error(string msg = "Invalid symbol") @trusted pure +{ + version (DigitalMars) pragma(inline, false); // tame dmd inliner + + //throw new ParseException( msg ); + debug(info) printf( "error: %.*s\n", cast(int) msg.length, msg.ptr ); + throw __ctfe ? new ParseException(msg) + : cast(ParseException) __traits(initSymbol, ParseException).ptr; +} + +/// Ditto +private noreturn overflow(string msg = "Buffer overflow") @trusted pure +{ + version (DigitalMars) pragma(inline, false); // tame dmd inliner + + //throw new OverflowException( msg ); + debug(info) printf( "overflow: %.*s\n", cast(int) msg.length, msg.ptr ); + throw cast(OverflowException) __traits(initSymbol, OverflowException).ptr; +} + +private struct Buffer +{ + enum size_t minSize = 4000; + + @safe pure: + + private char[] dst; + private size_t len; + + public alias opDollar = len; + + public size_t length () const scope @safe pure nothrow @nogc + { + return this.len; + } + + public inout(char)[] opSlice (size_t from, size_t to) + inout return scope @safe pure nothrow @nogc + { + assert(from <= to); + assert(to <= len); + return this.dst[from .. to]; + } + + static bool contains(scope const(char)[] a, scope const(char)[] b) @trusted + { + if (a.length && b.length) + { + auto bend = b.ptr + b.length; + auto aend = a.ptr + a.length; + return a.ptr <= b.ptr && bend <= aend; + } + return false; + } + + char[] copyInput(scope const(char)[] buf) + return scope nothrow + { + if (dst.length < buf.length) + dst.length = buf.length; + char[] r = dst[0 .. buf.length]; + r[] = buf[]; + return r; + } + + // move val to the end of the dst buffer + char[] shift(scope const(char)[] val) return scope + { + version (DigitalMars) pragma(inline, false); // tame dmd inliner + + if (val.length) + { + assert( contains( dst[0 .. len], val ) ); + debug(info) printf( "shifting (%.*s)\n", cast(int) val.length, val.ptr ); + + if (len + val.length > dst.length) + overflow(); + size_t v = &val[0] - &dst[0]; + dst[len .. len + val.length] = val[]; + for (size_t p = v; p < len; p++) + dst[p] = dst[p + val.length]; + + return dst[len - val.length .. len]; + } + return null; + } + + // remove val from dst buffer + void remove(scope const(char)[] val) scope + { + version (DigitalMars) pragma(inline, false); // tame dmd inliner + + if ( val.length ) + { + assert( contains( dst[0 .. len], val ) ); + debug(info) printf( "removing (%.*s)\n", cast(int) val.length, val.ptr ); + size_t v = &val[0] - &dst[0]; + assert( len >= val.length && len <= dst.length ); + len -= val.length; + for (size_t p = v; p < len; p++) + dst[p] = dst[p + val.length]; + } + } + + char[] append(scope const(char)[] val) return scope + { + version (DigitalMars) pragma(inline, false); // tame dmd inliner + + if (val.length) + { + if ( !dst.length ) + dst.length = minSize; + assert( !contains( dst[0 .. len], val ) ); + debug(info) printf( "appending (%.*s)\n", cast(int) val.length, val.ptr ); + + if ( dst.length - len >= val.length && &dst[len] == &val[0] ) + { + // data is already in place + auto t = dst[len .. len + val.length]; + len += val.length; + return t; + } + if ( dst.length - len >= val.length ) + { + dst[len .. len + val.length] = val[]; + auto t = dst[len .. len + val.length]; + len += val.length; + return t; + } + overflow(); + } + return null; + } +} diff --git a/runtime/druntime/src/core/internal/array/appending.d b/runtime/druntime/src/core/internal/array/appending.d index b609167eefe..bb24813ae9e 100644 --- a/runtime/druntime/src/core/internal/array/appending.d +++ b/runtime/druntime/src/core/internal/array/appending.d @@ -35,7 +35,7 @@ template _d_arrayappendcTXImpl(Tarr : T[], T) ref Tarr _d_arrayappendcTX(return ref scope Tarr px, size_t n) @trusted pure nothrow { // needed for CTFE: https://github.com/dlang/druntime/pull/3870#issuecomment-1178800718 - pragma(inline, false); + version (DigitalMars) pragma(inline, false); version (D_TypeInfo) { auto ti = typeid(Tarr); @@ -70,7 +70,7 @@ template _d_arrayappendcTXImpl(Tarr : T[], T) /// Implementation of `_d_arrayappendT` ref Tarr _d_arrayappendT(Tarr : T[], T)(return ref scope Tarr x, scope Tarr y) @trusted { - pragma(inline, false); + version (DigitalMars) pragma(inline, false); import core.stdc.string : memcpy; import core.internal.traits : hasElaborateCopyConstructor, Unqual; diff --git a/runtime/druntime/src/core/internal/array/capacity.d b/runtime/druntime/src/core/internal/array/capacity.d index 254e9501f63..10ce2c65c95 100644 --- a/runtime/druntime/src/core/internal/array/capacity.d +++ b/runtime/druntime/src/core/internal/array/capacity.d @@ -36,7 +36,7 @@ template _d_arraysetlengthTImpl(Tarr : T[], T) */ size_t _d_arraysetlengthT(return scope ref Tarr arr, size_t newlength) @trusted pure nothrow { - pragma(inline, false); + version (DigitalMars) pragma(inline, false); version (D_TypeInfo) { auto ti = typeid(Tarr); diff --git a/runtime/druntime/src/core/internal/array/comparison.d b/runtime/druntime/src/core/internal/array/comparison.d index 821f96e25c0..94fa2433da8 100644 --- a/runtime/druntime/src/core/internal/array/comparison.d +++ b/runtime/druntime/src/core/internal/array/comparison.d @@ -83,7 +83,7 @@ int __cmp(T)(scope const T[] lhs, scope const T[] rhs) @trusted // This function is called by the compiler when dealing with array // comparisons in the semantic analysis phase of CmpExp. The ordering // comparison is lowered to a call to this template. -int __cmp(T1, T2)(T1[] s1, T2[] s2) +auto __cmp(T1, T2)(T1[] s1, T2[] s2) if (!__traits(isScalar, T1) && !__traits(isScalar, T2)) { import core.internal.traits : Unqual; @@ -237,3 +237,26 @@ if (!__traits(isScalar, T1) && !__traits(isScalar, T2)) auto vb = [cast(void[])b[0], b[1]]; assert(less2(va, vb)); } + +// custom aggregate types +@safe unittest +{ + // https://issues.dlang.org/show_bug.cgi?id=24044 + // Support float opCmp(...) with array + static struct F + { + float f; + float opCmp(F other) const { return this.f - other.f; } + } + + F[2] a = [F(1.0f), F(float.nan)]; + F[2] b = [F(1.0f), F(1.0f)]; + F[1] c = [F(1.0f)]; + + bool isNan(float f) { return f != f; } + + assert(isNan(__cmp(a, b))); + assert(isNan(__cmp(a, a))); + assert(__cmp(b, b) == 0); + assert(__cmp(a, c) > 0); +} diff --git a/runtime/druntime/src/core/internal/array/concatenation.d b/runtime/druntime/src/core/internal/array/concatenation.d index 99f33da7683..ff777a6b3ab 100644 --- a/runtime/druntime/src/core/internal/array/concatenation.d +++ b/runtime/druntime/src/core/internal/array/concatenation.d @@ -8,71 +8,172 @@ */ module core.internal.array.concatenation; -/// See $(REF _d_arraycatnTX, rt,lifetime) -private extern (C) void[] _d_arraycatnTX(const TypeInfo ti, scope byte[][] arrs) pure nothrow; - -/// Implementation of `_d_arraycatnTX` and `_d_arraycatnTXTrace` -template _d_arraycatnTXImpl(Tarr : ResultArrT[], ResultArrT : T[], T) +/** + * Concatenate the arrays inside of `froms`. + * `_d_arraycatnTX(a, b, c)` means `a ~ b ~ c`. + * + * Params: + * froms = Arrays to be concatenated. + * Returns: + * A newly allocated array that contains all the elements from `froms`. + */ +Tret _d_arraycatnTX(Tret, Tarr...)(auto ref Tarr froms) @trusted { - private enum errorMessage = "Cannot concatenate arrays if compiling without support for runtime type information!"; + import core.internal.traits : hasElaborateCopyConstructor, Unqual; + import core.lifetime : copyEmplace; + import core.stdc.string : memcpy; - /** - * Concatenating the arrays inside of `arrs`. - * `_d_arraycatnTX([a, b, c])` means `a ~ b ~ c`. - * Params: - * arrs = Array containing arrays that will be concatenated. - * Returns: - * A newly allocated array that contains all the elements from all the arrays in `arrs`. - * Bugs: - * This function template was ported from a much older runtime hook that bypassed safety, - * purity, and throwabilty checks. To prevent breaking existing code, this function template - * is temporarily declared `@trusted pure nothrow` until the implementation can be brought up to modern D expectations. - */ - ResultArrT _d_arraycatnTX(scope const Tarr arrs) @trusted pure nothrow + Tret res; + size_t totalLen; + + alias T = typeof(res[0]); + enum elemSize = T.sizeof; + enum hasPostblit = __traits(hasPostblit, T); + + static foreach (from; froms) + static if (is (typeof(from) : T)) + totalLen++; + else + totalLen += from.length; + + if (totalLen == 0) + return res; + res.length = totalLen; + + /* Currently, if both a postblit and a cpctor are defined, the postblit is + * used. If this changes, the condition below will have to be adapted. + */ + static if (hasElaborateCopyConstructor!T && !hasPostblit) { - pragma(inline, false); - version (D_TypeInfo) - { - auto ti = typeid(ResultArrT); + size_t i = 0; + foreach (ref from; froms) + static if (is (typeof(from) : T)) + copyEmplace(cast(T) from, res[i++]); + else + { + if (from.length) + foreach (ref elem; from) + copyEmplace(cast(T) elem, res[i++]); + } + } + else + { + auto resptr = cast(Unqual!T *) res; + foreach (ref from; froms) + static if (is (typeof(from) : T)) + memcpy(resptr++, cast(Unqual!T *) &from, elemSize); + else + { + const len = from.length; + if (len) + { + memcpy(resptr, cast(Unqual!T *) from, len * elemSize); + resptr += len; + } + } + + static if (hasPostblit) + foreach (ref elem; res) + (cast() elem).__xpostblit(); + } + + return res; +} - byte[][] arrs2 = (cast(byte[]*)arrs.ptr)[0 .. arrs.length]; - void[] result = ._d_arraycatnTX(ti, arrs2); - return (cast(T*)result.ptr)[0 .. result.length]; +// postblit +@safe unittest +{ + int counter; + struct S + { + int val; + this(this) + { + counter++; } - else - assert(0, errorMessage); } - version (D_ProfileGC) + S[] arr1 = [S(0), S(1), S(2)]; + S[] arr2 = []; + S[] arr3 = [S(6), S(7), S(8)]; + S elem = S(9); + S[] result = _d_arraycatnTX!(S[])(arr1, arr2, arr3, elem); + + assert(counter == 7); + assert(result == [S(0), S(1), S(2), S(6), S(7), S(8), S(9)]); +} + +// copy constructor +@safe unittest +{ + int counter; + struct S { - import core.internal.array.utils : _d_HookTraceImpl; - - /** - * TraceGC wrapper around $(REF _d_arraycatnTX, core,internal,array,concat). - * Bugs: - * This function template was ported from a much older runtime hook that bypassed safety, - * purity, and throwabilty checks. To prevent breaking existing code, this function template - * is temporarily declared `@trusted pure nothrow` until the implementation can be brought up to modern D expectations. - */ - alias _d_arraycatnTXTrace = _d_HookTraceImpl!(ResultArrT, _d_arraycatnTX, errorMessage); + int val; + this(ref return scope S rhs) + { + val = rhs.val; + counter++; + } } + + S[] arr1 = [S(0), S(1), S(2)]; + S[] arr2 = [S(3), S(4), S(5)]; + S[] arr3 = [S(6), S(7), S(8)]; + S elem = S(9); + S[] result = _d_arraycatnTX!(S[])(arr1, elem, arr2, arr3); + + assert(counter == 10); + assert(result == [S(0), S(1), S(2), S(9), S(3), S(4), S(5), S(6), S(7), S(8)]); } +// throwing @safe unittest { int counter; + bool didThrow; struct S { int val; this(this) { counter++; + if (counter == 4) + throw new Exception(""); } } - S[][] arr = [[S(0), S(1), S(2), S(3)], [S(4), S(5), S(6), S(7)]]; - S[] result = _d_arraycatnTXImpl!(typeof(arr))._d_arraycatnTX(arr); + try + { + S[] arr1 = [S(0), S(1), S(2)]; + S[] arr2 = [S(3), S(4), S(5)]; + _d_arraycatnTX!(S[])(arr1, arr2); + } + catch (Exception) + { + didThrow = true; + } + + assert(counter == 4); + assert(didThrow); +} + +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 + { + version (D_TypeInfo) + { + import core.internal.array.utils: TraceHook, gcStatsPure, accumulatePure; + mixin(TraceHook!(Tarr.stringof, "_d_arraycatnTX")); - assert(counter == 8); - assert(result == [S(0), S(1), S(2), S(3), S(4), S(5), S(6), S(7)]); + import core.lifetime: forward; + return _d_arraycatnTX!Tret(forward!froms); + } + else + assert(0, "Cannot concatenate arrays if compiling without support for runtime type information!"); + } } diff --git a/runtime/druntime/src/core/internal/array/construction.d b/runtime/druntime/src/core/internal/array/construction.d index ae71f513129..25083597761 100644 --- a/runtime/druntime/src/core/internal/array/construction.d +++ b/runtime/druntime/src/core/internal/array/construction.d @@ -36,7 +36,7 @@ import core.internal.traits : Unqual; */ Tarr _d_arrayctor(Tarr : T[], T)(return scope Tarr to, scope Tarr from, char* makeWeaklyPure = null) @trusted { - pragma(inline, false); + version (DigitalMars) pragma(inline, false); import core.internal.traits : hasElaborateCopyConstructor; import core.lifetime : copyEmplace; import core.stdc.string : memcpy; @@ -200,7 +200,7 @@ Tarr _d_arrayctor(Tarr : T[], T)(return scope Tarr to, scope Tarr from, char* ma */ void _d_arraysetctor(Tarr : T[], T)(scope Tarr p, scope ref T value) @trusted { - pragma(inline, false); + version (DigitalMars) pragma(inline, false); import core.lifetime : copyEmplace; size_t i; 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 928beacdcaf..23ba18971d0 100644 --- a/runtime/druntime/src/core/internal/gc/impl/conservative/gc.d +++ b/runtime/druntime/src/core/internal/gc/impl/conservative/gc.d @@ -1510,7 +1510,7 @@ struct Gcx List*[Bins.B_NUMSMALL] bucket; // free list for each small size // run a collection when reaching those thresholds (number of used pages) - float smallCollectThreshold, largeCollectThreshold; + float smallCollectThreshold = 0.0f, largeCollectThreshold = 0.0f; uint usedSmallPages, usedLargePages; // total number of mapped pages uint mappedPages; @@ -3533,7 +3533,7 @@ struct Pool Small = 4, Large = 12 } - ShiftBy shiftBy; // shift count for the divisor used for determining bit indices. + ShiftBy shiftBy = void; // shift count for the divisor used for determining bit indices. // This tracks how far back we have to go to find the nearest B_PAGE at // a smaller address than a B_PAGEPLUS. To save space, we use a uint. diff --git a/runtime/druntime/src/core/internal/string.d b/runtime/druntime/src/core/internal/string.d index 64a9cc92ffb..e09bba4707b 100644 --- a/runtime/druntime/src/core/internal/string.d +++ b/runtime/druntime/src/core/internal/string.d @@ -12,26 +12,57 @@ module core.internal.string; pure: nothrow: @nogc: +@safe: -alias UnsignedStringBuf = char[20]; +alias UnsignedStringBuf = char[64]; /** Converts an unsigned integer value to a string of characters. -This implementation is a template so it can be used when compiling with -betterC. +Can be used when compiling with -betterC. Does not allocate memory. Params: + T = char, wchar or dchar value = the unsigned integer value to convert buf = the pre-allocated buffer used to store the result - radix = the numeric base to use in the conversion (defaults to 10) + radix = the numeric base to use in the conversion 2 through 36 (defaults to 10) + upperCase = use upper case letters for radices 11 - 36 Returns: The unsigned integer value as a string of characters */ -char[] unsignedToTempString(uint radix = 10)(ulong value, return scope char[] buf) @safe -if (radix >= 2 && radix <= 16) +T[] unsignedToTempString(uint radix = 10, bool upperCase = false, T)(ulong value, return scope T[] buf) +if (radix >= 2 && radix <= 36 && + (is(T == char) || is(T == wchar) || is(T == dchar))) { + enum baseChar = upperCase ? 'A' : 'a'; size_t i = buf.length; + + static if (size_t.sizeof == 4) // 32 bit CPU + { + if (value <= uint.max) + { + // use faster 32 bit arithmetic + uint val = cast(uint) value; + do + { + uint x = void; + if (val < radix) + { + x = cast(uint)val; + val = 0; + } + else + { + x = cast(uint)(val % radix); + val /= radix; + } + buf[--i] = cast(char)((radix <= 10 || x < 10) ? x + '0' : x - 10 + baseChar); + } while (val); + return buf[i .. $]; + } + } + do { uint x = void; @@ -45,7 +76,7 @@ if (radix >= 2 && radix <= 16) x = cast(uint)(value % radix); value /= radix; } - buf[--i] = cast(char)((radix <= 10 || x < 10) ? x + '0' : x - 10 + 'a'); + buf[--i] = cast(char)((radix <= 10 || x < 10) ? x + '0' : x - 10 + baseChar); } while (value); return buf[i .. $]; } @@ -73,7 +104,7 @@ Params: Returns: The unsigned integer value as a string of characters */ -auto unsignedToTempString(uint radix = 10)(ulong value) @safe +auto unsignedToTempString(uint radix = 10)(ulong value) { // Need a buffer of 65 bytes for radix of 2 with room for // signedToTempString to possibly add a negative sign. @@ -85,11 +116,12 @@ auto unsignedToTempString(uint radix = 10)(ulong value) @safe unittest { - UnsignedStringBuf buf; + UnsignedStringBuf buf = void; assert(0.unsignedToTempString(buf) == "0"); assert(1.unsignedToTempString(buf) == "1"); assert(12.unsignedToTempString(buf) == "12"); assert(0x12ABCF .unsignedToTempString!16(buf) == "12abcf"); + assert(0x12ABCF .unsignedToTempString!(16, true)(buf) == "12ABCF"); assert(long.sizeof.unsignedToTempString(buf) == "8"); assert(uint.max.unsignedToTempString(buf) == "4294967295"); assert(ulong.max.unsignedToTempString(buf) == "18446744073709551615"); @@ -106,16 +138,17 @@ unittest // test bad radices assert(!is(typeof(100.unsignedToTempString!1(buf)))); assert(!is(typeof(100.unsignedToTempString!0(buf) == ""))); + assert(!is(typeof(100.unsignedToTempString!37(buf) == ""))); } -alias SignedStringBuf = char[20]; +alias SignedStringBuf = char[65]; -char[] signedToTempString(uint radix = 10)(long value, return scope char[] buf) @safe +T[] signedToTempString(uint radix = 10, bool upperCase = false, T)(long value, return scope T[] buf) { bool neg = value < 0; if (neg) value = cast(ulong)-value; - auto r = unsignedToTempString!radix(value, buf); + auto r = unsignedToTempString!(radix, upperCase)(value, buf); if (neg) { // about to do a slice without a bounds check @@ -126,7 +159,7 @@ char[] signedToTempString(uint radix = 10)(long value, return scope char[] buf) return r; } -auto signedToTempString(uint radix = 10)(long value) @safe +auto signedToTempString(uint radix = 10)(long value) { bool neg = value < 0; if (neg) @@ -142,7 +175,7 @@ auto signedToTempString(uint radix = 10)(long value) @safe unittest { - SignedStringBuf buf; + SignedStringBuf buf = void; assert(0.signedToTempString(buf) == "0"); assert(1.signedToTempString(buf) == "1"); assert((-1).signedToTempString(buf) == "-1"); @@ -150,6 +183,7 @@ unittest assert((-12).signedToTempString(buf) == "-12"); assert(0x12ABCF .signedToTempString!16(buf) == "12abcf"); assert((-0x12ABCF) .signedToTempString!16(buf) == "-12abcf"); + assert((-0x12ABCF) .signedToTempString!(16, true)(buf) == "-12ABCF"); assert(long.sizeof.signedToTempString(buf) == "8"); assert(int.max.signedToTempString(buf) == "2147483647"); assert(int.min.signedToTempString(buf) == "-2147483648"); @@ -183,7 +217,7 @@ unittest * Returns: * number of digits */ -int numDigits(uint radix = 10)(ulong value) @safe if (radix >= 2 && radix <= 36) +int numDigits(uint radix = 10)(ulong value) if (radix >= 2 && radix <= 36) { int n = 1; while (1) diff --git a/runtime/druntime/src/core/lifetime.d b/runtime/druntime/src/core/lifetime.d index 5e339c041d1..2745d54e105 100644 --- a/runtime/druntime/src/core/lifetime.d +++ b/runtime/druntime/src/core/lifetime.d @@ -1570,7 +1570,11 @@ template forward(args...) alias fwd = arg; // (r)value else - @property auto fwd(){ pragma(inline, true); return move(arg); } + @property auto fwd() + { + version (DigitalMars) { /* @@BUG 23890@@ */ } else pragma(inline, true); + return move(arg); + } } alias Result = AliasSeq!(); diff --git a/runtime/druntime/src/core/stdc/assert_.d b/runtime/druntime/src/core/stdc/assert_.d index fc9402f6051..a7a54e873c2 100644 --- a/runtime/druntime/src/core/stdc/assert_.d +++ b/runtime/druntime/src/core/stdc/assert_.d @@ -60,7 +60,7 @@ else version (FreeBSD) /*** * Assert failure function in the FreeBSD C library. */ - void __assert(const(char)* exp, const(char)* file, uint line); + void __assert(const(char)* func, const(char)* file, uint line, const(char)* exp); } else version (NetBSD) { @@ -83,7 +83,7 @@ else version (DragonFlyBSD) /*** * Assert failure function in the DragonFlyBSD C library. */ - void __assert(const(char)* exp, const(char)* file, uint line); + void __assert(const(char)* func, const(char)* file, uint line, const(char)* exp); } else version (CRuntime_Glibc) { diff --git a/runtime/druntime/src/core/stdc/config.d b/runtime/druntime/src/core/stdc/config.d index 8033018bf66..3ab41f834da 100644 --- a/runtime/druntime/src/core/stdc/config.d +++ b/runtime/druntime/src/core/stdc/config.d @@ -259,11 +259,185 @@ else alias cpp_ptrdiff_t = ptrdiff_t; } -// ABI layout of native complex types. -private struct _Complex(T) +/** ABI layout of native complex types. + */ +struct _Complex(T) + if (is(T == float) || is(T == double) || is(T == c_long_double)) { - T re; - T im; + T re = 0; + T im = 0; + + // Construction +/+ https://issues.dlang.org/show_bug.cgi?id=23788 dmd codegen problem with constructors and _Complex!float + this(_Complex!float c) { re = c.re; im = c.im; } + this(_Complex!double c) { re = c.re; im = c.im; } + this(_Complex!c_long_double c) { re = c.re; im = c.im; } + + this(T re, T im) { this.re = re; this.im = im; } + + this(T re) { this.re = re; this.im = 0; } ++/ + // Cast + R opCast(R)() + if (is(R == _Complex!float) || is(R == _Complex!double) || is(R == _Complex!c_long_double)) + { + return R(this.re, this.im); + } + + // Assignment + + ref _Complex opAssign(_Complex!float c) { re = c.re; im = c.im; return this; } + ref _Complex opAssign(_Complex!double c) { re = c.re; im = c.im; return this; } + ref _Complex opAssign(_Complex!c_long_double c) { re = c.re; im = c.im; return this; } + + ref _Complex opAssign(T t) { re = t; im = 0; return this; } + + // Equals + + bool opEquals(_Complex!float c) { return re == c.re && im == c.im; } + bool opEquals(_Complex!double c) { return re == c.re && im == c.im; } + bool opEquals(_Complex!c_long_double c) { return re == c.re && im == c.im; } + + bool opEquals(T t) { return re == t && im == 0; } + + // Unary operators + + // +complex + _Complex opUnary(string op)() + if (op == "+") + { + return this; + } + + // -complex + _Complex opUnary(string op)() + if (op == "-") + { + return _Complex(-re, -im); + } + + // BINARY OPERATORS + + // complex op complex + _Complex!(CommonType!(T,R)) opBinary(string op, R)(_Complex!R z) + { + alias C = typeof(return); + auto w = C(this.re, this.im); + return w.opOpAssign!(op)(z); + } + + // complex op numeric + _Complex!(CommonType!(T,R)) opBinary(string op, R)(R r) + if (is(R : c_long_double)) + { + alias C = typeof(return); + auto w = C(this.re, this.im); + return w.opOpAssign!(op)(r); + } + + // numeric + complex, numeric * complex + _Complex!(CommonType!(T, R)) opBinaryRight(string op, R)(R r) + if ((op == "+" || op == "*") && is(R : c_long_double)) + { + return opBinary!(op)(r); + } + + // numeric - complex + _Complex!(CommonType!(T, R)) opBinaryRight(string op, R)(R r) + if (op == "-" && is(R : c_long_double)) + { + return _Complex(r - re, -im); + } + + // numeric / complex + _Complex!(CommonType!(T, R)) opBinaryRight(string op, R)(R r) + if (op == "/" && is(R : c_long_double)) + { + import core.math : fabs; + typeof(return) w = void; + if (fabs(re) < fabs(im)) + { + immutable ratio = re/im; + immutable rdivd = r/(re*ratio + im); + + w.re = rdivd*ratio; + w.im = -rdivd; + } + else + { + immutable ratio = im/re; + immutable rdivd = r/(re + im*ratio); + + w.re = rdivd; + w.im = -rdivd*ratio; + } + + return w; + } + + // OP-ASSIGN OPERATORS + + // complex += complex, complex -= complex + ref _Complex opOpAssign(string op, C)(C z) + if ((op == "+" || op == "-") && is(C R == _Complex!R)) + { + mixin ("re "~op~"= z.re;"); + mixin ("im "~op~"= z.im;"); + return this; + } + + // complex *= complex + ref _Complex opOpAssign(string op, C)(C z) + if (op == "*" && is(C R == _Complex!R)) + { + auto temp = re*z.re - im*z.im; + im = im*z.re + re*z.im; + re = temp; + return this; + } + + // complex /= complex + ref _Complex opOpAssign(string op, C)(C z) + if (op == "/" && is(C R == _Complex!R)) + { + import core.math : fabs; + if (fabs(z.re) < fabs(z.im)) + { + immutable ratio = z.re/z.im; + immutable denom = z.re*ratio + z.im; + + immutable temp = (re*ratio + im)/denom; + im = (im*ratio - re)/denom; + re = temp; + } + else + { + immutable ratio = z.im/z.re; + immutable denom = z.re + z.im*ratio; + + immutable temp = (re + im*ratio)/denom; + im = (im - re*ratio)/denom; + re = temp; + } + return this; + } + + // complex += numeric, complex -= numeric + ref _Complex opOpAssign(string op, U : T)(U a) + if (op == "+" || op == "-") + { + mixin ("re "~op~"= a;"); + return this; + } + + // complex *= numeric, complex /= numeric + ref _Complex opOpAssign(string op, U : T)(U a) + if (op == "*" || op == "/") + { + mixin ("re "~op~"= a;"); + mixin ("im "~op~"= a;"); + return this; + } // Helper properties. pragma(inline, true) @@ -289,6 +463,168 @@ enum __c_complex_real : _Complex!c_long_double; alias c_complex_float = __c_complex_float; alias c_complex_double = __c_complex_double; alias c_complex_real = __c_complex_real; + +private template CommonType(T, R) +{ + // Special kludge for Microsoft c_long_double + static if (is(T == c_long_double)) + alias CommonType = T; + else static if (is(R == c_long_double)) + alias CommonType = R; + else + alias CommonType = typeof(true ? T.init : R.init); +} + +/************ unittests ****************/ + +version (unittest) +{ + private: + + alias _cfloat = _Complex!float; + alias _cdouble = _Complex!double; + alias _creal = _Complex!c_long_double; + + T abs(T)(T t) => t < 0 ? -t : t; +} + +@safe pure nothrow unittest +{ + auto c1 = _cdouble(1.0, 1.0); + + // Check unary operations. + auto c2 = _cdouble(0.5, 2.0); + + assert(c2 == +c2); + + assert((-c2).re == -(c2.re)); + assert((-c2).im == -(c2.im)); + assert(c2 == -(-c2)); + + // Check complex-complex operations. + auto cpc = c1 + c2; + assert(cpc.re == c1.re + c2.re); + assert(cpc.im == c1.im + c2.im); + + auto cmc = c1 - c2; + assert(cmc.re == c1.re - c2.re); + assert(cmc.im == c1.im - c2.im); + + auto ctc = c1 * c2; + assert(ctc == _cdouble(-1.5, 2.5)); + + auto cdc = c1 / c2; + assert(abs(cdc.re - 0.5882352941177) < 1e-12); + assert(abs(cdc.im - -0.3529411764706) < 1e-12); + + // Check complex-real operations. + double a = 123.456; + + auto cpr = c1 + a; + assert(cpr.re == c1.re + a); + assert(cpr.im == c1.im); + + auto cmr = c1 - a; + assert(cmr.re == c1.re - a); + assert(cmr.im == c1.im); + + auto ctr = c1 * a; + assert(ctr.re == c1.re*a); + assert(ctr.im == c1.im*a); + + auto cdr = c1 / a; + assert(abs(cdr.re - 0.00810005184033) < 1e-12); + assert(abs(cdr.im - 0.00810005184033) < 1e-12); + + auto rpc = a + c1; + assert(rpc == cpr); + + auto rmc = a - c1; + assert(rmc.re == a-c1.re); + assert(rmc.im == -c1.im); + + auto rtc = a * c1; + assert(rtc == ctr); + + auto rdc = a / c1; + assert(abs(rdc.re - 61.728) < 1e-12); + assert(abs(rdc.im - -61.728) < 1e-12); + + rdc = a / c2; + assert(abs(rdc.re - 14.5242352941) < 1e-10); + assert(abs(rdc.im - -58.0969411765) < 1e-10); + + // Check operations between different complex types. + auto cf = _cfloat(1.0, 1.0); + auto cr = _creal(1.0, 1.0); + auto c1pcf = c1 + cf; + auto c1pcr = c1 + cr; + static assert(is(typeof(c1pcf) == _cdouble)); + static assert(is(typeof(c1pcr) == _creal)); + assert(c1pcf.re == c1pcr.re); + assert(c1pcf.im == c1pcr.im); + + auto c1c = c1; + auto c2c = c2; + + c1c /= c1; + assert(abs(c1c.re - 1.0) < 1e-10); + assert(abs(c1c.im - 0.0) < 1e-10); + + c1c = c1; + c1c /= c2; + assert(abs(c1c.re - 0.5882352941177) < 1e-12); + assert(abs(c1c.im - -0.3529411764706) < 1e-12); + + c2c /= c1; + assert(abs(c2c.re - 1.25) < 1e-11); + assert(abs(c2c.im - 0.75) < 1e-12); + + c2c = c2; + c2c /= c2; + assert(abs(c2c.re - 1.0) < 1e-11); + assert(abs(c2c.im - 0.0) < 1e-12); +} + +@safe pure nothrow unittest +{ + // Initialization + _cdouble a = _cdouble(1, 0); + assert(a.re == 1 && a.im == 0); + _cdouble b = _cdouble(1.0, 0); + assert(b.re == 1.0 && b.im == 0); +// _cdouble c = _creal(1.0, 2); +// assert(c.re == 1.0 && c.im == 2); +} + +@safe pure nothrow unittest +{ + // Assignments and comparisons + _cdouble z; + + z = 1; + assert(z == 1); + assert(z.re == 1.0 && z.im == 0.0); + + z = 2.0; + assert(z == 2.0); + assert(z.re == 2.0 && z.im == 0.0); + + z = 1.0L; + assert(z == 1.0L); + assert(z.re == 1.0 && z.im == 0.0); + + auto w = _creal(1.0, 1.0); + z = w; + assert(z == w); + assert(z.re == 1.0 && z.im == 1.0); + + auto c = _cfloat(2.0, 2.0); + z = c; + assert(z == c); + assert(z.re == 2.0 && z.im == 2.0); +} + } diff --git a/runtime/druntime/src/core/sync/condition.d b/runtime/druntime/src/core/sync/condition.d index ddd04ae0576..afcfd744f0a 100644 --- a/runtime/druntime/src/core/sync/condition.d +++ b/runtime/druntime/src/core/sync/condition.d @@ -84,7 +84,8 @@ class Condition /// ditto this( shared Mutex m ) shared nothrow @safe @nogc { - this(m, true); + import core.atomic : atomicLoad; + this(atomicLoad(m), true); } // @@ -117,7 +118,15 @@ class Condition } else version (Posix) { - m_assocMutex = m; + static if (is(Q == shared)) + { + import core.atomic : atomicLoad; + m_assocMutex = atomicLoad(m); + } + else + { + m_assocMutex = m; + } static if ( is( typeof( pthread_condattr_setclock ) ) ) { () @trusted @@ -183,7 +192,8 @@ class Condition /// ditto @property shared(Mutex) mutex() shared { - return m_assocMutex; + import core.atomic : atomicLoad; + return atomicLoad(m_assocMutex); } // undocumented function for internal use @@ -195,7 +205,8 @@ class Condition // ditto final @property shared(Mutex) mutex_nothrow() shared pure nothrow @safe @nogc { - return m_assocMutex; + import core.atomic : atomicLoad; + return atomicLoad(m_assocMutex); } //////////////////////////////////////////////////////////////////////////// diff --git a/runtime/druntime/src/core/thread/package.d b/runtime/druntime/src/core/thread/package.d index 71b0237c114..d81ebbdcc82 100644 --- a/runtime/druntime/src/core/thread/package.d +++ b/runtime/druntime/src/core/thread/package.d @@ -18,3 +18,42 @@ public import core.thread.threadbase; public import core.thread.threadgroup; public import core.thread.types; public import core.thread.context; + + +// this test is here to avoid a cyclic dependency between +// core.thread and core.atomic +unittest +{ + import core.atomic; + + // Use heap memory to ensure an optimizing + // compiler doesn't put things in registers. + uint* x = new uint(); + bool* f = new bool(); + uint* r = new uint(); + + auto thr = new Thread(() + { + while (!*f) + { + } + + atomicFence(); + + *r = *x; + }); + + thr.start(); + + *x = 42; + + atomicFence(); + + *f = true; + + atomicFence(); + + thr.join(); + + assert(*r == 42); +} diff --git a/runtime/druntime/src/core/thread/types.d b/runtime/druntime/src/core/thread/types.d index eb84ad74b48..991299b808d 100644 --- a/runtime/druntime/src/core/thread/types.d +++ b/runtime/druntime/src/core/thread/types.d @@ -39,11 +39,20 @@ version (GNU) else enum isStackGrowingDown = false; } -else +else version (LDC) { - // this should be true for most architectures + // The only LLVM targets as of LLVM 16 with stack growing *upwards* are + // apparently NVPTX and AMDGPU, both without druntime support. + // Note that there's an analogous `version = StackGrowsDown` in + // core.thread.fiber. enum isStackGrowingDown = true; } +else +{ + version (X86) enum isStackGrowingDown = true; + else version (X86_64) enum isStackGrowingDown = true; + else static assert(0, "It is undefined how the stack grows on this architecture."); +} package { diff --git a/runtime/druntime/src/core/time.d b/runtime/druntime/src/core/time.d index 8d508755c7d..be941e2abcd 100644 --- a/runtime/druntime/src/core/time.d +++ b/runtime/druntime/src/core/time.d @@ -18,7 +18,7 @@ $(LEADINGROW Types) $(TR $(TDNW $(LREF Duration)) $(TD Represents a duration of time of weeks or less (kept internally as hnsecs). (e.g. 22 days or 700 seconds).)) - $(TR $(TDNW $(LREF TickDuration)) $(TD Represents a duration of time in + $(TR $(TDNW $(LREF TickDuration)) $(TD $(RED DEPRECATED) Represents a duration of time in system clock ticks, using the highest precision that the system provides.)) $(TR $(TDNW $(LREF MonoTime)) $(TD Represents a monotonic timestamp in system clock ticks, using the highest precision that the system provides.)) @@ -682,21 +682,21 @@ public: $(TR $(TD Duration) $(TD +) $(TD Duration) $(TD -->) $(TD Duration)) $(TR $(TD Duration) $(TD -) $(TD Duration) $(TD -->) $(TD Duration)) $(TR $(TD Duration) $(TD %) $(TD Duration) $(TD -->) $(TD Duration)) - $(TR $(TD Duration) $(TD +) $(TD TickDuration) $(TD -->) $(TD Duration)) - $(TR $(TD Duration) $(TD -) $(TD TickDuration) $(TD -->) $(TD Duration)) ) Params: rhs = The duration to add to or subtract from this $(D Duration). +/ - Duration opBinary(string op, D)(D rhs) const nothrow @nogc - if (((op == "+" || op == "-" || op == "%") && is(immutable D == immutable Duration)) || - ((op == "+" || op == "-") && is(immutable D == immutable TickDuration))) + Duration opBinary(string op)(const Duration rhs) const nothrow @nogc + if (op == "+" || op == "-" || op == "%") { - static if (is(immutable D == immutable Duration)) - return Duration(mixin("_hnsecs " ~ op ~ " rhs._hnsecs")); - else - return Duration(mixin("_hnsecs " ~ op ~ " rhs.hnsecs")); + return Duration(mixin("_hnsecs " ~ op ~ " rhs._hnsecs")); + } + + deprecated Duration opBinary(string op)(const TickDuration rhs) const nothrow @nogc + if (op == "+" || op == "-") + { + return Duration(mixin("_hnsecs " ~ op ~ " rhs.hnsecs")); } version (CoreUnittest) unittest @@ -733,7 +733,13 @@ public: assert((cast(D)Duration(-7)) - (cast(E)Duration(-5)) == Duration(-2)); assert((cast(D)Duration(-7)) % (cast(E)Duration(5)) == Duration(-2)); } + } + } + version (CoreUnittest) deprecated unittest + { + foreach (D; AliasSeq!(Duration, const Duration, immutable Duration)) + { foreach (T; AliasSeq!(TickDuration, const TickDuration, immutable TickDuration)) { assertApprox((cast(D)Duration(5)) + cast(T)TickDuration.from!"usecs"(7), Duration(70), Duration(80)); @@ -761,6 +767,8 @@ public: /++ + $(RED TickDuration is Deprecated) + Adds or subtracts two durations. The legal types of arithmetic for $(D Duration) using this operator are @@ -774,14 +782,14 @@ public: lhs = The $(D TickDuration) to add to this $(D Duration) or to subtract this $(D Duration) from. +/ - Duration opBinaryRight(string op, D)(D lhs) const nothrow @nogc + deprecated Duration opBinaryRight(string op, D)(D lhs) const nothrow @nogc if ((op == "+" || op == "-") && is(immutable D == immutable TickDuration)) { return Duration(mixin("lhs.hnsecs " ~ op ~ " _hnsecs")); } - version (CoreUnittest) unittest + version (CoreUnittest) deprecated unittest { foreach (D; AliasSeq!(Duration, const Duration, immutable Duration)) { @@ -821,21 +829,22 @@ public: $(TR $(TD Duration) $(TD +) $(TD Duration) $(TD -->) $(TD Duration)) $(TR $(TD Duration) $(TD -) $(TD Duration) $(TD -->) $(TD Duration)) $(TR $(TD Duration) $(TD %) $(TD Duration) $(TD -->) $(TD Duration)) - $(TR $(TD Duration) $(TD +) $(TD TickDuration) $(TD -->) $(TD Duration)) - $(TR $(TD Duration) $(TD -) $(TD TickDuration) $(TD -->) $(TD Duration)) ) Params: rhs = The duration to add to or subtract from this $(D Duration). +/ - ref Duration opOpAssign(string op, D)(const scope D rhs) nothrow @nogc - if (((op == "+" || op == "-" || op == "%") && is(immutable D == immutable Duration)) || - ((op == "+" || op == "-") && is(immutable D == immutable TickDuration))) + ref Duration opOpAssign(string op)(const Duration rhs) nothrow @nogc + if (op == "+" || op == "-" || op == "%") { - static if (is(immutable D == immutable Duration)) - mixin("_hnsecs " ~ op ~ "= rhs._hnsecs;"); - else - mixin("_hnsecs " ~ op ~ "= rhs.hnsecs;"); + mixin("_hnsecs " ~ op ~ "= rhs._hnsecs;"); + return this; + } + + deprecated ref Duration opOpAssign(string op)(const TickDuration rhs) nothrow @nogc + if (op == "+" || op == "-") + { + mixin("_hnsecs " ~ op ~ "= rhs.hnsecs;"); return this; } @@ -850,13 +859,6 @@ public: throw new AssertError("op assign failed", __FILE__, line); } - static void test2(string op, E) - (Duration actual, in E rhs, Duration lower, Duration upper, size_t line = __LINE__) - { - assertApprox(mixin("actual " ~ op ~ " rhs"), lower, upper, "op failed", line); - assertApprox(actual, lower, upper, "op assign failed", line); - } - foreach (E; AliasSeq!(Duration, const Duration, immutable Duration)) { test1!"+="(Duration(5), (cast(E)Duration(7)), Duration(12)); @@ -888,6 +890,26 @@ public: test1!"%="(Duration(-7), (cast(E)Duration(-5)), Duration(-2)); } + foreach (D; AliasSeq!(const Duration, immutable Duration)) + { + foreach (E; AliasSeq!(Duration, const Duration, immutable Duration)) + { + D lhs = D(120); + E rhs = E(120); + static assert(!__traits(compiles, lhs += rhs), D.stringof ~ " " ~ E.stringof); + } + } + } + + version (CoreUnittest) deprecated unittest + { + static void test2(string op, E) + (Duration actual, in E rhs, Duration lower, Duration upper, size_t line = __LINE__) + { + assertApprox(mixin("actual " ~ op ~ " rhs"), lower, upper, "op failed", line); + assertApprox(actual, lower, upper, "op assign failed", line); + } + foreach (T; AliasSeq!(TickDuration, const TickDuration, immutable TickDuration)) { test2!"+="(Duration(5), cast(T)TickDuration.from!"usecs"(7), Duration(70), Duration(80)); @@ -913,8 +935,7 @@ public: foreach (D; AliasSeq!(const Duration, immutable Duration)) { - foreach (E; AliasSeq!(Duration, const Duration, immutable Duration, - TickDuration, const TickDuration, immutable TickDuration)) + foreach (E; AliasSeq!(TickDuration, const TickDuration, immutable TickDuration)) { D lhs = D(120); E rhs = E(120); @@ -1170,19 +1191,21 @@ public: /++ + $(RED TickDuration is Deprecated) + Returns a $(LREF TickDuration) with the same number of hnsecs as this $(D Duration). Note that the conventional way to convert between $(D Duration) and $(D TickDuration) is using $(REF to, std,conv), e.g.: $(D duration.to!TickDuration()) +/ - TickDuration opCast(T)() const nothrow @nogc + deprecated TickDuration opCast(T)() const nothrow @nogc if (is(immutable T == immutable TickDuration)) { return TickDuration.from!"hnsecs"(_hnsecs); } - version (CoreUnittest) unittest + version (CoreUnittest) deprecated unittest { foreach (D; AliasSeq!(Duration, const Duration, immutable Duration)) { @@ -1762,6 +1785,8 @@ version (CoreUnittest) @safe pure nothrow @nogc unittest } /++ + $(RED TickDuration is DEPRECATED) + Converts a $(D TickDuration) to the given units as either an integral value or a floating point value. @@ -1773,6 +1798,7 @@ version (CoreUnittest) @safe pure nothrow @nogc unittest td = The TickDuration to convert +/ +deprecated("TickDuration has been deprecated, please use Duration or MonoTime instead") T to(string units, T, D)(D td) @safe pure nothrow @nogc if (is(immutable D == immutable TickDuration) && (units == "seconds" || @@ -1804,7 +1830,7 @@ T to(string units, T, D)(D td) @safe pure nothrow @nogc } /// -unittest +deprecated unittest { auto t = TickDuration.from!"seconds"(1000); @@ -1816,7 +1842,7 @@ unittest assert(fabs(td - 1000) < 0.001); } -unittest +deprecated unittest { void testFun(string U)() { auto t1v = 1000; @@ -2756,22 +2782,24 @@ unittest /++ - $(RED Warning: TickDuration will be deprecated in the near future (once all - uses of it in Phobos have been deprecated). Please use + $(RED Warning: TickDuration is deprecated. Please use $(LREF MonoTime) for the cases where a monotonic timestamp is needed and $(LREF Duration) when a duration is needed, rather than using - TickDuration. It has been decided that TickDuration is too confusing - (e.g. it conflates a monotonic timestamp and a duration in monotonic - clock ticks) and that having multiple duration types is too awkward - and confusing.) + TickDuration.) Represents a duration of time in system clock ticks. The system clock ticks are the ticks of the system clock at the highest precision that the system provides. +/ +deprecated("TickDuration has been deprecated, please use Duration or MonoTime instead") struct TickDuration { +deprecated: + private static TickDuration TDRvalueOf(TickDuration td) + { + return td; + } /++ The number of ticks that the system clock has in one second. @@ -2811,14 +2839,14 @@ struct TickDuration version (CoreUnittest) unittest { - assert(zero == TickDuration(0)); - assert(TickDuration.max == TickDuration(long.max)); - assert(TickDuration.min == TickDuration(long.min)); - assert(TickDuration.min < TickDuration.zero); - assert(TickDuration.zero < TickDuration.max); - assert(TickDuration.min < TickDuration.max); - assert(TickDuration.min - TickDuration(1) == TickDuration.max); - assert(TickDuration.max + TickDuration(1) == TickDuration.min); + assert((zero == TickDuration(0)) == true); + assert((TickDuration.max == TickDuration(long.max)) == true); + assert((TickDuration.min == TickDuration(long.min)) == true); + assert((TickDuration.min < TickDuration.zero) == true); + assert((TickDuration.zero < TickDuration.max) == true); + assert((TickDuration.min < TickDuration.max) == true); + assert((TickDuration.min - TickDuration(1) == TickDuration.max) == true); + assert((TickDuration.max + TickDuration(1) == TickDuration.min) == true); } @@ -3040,12 +3068,12 @@ struct TickDuration { auto a = TickDuration.currSystemTick; auto result = a += cast(T)TickDuration.currSystemTick; - assert(a == result); + assert((a == result) == true); assert(a.to!("seconds", real)() >= 0); auto b = TickDuration.currSystemTick; result = b -= cast(T)TickDuration.currSystemTick; - assert(b == result); + assert((b == result) == true); assert(b.to!("seconds", real)() <= 0); foreach (U; AliasSeq!(const TickDuration, immutable TickDuration)) @@ -3104,11 +3132,11 @@ struct TickDuration { foreach (T; AliasSeq!(TickDuration, const TickDuration, immutable TickDuration)) { - assert(-(cast(T)TickDuration(7)) == TickDuration(-7)); - assert(-(cast(T)TickDuration(5)) == TickDuration(-5)); - assert(-(cast(T)TickDuration(-7)) == TickDuration(7)); - assert(-(cast(T)TickDuration(-5)) == TickDuration(5)); - assert(-(cast(T)TickDuration(0)) == TickDuration(0)); + assert((-(cast(T)TickDuration(7)) == TickDuration(-7)) == true); + assert((-(cast(T)TickDuration(5)) == TickDuration(-5)) == true); + assert((-(cast(T)TickDuration(-7)) == TickDuration(7)) == true); + assert((-(cast(T)TickDuration(-5)) == TickDuration(5)) == true); + assert((-(cast(T)TickDuration(0)) == TickDuration(0)) == true); } } @@ -3130,9 +3158,9 @@ struct TickDuration { T t = TickDuration.currSystemTick; U u = t; - assert(t == u); - assert(rvalueOf(t) == u); - assert(t == rvalueOf(u)); + assert((t == u) == true); + assert((TDRvalueOf(t) == u) == true); + assert((t == TDRvalueOf(u)) == true); } } @@ -3142,20 +3170,20 @@ struct TickDuration { T t = TickDuration.currSystemTick; U u = t + t; - assert(t < u); - assert(t <= t); - assert(u > t); - assert(u >= u); - - assert(rvalueOf(t) < u); - assert(rvalueOf(t) <= t); - assert(rvalueOf(u) > t); - assert(rvalueOf(u) >= u); - - assert(t < rvalueOf(u)); - assert(t <= rvalueOf(t)); - assert(u > rvalueOf(t)); - assert(u >= rvalueOf(u)); + assert((t < u) == true); + assert((t <= t) == true); + assert((u > t) == true); + assert((u >= u) == true); + + assert((TDRvalueOf(t) < u) == true); + assert((TDRvalueOf(t) <= t) == true); + assert((TDRvalueOf(u) > t) == true); + assert((TDRvalueOf(u) >= u) == true); + + assert((t < TDRvalueOf(u)) == true); + assert((t <= TDRvalueOf(t)) == true); + assert((u > TDRvalueOf(t)) == true); + assert((u >= TDRvalueOf(u)) == true); } } } @@ -3186,7 +3214,7 @@ struct TickDuration TickDuration t1 = curr; immutable t2 = curr + curr; t1 *= 2; - assert(t1 == t2); + assert((t1 == t2) == true); t1 = curr; t1 *= 2.0; @@ -3195,7 +3223,7 @@ struct TickDuration t1 = curr; t1 *= 2.1; - assert(t1 > t2); + assert((t1 > t2) == true); foreach (T; AliasSeq!(const TickDuration, immutable TickDuration)) { @@ -3237,7 +3265,7 @@ struct TickDuration immutable t1 = curr; TickDuration t2 = curr + curr; t2 /= 2; - assert(t1 == t2); + assert((t1 == t2) == true); t2 = curr + curr; t2 /= 2.0; @@ -3246,7 +3274,7 @@ struct TickDuration t2 = curr + curr; t2 /= 2.1; - assert(t1 > t2); + assert((t1 > t2) == true); _assertThrown!TimeException(t2 /= 0); @@ -3284,10 +3312,10 @@ struct TickDuration { T t1 = TickDuration.currSystemTick; T t2 = t1 + t1; - assert(t1 * 2 == t2); + assert((t1 * 2 == t2) == true); immutable tol = TickDuration(cast(long)(_abs(t1.length) * double.epsilon * 2.0)); assertApprox(t1 * 2.0, t2 - tol, t2 + tol); - assert(t1 * 2.1 > t2); + assert((t1 * 2.1 > t2) == true); } } @@ -3323,12 +3351,12 @@ struct TickDuration { T t1 = TickDuration.currSystemTick; T t2 = t1 + t1; - assert(t2 / 2 == t1); + assert((t2 / 2 == t1) == true); immutable tol = TickDuration(cast(long)(_abs(t2.length) * double.epsilon / 2.0)); assertApprox(t2 / 2.0, t1 - tol, t1 + tol); - assert(t2 / 2.1 < t1); + assert((t2 / 2.1 < t1) == true); - _assertThrown!TimeException(t2 / 0); + _assertThrownDep!TimeException(t2 / 0); } } @@ -3430,7 +3458,6 @@ struct TickDuration } } - /++ Generic way of converting between two time units. Conversions to smaller units use truncating division. Years and months can be converted to each @@ -3641,6 +3668,7 @@ Duration abs(Duration duration) @safe pure nothrow @nogc } /++ Ditto +/ +deprecated("TickDuration has been deprecated, please use Duration or MonoTime instead") TickDuration abs(TickDuration duration) @safe pure nothrow @nogc { return TickDuration(_abs(duration.length)); @@ -3650,9 +3678,12 @@ unittest { assert(abs(dur!"msecs"(5)) == dur!"msecs"(5)); assert(abs(dur!"msecs"(-5)) == dur!"msecs"(5)); +} - assert(abs(TickDuration(17)) == TickDuration(17)); - assert(abs(TickDuration(-17)) == TickDuration(17)); +deprecated unittest +{ + assert((abs(TickDuration(17)) == TickDuration(17)) == true); + assert((abs(TickDuration(-17)) == TickDuration(17)) == true); } @@ -3987,6 +4018,28 @@ unittest } } +version (CoreUnittest) deprecated void _assertThrownDep(T : Throwable = Exception, E) + (lazy E expression, + string msg = null, + string file = __FILE__, + size_t line = __LINE__) +{ + bool thrown = false; + + try + expression(); + catch (T t) + thrown = true; + + if (!thrown) + { + immutable tail = msg.length == 0 ? "." : ": " ~ msg; + + throw new AssertError("assertThrown() failed: No " ~ T.stringof ~ " was thrown" ~ tail, file, line); + } +} + + version (CoreUnittest) void assertApprox(D, E)(D actual, E lower, @@ -4001,7 +4054,7 @@ version (CoreUnittest) void assertApprox(D, E)(D actual, throw new AssertError(msg ~ ": upper: " ~ actual.toString(), __FILE__, line); } -version (CoreUnittest) void assertApprox(D, E)(D actual, +version (CoreUnittest) deprecated void assertApprox(D, E)(D actual, E lower, E upper, string msg = "unittest failure", diff --git a/runtime/druntime/src/importc.h b/runtime/druntime/src/importc.h index 5d50e541969..0b2f4c6958c 100644 --- a/runtime/druntime/src/importc.h +++ b/runtime/druntime/src/importc.h @@ -29,6 +29,7 @@ #define __asm asm #define __inline__ inline #define __inline inline +#define __volatile__ volatile /******************** * Clang nullability extension used by macOS headers. @@ -73,6 +74,9 @@ */ #define __extension__ /* ignore it, as ImportC doesn't do warnings */ +#define __builtin_isnan(x) isnan(x) +#define __builtin_isfinite(x) finite(x) + /******************************** * __has_extension is a clang thing: * https://clang.llvm.org/docs/LanguageExtensions.html @@ -100,11 +104,21 @@ #endif #if __FreeBSD__ +#define __volatile volatile +#define __sync_synchronize() +#define __sync_swap(A, B) 1 #endif #if _MSC_VER //#undef _Post_writable_size //#define _Post_writable_size(x) // consider #include +#define _CRT_INSECURE_DEPRECATE(x) +#define _CRT_NONSTDC_NO_DEPRECATE 1 +#define _CRT_SECURE_NO_WARNINGS 1 +#define __ptr32 +#define __ptr64 +#define __unaligned +#define _NO_CRT_STDIO_INLINE 1 #endif /**************************** @@ -123,9 +137,11 @@ // Ubuntu's assert.h uses this #define __PRETTY_FUNCTION__ __func__ +#ifndef __aarch64__ #define _Float128 long double #define __float128 long double #endif +#endif #if __APPLE__ #undef __SIZEOF_INT128__ diff --git a/runtime/druntime/src/ldc/eh_msvc.d b/runtime/druntime/src/ldc/eh_msvc.d index 9ea66485e7c..a19079c9233 100644 --- a/runtime/druntime/src/ldc/eh_msvc.d +++ b/runtime/druntime/src/ldc/eh_msvc.d @@ -99,7 +99,7 @@ struct CxxExceptionInfo extern(C) int _d_isbaseof(ClassInfo oc, ClassInfo c); // error and exit -extern(C) void fatalerror(in char* format, ...) +extern(C) void fatalerror(const(char)* format, ...) { import core.stdc.stdarg; import core.stdc.stdio; diff --git a/runtime/druntime/src/ldc/intrinsics.di b/runtime/druntime/src/ldc/intrinsics.di index c1950e4fcca..f18bf5035dc 100644 --- a/runtime/druntime/src/ldc/intrinsics.di +++ b/runtime/druntime/src/ldc/intrinsics.di @@ -19,9 +19,7 @@ else static assert(false, "This module is only valid for LDC"); } - version (LDC_LLVM_900) enum LLVM_version = 900; -else version (LDC_LLVM_1000) enum LLVM_version = 1000; -else version (LDC_LLVM_1100) enum LLVM_version = 1100; + version (LDC_LLVM_1100) enum LLVM_version = 1100; else version (LDC_LLVM_1101) enum LLVM_version = 1101; else version (LDC_LLVM_1200) enum LLVM_version = 1200; else version (LDC_LLVM_1300) enum LLVM_version = 1300; @@ -56,9 +54,7 @@ pragma(LDC_intrinsic, "llvm.returnaddress") /// The 'llvm.frameaddress' intrinsic attempts to return the target-specific /// frame pointer value for the specified stack frame. -pragma(LDC_intrinsic, - LLVM_version >= 1000 ? "llvm.frameaddress."~p0i8 : - "llvm.frameaddress") +pragma(LDC_intrinsic, "llvm.frameaddress."~p0i8) void* llvm_frameaddress(uint level); /// The 'llvm.stacksave' intrinsic is used to remember the current state of the @@ -85,9 +81,7 @@ pragma(LDC_intrinsic, "llvm.stackrestore") /// keep in cache. The cache type specifies whether the prefetch is performed on /// the data (1) or instruction (0) cache. The rw, locality and cache type /// arguments must be constant integers. -pragma(LDC_intrinsic, - LLVM_version >= 1000 ? "llvm.prefetch."~p0i8 : - "llvm.prefetch") +pragma(LDC_intrinsic, "llvm.prefetch."~p0i8) void llvm_prefetch(const(void)* ptr, uint rw, uint locality, uint cachetype) pure @safe; /// The 'llvm.pcmarker' intrinsic is a method to export a Program Counter (PC) diff --git a/runtime/druntime/src/object.d b/runtime/druntime/src/object.d index c24effd58d4..28c6db1b009 100644 --- a/runtime/druntime/src/object.d +++ b/runtime/druntime/src/object.d @@ -81,13 +81,13 @@ version (LDC) // note: there's a copy for importC in __builtins.di version (ARM) version = ARM_Any; version (AArch64) version = ARM_Any; - // Define a __va_list alias if the platform uses an elaborate type, as it + // Define a __va_list[_tag] alias if the platform uses an elaborate type, as it // is referenced from implicitly generated code for D-style variadics, etc. // LDC does not require people to manually import core.vararg like DMD does. version (X86_64) { version (Win64) {} else - public import core.internal.vararg.sysv_x64 : __va_list; + alias __va_list_tag = imported!"core.internal.vararg.sysv_x64".__va_list_tag; } else version (ARM_Any) { @@ -4588,12 +4588,15 @@ public import core.internal.entrypoint : _d_cmain; public import core.internal.array.appending : _d_arrayappendT; version (D_ProfileGC) +{ public import core.internal.array.appending : _d_arrayappendTTrace; + public import core.internal.array.concatenation : _d_arraycatnTXTrace; +} public import core.internal.array.appending : _d_arrayappendcTXImpl; public import core.internal.array.comparison : __cmp; public import core.internal.array.equality : __equals; public import core.internal.array.casting: __ArrayCast; -public import core.internal.array.concatenation : _d_arraycatnTXImpl; +public import core.internal.array.concatenation : _d_arraycatnTX; public import core.internal.array.construction : _d_arrayctor; public import core.internal.array.construction : _d_arraysetctor; public import core.internal.array.arrayassign : _d_arrayassign_l; diff --git a/runtime/druntime/src/rt/aApply.d b/runtime/druntime/src/rt/aApply.d index 5d5ddb34740..c59d9dc1234 100644 --- a/runtime/druntime/src/rt/aApply.d +++ b/runtime/druntime/src/rt/aApply.d @@ -71,7 +71,7 @@ Params: Returns: non-zero when the loop was exited through a `break` */ -extern (C) int _aApplycd1(in char[] aa, dg_t dg) +extern (C) int _aApplycd1(scope const(char)[] aa, dg_t dg) { int result; size_t len = aa.length; @@ -132,7 +132,7 @@ unittest } /// ditto -extern (C) int _aApplywd1(in wchar[] aa, dg_t dg) +extern (C) int _aApplywd1(scope const(wchar)[] aa, dg_t dg) { int result; size_t len = aa.length; @@ -193,7 +193,7 @@ unittest } /// ditto -extern (C) int _aApplycw1(in char[] aa, dg_t dg) +extern (C) int _aApplycw1(scope const(char)[] aa, dg_t dg) { int result; size_t len = aa.length; @@ -267,7 +267,7 @@ unittest } /// ditto -extern (C) int _aApplywc1(in wchar[] aa, dg_t dg) +extern (C) int _aApplywc1(scope const(wchar)[] aa, dg_t dg) { int result; size_t len = aa.length; @@ -347,7 +347,7 @@ unittest } /// ditto -extern (C) int _aApplydc1(in dchar[] aa, dg_t dg) +extern (C) int _aApplydc1(scope const(dchar)[] aa, dg_t dg) { int result; @@ -423,7 +423,7 @@ unittest } /// ditto -extern (C) int _aApplydw1(in dchar[] aa, dg_t dg) +extern (C) int _aApplydw1(scope const(dchar)[] aa, dg_t dg) { int result; @@ -508,7 +508,7 @@ extern (D) alias dg2_t = int delegate(void* i, void* c); /** Variants of _aApplyXXX that include a loop index. */ -extern (C) int _aApplycd2(in char[] aa, dg2_t dg) +extern (C) int _aApplycd2(scope const(char)[] aa, dg2_t dg) { int result; size_t len = aa.length; @@ -576,7 +576,7 @@ unittest } /// ditto -extern (C) int _aApplywd2(in wchar[] aa, dg2_t dg) +extern (C) int _aApplywd2(scope const(wchar)[] aa, dg2_t dg) { int result; size_t len = aa.length; @@ -644,7 +644,7 @@ unittest } /// ditto -extern (C) int _aApplycw2(in char[] aa, dg2_t dg) +extern (C) int _aApplycw2(scope const(char)[] aa, dg2_t dg) { int result; size_t len = aa.length; @@ -723,7 +723,7 @@ unittest } /// ditto -extern (C) int _aApplywc2(in wchar[] aa, dg2_t dg) +extern (C) int _aApplywc2(scope const(wchar)[] aa, dg2_t dg) { int result; size_t len = aa.length; @@ -808,7 +808,7 @@ unittest } /// ditto -extern (C) int _aApplydc2(in dchar[] aa, dg2_t dg) +extern (C) int _aApplydc2(scope const(dchar)[] aa, dg2_t dg) { int result; size_t len = aa.length; @@ -888,7 +888,7 @@ unittest } /// ditto -extern (C) int _aApplydw2(in dchar[] aa, dg2_t dg) +extern (C) int _aApplydw2(scope const(dchar)[] aa, dg2_t dg) { int result; debug(apply) printf("_aApplydw2(), len = %d\n", aa.length); diff --git a/runtime/druntime/src/rt/aApplyR.d b/runtime/druntime/src/rt/aApplyR.d index ce3bb9eaf70..560025c636d 100644 --- a/runtime/druntime/src/rt/aApplyR.d +++ b/runtime/druntime/src/rt/aApplyR.d @@ -34,7 +34,7 @@ Params: Returns: non-zero when the loop was exited through a `break` */ -extern (C) int _aApplyRcd1(in char[] aa, dg_t dg) +extern (C) int _aApplyRcd1(scope const(char)[] aa, dg_t dg) { int result; debug(apply) printf("_aApplyRcd1(), len = %d\n", aa.length); @@ -107,7 +107,7 @@ unittest } /// ditto -extern (C) int _aApplyRwd1(in wchar[] aa, dg_t dg) +extern (C) int _aApplyRwd1(scope const(wchar)[] aa, dg_t dg) { int result; debug(apply) printf("_aApplyRwd1(), len = %d\n", aa.length); @@ -170,7 +170,7 @@ unittest } /// ditto -extern (C) int _aApplyRcw1(in char[] aa, dg_t dg) +extern (C) int _aApplyRcw1(scope const(char)[] aa, dg_t dg) { int result; debug(apply) printf("_aApplyRcw1(), len = %d\n", aa.length); @@ -256,7 +256,7 @@ unittest } /// ditto -extern (C) int _aApplyRwc1(in wchar[] aa, dg_t dg) +extern (C) int _aApplyRwc1(scope const(wchar)[] aa, dg_t dg) { int result; debug(apply) printf("_aApplyRwc1(), len = %d\n", aa.length); @@ -340,7 +340,7 @@ unittest } /// ditto -extern (C) int _aApplyRdc1(in dchar[] aa, dg_t dg) +extern (C) int _aApplyRdc1(scope const(dchar)[] aa, dg_t dg) { int result; debug(apply) printf("_aApplyRdc1(), len = %d\n", aa.length); @@ -418,7 +418,7 @@ unittest } /// ditto -extern (C) int _aApplyRdw1(in dchar[] aa, dg_t dg) +extern (C) int _aApplyRdw1(scope const(dchar)[] aa, dg_t dg) { int result; debug(apply) printf("_aApplyRdw1(), len = %d\n", aa.length); @@ -502,7 +502,7 @@ extern (D) alias dg2_t = int delegate(void* i, void* c); /** Variants of _aApplyRXXX that include a loop index. */ -extern (C) int _aApplyRcd2(in char[] aa, dg2_t dg) +extern (C) int _aApplyRcd2(scope const(char)[] aa, dg2_t dg) { int result; size_t i; size_t len = aa.length; @@ -578,7 +578,7 @@ unittest } /// ditto -extern (C) int _aApplyRwd2(in wchar[] aa, dg2_t dg) +extern (C) int _aApplyRwd2(scope const(wchar)[] aa, dg2_t dg) { int result; debug(apply) printf("_aApplyRwd2(), len = %d\n", aa.length); @@ -643,7 +643,7 @@ unittest } /// ditto -extern (C) int _aApplyRcw2(in char[] aa, dg2_t dg) +extern (C) int _aApplyRcw2(scope const(char)[] aa, dg2_t dg) { int result; debug(apply) printf("_aApplyRcw2(), len = %d\n", aa.length); @@ -731,7 +731,7 @@ unittest } /// ditto -extern (C) int _aApplyRwc2(in wchar[] aa, dg2_t dg) +extern (C) int _aApplyRwc2(scope const(wchar)[] aa, dg2_t dg) { int result; debug(apply) printf("_aApplyRwc2(), len = %d\n", aa.length); @@ -817,7 +817,7 @@ unittest } /// ditto -extern (C) int _aApplyRdc2(in dchar[] aa, dg2_t dg) +extern (C) int _aApplyRdc2(scope const(dchar)[] aa, dg2_t dg) { int result; debug(apply) printf("_aApplyRdc2(), len = %d\n", aa.length); @@ -896,7 +896,7 @@ unittest } /// ditto -extern (C) int _aApplyRdw2(in dchar[] aa, dg2_t dg) +extern (C) int _aApplyRdw2(scope const(dchar)[] aa, dg2_t dg) { int result; debug(apply) printf("_aApplyRdw2(), len = %d\n", aa.length); diff --git a/runtime/druntime/src/rt/lifetime.d b/runtime/druntime/src/rt/lifetime.d index 13d3c9132ae..6405cee0017 100644 --- a/runtime/druntime/src/rt/lifetime.d +++ b/runtime/druntime/src/rt/lifetime.d @@ -1237,7 +1237,7 @@ extern (C) void* _d_newitemU(scope const TypeInfo _ti) pure nothrow @weak } /// ditto -extern (C) void* _d_newitemT(in TypeInfo _ti) pure nothrow @weak +extern (C) void* _d_newitemT(const TypeInfo _ti) pure nothrow @weak { import core.stdc.string; auto p = _d_newitemU(_ti); @@ -1246,7 +1246,7 @@ extern (C) void* _d_newitemT(in TypeInfo _ti) pure nothrow @weak } /// Same as above, for item with non-zero initializer. -extern (C) void* _d_newitemiT(in TypeInfo _ti) pure nothrow @weak +extern (C) void* _d_newitemiT(const TypeInfo _ti) pure nothrow @weak { import core.stdc.string; auto p = _d_newitemU(_ti); @@ -1326,7 +1326,7 @@ extern (C) CollectHandler rt_getCollectHandler() /** * */ -extern (C) int rt_hasFinalizerInSegment(void* p, size_t size, uint attr, in void[] segment) nothrow +extern (C) int rt_hasFinalizerInSegment(void* p, size_t size, uint attr, scope const(void)[] segment) nothrow { if (attr & BlkAttr.STRUCTFINAL) { @@ -2271,148 +2271,6 @@ extern (C) void[] _d_arrayappendwd(ref byte[] x, dchar c) @weak return x; } - -/** -Concatenate two arrays into a new array - ---- -void main() -{ - int[] x = [10, 20, 30]; - int[] y = [40, 50]; - int[] c = x ~ y; // _d_arraycatT(typeid(int[]), (cast(byte*) x)[0..x.length], (cast(byte*) y)[0..y.length]); -} ---- - -Params: - ti = type that the two arrays share - x = left hand side array casted to `byte[]`. Despite this cast, its `.length` is original element length, not byte length - y = right hand side array casted to `byte[]`. Despite this cast, its `.length` is original element length, not byte length -Returns: - resulting concatenated array, with `.length` equal to new element length despite `byte` type -*/ -extern (C) byte[] _d_arraycatT(const TypeInfo ti, byte[] x, byte[] y) @weak -out (result) -{ - auto tinext = unqualify(ti.next); - auto sizeelem = tinext.tsize; // array element size - debug(PRINTF) printf("_d_arraycatT(%d,%p ~ %d,%p sizeelem = %d => %d,%p)\n", x.length, x.ptr, y.length, y.ptr, sizeelem, result.length, result.ptr); - assert(result.length == x.length + y.length); - - // If a postblit is involved, the contents of result might rightly differ - // from the bitwise concatenation of x and y. - if (!hasPostblit(tinext)) - { - for (size_t i = 0; i < x.length * sizeelem; i++) - assert((cast(byte*)result)[i] == (cast(byte*)x)[i]); - for (size_t i = 0; i < y.length * sizeelem; i++) - assert((cast(byte*)result)[x.length * sizeelem + i] == (cast(byte*)y)[i]); - } - - size_t cap = GC.sizeOf(result.ptr); - assert(!cap || cap > result.length * sizeelem); -} -do -{ - import core.stdc.string; - version (none) - { - /* Cannot use this optimization because: - * char[] a, b; - * char c = 'a'; - * b = a ~ c; - * c = 'b'; - * will change the contents of b. - */ - if (!y.length) - return x; - if (!x.length) - return y; - } - - auto tinext = unqualify(ti.next); - auto sizeelem = tinext.tsize; // array element size - debug(PRINTF) printf("_d_arraycatT(%d,%p ~ %d,%p sizeelem = %d)\n", x.length, x.ptr, y.length, y.ptr, sizeelem); - size_t xlen = x.length * sizeelem; - size_t ylen = y.length * sizeelem; - size_t len = xlen + ylen; - - if (!len) - return null; - - auto info = __arrayAlloc(len, ti, tinext); - byte* p = cast(byte*)__arrayStart(info); - p[len] = 0; // guessing this is to optimize for null-terminated arrays? - memcpy(p, x.ptr, xlen); - memcpy(p + xlen, y.ptr, ylen); - // do postblit processing - __doPostblit(p, xlen + ylen, tinext); - - auto isshared = typeid(ti) is typeid(TypeInfo_Shared); - __setArrayAllocLength(info, len, isshared, tinext); - return p[0 .. x.length + y.length]; -} - - -/** -Concatenate multiple arrays at once - -This is more efficient than repeatedly concatenating pairs of arrays because the total size is known in advance. - -``` -void main() -{ - int[] a, b, c; - int[] res = a ~ b ~ c; - // _d_arraycatnTX(typeid(int[]), - // [(cast(byte*)a.ptr)[0..a.length], (cast(byte*)b.ptr)[0..b.length], (cast(byte*)c.ptr)[0..c.length]]); -} -``` - -Params: - ti = type of arrays to concatenate and resulting array - arrs = array of arrays to concatenate, cast to `byte[]` while keeping `.length` the same - -Returns: - newly created concatenated array, `.length` equal to the total element length despite `void` type -*/ -extern (C) void[] _d_arraycatnTX(const TypeInfo ti, scope byte[][] arrs) @weak -{ - import core.stdc.string; - - size_t length; - auto tinext = unqualify(ti.next); - auto size = tinext.tsize; // array element size - - foreach (b; arrs) - length += b.length; - - if (!length) - return null; - - auto allocsize = length * size; - auto info = __arrayAlloc(allocsize, ti, tinext); - auto isshared = typeid(ti) is typeid(TypeInfo_Shared); - __setArrayAllocLength(info, allocsize, isshared, tinext); - void *a = __arrayStart (info); - - size_t j = 0; - foreach (b; arrs) - { - if (b.length) - { - memcpy(a + j, b.ptr, b.length * size); - j += b.length * size; - } - } - - // do postblit processing - __doPostblit(a, j, tinext); - - return a[0..length]; -} - - /** Allocate an array literal diff --git a/runtime/druntime/src/rt/profilegc.d b/runtime/druntime/src/rt/profilegc.d index 45e0d51b711..b97a5c5437b 100644 --- a/runtime/druntime/src/rt/profilegc.d +++ b/runtime/druntime/src/rt/profilegc.d @@ -15,6 +15,7 @@ module rt.profilegc; private: +import core.stdc.errno; import core.stdc.stdio; import core.stdc.stdlib; import core.stdc.string; @@ -151,7 +152,7 @@ shared static ~this() { qsort(counts.ptr, counts.length, Result.sizeof, &Result.qsort_cmp); - FILE* fp = logfilename.length == 0 ? stdout : fopen((logfilename).ptr, "w"); + FILE* fp = logfilename == "\0" ? stdout : fopen((logfilename).ptr, "w"); if (fp) { fprintf(fp, "bytes allocated, allocations, type, function, file:line\n"); @@ -165,6 +166,12 @@ shared static ~this() fclose(fp); } else - fprintf(stderr, "cannot write profilegc log file '%.*s'", cast(int) logfilename.length, logfilename.ptr); + { + const err = errno; + fprintf(stderr, "cannot write profilegc log file '%.*s' (errno=%d)", + cast(int) logfilename.length, + logfilename.ptr, + cast(int) err); + } } } diff --git a/runtime/druntime/src/rt/sections_ldc.d b/runtime/druntime/src/rt/sections_ldc.d index 5ee022bcf05..abeffadde3b 100644 --- a/runtime/druntime/src/rt/sections_ldc.d +++ b/runtime/druntime/src/rt/sections_ldc.d @@ -100,7 +100,7 @@ private * Scan segments in Linux dl_phdr_info struct and store * the TLS and writeable data segments in *pdso. */ - void scanSegments(in ref dl_phdr_info info, DSO* pdso) nothrow @nogc + void scanSegments(const scope ref dl_phdr_info info, DSO* pdso) nothrow @nogc { foreach (ref phdr; info.dlpi_phdr[0 .. info.dlpi_phnum]) { @@ -153,7 +153,7 @@ private return dl_iterate_phdr(&callback, &dg) != 0; } - bool findSegmentForAddr(in ref dl_phdr_info info, in void* addr, ElfW!"Phdr"* result=null) nothrow @nogc + bool findSegmentForAddr(const scope ref dl_phdr_info info, in void* addr, ElfW!"Phdr"* result=null) nothrow @nogc { if (addr < cast(void*)info.dlpi_addr) // quick reject return false; diff --git a/runtime/druntime/src/rt/tracegc.d b/runtime/druntime/src/rt/tracegc.d index 29b61468056..ab65cb9887a 100644 --- a/runtime/druntime/src/rt/tracegc.d +++ b/runtime/druntime/src/rt/tracegc.d @@ -22,15 +22,13 @@ extern (C) void[] _d_newarrayU(const scope TypeInfo ti, size_t length); extern (C) void[] _d_newarrayiT(const TypeInfo ti, size_t length); extern (C) void[] _d_newarraymTX(const TypeInfo ti, size_t[] dims); extern (C) void[] _d_newarraymiTX(const TypeInfo ti, size_t[] dims); -extern (C) void* _d_newitemT(in TypeInfo ti); -extern (C) void* _d_newitemiT(in TypeInfo ti); +extern (C) void* _d_newitemT(const TypeInfo ti); +extern (C) void* _d_newitemiT(const TypeInfo ti); extern (C) void _d_callfinalizer(void* p); extern (C) void _d_callinterfacefinalizer(void *p); extern (C) void _d_delclass(Object* p); extern (C) void _d_delinterface(void** p); extern (C) void _d_delmemory(void* *p); -extern (C) byte[] _d_arraycatT(const TypeInfo ti, byte[] x, byte[] y); -extern (C) void[] _d_arraycatnTX(const TypeInfo ti, scope byte[][] arrs); extern (C) void* _d_arrayliteralTX(const TypeInfo ti, size_t length); extern (C) void* _d_assocarrayliteralTX(const TypeInfo_AssociativeArray ti, void[] keys, void[] vals); diff --git a/runtime/druntime/test/profile/Makefile b/runtime/druntime/test/profile/Makefile index b0110c66e0c..f6a4bbe4cdb 100644 --- a/runtime/druntime/test/profile/Makefile +++ b/runtime/druntime/test/profile/Makefile @@ -7,6 +7,18 @@ TESTS:=profile DIFF:=diff --strip-trailing-cr GREP:=grep +ifeq (freebsd,$(OS)) + SHELL=/usr/local/bin/bash +else ifeq (openbsd,$(OS)) + SHELL=/usr/local/bin/bash +else ifeq (netbsd,$(OS)) + SHELL=/usr/pkg/bin/bash +else ifeq (dragonflybsd,$(OS)) + SHELL=/usr/local/bin/bash +else + SHELL=/bin/bash +endif + .PHONY: all clean all: $(addprefix $(ROOT)/,$(addsuffix .done,$(TESTS))) @@ -28,7 +40,9 @@ $(ROOT)/profilegc.done: $(ROOT)/%.done: $(ROOT)/% @echo Testing $* @rm -f $(ROOT)/myprofilegc.log $(QUIET)$(TIMELIMIT)$(ROOT)/$* $(ROOT)/myprofilegc.log - $(QUIET)$(DIFF) myprofilegc.log.$(OS).$(MODEL).exp $(ROOT)/myprofilegc.log + $(QUIET)$(DIFF) \ + <($(GREP) -vF 'core.' myprofilegc.log.$(OS).$(MODEL).exp) \ + <($(GREP) -vF 'core.' $(ROOT)/myprofilegc.log) @touch $@ $(ROOT)/both.done: DFLAGS+=-profile -profile=gc diff --git a/runtime/druntime/test/profile/myprofilegc.log.freebsd.32.exp b/runtime/druntime/test/profile/myprofilegc.log.freebsd.32.exp index 15b5e41fd7c..5f203cb7da3 100644 --- a/runtime/druntime/test/profile/myprofilegc.log.freebsd.32.exp +++ b/runtime/druntime/test/profile/myprofilegc.log.freebsd.32.exp @@ -16,5 +16,4 @@ bytes allocated, allocations, type, function, file:line 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 profilegc.main.C core.lifetime._d_newclassT!(C)._d_newclassT ../../src/core/lifetime.d:2755 16 1 wchar[] D 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 79c86edcfb3..73c4147f238 100644 --- a/runtime/druntime/test/profile/myprofilegc.log.freebsd.64.exp +++ b/runtime/druntime/test/profile/myprofilegc.log.freebsd.64.exp @@ -6,7 +6,6 @@ bytes allocated, allocations, type, function, file:line 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 profilegc.main.C core.lifetime._d_newclassT!(C)._d_newclassT ../../src/core/lifetime.d:2755 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 diff --git a/runtime/druntime/test/profile/myprofilegc.log.linux.32.exp b/runtime/druntime/test/profile/myprofilegc.log.linux.32.exp index 15b5e41fd7c..5f203cb7da3 100644 --- a/runtime/druntime/test/profile/myprofilegc.log.linux.32.exp +++ b/runtime/druntime/test/profile/myprofilegc.log.linux.32.exp @@ -16,5 +16,4 @@ bytes allocated, allocations, type, function, file:line 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 profilegc.main.C core.lifetime._d_newclassT!(C)._d_newclassT ../../src/core/lifetime.d:2755 16 1 wchar[] D 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 79c86edcfb3..73c4147f238 100644 --- a/runtime/druntime/test/profile/myprofilegc.log.linux.64.exp +++ b/runtime/druntime/test/profile/myprofilegc.log.linux.64.exp @@ -6,7 +6,6 @@ bytes allocated, allocations, type, function, file:line 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 profilegc.main.C core.lifetime._d_newclassT!(C)._d_newclassT ../../src/core/lifetime.d:2755 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 diff --git a/runtime/druntime/test/profile/myprofilegc.log.osx.32.exp b/runtime/druntime/test/profile/myprofilegc.log.osx.32.exp index 4faa76ae777..4c5494b7e11 100644 --- a/runtime/druntime/test/profile/myprofilegc.log.osx.32.exp +++ b/runtime/druntime/test/profile/myprofilegc.log.osx.32.exp @@ -16,5 +16,4 @@ bytes allocated, allocations, type, function, file:line 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 profilegc.main.C core.lifetime._d_newclassT!(C)._d_newclassT ../../src/core/lifetime.d:2755 16 1 wchar[] D 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 79c86edcfb3..73c4147f238 100644 --- a/runtime/druntime/test/profile/myprofilegc.log.osx.64.exp +++ b/runtime/druntime/test/profile/myprofilegc.log.osx.64.exp @@ -6,7 +6,6 @@ bytes allocated, allocations, type, function, file:line 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 profilegc.main.C core.lifetime._d_newclassT!(C)._d_newclassT ../../src/core/lifetime.d:2755 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 diff --git a/runtime/jit-rt/cpp-so/bind.cpp b/runtime/jit-rt/cpp-so/bind.cpp index 05d2b260c0e..ff87e81b2ef 100644 --- a/runtime/jit-rt/cpp-so/bind.cpp +++ b/runtime/jit-rt/cpp-so/bind.cpp @@ -18,18 +18,6 @@ #include "valueparser.h" -#if LDC_LLVM_VER >= 1000 -#if LDC_LLVM_VER >= 1100 -#define LLAlign llvm::Align -#else -#define LLAlign llvm::MaybeAlign -#endif -#define LLMaybeAlign llvm::MaybeAlign -#else -#define LLAlign -#define LLMaybeAlign -#endif - namespace { enum { SmallParamsCount = 5 }; @@ -83,16 +71,14 @@ allocParam(llvm::IRBuilder<> &builder, llvm::Type &srcType, if (param.type == ParamType::Aggregate && srcType.isPointerTy()) { auto elemType = srcType.getPointerElementType(); auto stackArg = builder.CreateAlloca(elemType); - if (auto alignment = layout.getABITypeAlignment(elemType)) - stackArg->setAlignment(LLAlign(alignment)); + stackArg->setAlignment(layout.getABITypeAlign(elemType)); auto init = parseInitializer(layout, *elemType, param.data, errHandler, override); builder.CreateStore(init, stackArg); return stackArg; } auto stackArg = builder.CreateAlloca(&srcType); - if (auto alignment = layout.getABITypeAlignment(&srcType)) - stackArg->setAlignment(LLAlign(alignment)); + stackArg->setAlignment(layout.getABITypeAlign(&srcType)); auto init = parseInitializer(layout, srcType, param.data, errHandler, override); builder.CreateStore(init, stackArg); diff --git a/runtime/jit-rt/cpp-so/disassembler.cpp b/runtime/jit-rt/cpp-so/disassembler.cpp index 44bffba7c14..e4b73e46d5d 100644 --- a/runtime/jit-rt/cpp-so/disassembler.cpp +++ b/runtime/jit-rt/cpp-so/disassembler.cpp @@ -35,12 +35,6 @@ #endif #include "llvm/Target/TargetMachine.h" -#if LDC_LLVM_VER >= 1000 -namespace llvm { -using std::make_unique; -} -#endif - namespace { template std::unique_ptr unique(T *ptr) { return std::unique_ptr(ptr); @@ -132,28 +126,15 @@ void printFunction(const llvm::MCDisassembler &disasm, std::string comment; llvm::raw_string_ostream commentStream(comment); auto status = disasm.getInstruction(inst, size, data.slice(pos), pos, -#if LDC_LLVM_VER < 1000 - llvm::nulls(), -#endif commentStream); switch (status) { case llvm::MCDisassembler::Fail: -#if LDC_LLVM_VER >= 1100 - streamer.emitRawText( -#else - streamer.EmitRawText( -#endif - "failed to disassemble"); + streamer.emitRawText("failed to disassemble"); return; case llvm::MCDisassembler::SoftFail: -#if LDC_LLVM_VER >= 1100 - streamer.emitRawText( -#else - streamer.EmitRawText( -#endif - "potentially undefined instruction encoding:"); + streamer.emitRawText("potentially undefined instruction encoding:"); LLVM_FALLTHROUGH; case llvm::MCDisassembler::Success: @@ -166,22 +147,14 @@ void printFunction(const llvm::MCDisassembler &disasm, } } else if (Stage::Emit == stage) { if (auto label = symTable.getTargetLabel(pos)) { -#if LDC_LLVM_VER >= 1100 streamer.emitLabel(label); -#else - streamer.EmitLabel(label); -#endif } commentStream.flush(); if (!comment.empty()) { streamer.AddComment(comment); comment.clear(); } -#if LDC_LLVM_VER >= 1100 streamer.emitInstruction(inst, sti); -#else - streamer.EmitInstruction(inst, sti); -#endif } break; } @@ -286,8 +259,8 @@ void disassemble(const llvm::TargetMachine &tm, } SymTable symTable(ctx); - disasm->setSymbolizer(llvm::make_unique( - ctx, llvm::make_unique(ctx), symTable)); + disasm->setSymbolizer(std::make_unique( + ctx, std::make_unique(ctx), symTable)); auto mcia = unique(target.createMCInstrAnalysis(mii)); if (nullptr == mcia) { @@ -308,7 +281,7 @@ void disassemble(const llvm::TargetMachine &tm, // Streamer takes ownership of mip mab auto asmStreamer = unique(target.createAsmStreamer( - ctx, llvm::make_unique(os), true, true, + ctx, std::make_unique(os), true, true, mip.release(), nullptr, std::move(mab), false)); if (nullptr == asmStreamer) { return; @@ -320,11 +293,7 @@ void disassemble(const llvm::TargetMachine &tm, for (const auto &symbol : object.symbols()) { const auto secIt = llvm::cantFail(symbol.getSection()); if (object.section_end() != secIt) { -#if LDC_LLVM_VER >= 1100 auto offset = llvm::cantFail(symbol.getValue()); -#else - auto offset = symbol.getValue(); -#endif sectionsToProcess[secIt->getIndex()].push_back(offset); } } @@ -346,21 +315,13 @@ void disassemble(const llvm::TargetMachine &tm, llvm::cantFail(symbol.getType())) { symTable.reset(); symTable.addLabel(0, 0, name); // Function start -#if LDC_LLVM_VER >= 1100 auto offset = llvm::cantFail(symbol.getValue()); -#else - auto offset = symbol.getValue(); -#endif processRelocations(symTable, offset, object, sec); // TODO: something more optimal for (const auto &globalSec : object.sections()) { -#if LDC_LLVM_VER >= 1000 auto rs = globalSec.getRelocatedSection(); if (rs && *rs == secIt) { -#else - if (globalSec.getRelocatedSection() == secIt) { -#endif processRelocations(symTable, offset, object, globalSec); } } @@ -377,11 +338,7 @@ void disassemble(const llvm::TargetMachine &tm, reinterpret_cast(data.data() + offset), size); printFunction(*disasm, *mcia, buff, symTable, *sti, *asmStreamer); -#if LDC_LLVM_VER >= 1100 asmStreamer->emitRawText(""); -#else - asmStreamer->EmitRawText(""); -#endif } } } diff --git a/runtime/jit-rt/cpp-so/jit_context.cpp b/runtime/jit-rt/cpp-so/jit_context.cpp index 5e9c0cf4626..535647b17e2 100644 --- a/runtime/jit-rt/cpp-so/jit_context.cpp +++ b/runtime/jit-rt/cpp-so/jit_context.cpp @@ -185,12 +185,8 @@ std::shared_ptr DynamicCompilerContext::createResolver() { return llvm::orc::createLegacyLookupResolver( execSession, -#if LDC_LLVM_VER >= 1100 [this](llvm::StringRef name_) -> llvm::JITSymbol { const std::string name = name_.str(); -#else - [this](const std::string &name) -> llvm::JITSymbol { -#endif if (auto Sym = compileLayer.findSymbol(name, false)) { return Sym; } else if (auto Err = Sym.takeError()) { diff --git a/runtime/phobos b/runtime/phobos index 6c83b490f7d..f9112228cac 160000 --- a/runtime/phobos +++ b/runtime/phobos @@ -1 +1 @@ -Subproject commit 6c83b490f7d6c66bf430e5249dae608848d3ac2c +Subproject commit f9112228cac41b64a42b4720cc821ba6189ee88b diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index b8879ecbc43..e6ce8521428 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,6 +1,7 @@ set( LDC2_BIN ${PROJECT_BINARY_DIR}/bin/${LDC_EXE} ) set( LDCPROFDATA_BIN ${PROJECT_BINARY_DIR}/bin/ldc-profdata ) set( LDCPRUNECACHE_BIN ${PROJECT_BINARY_DIR}/bin/${LDCPRUNECACHE_EXE} ) +set( LDCBUILDPLUGIN_BIN ${PROJECT_BINARY_DIR}/bin/${LDC_BUILD_PLUGIN_EXE} ) set( TIMETRACE2TXT_BIN ${PROJECT_BINARY_DIR}/bin/${TIMETRACE2TXT_EXE} ) set( LLVM_TOOLS_DIR ${LLVM_ROOT_DIR}/bin ) set( LDC2_BIN_DIR ${PROJECT_BINARY_DIR}/bin ) diff --git a/tests/PGO/allstatementtypes.d b/tests/PGO/allstatementtypes.d index 9cf8cf0823c..fffe2f14a1c 100644 --- a/tests/PGO/allstatementtypes.d +++ b/tests/PGO/allstatementtypes.d @@ -6,14 +6,14 @@ // See boundscheck.d for boundschecking instrumenation tests. // RUN: %ldc -boundscheck=off -c -output-ll -fprofile-instr-generate="hoihoihoi" -of=%t.ll %s \ -// RUN: && FileCheck %allow-deprecated-dag-overlap %s --check-prefix=PROFGEN < %t.ll +// RUN: && FileCheck -allow-deprecated-dag-overlap %s --check-prefix=PROFGEN < %t.ll // PROFGEN-DAG: @[[FILENAME:.+]] ={{.*}} constant{{.*}} c"hoihoihoi\00" // RUN: %ldc -boundscheck=off -fprofile-instr-generate=%t.profraw -run %s \ // RUN: && %profdata merge %t.profraw -o %t.profdata \ // RUN: && %ldc -boundscheck=off -c -output-ll -of=%t2.ll -fprofile-instr-use=%t.profdata %s \ -// RUN: && FileCheck %allow-deprecated-dag-overlap %s -check-prefix=PROFUSE < %t2.ll +// RUN: && FileCheck -allow-deprecated-dag-overlap %s -check-prefix=PROFUSE < %t2.ll extern(C): // simplify name mangling for simpler string matching diff --git a/tests/PGO/branching_switch.d b/tests/PGO/branching_switch.d index e8ba5745524..3e2f8a0837a 100644 --- a/tests/PGO/branching_switch.d +++ b/tests/PGO/branching_switch.d @@ -3,12 +3,12 @@ // REQUIRES: PGO_RT // RUN: %ldc -c -output-ll -fprofile-instr-generate -of=%t.ll %s \ -// RUN: && FileCheck %allow-deprecated-dag-overlap %s --check-prefix=PROFGEN < %t.ll +// RUN: && FileCheck -allow-deprecated-dag-overlap %s --check-prefix=PROFGEN < %t.ll // RUN: %ldc -fprofile-instr-generate=%t.profraw -run %s \ // RUN: && %profdata merge %t.profraw -o %t.profdata \ // RUN: && %ldc -boundscheck=off -c -output-ll -of=%t2.ll -fprofile-instr-use=%t.profdata %s \ -// RUN: && FileCheck %allow-deprecated-dag-overlap %s -check-prefix=PROFUSE < %t2.ll +// RUN: && FileCheck -allow-deprecated-dag-overlap %s -check-prefix=PROFUSE < %t2.ll extern(C): // simplify name mangling for simpler string matching diff --git a/tests/PGO/break.d b/tests/PGO/break.d index 0164fb82f29..20ef7d6c639 100644 --- a/tests/PGO/break.d +++ b/tests/PGO/break.d @@ -3,12 +3,12 @@ // REQUIRES: PGO_RT // RUN: %ldc -c -output-ll -fprofile-instr-generate -of=%t.ll %s \ -// RUN: && FileCheck %allow-deprecated-dag-overlap %s --check-prefix=PROFGEN < %t.ll +// RUN: && FileCheck -allow-deprecated-dag-overlap %s --check-prefix=PROFGEN < %t.ll // RUN: %ldc -fprofile-instr-generate=%t.profraw -run %s \ // RUN: && %profdata merge %t.profraw -o %t.profdata \ // RUN: && %ldc -boundscheck=off -c -output-ll -of=%t2.ll -fprofile-instr-use=%t.profdata %s \ -// RUN: && FileCheck %allow-deprecated-dag-overlap %s -check-prefix=PROFUSE < %t2.ll +// RUN: && FileCheck -allow-deprecated-dag-overlap %s -check-prefix=PROFUSE < %t2.ll extern(C): // simplify name mangling for simpler string matching diff --git a/tests/PGO/functions.d b/tests/PGO/functions.d index bce08f63c2b..2c5d24114f0 100644 --- a/tests/PGO/functions.d +++ b/tests/PGO/functions.d @@ -6,12 +6,12 @@ // REQUIRES: PGO_RT // RUN: %ldc -c -output-ll -fprofile-instr-generate -of=%t.ll %s \ -// RUN: && FileCheck %allow-deprecated-dag-overlap %s --check-prefix=PROFGEN < %t.ll +// RUN: && FileCheck -allow-deprecated-dag-overlap %s --check-prefix=PROFGEN < %t.ll // RUN: %ldc -fprofile-instr-generate=%t.profraw -run %s \ // RUN: && %profdata merge %t.profraw -o %t.profdata \ // RUN: && %ldc -c -output-ll -of=%t2.ll -fprofile-instr-use=%t.profdata %s \ -// RUN: && FileCheck %allow-deprecated-dag-overlap %s -check-prefix=PROFUSE < %t2.ll +// RUN: && FileCheck -allow-deprecated-dag-overlap %s -check-prefix=PROFUSE < %t2.ll // PROFGEN-DAG: @[[SMPL:__(llvm_profile_counters|profc).*simplefunction[A-Za-z0-9]*]] ={{.*}} [2 x i64] zeroinitializer // PROFGEN-DAG: @[[TMPL:__(llvm_profile_counters|profc).*templatefunc[A-Za-z0-9]*]] ={{.*}} [2 x i64] zeroinitializer diff --git a/tests/PGO/hash.d b/tests/PGO/hash.d index e14f27bbc86..623b7e5088e 100644 --- a/tests/PGO/hash.d +++ b/tests/PGO/hash.d @@ -7,9 +7,9 @@ // RUN: %ldc -d-version=ProfData -fprofile-instr-generate=%t.profraw -run %s \ // RUN: && %profdata merge %t.profraw -o %t.profdata \ // RUN: && %ldc -d-version=ProfData -c -output-ll -of=%t2.ll -fprofile-instr-use=%t.profdata %s \ -// RUN: && FileCheck %allow-deprecated-dag-overlap %s -check-prefix=PROFDATA < %t2.ll \ +// RUN: && FileCheck -allow-deprecated-dag-overlap %s -check-prefix=PROFDATA < %t2.ll \ // RUN: && %ldc -wi -c -output-ll -of=%t3.ll -fprofile-instr-use=%t.profdata %s \ -// RUN: && FileCheck %allow-deprecated-dag-overlap %s -check-prefix=NODATA < %t3.ll +// RUN: && FileCheck -allow-deprecated-dag-overlap %s -check-prefix=NODATA < %t3.ll extern(C): diff --git a/tests/PGO/lit.local.cfg b/tests/PGO/lit.local.cfg index 3cf5779cfbc..9638e26779b 100644 --- a/tests/PGO/lit.local.cfg +++ b/tests/PGO/lit.local.cfg @@ -1,7 +1,2 @@ # Add "PGO_RT" feature, assuming the `profile` compiler-rt library is available config.available_features.add('PGO_RT') - -if (config.llvm_version >= 700): - config.substitutions.append( ('%allow-deprecated-dag-overlap ', '-allow-deprecated-dag-overlap ') ) -else: - config.substitutions.append( ('%allow-deprecated-dag-overlap ', '') ) diff --git a/tests/PGO/unrolledloopstatement_gh3375.d b/tests/PGO/unrolledloopstatement_gh3375.d index 4a3511c6e57..02b10825f39 100644 --- a/tests/PGO/unrolledloopstatement_gh3375.d +++ b/tests/PGO/unrolledloopstatement_gh3375.d @@ -5,7 +5,7 @@ // RUN: %ldc -fprofile-instr-generate=%t.profraw -run %s // RUN: %profdata merge %t.profraw -o %t.profdata // RUN: %ldc -c -output-ll -of=%t2.ll -fprofile-instr-use=%t.profdata %s -// RUN: FileCheck %allow-deprecated-dag-overlap %s -check-prefix=PROFUSE < %t2.ll +// RUN: FileCheck -allow-deprecated-dag-overlap %s -check-prefix=PROFUSE < %t2.ll alias AliasSeq(TList...) = TList; diff --git a/tests/codegen/attr_callingconvention.d b/tests/codegen/attr_callingconvention.d index 84873fdca26..2d127fb8d40 100644 --- a/tests/codegen/attr_callingconvention.d +++ b/tests/codegen/attr_callingconvention.d @@ -59,9 +59,6 @@ void fooinvokesexternCfoofoofoo() ////////////////////////////////////////////////////////// /// Forward-declared function calls and invokes: -// CHECK-LABEL: declare x86_vectorcallcc void @{{.*}}forward_declared_function -@callingConvention("vectorcall") void forward_declared_function(); - // CHECK-LABEL: define{{.*}} @{{.*}}attr_callingconvention34foocalls_forward_declared_function void foocalls_forward_declared_function() { @@ -69,6 +66,9 @@ void foocalls_forward_declared_function() forward_declared_function(); } +// CHECK-LABEL: declare x86_vectorcallcc void @{{.*}}forward_declared_function +@callingConvention("vectorcall") void forward_declared_function(); + // CHECK-LABEL: define{{.*}} @{{.*}}attr_callingconvention36fooinvokes_forward_declared_function void fooinvokes_forward_declared_function() { diff --git a/tests/codegen/attr_targetoptions.d b/tests/codegen/attr_targetoptions.d index c5f1ba1a8c3..8e6e21f2e2d 100644 --- a/tests/codegen/attr_targetoptions.d +++ b/tests/codegen/attr_targetoptions.d @@ -4,9 +4,9 @@ // 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 %disable_fp_elim +// 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 %enable_fp_elim -mattr=test +// RUN: %ldc -c -output-ll -of=%t.ll %s -frame-pointer=none -mattr=test // RUN: FileCheck %s --check-prefix=COMMON --check-prefix=NO_FP --check-prefix=ATTR < %t.ll // COMMON: define{{.*}} @{{.*}}3fooFZv{{.*}} #[[KEYVALUE:[0-9]+]] diff --git a/tests/codegen/dcompute_cl_images.d b/tests/codegen/dcompute_cl_images.d index b3f5f5ca07f..bb653a5c45c 100644 --- a/tests/codegen/dcompute_cl_images.d +++ b/tests/codegen/dcompute_cl_images.d @@ -1,7 +1,7 @@ // REQUIRES: target_SPIRV -// FIXME: hits an assertion with recent SPIRV-LLVM-Translator, see https://github.com/ldc-developers/ldc/pull/4010#issuecomment-1191820165 -// XFAIL: atleast_llvm1500 +// FIXME: hits an assertion with SPIRV-LLVM-Translator for LLVM 15, see https://github.com/ldc-developers/ldc/pull/4010#issuecomment-1191820165 +// XFAIL: atleast_llvm1500 && atmost_llvm1509 // RUN: %ldc -c -mdcompute-targets=ocl-220 -m64 -I%S/inputs -mdcompute-file-prefix=%t -output-ll -output-o %s && FileCheck %s < %t_ocl220_64.ll @compute(CompileFor.deviceOnly) module dcompute_cl_images; diff --git a/tests/codegen/exception_stack_trace.d b/tests/codegen/exception_stack_trace.d index 1594ac5a7f9..71002d74e9b 100644 --- a/tests/codegen/exception_stack_trace.d +++ b/tests/codegen/exception_stack_trace.d @@ -1,4 +1,4 @@ -// RUN: %ldc -g %disable_fp_elim -link-defaultlib-debug %s -of=%t%exe +// RUN: %ldc -g -frame-pointer=all -link-defaultlib-debug %s -of=%t%exe // RUN: %t%exe | FileCheck %s void bar() diff --git a/tests/codegen/fcf_protection.d b/tests/codegen/fcf_protection.d new file mode 100644 index 00000000000..e5c28cea2ab --- /dev/null +++ b/tests/codegen/fcf_protection.d @@ -0,0 +1,31 @@ +// Test -fcf-protection + +// REQUIRES: target_X86 + +// RUN: %ldc -mtriple=x86_64-linux-gnu -output-ll -of=%t.ll %s -d-version=NOTHING && FileCheck %s --check-prefix=NOTHING < %t.ll + +// RUN: %ldc -mtriple=x86_64-linux-gnu -output-ll -of=%t_branch.ll %s --fcf-protection=branch -d-version=BRANCH && FileCheck %s --check-prefix=BRANCH < %t_branch.ll +// RUN: %ldc -mtriple=x86_64-linux-gnu -output-ll -of=%t_return.ll %s --fcf-protection=return -d-version=RETURN && FileCheck %s --check-prefix=RETURN < %t_return.ll +// RUN: %ldc -mtriple=x86_64-linux-gnu -output-ll -of=%t_full.ll %s --fcf-protection=full -d-version=FULL && FileCheck %s --check-prefix=FULL < %t_full.ll +// RUN: %ldc -mtriple=x86_64-linux-gnu -output-ll -of=%t_noarg.ll %s --fcf-protection -d-version=FULL && FileCheck %s --check-prefix=FULL < %t_noarg.ll + +// NOTHING-NOT: cf-prot +// BRANCH-DAG: "cf-protection-branch", i32 1 +// RETURN-DAG: "cf-protection-return", i32 1 +// FULL-DAG: "cf-protection-branch", i32 1 +// FULL-DAG: "cf-protection-return", i32 1 + +void foo() {} + +version(NOTHING) { + static assert(__traits(getTargetInfo, "CET") == 0); +} +version(BRANCH) { + static assert(__traits(getTargetInfo, "CET") == 1); +} +version(RETURN) { + static assert(__traits(getTargetInfo, "CET") == 2); +} +version(FULL) { + static assert(__traits(getTargetInfo, "CET") == 3); +} diff --git a/tests/codegen/frame_pointer_x86.d b/tests/codegen/frame_pointer_x86.d index 470b425f413..c0450591f78 100644 --- a/tests/codegen/frame_pointer_x86.d +++ b/tests/codegen/frame_pointer_x86.d @@ -4,9 +4,9 @@ // RUN: FileCheck %s --check-prefixes=COMMON,FP < %t.s // RUN: %ldc -c -mtriple=x86_64 -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 %disable_fp_elim +// RUN: %ldc -c -mtriple=x86_64 -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 %enable_fp_elim +// RUN: %ldc -c -mtriple=x86_64 -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/funcptr_harvard_gh4432.d b/tests/codegen/funcptr_harvard_gh4432.d new file mode 100644 index 00000000000..9400d982b07 --- /dev/null +++ b/tests/codegen/funcptr_harvard_gh4432.d @@ -0,0 +1,37 @@ +// Tests function pointers/delegates on a Harvard architecture, +// with code residing in a separate address space. + +// REQUIRES: target_AVR +// RUN: %ldc -mtriple=avr -betterC -output-ll -of=%t.ll %s && FileCheck %s < %t.ll +// RUN: %ldc -mtriple=avr -betterC -c %s + +alias FP = void function(); +alias DG = void delegate(); + +// CHECK: @_D22funcptr_harvard_gh44328globalFPPFZv = global {{void \(\) addrspace\(1\)\*|ptr addrspace\(1\)}} @_D22funcptr_harvard_gh44323barFZv, align 2 +__gshared FP globalFP = &bar; +// CHECK: @_D22funcptr_harvard_gh443217globalDataPointerPPFZv = global {{void \(\) addrspace\(1\)\*\*|ptr}} @_D22funcptr_harvard_gh44328globalFPPFZv, align 2 +__gshared FP* globalDataPointer = &globalFP; + +// CHECK: define void @_D22funcptr_harvard_gh44323fooFPFZvDQeZv({{.*}} addrspace(1){{\*?}} %fp_arg, { {{.*}} addrspace(1){{\*?}} } %dg_arg) addrspace(1) +void foo(FP fp, DG dg) +{ + // CHECK: call addrspace(1) void %1() + fp(); + // CHECK: call addrspace(1) void %.funcptr + dg(); + // CHECK-NEXT: ret void +} + +void bar() +{ + foo(() {}, delegate() {}); + + FP fp = &bar; + DG dg; + dg.funcptr = &bar; + foo(fp, dg); + + dg.funcptr = *globalDataPointer; + foo(globalFP, dg); +} diff --git a/tests/codegen/inputs/lambdas_dmd23722b.d b/tests/codegen/inputs/lambdas_dmd23722b.d new file mode 100644 index 00000000000..45aedc6d72a --- /dev/null +++ b/tests/codegen/inputs/lambdas_dmd23722b.d @@ -0,0 +1,16 @@ +module lambdas_dmd23722b; + +struct A { + import core.stdc.stdio; + alias x = () { + printf("x\n"); + }; + alias y = () { + printf("y\n"); + }; +} + +// do_x should call A.x (and print "x") +void do_x() { + A.x(); +} diff --git a/tests/codegen/lambdas_dmd23722.d b/tests/codegen/lambdas_dmd23722.d new file mode 100644 index 00000000000..b311b730ea4 --- /dev/null +++ b/tests/codegen/lambdas_dmd23722.d @@ -0,0 +1,21 @@ +// Test that colliding lambda mangles don't lead to symbol collision during linking, +// see https://issues.dlang.org/show_bug.cgi?id=23722 + +// compile both modules separately, then link and check runtime output +// RUN: %ldc -c %S/inputs/lambdas_dmd23722b.d -of=%t_b%obj +// RUN: %ldc -I%S/inputs %s %t_b%obj -of=%t%exe +// RUN: %t%exe | FileCheck %s + +import lambdas_dmd23722b; + +// do_y should call A.y (and print "y") +void do_y() { + A.y(); +} + +void main() { + // CHECK: y + do_y(); // should print y + // CHECK-NEXT: x + do_x(); // should print x +} diff --git a/tests/codegen/lambdas_gh3648.d b/tests/codegen/lambdas_gh3648.d index 32ea4b2f7ce..e50a69e95ef 100644 --- a/tests/codegen/lambdas_gh3648.d +++ b/tests/codegen/lambdas_gh3648.d @@ -1,4 +1,4 @@ -// Tests that lambdas and contained globals are emitted as linkonce_odr. +// Tests that lambdas and contained globals are emitted with internal linkage. // RUN: %ldc -output-ll -of=%t.ll %s && FileCheck %s < %t.ll @@ -25,10 +25,10 @@ void foo() }(123); } -// the global variables should be defined as linkonce_odr: -// CHECK: _D14lambdas_gh36489__lambda5FZ10global_bari{{.*}} = linkonce_odr {{(hidden )?}}thread_local global -// CHECK: _D14lambdas_gh36489__lambda6FZ18global_bar_inlinedOi{{.*}} = linkonce_odr {{(hidden )?}}global -// CHECK: _D14lambdas_gh36483fooFZ__T9__lambda1TiZQnFiZ12lambda_templi{{.*}} = linkonce_odr {{(hidden )?}}global +// the global variables should be defined as internal: +// CHECK: _D14lambdas_gh36489__lambda5FZ10global_bari{{.*}} = internal thread_local global +// CHECK: _D14lambdas_gh36489__lambda6FZ18global_bar_inlinedOi{{.*}} = internal global +// CHECK: _D14lambdas_gh36483fooFZ__T9__lambda1TiZQnFiZ12lambda_templi{{.*}} = internal global // foo() should only call two lambdas: // CHECK: define {{.*}}_D14lambdas_gh36483fooFZv @@ -36,11 +36,11 @@ void foo() // CHECK-NEXT: call {{.*}}__T9__lambda1 // CHECK-NEXT: ret void -// bar() should be defined as linkonce_odr: -// CHECK: define linkonce_odr {{.*}}__lambda5 +// bar() should be defined as internal: +// CHECK: define internal {{.*}}__lambda5 // bar_inlined() should NOT have made it to the .ll: // CHECK-NOT: define {{.*}}__lambda6 -// the template lambda instance should be defined as linkonce_odr: -// CHECK: define linkonce_odr {{.*}}__T9__lambda1 +// the template lambda instance should be defined as internal: +// CHECK: define internal {{.*}}__T9__lambda1 diff --git a/tests/codegen/lambdas_gh3648b.d b/tests/codegen/lambdas_gh3648b.d index 226a2042044..830bc535d04 100644 --- a/tests/codegen/lambdas_gh3648b.d +++ b/tests/codegen/lambdas_gh3648b.d @@ -1,4 +1,4 @@ -// Tests that *imported* lambdas and contained globals are emitted as linkonce_odr. +// Tests that *imported* lambdas and contained globals are emitted with internal linkage. // RUN: %ldc -output-ll -of=%t.ll %s -I%S && FileCheck %s < %t.ll @@ -10,17 +10,17 @@ void foo() bar_inlined(); } -// the global variables should be defined as linkonce_odr: -// CHECK: _D14lambdas_gh36489__lambda5FZ10global_bari{{.*}} = linkonce_odr {{(hidden )?}}thread_local global -// CHECK: _D14lambdas_gh36489__lambda6FZ18global_bar_inlinedOi{{.*}} = linkonce_odr {{(hidden )?}}global +// the global variables should be defined as internal: +// CHECK: _D14lambdas_gh36489__lambda5FZ10global_bari{{.*}} = internal thread_local global +// CHECK: _D14lambdas_gh36489__lambda6FZ18global_bar_inlinedOi{{.*}} = internal global // foo() should only call one lambda: // CHECK: define {{.*}}_D15lambdas_gh3648b3fooFZv // CHECK-NEXT: call {{.*}}__lambda5 // CHECK-NEXT: ret void -// bar() should be defined as linkonce_odr: -// CHECK: define linkonce_odr {{.*}}__lambda5 +// bar() should be defined as internal: +// CHECK: define internal {{.*}}__lambda5 // bar_inlined() should NOT have made it to the .ll: // CHECK-NOT: define {{.*}}__lambda6 diff --git a/tests/codegen/lifetime_local_variables.d b/tests/codegen/lifetime_local_variables.d new file mode 100644 index 00000000000..5036da5f017 --- /dev/null +++ b/tests/codegen/lifetime_local_variables.d @@ -0,0 +1,122 @@ +// RUN: %ldc -femit-local-var-lifetime -c -output-ll -of=%t.ll %s && FileCheck %s < %t.ll + +extern(C): // disable mangling for easier matching + +void opaque(byte* i); + +// CHECK-LABEL: define void @foo_array_foo() +void foo_array_foo() { + // CHECK: alloca [400 x i8] + // CHECK: alloca [800 x i8] + { + // CHECK: call void @llvm.lifetime.start.p0i8(i64 immarg 400 + byte[400] arr = void; + // CHECK: call void @opaque + opaque(&arr[0]); + // CHECK: call void @llvm.lifetime.end.p0i8(i64 immarg 400 + } + + { + // CHECK: call void @llvm.lifetime.start.p0i8(i64 immarg 800 + byte[800] arr = void; + // CHECK: call void @opaque + opaque(&arr[0]); + // CHECK: call void @llvm.lifetime.end.p0i8(i64 immarg 800 + } + + // CHECK-LABEL: ret void +} + +// CHECK-LABEL: define void @foo_forloop_foo() +void foo_forloop_foo() { + byte i; + // CHECK: call void @opaque + // This call should appear before lifetime start of while-loop variable. + opaque(&i); + for (byte[13] d; d[0] < 2; d[0]++) { + // CHECK: call void @llvm.lifetime.start.p0i8(i64 immarg 13 + // Lifetime should start before initializing the variable + // CHECK: call void @llvm.memset.p0i8.i{{.*}}13 + // CHECK: call void @llvm.lifetime.start.p0i8(i64 immarg 44 + byte[44] arr = void; + // CHECK: call void @opaque + opaque(&arr[0]); + // CHECK: call void @llvm.lifetime.end.p0i8(i64 immarg 44 + // CHECK: endfor: + // CHECK: call void @llvm.lifetime.end.p0i8(i64 immarg 13 + } + + // CHECK-LABEL: ret void +} + +// CHECK-LABEL: define void @foo_whileloop_foo() +void foo_whileloop_foo() { + byte i; + // CHECK: call void @opaque + // This call should appear before lifetime start of while-loop variable. + opaque(&i); + while (ulong d = 131) { + // CHECK: call void @llvm.lifetime.start.p0i8(i64 immarg 8 + // Lifetime should start before initializing the variable + // CHECK: store i64 131 + // CHECK: call void @llvm.lifetime.start.p0i8(i64 immarg 33 + byte[33] arr = void; + // CHECK: call void @opaque + opaque(&arr[0]); + // CHECK: call void @llvm.lifetime.end.p0i8(i64 immarg 33 + // CHECK: call void @llvm.lifetime.end.p0i8(i64 immarg 8 + } + + // CHECK-LABEL: ret void +} + +// CHECK-LABEL: define void @foo_if_foo() +void foo_if_foo() { + byte i; + // CHECK: call void @opaque + // This call should appear before lifetime start of if-statement condition variable. + opaque(&i); + // CHECK: call void @llvm.lifetime.start.p0i8(i64 immarg 8 + // Lifetime should start before initializing the variable + // CHECK: store i64 565 + if (ulong d = 565) { + // CHECK: call void @llvm.lifetime.start.p0i8(i64 immarg 72 + byte[72] arr = void; + // CHECK: call void @opaque + opaque(&arr[0]); + // CHECK: call void @llvm.lifetime.end.p0i8(i64 immarg 72 + } else { + // d is out of scope here. + // CHECK: call void @llvm.lifetime.start.p0i8(i64 immarg 51 + byte[51] arr = void; + // CHECK: call void @opaque + opaque(&arr[0]); + // CHECK: call void @llvm.lifetime.end.p0i8(i64 immarg 51 + } + // CHECK: endif: + // CHECK: call void @llvm.lifetime.end.p0i8(i64 immarg 8 + + // CHECK-LABEL: ret void +} + +struct S { + byte[123] a; + ~this() { + opaque(&a[1]); + } +} + +void opaque_S(S* i); + +// CHECK-LABEL: define void @foo_struct_foo() +void foo_struct_foo() { + { + // CHECK: call void @llvm.lifetime.start.p0i8(i64 immarg 123 + S s; + // CHECK: invoke void @opaque_S + opaque_S(&s); + } + + // CHECK: call void @llvm.lifetime.end.p0i8(i64 immarg 123 + // CHECK-NEXT: ret void +} diff --git a/tests/compilable/gh2782.d b/tests/compilable/gh2782.d new file mode 100644 index 00000000000..a80cecfd394 --- /dev/null +++ b/tests/compilable/gh2782.d @@ -0,0 +1,4 @@ +// RUN: %ldc -c -singleobj %s %S/inputs/gh2782b.d + +struct S {} +extern(C) void foo(S); diff --git a/tests/compilable/inputs/gh2782b.d b/tests/compilable/inputs/gh2782b.d new file mode 100644 index 00000000000..3fea3c80447 --- /dev/null +++ b/tests/compilable/inputs/gh2782b.d @@ -0,0 +1,2 @@ +struct S {} +extern(C) void foo(S); diff --git a/tests/dmd/compilable/always_inline.i b/tests/dmd/compilable/always_inline.i new file mode 100644 index 00000000000..d7b4b0814b1 --- /dev/null +++ b/tests/dmd/compilable/always_inline.i @@ -0,0 +1,5 @@ +// https://issues.dlang.org/show_bug?id=21938 + +__attribute__((always_inline)) int square(int x) { return x * x; } + +int doSquare(int x) { return square(x); } diff --git a/tests/dmd/compilable/cattributes.i b/tests/dmd/compilable/cattributes.i new file mode 100644 index 00000000000..60b2f4c8b94 --- /dev/null +++ b/tests/dmd/compilable/cattributes.i @@ -0,0 +1,33 @@ +/* Smoke test dllimport, dllexport, and naked attributes */ + +__declspec(dllimport) int abc; + +__declspec(dllimport) int def(); + +__declspec(dllexport) int ghi() { return 3; } + +__declspec(dllexport) int jkl; + +/* LDC FIXME: __declspec(naked) restricts bodies to DMD-style inline asm +__declspec(naked)*/ __declspec(dllexport) +int test(int a, int b, int c, int d, int e, int f) +{ + return a + b + c + d + e + f + abc + def() + ghi() + jkl; +} + +/*****************************************/ + +__attribute__((dllimport)) int abcx; + +__attribute__((dllimport)) int defx(); + +__attribute__((dllexport)) int ghix() { return 3; } + +__attribute__((dllexport)) int jklx; + +/* LDC FIXME: ditto for __attribute__((naked)) +__attribute__((naked))*/ __attribute__((dllexport)) +int testx(int a, int b, int c, int d, int e, int f) +{ + return a + b + c + d + e + f + abcx + defx() + ghix() + jklx; +} diff --git a/tests/dmd/compilable/cppflags.c b/tests/dmd/compilable/cppflags.c index e5be469fe92..927463631fa 100644 --- a/tests/dmd/compilable/cppflags.c +++ b/tests/dmd/compilable/cppflags.c @@ -1,5 +1,4 @@ /* REQUIRED_ARGS: -P=-DABC=3 */ -// DISABLED: LDC // FIXME _Static_assert(ABC == 3, "1"); diff --git a/tests/dmd/compilable/cppmangle.d b/tests/dmd/compilable/cppmangle.d index 1fe135d00bc..9501b4f3b04 100644 --- a/tests/dmd/compilable/cppmangle.d +++ b/tests/dmd/compilable/cppmangle.d @@ -550,7 +550,6 @@ version (CppMangle_Itanium) static assert(basic_ostream!(char, char_traits!char).food.mangleof == "_ZNSo4foodEv"); static assert(basic_iostream!(char, char_traits!char).fooe.mangleof == "_ZNSd4fooeEv"); - static assert(func_18957_2.mangleof == `_Z12func_18957_2PSaIiE`); static assert(func_18957_2!(allocator!int).mangleof == `_Z12func_18957_2ISaIiEET_PS1_`); static assert(func_20413.mangleof == `_Z10func_20413St4pairIifES_IfiE`); diff --git a/tests/dmd/compilable/cvariadic.i b/tests/dmd/compilable/cvariadic.i new file mode 100644 index 00000000000..70a9fef8cd1 --- /dev/null +++ b/tests/dmd/compilable/cvariadic.i @@ -0,0 +1,11 @@ +int abc() { return 1; } +int def(const char *p, ...) { return 2; } +int ghi(const char *p, int i) { return 3; } + +int main() +{ + abc("hello world %d\n", 1); + def("hello world %d\n", 2); + ghi("hello world %d\n", 3); + return 0; +} diff --git a/tests/dmd/compilable/dtoh_CPPNamespaceDeclaration.d b/tests/dmd/compilable/dtoh_CPPNamespaceDeclaration.d index 870387c7113..04363711bd3 100644 --- a/tests/dmd/compilable/dtoh_CPPNamespaceDeclaration.d +++ b/tests/dmd/compilable/dtoh_CPPNamespaceDeclaration.d @@ -48,7 +48,7 @@ namespace nameSpace extern void fn2(); } - extern double identity(double _param_0); + extern double identity(double __param_0_); } --- @@ -63,5 +63,5 @@ extern(C++, "nameSpace") void fn2() {} } - double identity(double) { return _param_0; } + double identity(double) { return __param_0; } } diff --git a/tests/dmd/compilable/dtoh_functions.d b/tests/dmd/compilable/dtoh_functions.d index 1feff408adc..38607f6d4b1 100644 --- a/tests/dmd/compilable/dtoh_functions.d +++ b/tests/dmd/compilable/dtoh_functions.d @@ -159,7 +159,7 @@ extern int32_t(*f)(int32_t ); extern void special(int32_t a = ptr->i, int32_t b = ptr->get(1, 2), int32_t j = (*f)(1)); -extern void variadic(int32_t _param_0, ...); +extern void variadic(int32_t __param_0_, ...); --- +/ diff --git a/tests/dmd/compilable/dtoh_invalid_identifiers.d b/tests/dmd/compilable/dtoh_invalid_identifiers.d index b8e8d05649c..a8f5b990413 100644 --- a/tests/dmd/compilable/dtoh_invalid_identifiers.d +++ b/tests/dmd/compilable/dtoh_invalid_identifiers.d @@ -110,7 +110,7 @@ struct InvalidNames final } }; -extern void useInvalid(InvalidNames _param_0); +extern void useInvalid(InvalidNames __param_0_); extern size_t offsetof(); diff --git a/tests/dmd/compilable/dtoh_special_enum.d b/tests/dmd/compilable/dtoh_special_enum.d index 37b450703a9..ee86a5e7121 100644 --- a/tests/dmd/compilable/dtoh_special_enum.d +++ b/tests/dmd/compilable/dtoh_special_enum.d @@ -40,25 +40,25 @@ struct _d_dynamicArray final #endif enum class __c_not_special; -extern "C" void fn_long(long _param_0); +extern "C" void fn_long(long __param_0_); -extern "C" void fn_ulong(unsigned long _param_0); +extern "C" void fn_ulong(unsigned long __param_0_); -extern "C" void fn_longlong(long long _param_0); +extern "C" void fn_longlong(long long __param_0_); -extern "C" void fn_ulonglong(unsigned long long _param_0); +extern "C" void fn_ulonglong(unsigned long long __param_0_); -extern "C" void fn_long_double(long double _param_0); +extern "C" void fn_long_double(long double __param_0_); -extern "C" void fn_wchar_t(wchar_t _param_0); +extern "C" void fn_wchar_t(wchar_t __param_0_); -extern "C" void fn_complex_float(_Complex float _param_0); +extern "C" void fn_complex_float(_Complex float __param_0_); -extern "C" void fn_complex_double(_Complex double _param_0); +extern "C" void fn_complex_double(_Complex double __param_0_); -extern "C" void fn_complex_real(_Complex long double _param_0); +extern "C" void fn_complex_real(_Complex long double __param_0_); -extern "C" void fn_not_special(__c_not_special _param_0); +extern "C" void fn_not_special(__c_not_special __param_0_); --- +/ diff --git a/tests/dmd/compilable/enumbase.c b/tests/dmd/compilable/enumbase.c index f1fabfa2319..683a3ff04e5 100644 --- a/tests/dmd/compilable/enumbase.c +++ b/tests/dmd/compilable/enumbase.c @@ -23,3 +23,14 @@ enum U2: unsigned { enum U3: unsigned long { U3_A = 1, }; + +// https://issues.dlang.org/show_bug.cgi?id=23801 + +enum +{ + X = ~1ull, + Y, +}; + +_Static_assert(X == ~1ull, "3"); +_Static_assert(Y == ~1ull + 1, "4"); diff --git a/tests/dmd/compilable/imports/c23789.i b/tests/dmd/compilable/imports/c23789.i new file mode 100644 index 00000000000..74d7e80c2ad --- /dev/null +++ b/tests/dmd/compilable/imports/c23789.i @@ -0,0 +1,31 @@ +// https://issues.dlang.org/show_bug.cgi?id=23789 + +struct __declspec(align(64)) M128A { + char c; +}; + +typedef struct __declspec(align(32)) _M128B { + int x; +} M128B, *PM128A; + + +void testpl(p) +struct __declspec(align(2)) S *p; +{ +} + +///// + +struct __attribute__((aligned(64))) N128A { + char c; +}; + +typedef struct __attribute__((aligned(32))) _N128B { + int x; +} N128B, *PN128A; + + +void testpl2(p) +struct __attribute__((aligned(2))) S *p; +{ +} diff --git a/tests/dmd/compilable/imports/imp24022.c b/tests/dmd/compilable/imports/imp24022.c new file mode 100644 index 00000000000..b65e4e155bf --- /dev/null +++ b/tests/dmd/compilable/imports/imp24022.c @@ -0,0 +1,5 @@ +// https://issues.dlang.org/show_bug.cgi?id=24022 +typedef enum { + A = 1, + B, +} E; diff --git a/tests/dmd/compilable/imports/library.c b/tests/dmd/compilable/imports/library.c new file mode 100644 index 00000000000..4951e465574 --- /dev/null +++ b/tests/dmd/compilable/imports/library.c @@ -0,0 +1,5 @@ +typedef enum SomeEnum +{ + foo = 0, + bar = -10000, +} SomeEnum; diff --git a/tests/dmd/compilable/interpret3.d b/tests/dmd/compilable/interpret3.d index 2c9a84eddfb..14142630454 100644 --- a/tests/dmd/compilable/interpret3.d +++ b/tests/dmd/compilable/interpret3.d @@ -7208,7 +7208,7 @@ struct S13630(T) { T[3] arr; - this(A...)(auto ref in A args) + this(A...)(const auto ref A args) { auto p = arr.ptr; @@ -7238,7 +7238,7 @@ struct Matrix13827(T, uint N) T[N] flat; } - this(A...)(auto ref in A args) + this(A...)(const auto ref A args) { uint k; diff --git a/tests/dmd/compilable/posixbitfields.c b/tests/dmd/compilable/posixbitfields.c new file mode 100644 index 00000000000..8bda9d4b8b9 --- /dev/null +++ b/tests/dmd/compilable/posixbitfields.c @@ -0,0 +1,159 @@ +// DISABLED: win32 win64 + +// https://issues.dlang.org/show_bug.cgi?id=23427 + +_Static_assert(sizeof(unsigned) == 4, "1"); + +struct A { + unsigned x :8; + unsigned y :4; + unsigned z :20; +}; +_Static_assert(sizeof(struct A)==4, "2"); + +struct B { + unsigned x :4; + unsigned y :2; + unsigned z :26; +}; +_Static_assert(sizeof(struct B)==4, "3"); + +struct C { + unsigned x :4; + unsigned y :4; + unsigned z :24; +}; +_Static_assert(sizeof(struct C)==4, "4"); // This one fails + + +_Static_assert(sizeof(struct { + unsigned a: 1; + unsigned b: 7; + unsigned c: 24; +}) == sizeof(unsigned), "1 7 24"); + +_Static_assert(sizeof(struct { + unsigned a: 2; + unsigned b: 6; + unsigned c: 24; +}) == sizeof(unsigned), "2 6 24"); + +_Static_assert(sizeof(struct { + unsigned a: 3; + unsigned b: 5; + unsigned c: 24; +}) == sizeof(unsigned), "3 5 24"); + +_Static_assert(sizeof(struct { + unsigned a: 4; + unsigned b: 4; + unsigned c: 24; +}) == sizeof(unsigned), "4 4 24"); + +_Static_assert(sizeof(struct { + unsigned a: 5; + unsigned b: 3; + unsigned c: 24; +}) == sizeof(unsigned), "5 3 24"); + +_Static_assert(sizeof(struct { + unsigned a: 6; + unsigned b: 2; + unsigned c: 24; +}) == sizeof(unsigned), "6 2 24"); + +_Static_assert(sizeof(struct { + unsigned a: 7; + unsigned b: 1; + unsigned c: 24; +}) == sizeof(unsigned), "7 1 24"); + +_Static_assert(sizeof(struct { + unsigned a: 8; + unsigned b: 9; + unsigned c: 15; +}) == sizeof(unsigned), "8 9 15"); + +_Static_assert(sizeof(struct { + unsigned a: 8; + unsigned b: 10; + unsigned c: 14; +}) == sizeof(unsigned), "8 10 14"); + +_Static_assert(sizeof(struct { + unsigned a: 8; + unsigned b: 11; + unsigned c: 13; +}) == sizeof(unsigned), "8 11 13"); + +_Static_assert(sizeof(struct { + unsigned a: 8; + unsigned b: 12; + unsigned c: 12; +}) == sizeof(unsigned), "8 12 12"); + +_Static_assert(sizeof(struct { + unsigned a: 8; + unsigned b: 13; + unsigned c: 11; +}) == sizeof(unsigned), "8 13 11"); + +_Static_assert(sizeof(struct { + unsigned a: 8; + unsigned b: 14; + unsigned c: 10; +}) == sizeof(unsigned), "8 14 10"); + +_Static_assert(sizeof(struct { + unsigned a: 8; + unsigned b: 15; + unsigned c: 9; +}) == sizeof(unsigned), "8 15 9"); + +_Static_assert(sizeof(struct { + unsigned a: 8; + unsigned b: 16; + unsigned c: 8; +}) == sizeof(unsigned), "8 16 8"); + +_Static_assert(sizeof(struct { + unsigned a: 8; + unsigned b: 17; + unsigned c: 7; +}) == sizeof(unsigned), "8 17 7"); + +_Static_assert(sizeof(struct { + unsigned a: 8; + unsigned b: 18; + unsigned c: 6; +}) == sizeof(unsigned), "8 18 6"); + +_Static_assert(sizeof(struct { + unsigned a: 8; + unsigned b: 19; + unsigned c: 5; +}) == sizeof(unsigned), "8 19 5"); + +_Static_assert(sizeof(struct { + unsigned a: 8; + unsigned b: 20; + unsigned c: 4; +}) == sizeof(unsigned), "8 20 4"); + +_Static_assert(sizeof(struct { + unsigned a: 8; + unsigned b: 21; + unsigned c: 3; +}) == sizeof(unsigned), "8 21 3"); + +_Static_assert(sizeof(struct { + unsigned a: 8; + unsigned b: 22; + unsigned c: 2; +}) == sizeof(unsigned), "8 22 2"); + +_Static_assert(sizeof(struct { + unsigned a: 8; + unsigned b: 23; + unsigned c: 1; +}) == sizeof(unsigned), "8 23 1"); diff --git a/tests/dmd/compilable/stdcheaders.c b/tests/dmd/compilable/stdcheaders.c index 5fef0eeff78..63bbcb88caa 100644 --- a/tests/dmd/compilable/stdcheaders.c +++ b/tests/dmd/compilable/stdcheaders.c @@ -1,21 +1,18 @@ /* Do a smoke test of the C Standard headers. * Many platforms do not support all the C Standard headers. - * DISABLED: LDC // FIXME: needs preprocessor */ #include #ifndef __DMC__ // D:\a\1\s\tools\dm\include\complex.h(105): Deprecation: use of complex type `cdouble` is deprecated, use `std.complex.Complex!(double)` instead -#ifndef __FreeBSD__ // defines _COMPLEX_I with use of `i` postfix #include #endif -#endif #include #include #ifndef _MSC_VER // C:\Program Files (x86)\Windows Kits\10\include\10.0.22621.0\ucrt\fenv.h(68): Error: variable `stdcheaders._Fenv1` extern symbols cannot have initializers -#ifndef __FreeBSD__ // cannot turn off __GNUCLIKE_ASM in machine/ieeefp.h +#ifndef __FreeBSD__ // /usr/include/fenv.h(341): Error: use `.` for member lookup, not `->` #include #endif #endif @@ -46,13 +43,11 @@ #ifndef __linux__ #ifndef _MSC_VER #ifndef __APPLE__ // /Applications/Xcode-14.2.0.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/14.0.0/include/stdatomic.h(80): Error: type-specifier is missing -#ifndef __FreeBSD__ // /stdatomic.h(162): Error: found `volatile` when expecting `{` #include #endif #endif #endif #endif -#endif #include #include @@ -72,12 +67,14 @@ #ifndef __DMC__ // no tgmath.h #ifndef _MSC_VER // C:\Program Files (x86)\Windows Kits\10\include\10.0.22621.0\ucrt\tgmath.h(33): Error: no type for declarator before `)` #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 `...` -#ifndef __FreeBSD__ // #includes complex.h +#if !(defined(__linux__) && defined(__aarch64__)) // /tmp/clang/lib/clang/15.0.3/include/tgmath.h(34): Error: named parameter required before `...` +#ifndef __FreeBSD__ // /usr/local/llvm15/lib/clang/15.0.7/include/tgmath.h(34): Error: named parameter required before `...` #include #endif #endif #endif #endif +#endif #ifndef __DMC__ #ifndef __linux__ diff --git a/tests/dmd/compilable/test18493.d b/tests/dmd/compilable/test18493.d new file mode 100644 index 00000000000..558bf447904 --- /dev/null +++ b/tests/dmd/compilable/test18493.d @@ -0,0 +1,19 @@ +// https://issues.dlang.org/show_bug.cgi?id=18493 +// REQUIRED_ARGS: -betterC + +struct S +{ + this(this) + { + } + + ~this() + { + } +} + +struct C +{ + S s1; + S s2; +} diff --git a/tests/dmd/runnable/test19688.d b/tests/dmd/compilable/test19688.d similarity index 100% rename from tests/dmd/runnable/test19688.d rename to tests/dmd/compilable/test19688.d diff --git a/tests/dmd/compilable/test21667.d b/tests/dmd/compilable/test21667.d new file mode 100644 index 00000000000..6d83dc056c4 --- /dev/null +++ b/tests/dmd/compilable/test21667.d @@ -0,0 +1,19 @@ +// Issue 21667 - scope parameter causes 'no size because of forward references' +// https://issues.dlang.org/show_bug.cgi?id=21667 +@safe: + +struct Foo +{ + void delegate(scope Foo) dg; +} + +struct M +{ + F.Type f; +} + +struct F +{ + enum Type {a} + void foo(scope M m) {} +} diff --git a/tests/dmd/compilable/test22559.i b/tests/dmd/compilable/test22559.i new file mode 100644 index 00000000000..d8e5cc8847e --- /dev/null +++ b/tests/dmd/compilable/test22559.i @@ -0,0 +1,17 @@ +// https://gcc.gnu.org/onlinedocs/gcc/Case-Ranges.html + +int alpha(char c) +{ + switch (c) + { + case 'A' ... 'Z': return 1; + case 'a' ... 'z': return 1; + default: return 0; + } +} + +_Static_assert(alpha('A') == 1, "1"); +_Static_assert(alpha('B') == 1, "2"); +_Static_assert(alpha('z') == 1, "3"); +_Static_assert(alpha('z' + 1) == 0, "3"); +_Static_assert(alpha('0') == 0, "4"); diff --git a/tests/dmd/compilable/test22760.d b/tests/dmd/compilable/test22760.d new file mode 100644 index 00000000000..5957db3256c --- /dev/null +++ b/tests/dmd/compilable/test22760.d @@ -0,0 +1,15 @@ +// https://issues.dlang.org/show_bug.cgi?id=22760 + +extern(C++) void f(T)(T) +{ +} +struct S1(T) +{ + struct S2 + { + } +} +void fun() +{ + f(S1!int.S2()); +} diff --git a/tests/dmd/compilable/test22809.c b/tests/dmd/compilable/test22809.c index fcf3fbf4c4b..68752d50a04 100644 --- a/tests/dmd/compilable/test22809.c +++ b/tests/dmd/compilable/test22809.c @@ -15,7 +15,6 @@ int y = ((size_t)((char *)&((struct Foo *)1)->x - (char *)1)); _Static_assert(((size_t)((char *)&((struct Foo *)0)->y - (char *)0))==4, ""); -/* LDC FIXME: needs preprocessor (for including importc.h) // https://issues.dlang.org/show_bug.cgi?id=23584 int foo(float bar) @@ -28,4 +27,3 @@ void test23584() int i = foo(3.5); _Static_assert(foo(3.5) == 0x40600000, "1"); } -*/ diff --git a/tests/dmd/compilable/test23214.c b/tests/dmd/compilable/test23214.c index 92f35e8de76..9aa38bef542 100644 --- a/tests/dmd/compilable/test23214.c +++ b/tests/dmd/compilable/test23214.c @@ -1,4 +1,3 @@ // https://issues.dlang.org/show_bug.cgi?id=23214 -// DISABLED: LDC // FIXME - need to invoke C pre-processor typedef unsigned __int64 uintptr_t; diff --git a/tests/dmd/compilable/test23509.i b/tests/dmd/compilable/test23509.i new file mode 100644 index 00000000000..fd2c5d85e23 --- /dev/null +++ b/tests/dmd/compilable/test23509.i @@ -0,0 +1,8 @@ +// https://issues.dlang.org/show_bug.cgi?id=23509 + +int max(int a, int b) +{ + return ({int _a = (a), _b = (b); _a > _b ? _a : _b; }); +} + +_Static_assert(max(3,4) == 4, "1"); diff --git a/tests/dmd/compilable/test23583.c b/tests/dmd/compilable/test23583.c index 60ca47cab94..480ceff1822 100644 --- a/tests/dmd/compilable/test23583.c +++ b/tests/dmd/compilable/test23583.c @@ -1,4 +1,3 @@ -// DISABLED: LDC // FIXME: needs preprocessor // https://issues.dlang.org/show_bug.cgi?id=23580 // https://issues.dlang.org/show_bug.cgi?id=23581 // https://issues.dlang.org/show_bug.cgi?id=23582 diff --git a/tests/dmd/compilable/test23616.c b/tests/dmd/compilable/test23616.c index 019cea90afc..4b9ebca4fe8 100644 --- a/tests/dmd/compilable/test23616.c +++ b/tests/dmd/compilable/test23616.c @@ -1,4 +1,3 @@ -// DISABLED: LDC // FIXME: needs preprocessor // https://issues.dlang.org/show_bug.cgi?id=23616 #if __has_extension(gnu_asm) diff --git a/tests/dmd/compilable/test23789.d b/tests/dmd/compilable/test23789.d new file mode 100644 index 00000000000..37fc3d4bc20 --- /dev/null +++ b/tests/dmd/compilable/test23789.d @@ -0,0 +1,11 @@ +// https://issues.dlang.org/show_bug.cgi?id=23789 + +import imports.c23789; + +static assert(M128A.alignof == 64); +static assert(_M128B.alignof == 32); +static assert(M128B.alignof == 32); + +static assert(N128A.alignof == 64); +static assert(_N128B.alignof == 32); +static assert(N128B.alignof == 32); diff --git a/tests/dmd/compilable/test23862.d b/tests/dmd/compilable/test23862.d new file mode 100644 index 00000000000..c5b063b2784 --- /dev/null +++ b/tests/dmd/compilable/test23862.d @@ -0,0 +1,15 @@ +// https://issues.dlang.org/show_bug.cgi?id=23862 + +enum E { A, B } + +void test(E e) +{ + with (e) + switch (e) + { + case A: + case B: + default: + break; + } +} diff --git a/tests/dmd/compilable/test23863.d b/tests/dmd/compilable/test23863.d new file mode 100644 index 00000000000..c3941ce2f81 --- /dev/null +++ b/tests/dmd/compilable/test23863.d @@ -0,0 +1,15 @@ +// https://issues.dlang.org/show_bug.cgi?id=23863 + +alias AliasSeq(T...) = T; + +struct S +{ +} +alias Empty = S.tupleof; +Empty x; // accepts valid + +static assert(is(typeof(x))); +static assert(is(typeof(Empty))); +static assert(is(typeof(AliasSeq!(int)))); +static assert(is(typeof(Empty) == AliasSeq!())); +static assert(is(typeof(AliasSeq!()) == AliasSeq!())); diff --git a/tests/dmd/compilable/test23866.i b/tests/dmd/compilable/test23866.i new file mode 100644 index 00000000000..71a251a364d --- /dev/null +++ b/tests/dmd/compilable/test23866.i @@ -0,0 +1,5 @@ +// https://issues.dlang.org/show_bug.cgi?id=23866 + +struct __declspec(align(16)) __declspec(no_init_all) S { }; + +typedef struct __attribute__((aligned(16))) __declspec(no_init_all) S2 { } S2; diff --git a/tests/dmd/compilable/test23874.d b/tests/dmd/compilable/test23874.d new file mode 100644 index 00000000000..09121844ee1 --- /dev/null +++ b/tests/dmd/compilable/test23874.d @@ -0,0 +1,11 @@ +// https://issues.dlang.org/show_bug.cgi?id=23874 +// REQUIRED_ARGS: -profile=gc +// DISABLED: LDC // -profile=gc not supported + +string myToString() +{ + return ""; +} + +enum x = myToString ~ ""; +immutable x2 = myToString ~ ""; diff --git a/tests/dmd/compilable/test23913.d b/tests/dmd/compilable/test23913.d new file mode 100644 index 00000000000..e39c6dec317 --- /dev/null +++ b/tests/dmd/compilable/test23913.d @@ -0,0 +1,7 @@ +// EXTRA_FILES: imports/library.c + +// https://issues.dlang.org/show_bug.cgi?id=23913 + +import imports.library; + +alias x = __traits(getMember, imports.library, "SomeEnum"); diff --git a/tests/dmd/compilable/test23951.d b/tests/dmd/compilable/test23951.d new file mode 100644 index 00000000000..e09a3d7b5d1 --- /dev/null +++ b/tests/dmd/compilable/test23951.d @@ -0,0 +1,10 @@ +// https://issues.dlang.org/show_bug.cgi?id=23951 + +struct S { int x; } +struct T { S a; alias a this; } +struct U { T t; } +static assert(__traits(hasMember, T, "x")); +static assert(__traits(hasMember, T.init, "x")); +static assert(__traits(hasMember, U.init.t, "x")); +static assert(__traits(hasMember, U.t, "a")); +static assert(__traits(hasMember, U.t, "x")); diff --git a/tests/dmd/compilable/test23965.d b/tests/dmd/compilable/test23965.d new file mode 100644 index 00000000000..36e4e428a85 --- /dev/null +++ b/tests/dmd/compilable/test23965.d @@ -0,0 +1,11 @@ +// https://issues.dlang.org/show_bug.cgi?id=23965 +// REQUIRED_ARGS: -de +deprecated: + +struct S {} + +void fun() +{ + S[] arr; + arr ~= S(); +} diff --git a/tests/dmd/compilable/test23966.d b/tests/dmd/compilable/test23966.d new file mode 100644 index 00000000000..71aa8ca229e --- /dev/null +++ b/tests/dmd/compilable/test23966.d @@ -0,0 +1,19 @@ +// https://issues.dlang.org/show_bug.cgi?id=23966 +module test23966; + +@("gigi") +void fun() {} +@("mimi") +void fun(int) {} +@("hihi") +void fun(int, int) {} +@("bibi") +void fun()(int, ulong) {} + +void main() +{ + static foreach (t; __traits(getOverloads, test23966, "fun", true)) + static foreach(attr; __traits(getAttributes, t)) + {} + +} diff --git a/tests/dmd/compilable/test23978.d b/tests/dmd/compilable/test23978.d new file mode 100644 index 00000000000..cc30f728dee --- /dev/null +++ b/tests/dmd/compilable/test23978.d @@ -0,0 +1,30 @@ +// REQUIRED_ARGS: -preview=dip1021 -lowmem +// https://issues.dlang.org/show_bug.cgi?id=23978 + +// Note: this is a memory corruption bug. +// Memory returned by `GC.realloc` retains references to old memory in it, +// mostly because of the smallarray optimization for `Array(T)`. +// If this fails again, it might not be consistent, so try running it multiple times. + +class LUBench { } +void lup(ulong , ulong , int , int = 1) +{ + new LUBench; +} +void lup_3200(ulong iters, ulong flops) +{ + lup(iters, flops, 3200); +} +void raytrace() +{ + struct V + { + float x, y, z; + auto normalize() { } + struct Tid { } + auto spawnLinked() { } + string[] namesByTid; + class MessageBox { } + auto cross() { } + } +} diff --git a/tests/dmd/compilable/test23979.d b/tests/dmd/compilable/test23979.d new file mode 100644 index 00000000000..f7eb2c554ee --- /dev/null +++ b/tests/dmd/compilable/test23979.d @@ -0,0 +1,17 @@ +// REQUIRED_ARGS: -o- +// https://issues.dlang.org/show_bug.cgi?id=23979 +// Issue 23979 - ICE on failed alias this attempt on pointer expression + +class A {} + +void h() +{ + const auto classPtr = SPtr.init; + assert(!__traits(compiles, *classPtr)); +} + +struct SPtr +{ + A ptr() { return A.init; } + alias ptr this; +} diff --git a/tests/dmd/compilable/test23986.d b/tests/dmd/compilable/test23986.d new file mode 100644 index 00000000000..6bbf1c2aeaa --- /dev/null +++ b/tests/dmd/compilable/test23986.d @@ -0,0 +1,11 @@ +// REQUIRED_ARGS: -preview=dip1021 -o- +// https://issues.dlang.org/show_bug.cgi?id=23986 +// dip1021 asserts on `typeof(null)` parameter +@safe: + +void f(typeof(null) obj, int* x) {} + +void g() +{ + f(null, null); +} diff --git a/tests/dmd/compilable/test24013.d b/tests/dmd/compilable/test24013.d new file mode 100644 index 00000000000..132ada61c8e --- /dev/null +++ b/tests/dmd/compilable/test24013.d @@ -0,0 +1,43 @@ +// https://issues.dlang.org/show_bug.cgi?id=24013 + +struct PropDescriptor(T) +{ + void define(T delegate() aGetter, string aName) {} +} + +enum Get; + +mixin template PropertyPublisherImpl() +{ + void collectPublicationsFromPairs(T)() + { + foreach (member; __traits(derivedMembers, T)) + foreach (overload; __traits(getOverloads, T, member)) + { + alias Attributes = __traits(getAttributes, overload); + static if (Attributes.length != 0) + { + auto descriptor = new PropDescriptor!size_t; + auto dg = &overload; + descriptor.define(dg, member); + } + } + } +} + +void main() +{ + class Bar + { + size_t _field; + mixin PropertyPublisherImpl; + this() + { + collectPublicationsFromPairs!Bar; + } + @Get size_t field() + { + return _field; + } + } +} diff --git a/tests/dmd/compilable/test24022.d b/tests/dmd/compilable/test24022.d new file mode 100644 index 00000000000..f499636f126 --- /dev/null +++ b/tests/dmd/compilable/test24022.d @@ -0,0 +1,30 @@ +// https://issues.dlang.org/show_bug.cgi?id=24022 +// EXTRA_FILES: imports/imp24022.c +import imports.imp24022; + +auto some_d_func(E v) { + return v; +} + +auto some_d_other_func() { + const struct R { + E r; + this(in E vparam) { r = vparam; } + } + return R(A); +} + +void main(string[] args) { + E expected = E.A; + E res = some_d_func(A); + assert (res == A); + assert (res == expected); + + res = some_d_func(E.B); + assert (res == B); + assert (res == E.B); + + auto res2 = some_d_other_func(); + assert (res2.r == A); + assert (res2.r == expected); +} diff --git a/tests/dmd/compilable/testcomplex.i b/tests/dmd/compilable/testcomplex.i new file mode 100644 index 00000000000..3cc0021fa0e --- /dev/null +++ b/tests/dmd/compilable/testcomplex.i @@ -0,0 +1,35 @@ + +/* GCC header complex.h requires supporting `i` suffix extension + */ + +_Complex float testf() +{ + _Complex float x = 1.0if; + return x; +} + +_Complex float testf2() +{ + _Complex float x = (float _Complex)1.0i; + return x; +} + +_Complex double testd() +{ + _Complex double x = 1.0i; + return x; +} + +_Complex long double testld() +{ + _Complex long double x = 1.0iL; + return x; +} + +_Complex float testcast() +{ + _Complex double y = 1.0i; + return (_Complex float)y; +} + +_Static_assert((float _Complex)1.0i == 1.0i, "1"); diff --git a/tests/dmd/compilable/testcstuff1.c b/tests/dmd/compilable/testcstuff1.c index 2067e72b0c3..3a1f66358e1 100644 --- a/tests/dmd/compilable/testcstuff1.c +++ b/tests/dmd/compilable/testcstuff1.c @@ -201,6 +201,13 @@ _Static_assert(testexpinit() == 1 + 2 + 3, "ok"); /********************************/ +__declspec(restrict) void* testrestrictdeclspec() +{ + return 0; +} + +/********************************/ + // Character literals _Static_assert(sizeof('a') == 4, "ok"); _Static_assert(sizeof(u'a') == 4, "ok"); @@ -272,7 +279,8 @@ void test2() typedef int TI; //extern int ei; static int si; - _Thread_local int tli; + static _Thread_local int tli; + int __declspec(thread) tlj; auto int ai; register int reg; const int ci; diff --git a/tests/dmd/compilable/testcstuff2.c b/tests/dmd/compilable/testcstuff2.c index 077daf43ab4..ed4af3d84f0 100644 --- a/tests/dmd/compilable/testcstuff2.c +++ b/tests/dmd/compilable/testcstuff2.c @@ -389,6 +389,9 @@ __attribute__((static, unsigned, long, const, extern, register, typedef, short, _Thread_local, int, char, float, double, void, _Bool, _Atomic)) int test22196(); +_Atomic(_Bool) atomicbool; + + /***************************************************/ // https://issues.dlang.org/show_bug.cgi?id=22245 @@ -714,3 +717,20 @@ enum E2 { m1, m2 = m1 }; + +/************************************************************/ + +// https://issues.dlang.org/show_bug.cgi?id=23725 + +#define __fldcw(addr) asm volatile("fldcw %0" : : "m" (*(addr))) + +static __inline void +__fnldcw(unsigned short _cw, unsigned short _newcw) +{ + __fldcw(&_newcw); +} + +void test23725() +{ + __fnldcw(1, 2); +} diff --git a/tests/dmd/compilable/testnoinline.c b/tests/dmd/compilable/testnoinline.c new file mode 100644 index 00000000000..cf3928eb58b --- /dev/null +++ b/tests/dmd/compilable/testnoinline.c @@ -0,0 +1,9 @@ + +__attribute__((noinline)) int abc() { return 1; } +__declspec(noinline) int def() { return 2; } +inline int ghi() { return 3; } + +int test() +{ + return abc() + def() + ghi(); +} diff --git a/tests/dmd/compilable/traits_getFunctionAttributes.d b/tests/dmd/compilable/traits_getFunctionAttributes.d index 1f25b269053..f4defb48095 100644 --- a/tests/dmd/compilable/traits_getFunctionAttributes.d +++ b/tests/dmd/compilable/traits_getFunctionAttributes.d @@ -1,9 +1,10 @@ module traits_getFunctionAttributes; +alias tuple(T...) = T; + void test_getFunctionAttributes() { - alias tuple(T...) = T; struct S { @@ -118,3 +119,14 @@ void test_getFunctionAttributes() static assert(__traits(getFunctionAttributes, systemDel) == tuple!("pure", "nothrow", "@nogc", "@system")); static assert(__traits(getFunctionAttributes, typeof(systemDel)) == tuple!("pure", "nothrow", "@nogc", "@system")); } + +void bug19706() +{ + struct S + { + static int fImpl(Ret)() { return Ret.init; } + + // tells us: `fImpl!int` is @system + static assert(__traits(getFunctionAttributes, fImpl!int) == tuple!("pure", "nothrow", "@nogc", "@safe")); + } +} diff --git a/tests/dmd/compilable/user_defined_attributes.d b/tests/dmd/compilable/user_defined_attributes.d new file mode 100644 index 00000000000..169ca49eb0d --- /dev/null +++ b/tests/dmd/compilable/user_defined_attributes.d @@ -0,0 +1,22 @@ + +enum Test; + +@true @null @byte int x; +@(int) int y; +@"test" @`test2` @30 @'a' @__LINE__ void f(); + +@Test void h(); + +static assert( __traits(getAttributes, x)[0] == true); +static assert( __traits(getAttributes, x)[1] == null); +static assert(is(__traits(getAttributes, x)[2] == byte)); + +static assert(is(__traits(getAttributes, y)[0] == int)); + +static assert( __traits(getAttributes, f)[0] == "test"); +static assert( __traits(getAttributes, f)[1] == "test2"); +static assert( __traits(getAttributes, f)[2] == 30); +static assert( __traits(getAttributes, f)[3] == 'a'); +static assert( __traits(getAttributes, f)[4] == 6); + +static assert(is(__traits(getAttributes, h)[0] == enum)); diff --git a/tests/dmd/compilable/warn3882.d b/tests/dmd/compilable/warn3882.d index f02a87bd04b..0474315be57 100644 --- a/tests/dmd/compilable/warn3882.d +++ b/tests/dmd/compilable/warn3882.d @@ -12,7 +12,7 @@ void test3882() /******************************************/ // https://issues.dlang.org/show_bug.cgi?id=12619 -extern (C) @system nothrow pure void* memcpy(void* s1, in void* s2, size_t n); +extern (C) @system nothrow pure void* memcpy(void* s1, const void* s2, size_t n); // -> weakly pure void test12619() pure @@ -64,7 +64,7 @@ void test12909() const struct Foo13899 { - int opApply(immutable int delegate(in ref int) pure nothrow dg) pure nothrow + int opApply(immutable int delegate(const ref int) pure nothrow dg) pure nothrow { return 1; } diff --git a/tests/dmd/dub_package/avg.d b/tests/dmd/dub_package/avg.d index 4ce5776e777..b6f9f1e8898 100755 --- a/tests/dmd/dub_package/avg.d +++ b/tests/dmd/dub_package/avg.d @@ -1,6 +1,6 @@ #!/usr/bin/env dub /+dub.sdl: -dependency "dmd" path="../.." +dependency "dmd" path="../../.." +/ /* This file contains an example on how to use the transitive visitor. It implements a visitor which computes the average function length from @@ -10,7 +10,7 @@ dependency "dmd" path="../.." module examples.avg; import dmd.astbase; -import dmd.errors; +import dmd.errorsink; import dmd.parse; import dmd.target; import dmd.transitivevisitor; @@ -60,8 +60,9 @@ void main() auto id = Identifier.idPool(fname); auto m = new ASTBase.Module(&(fname.dup)[0], id, false, false); auto input = readText(fname); + input ~= '\0'; - scope p = new Parser!ASTBase(m, input, false); + scope p = new Parser!ASTBase(m, input, false, new ErrorSinkStderr(), null, false); p.nextToken(); m.members = p.parseModule(); diff --git a/tests/dmd/dub_package/frontend.d b/tests/dmd/dub_package/frontend.d index b034027deb9..184f5960d7b 100755 --- a/tests/dmd/dub_package/frontend.d +++ b/tests/dmd/dub_package/frontend.d @@ -1,6 +1,6 @@ #!/usr/bin/env dub /+dub.sdl: -dependency "dmd" path="../.." +dependency "dmd" path="../../.." +/ import std.stdio; diff --git a/tests/dmd/dub_package/frontend_file.d b/tests/dmd/dub_package/frontend_file.d index 71e62f51528..a6d662ebf64 100755 --- a/tests/dmd/dub_package/frontend_file.d +++ b/tests/dmd/dub_package/frontend_file.d @@ -1,6 +1,6 @@ #!/usr/bin/env dub /+dub.sdl: -dependency "dmd" path="../.." +dependency "dmd" path="../../.." +/ import std.stdio; import std.string : replace; diff --git a/tests/dmd/dub_package/impvisitor.d b/tests/dmd/dub_package/impvisitor.d index 85df47da226..c27a71abea0 100755 --- a/tests/dmd/dub_package/impvisitor.d +++ b/tests/dmd/dub_package/impvisitor.d @@ -1,6 +1,6 @@ #!/usr/bin/env dub /+dub.sdl: -dependency "dmd" path="../.." +dependency "dmd" path="../../.." +/ import dmd.permissivevisitor; @@ -27,7 +27,7 @@ extern(C++) class ImportVisitor2(AST) : ParseTimeTransitiveVisitor!AST printf("%s", imp.id.toChars()); - if (imp.names.dim) + if (imp.names.length) { printf(" : "); foreach (const i, const name; imp.names) @@ -82,12 +82,13 @@ void main() import dmd.id; import dmd.globals; import dmd.identifier; + import dmd.errorsink; import dmd.target; import core.memory; GC.disable(); - string path = __FILE_FULL_PATH__.dirName.buildPath("../../../phobos/std/"); + string path = __FILE_FULL_PATH__.dirName.buildPath("../../../../phobos/std/"); string regex = "*.d"; auto dFiles = dirEntries(path, regex, SpanMode.depth); @@ -106,9 +107,10 @@ void main() auto id = Identifier.idPool(fn); auto m = new ASTBase.Module(&(fn.dup)[0], id, false, false); auto input = readText(fn); + input ~= '\0'; //writeln("Started parsing..."); - scope p = new Parser!ASTBase(m, input, false); + scope p = new Parser!ASTBase(m, input, false, new ErrorSinkStderr(), null, false); p.nextToken(); m.members = p.parseModule(); //writeln("Finished parsing. Starting transitive visitor"); diff --git a/tests/dmd/dub_package/lexer.d b/tests/dmd/dub_package/lexer.d index 608f18e031b..b496d4b62c9 100755 --- a/tests/dmd/dub_package/lexer.d +++ b/tests/dmd/dub_package/lexer.d @@ -1,12 +1,13 @@ #!/usr/bin/env dub /+dub.sdl: -dependency "dmd" path="../.." +dependency "dmd" path="../../.." +/ void main() { import dmd.globals; import dmd.lexer; import dmd.tokens; + import dmd.errorsink; immutable expected = [ TOK.void_, @@ -18,7 +19,7 @@ void main() ]; immutable sourceCode = "void test() {} // foobar"; - scope lexer = new Lexer("test", sourceCode.ptr, 0, sourceCode.length, 0, 0); + scope lexer = new Lexer("test", sourceCode.ptr, 0, sourceCode.length, 0, 0, 0, new ErrorSinkStderr); lexer.nextToken; TOK[] result; diff --git a/tests/dmd/dub_package/parser.d b/tests/dmd/dub_package/parser.d index 2c9d7668866..9d24a77089a 100755 --- a/tests/dmd/dub_package/parser.d +++ b/tests/dmd/dub_package/parser.d @@ -1,13 +1,14 @@ #!/usr/bin/env dub /+dub.sdl: -dependency "dmd" path="../.." +dependency "dmd" path="../../.." +/ void main() { import dmd.astbase; import dmd.globals; import dmd.parse; + import dmd.errorsink; - scope parser = new Parser!ASTBase(null, null, false); + scope parser = new Parser!ASTBase(null, null, false, new ErrorSinkStderr, null, false); assert(parser !is null); } diff --git a/tests/dmd/dub_package/retrieveScope.d b/tests/dmd/dub_package/retrieveScope.d index 36e05c365fe..bfd6d5f4614 100755 --- a/tests/dmd/dub_package/retrieveScope.d +++ b/tests/dmd/dub_package/retrieveScope.d @@ -1,6 +1,6 @@ #!/usr/bin/env dub /+dub.sdl: -dependency "dmd" path="../.." +dependency "dmd" path="../../.." versions "CallbackAPI" +/ /* @@ -20,13 +20,13 @@ import std.path : dirName; import dmd.errors; import dmd.frontend; -import dmd.mars; import dmd.console; import dmd.arraytypes; import dmd.compiler; import dmd.dmodule; import dmd.dsymbol; import dmd.dsymbolsem; +import dmd.location; import dmd.semantic2; import dmd.semantic3; import dmd.statement; @@ -77,14 +77,9 @@ int main() global.gag = 1; initDMD(diagnosticHandler); - Strings libmodules; - Module m = createModule((dirName(__FILE_FULL_PATH__) ~ "/testfiles/correct.d").ptr, - libmodules); + Module m = parseModule(__FILE_FULL_PATH__ ~ "/testfiles/correct.d").module_; m.importedFrom = m; // m.isRoot() == true - m.read(Loc.initial); - m.parse(); - CallbackHelper.cursorLoc = Loc(to!string(m.srcfile).ptr, 22, 10); Compiler.onStatementSemanticStart = &CallbackHelper.statementSem; diff --git a/tests/dmd/fail_compilation/alignedext.i b/tests/dmd/fail_compilation/alignedext.i new file mode 100644 index 00000000000..eae3137ce47 --- /dev/null +++ b/tests/dmd/fail_compilation/alignedext.i @@ -0,0 +1,14 @@ +/* TEST_OUTPUT: +--- +fail_compilation/alignedext.i(10): Error: __decspec(align(123)) must be an integer positive power of 2 and be <= 8,192 +fail_compilation/alignedext.i(11): Error: __decspec(align(16384)) must be an integer positive power of 2 and be <= 8,192 +fail_compilation/alignedext.i(13): Error: __attribute__((aligned(123))) must be an integer positive power of 2 and be <= 32,768 +fail_compilation/alignedext.i(14): Error: __attribute__((aligned(65536))) must be an integer positive power of 2 and be <= 32,768 +--- +*/ + +typedef struct __declspec(align(123)) S { int a; } S; +struct __declspec(align(16384)) T { int a; }; + +typedef struct __attribute__((aligned(123))) U { int a; } S; +struct __attribute__((aligned(65536))) V { int a; }; diff --git a/tests/dmd/fail_compilation/attributediagnostic.d b/tests/dmd/fail_compilation/attributediagnostic.d index 8360e1ac484..523a183d767 100644 --- a/tests/dmd/fail_compilation/attributediagnostic.d +++ b/tests/dmd/fail_compilation/attributediagnostic.d @@ -4,15 +4,15 @@ 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 was inferred `@system` because of: +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 was inferred `@system` because of: +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 was inferred `@system` because of: +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 --- diff --git a/tests/dmd/fail_compilation/attributediagnostic_nogc.d b/tests/dmd/fail_compilation/attributediagnostic_nogc.d new file mode 100644 index 00000000000..e3dbee899fc --- /dev/null +++ b/tests/dmd/fail_compilation/attributediagnostic_nogc.d @@ -0,0 +1,56 @@ +/* +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(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(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(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 +--- +*/ +#line 18 +// Issue 17374 - Improve inferred attribute error message +// https://issues.dlang.org/show_bug.cgi?id=17374 + +auto layer2() @nogc { layer1(); } +auto layer1() { layer0(); } +auto layer0() { gc(); } + +auto gc() +{ + asm {} +} + +auto gc1() +{ + int* x = new int; +} + +auto fgc = function void() {new int[10];}; +auto gc2() +{ + fgc(); +} + +void main() @nogc +{ + gc1(); + gc2(); + gcClosure(); +} + +auto gcClosure() +{ + int x; + int bar() { return x; } + return &bar; +} diff --git a/tests/dmd/fail_compilation/attributediagnostic_nothrow.d b/tests/dmd/fail_compilation/attributediagnostic_nothrow.d new file mode 100644 index 00000000000..7fea3224412 --- /dev/null +++ b/tests/dmd/fail_compilation/attributediagnostic_nothrow.d @@ -0,0 +1,45 @@ +/* +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` +--- +*/ + +// Issue 17374 - Improve inferred attribute error message +// https://issues.dlang.org/show_bug.cgi?id=17374 + +auto layer2() nothrow { layer1(); } +auto layer1() { layer0(); } +auto layer0() { gc(); } + +auto gc() +{ + asm {} +} + +auto gc1() +{ + throw new Exception("msg"); +} + +auto fgc = function void() {throw new Exception("msg");}; +auto gc2() +{ + fgc(); +} + +void main() nothrow +{ + gc1(); + gc2(); +} diff --git a/tests/dmd/fail_compilation/attributediagnostic_pure.d b/tests/dmd/fail_compilation/attributediagnostic_pure.d new file mode 100644 index 00000000000..a120dabf852 --- /dev/null +++ b/tests/dmd/fail_compilation/attributediagnostic_pure.d @@ -0,0 +1,21 @@ +/* +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 +--- +*/ + +// Issue 17374 - Improve inferred attribute error message +// https://issues.dlang.org/show_bug.cgi?id=17374 + +auto gc() +{ + asm {} +} + +void main() pure +{ + gc(); +} diff --git a/tests/dmd/fail_compilation/attrpure.i b/tests/dmd/fail_compilation/attrpure.i new file mode 100644 index 00000000000..ae2ed880b33 --- /dev/null +++ b/tests/dmd/fail_compilation/attrpure.i @@ -0,0 +1,12 @@ +/* TEST_OUTPUT: +--- +fail_compilation/attrpure.i(11): Error: `pure` function `attrpure.pureAsSnow` cannot call impure function `attrpure.impure` +--- +*/ + +void impure(); + +__attribute__((pure)) void pureAsSnow() +{ + impure(); +} diff --git a/tests/dmd/fail_compilation/bug9631.d b/tests/dmd/fail_compilation/bug9631.d index c980d76a73d..802d1c2983e 100644 --- a/tests/dmd/fail_compilation/bug9631.d +++ b/tests/dmd/fail_compilation/bug9631.d @@ -66,8 +66,8 @@ fail_compilation/bug9631.d(79): Error: function `bug9631.arg.f(int i, S s)` is n fail_compilation/bug9631.d(79): cannot pass argument `y` of type `bug9631.tem!().S` to parameter `bug9631.S s` fail_compilation/bug9631.d(80): Error: function literal `__lambda4(S s)` is not callable using argument types `(S)` fail_compilation/bug9631.d(80): cannot pass argument `x` of type `bug9631.S` to parameter `bug9631.tem!().S s` -fail_compilation/bug9631.d(86): Error: constructor `bug9631.arg.A.this(S _param_0)` is not callable using argument types `(S)` -fail_compilation/bug9631.d(86): cannot pass argument `S(0)` of type `bug9631.tem!().S` to parameter `bug9631.S _param_0` +fail_compilation/bug9631.d(86): Error: constructor `bug9631.arg.A.this(S __param_0)` is not callable using argument types `(S)` +fail_compilation/bug9631.d(86): cannot pass argument `S(0)` of type `bug9631.tem!().S` to parameter `bug9631.S __param_0` --- */ void arg() @@ -89,8 +89,8 @@ void arg() /* TEST_OUTPUT: --- -fail_compilation/bug9631.d(106): Error: function `bug9631.targ.ft!().ft(S _param_0)` is not callable using argument types `(S)` -fail_compilation/bug9631.d(106): cannot pass argument `x` of type `bug9631.S` to parameter `bug9631.tem!().S _param_0` +fail_compilation/bug9631.d(106): Error: function `bug9631.targ.ft!().ft(S __param_0)` is not callable using argument types `(S)` +fail_compilation/bug9631.d(106): cannot pass argument `x` of type `bug9631.S` to parameter `bug9631.tem!().S __param_0` fail_compilation/bug9631.d(107): Error: none of the overloads of template `bug9631.targ.ft` are callable using argument types `!()(S)` fail_compilation/bug9631.d(105): Candidate is: `ft()(tem!().S)` fail_compilation/bug9631.d(109): Error: none of the overloads of template `bug9631.targ.ft2` are callable using argument types `!()(S, int)` diff --git a/tests/dmd/fail_compilation/cdeprecated.i b/tests/dmd/fail_compilation/cdeprecated.i new file mode 100644 index 00000000000..75ce2acbe6e --- /dev/null +++ b/tests/dmd/fail_compilation/cdeprecated.i @@ -0,0 +1,22 @@ +/* REQUIRED_ARGS: -de +TEST_OUTPUT: +--- +fail_compilation/cdeprecated.i(18): Deprecation: function `cdeprecated.mars` is deprecated +fail_compilation/cdeprecated.i(19): Deprecation: function `cdeprecated.jupiter` is deprecated - jumping jupiter +fail_compilation/cdeprecated.i(20): Deprecation: function `cdeprecated.saturn` is deprecated +fail_compilation/cdeprecated.i(21): Deprecation: function `cdeprecated.neptune` is deprecated - spinning neptune +--- +*/ +__declspec(deprecated) int mars(); +__declspec(deprecated("jumping jupiter")) int jupiter(); +__attribute__((deprecated)) extern int saturn(); +__attribute__((deprecated("spinning neptune"))) extern int neptune(); + +int test() +{ + return + mars() + + jupiter() + + saturn() + + neptune(); +} diff --git a/tests/dmd/fail_compilation/ctfeblock.d b/tests/dmd/fail_compilation/ctfeblock.d index 2d8bf7a0fef..9c901033223 100644 --- a/tests/dmd/fail_compilation/ctfeblock.d +++ b/tests/dmd/fail_compilation/ctfeblock.d @@ -19,7 +19,7 @@ struct T { } { L1: new T(); - a = 3; + a = 3; } goto L1; } @@ -31,3 +31,14 @@ L1: new T(); } } + +@nogc void test3() +{ + if (!__ctfe) + { + } + else + { + int* p = new int; + } +} diff --git a/tests/dmd/fail_compilation/deprecatedinref.d b/tests/dmd/fail_compilation/deprecatedinref.d new file mode 100644 index 00000000000..20c3666bef1 --- /dev/null +++ b/tests/dmd/fail_compilation/deprecatedinref.d @@ -0,0 +1,10 @@ +/* +REQUIRED_ARGS: -de +TEST_OUTPUT: +--- +fail_compilation/deprecatedinref.d(9): Deprecation: using `in ref` is deprecated, use `-preview=in` and `in` instead +fail_compilation/deprecatedinref.d(10): Deprecation: using `ref in` is deprecated, use `-preview=in` and `in` instead +--- +*/ +void foo(in ref int); +void foor(ref in int); diff --git a/tests/dmd/fail_compilation/deprecations_preview_in.d b/tests/dmd/fail_compilation/deprecations_preview_in.d new file mode 100644 index 00000000000..33cc904e4be --- /dev/null +++ b/tests/dmd/fail_compilation/deprecations_preview_in.d @@ -0,0 +1,11 @@ +/* +REQUIRED_ARGS: -de +TEST_OUTPUT: +--- +fail_compilation/deprecations_preview_in.d(1): Deprecation: using `in` parameters with `extern(C)` functions is deprecated +fail_compilation/deprecations_preview_in.d(1): parameter `__anonymous_param` declared as `in` here +--- +*/ + +#line 1 +extern(C) void fun1(in char*); diff --git a/tests/dmd/fail_compilation/diag10319.d b/tests/dmd/fail_compilation/diag10319.d index 7b2eca79b80..efc818f30be 100644 --- a/tests/dmd/fail_compilation/diag10319.d +++ b/tests/dmd/fail_compilation/diag10319.d @@ -1,17 +1,21 @@ /* TEST_OUTPUT: --- -fail_compilation/diag10319.d(29): Error: `pure` function `D main` cannot call impure function `diag10319.foo` -fail_compilation/diag10319.d(29): Error: `@safe` function `D main` cannot call `@system` function `diag10319.foo` -fail_compilation/diag10319.d(18): `diag10319.foo` is declared here -fail_compilation/diag10319.d(30): Error: `pure` function `D main` cannot call impure function `diag10319.bar!int.bar` -fail_compilation/diag10319.d(30): Error: `@safe` function `D main` cannot call `@system` function `diag10319.bar!int.bar` -fail_compilation/diag10319.d(23): which was inferred `@system` because of: -fail_compilation/diag10319.d(23): cannot take address of local `x` in `@safe` function `bar` -fail_compilation/diag10319.d(20): `diag10319.bar!int.bar` is declared here -fail_compilation/diag10319.d(29): Error: function `diag10319.foo` is not `nothrow` -fail_compilation/diag10319.d(30): Error: function `diag10319.bar!int.bar` is not `nothrow` -fail_compilation/diag10319.d(27): Error: function `D main` may throw but is marked as `nothrow` +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` --- */ diff --git a/tests/dmd/fail_compilation/diag10415.d b/tests/dmd/fail_compilation/diag10415.d index 1fde171b713..207f6a4aa15 100644 --- a/tests/dmd/fail_compilation/diag10415.d +++ b/tests/dmd/fail_compilation/diag10415.d @@ -3,7 +3,7 @@ TEST_OUTPUT: --- fail_compilation/diag10415.d(36): Error: none of the overloads of `x` are callable using argument types `(int) const` fail_compilation/diag10415.d(13): Candidates are: `diag10415.C.x()` -fail_compilation/diag10415.d(18): `diag10415.C.x(int _param_0)` +fail_compilation/diag10415.d(18): `diag10415.C.x(int __param_0)` fail_compilation/diag10415.d(39): Error: d.x is not an lvalue --- */ diff --git a/tests/dmd/fail_compilation/diag11769.d b/tests/dmd/fail_compilation/diag11769.d index 2717de4a6e7..75047f53785 100644 --- a/tests/dmd/fail_compilation/diag11769.d +++ b/tests/dmd/fail_compilation/diag11769.d @@ -2,9 +2,9 @@ TEST_OUTPUT: --- fail_compilation/diag11769.d(18): Error: `diag11769.foo!string.bar` called with argument types `(string)` matches both: -fail_compilation/diag11769.d(13): `diag11769.foo!string.bar(wstring _param_0)` +fail_compilation/diag11769.d(13): `diag11769.foo!string.bar(wstring __param_0)` and: -fail_compilation/diag11769.d(14): `diag11769.foo!string.bar(dstring _param_0)` +fail_compilation/diag11769.d(14): `diag11769.foo!string.bar(dstring __param_0)` --- */ diff --git a/tests/dmd/fail_compilation/diag14818.d b/tests/dmd/fail_compilation/diag14818.d index f9b535ab8d7..6147f32d0db 100644 --- a/tests/dmd/fail_compilation/diag14818.d +++ b/tests/dmd/fail_compilation/diag14818.d @@ -2,8 +2,8 @@ TEST_OUTPUT: --- fail_compilation/diag14818.d(40): Error: none of the overloads of `func` are callable using argument types `(string)` -fail_compilation/diag14818.d(18): Candidates are: `diag14818.foo(int _param_0)` -fail_compilation/diag14818.d(19): `diag14818.bar(double _param_0)` +fail_compilation/diag14818.d(18): Candidates are: `diag14818.foo(int __param_0)` +fail_compilation/diag14818.d(19): `diag14818.bar(double __param_0)` fail_compilation/diag14818.d(41): Error: template instance `diag14818.X!string` does not match any template declaration fail_compilation/diag14818.d(41): Candidates are: fail_compilation/diag14818.d(24): Foo(T) if (is(T == int)) diff --git a/tests/dmd/fail_compilation/diag15974.d b/tests/dmd/fail_compilation/diag15974.d index 03f63f4c21a..1967bb1e4f2 100644 --- a/tests/dmd/fail_compilation/diag15974.d +++ b/tests/dmd/fail_compilation/diag15974.d @@ -22,7 +22,7 @@ void test15974() struct S { - // CompileDeclaration + // MixinDeclaration mixin(format("%s", f)); } } diff --git a/tests/dmd/fail_compilation/diag20268.d b/tests/dmd/fail_compilation/diag20268.d new file mode 100644 index 00000000000..a314561892a --- /dev/null +++ b/tests/dmd/fail_compilation/diag20268.d @@ -0,0 +1,12 @@ +// https://issues.dlang.org/show_bug.cgi?id=20268 + +/* +TEST_OUTPUT: +--- +fail_compilation/diag20268.d(12): Error: none of the overloads of template `diag20268.__lambda4` are callable using argument types `!()(int)` +fail_compilation/diag20268.d(11): Candidate is: `__lambda4(__T1, __T2)(x, y)` +--- +*/ + +alias f = (x,y) => true; +auto x = f(1); diff --git a/tests/dmd/fail_compilation/diag8101b.d b/tests/dmd/fail_compilation/diag8101b.d index bc0ee9d2fb7..a55ef731ad2 100644 --- a/tests/dmd/fail_compilation/diag8101b.d +++ b/tests/dmd/fail_compilation/diag8101b.d @@ -2,13 +2,13 @@ TEST_OUTPUT: --- fail_compilation/diag8101b.d(28): Error: none of the overloads of `foo` are callable using argument types `(double)` -fail_compilation/diag8101b.d(19): Candidates are: `diag8101b.S.foo(int _param_0)` -fail_compilation/diag8101b.d(20): `diag8101b.S.foo(int _param_0, int _param_1)` -fail_compilation/diag8101b.d(30): Error: function `diag8101b.S.bar(int _param_0)` is not callable using argument types `(double)` -fail_compilation/diag8101b.d(30): cannot pass argument `1.0` of type `double` to parameter `int _param_0` +fail_compilation/diag8101b.d(19): Candidates are: `diag8101b.S.foo(int __param_0)` +fail_compilation/diag8101b.d(20): `diag8101b.S.foo(int __param_0, int __param_1)` +fail_compilation/diag8101b.d(30): Error: function `diag8101b.S.bar(int __param_0)` is not callable using argument types `(double)` +fail_compilation/diag8101b.d(30): cannot pass argument `1.0` of type `double` to parameter `int __param_0` fail_compilation/diag8101b.d(33): Error: none of the overloads of `foo` are callable using a `const` object -fail_compilation/diag8101b.d(19): Candidates are: `diag8101b.S.foo(int _param_0)` -fail_compilation/diag8101b.d(20): `diag8101b.S.foo(int _param_0, int _param_1)` +fail_compilation/diag8101b.d(19): Candidates are: `diag8101b.S.foo(int __param_0)` +fail_compilation/diag8101b.d(20): `diag8101b.S.foo(int __param_0, int __param_1)` fail_compilation/diag8101b.d(35): Error: mutable method `diag8101b.S.bar` is not callable using a `const` object fail_compilation/diag8101b.d(22): Consider adding `const` or `inout` here --- diff --git a/tests/dmd/fail_compilation/diag9312.d b/tests/dmd/fail_compilation/diag9312.d index 94e3d3ffd43..98308133e02 100644 --- a/tests/dmd/fail_compilation/diag9312.d +++ b/tests/dmd/fail_compilation/diag9312.d @@ -1,7 +1,7 @@ /* TEST_OUTPUT: --- -fail_compilation/diag9312.d(10): Error: `with` expressions must be aggregate types or pointers to them, not `int` +fail_compilation/diag9312.d(10): Error: `with` expression types must be enums or aggregates or pointers to them, not `int` --- */ diff --git a/tests/dmd/fail_compilation/diag9620.d b/tests/dmd/fail_compilation/diag9620.d index d99290c6a39..4af87df0a2f 100644 --- a/tests/dmd/fail_compilation/diag9620.d +++ b/tests/dmd/fail_compilation/diag9620.d @@ -1,8 +1,10 @@ /* TEST_OUTPUT: --- -fail_compilation/diag9620.d(18): Error: `pure` function `diag9620.main.bar` cannot call impure function `diag9620.foo1` -fail_compilation/diag9620.d(19): Error: `pure` function `diag9620.main.bar` cannot call impure function `diag9620.foo2!().foo2` +fail_compilation/diag9620.d(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` --- */ diff --git a/tests/dmd/fail_compilation/diag9831.d b/tests/dmd/fail_compilation/diag9831.d index b990ced0522..c93a06a465a 100644 --- a/tests/dmd/fail_compilation/diag9831.d +++ b/tests/dmd/fail_compilation/diag9831.d @@ -1,7 +1,7 @@ /* TEST_OUTPUT: --- -fail_compilation/diag9831.d(13): Error: function `diag9831.main.__lambda3` cannot access variable `c` in frame of function `D main` +fail_compilation/diag9831.d(13): Error: function `diag9831.main.__lambda3(__T1)(x)` cannot access variable `c` in frame of function `D main` fail_compilation/diag9831.d(11): `c` declared here --- */ diff --git a/tests/dmd/fail_compilation/dip1000_deprecation.d b/tests/dmd/fail_compilation/dip1000_deprecation.d index bf51363c07e..61174395be3 100644 --- a/tests/dmd/fail_compilation/dip1000_deprecation.d +++ b/tests/dmd/fail_compilation/dip1000_deprecation.d @@ -3,11 +3,11 @@ REQUIRED_ARGS: -de TEST_OUTPUT: --- fail_compilation/dip1000_deprecation.d(20): Deprecation: `@safe` function `main` calling `inferred` -fail_compilation/dip1000_deprecation.d(28): which would be `@system` because of: +fail_compilation/dip1000_deprecation.d(28): which wouldn't be `@safe` because of: fail_compilation/dip1000_deprecation.d(28): scope variable `x0` may not be returned fail_compilation/dip1000_deprecation.d(22): Deprecation: `@safe` function `main` calling `inferredC` fail_compilation/dip1000_deprecation.d(39): which calls `dip1000_deprecation.inferred` -fail_compilation/dip1000_deprecation.d(28): which would be `@system` because of: +fail_compilation/dip1000_deprecation.d(28): which wouldn't be `@safe` because of: fail_compilation/dip1000_deprecation.d(28): scope variable `x0` may not be returned fail_compilation/dip1000_deprecation.d(54): Deprecation: escaping reference to stack allocated value returned by `S(null)` fail_compilation/dip1000_deprecation.d(55): Deprecation: escaping reference to stack allocated value returned by `createS()` diff --git a/tests/dmd/fail_compilation/dtor_attributes.d b/tests/dmd/fail_compilation/dtor_attributes.d index ce81d6bfd35..21a12ed0253 100644 --- a/tests/dmd/fail_compilation/dtor_attributes.d +++ b/tests/dmd/fail_compilation/dtor_attributes.d @@ -8,8 +8,6 @@ fail_compilation/dtor_attributes.d(113): generated `Strict.~this` is impu fail_compilation/dtor_attributes.d(111): - HasDtor member fail_compilation/dtor_attributes.d(103): impure `HasDtor.~this` is declared here fail_compilation/dtor_attributes.d(118): Error: `@safe` function `dtor_attributes.test1` cannot call `@system` destructor `dtor_attributes.Strict.~this` -fail_compilation/dtor_attributes.d(113): which calls `dtor_attributes.Strict.~this` -fail_compilation/dtor_attributes.d(103): which calls `dtor_attributes.HasDtor.~this` fail_compilation/dtor_attributes.d(113): `dtor_attributes.Strict.~this` is declared here fail_compilation/dtor_attributes.d(113): generated `Strict.~this` is @system because of the following field's destructors: fail_compilation/dtor_attributes.d(111): - HasDtor member diff --git a/tests/dmd/fail_compilation/dtorfields_attributes.d b/tests/dmd/fail_compilation/dtorfields_attributes.d index 45b23cece4d..f6cab893bb4 100644 --- a/tests/dmd/fail_compilation/dtorfields_attributes.d +++ b/tests/dmd/fail_compilation/dtorfields_attributes.d @@ -9,7 +9,6 @@ fail_compilation/dtorfields_attributes.d(119): generated `Strict.~this` i fail_compilation/dtorfields_attributes.d(115): - HasDtor member fail_compilation/dtorfields_attributes.d(103): impure `HasDtor.~this` is declared here fail_compilation/dtorfields_attributes.d(117): Error: `@safe` constructor `dtorfields_attributes.Strict.this` cannot call `@system` destructor `dtorfields_attributes.Strict.~this` -fail_compilation/dtorfields_attributes.d(103): which calls `dtorfields_attributes.HasDtor.~this` fail_compilation/dtorfields_attributes.d(119): `dtorfields_attributes.Strict.~this` is declared here fail_compilation/dtorfields_attributes.d(119): generated `Strict.~this` is @system because of the following field's destructors: fail_compilation/dtorfields_attributes.d(115): - HasDtor member diff --git a/tests/dmd/fail_compilation/enumtype.c b/tests/dmd/fail_compilation/enumtype.c index f6aae337e22..d283595f177 100644 --- a/tests/dmd/fail_compilation/enumtype.c +++ b/tests/dmd/fail_compilation/enumtype.c @@ -1,6 +1,6 @@ /* TEST_OUTPUT: --- -fail_compilation/enumtype.c(111): Error: enum member `enumtype.E2.A2` enum member value `549755813889L` does not fit in an `int` +fail_compilation/enumtype.c(111): Error: enum member `enumtype.E2.A2` enum member value `549755813889L` does not fit in `int` --- */ @@ -11,9 +11,9 @@ enum E1 { A1 = 0, B1 = sizeof(A1), C1 = 1LL, D1 = sizeof(C1), F1 = -1U, G1 }; _Static_assert(A1 == 0, "in"); _Static_assert(B1 == 4, "in"); _Static_assert(C1 == 1, "in"); -_Static_assert(D1 == 4, "in"); -_Static_assert(F1 == -1, "in"); -_Static_assert(G1 == 0, "in"); -_Static_assert(sizeof(enum E1) == 4, "in"); +_Static_assert(D1 == 8, "in"); +_Static_assert(F1 == -1U, "in"); +_Static_assert(G1 == 0x100000000, "in"); +_Static_assert(sizeof(enum E1) == 8, "in"); -enum E2 { A2 = 0x8000000001LL }; +enum E2 : int { A2 = 0x8000000001LL }; diff --git a/tests/dmd/fail_compilation/fail10968.d b/tests/dmd/fail_compilation/fail10968.d index cfda8f4d9ad..3b6c3a0610e 100644 --- a/tests/dmd/fail_compilation/fail10968.d +++ b/tests/dmd/fail_compilation/fail10968.d @@ -8,10 +8,14 @@ fail_compilation/fail10968.d(44): Error: `pure` function `fail10968.bar` cannot 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 @@ -19,13 +23,18 @@ fail_compilation/fail10968.d(49): Error: `pure` function `fail10968.bar` cannot 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` --- */ +#line 29 struct SA { this(this) diff --git a/tests/dmd/fail_compilation/fail11375.d b/tests/dmd/fail_compilation/fail11375.d index 7592a5a1dfd..cabf87a6cae 100644 --- a/tests/dmd/fail_compilation/fail11375.d +++ b/tests/dmd/fail_compilation/fail11375.d @@ -1,8 +1,9 @@ /* TEST_OUTPUT: --- -fail_compilation/fail11375.d(17): Error: constructor `fail11375.D!().D.this` is not `nothrow` -fail_compilation/fail11375.d(15): Error: function `D main` may throw but is marked as `nothrow` +fail_compilation/fail11375.d(18): Error: constructor `fail11375.D!().D.this` is not `nothrow` + which calls `fail11375.B.this` +fail_compilation/fail11375.d(16): Error: function `D main` may throw but is marked as `nothrow` --- */ diff --git a/tests/dmd/fail_compilation/fail12236.d b/tests/dmd/fail_compilation/fail12236.d index 738864cca35..824f5e48db2 100644 --- a/tests/dmd/fail_compilation/fail12236.d +++ b/tests/dmd/fail_compilation/fail12236.d @@ -7,7 +7,7 @@ fail_compilation/fail12236.d(21): Error: forward reference to inferred return ty fail_compilation/fail12236.d(21): while evaluating `pragma(msg, f2(T)(T).mangleof)` fail_compilation/fail12236.d(27): Error: template instance `fail12236.f2!int` error instantiating fail_compilation/fail12236.d(31): Error: forward reference to inferred return type of function `__lambda1` -fail_compilation/fail12236.d(31): while evaluating `pragma(msg, __lambda1.mangleof)` +fail_compilation/fail12236.d(31): while evaluating `pragma(msg, __lambda1(__T1)(a).mangleof)` --- */ diff --git a/tests/dmd/fail_compilation/fail13120.d b/tests/dmd/fail_compilation/fail13120.d index f1cf340b6a0..6a1335eebd5 100644 --- a/tests/dmd/fail_compilation/fail13120.d +++ b/tests/dmd/fail_compilation/fail13120.d @@ -17,13 +17,13 @@ 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(35): Error: `@safe` function `fail13120.h2` cannot call `@system` function `fail13120.g2!().g2` fail_compilation/fail13120.d(27): `fail13120.g2!().g2` is declared here fail_compilation/fail13120.d(35): Error: `@nogc` function `fail13120.h2` cannot call non-@nogc function `fail13120.g2!().g2` --- */ void f2() {} - void g2()(char[] s) { foreach (dchar dc; s) diff --git a/tests/dmd/fail_compilation/fail13577.d b/tests/dmd/fail_compilation/fail13577.d new file mode 100644 index 00000000000..79f9068c759 --- /dev/null +++ b/tests/dmd/fail_compilation/fail13577.d @@ -0,0 +1,28 @@ +// https://issues.dlang.org/show_bug.cgi?id=13577 + +/* +TEST_OUTPUT: +--- +fail_compilation/fail13577.d(27): Error: cannot implicilty convert range element of type `int[]` to variable `x` of type `immutable(int[])` +--- +*/ + +struct Tuple(Types...) +{ + Types items; + alias items this; +} + +struct Range(T) +{ + T[] arr; + alias ElemType = Tuple!(int, T); + ElemType front() { return typeof(return)(0, arr[0]); } + bool empty() { return false; } + void popFront() {} +} + +void main() +{ + foreach (immutable i, immutable x; Range!(int[])()) {} // Error +} diff --git a/tests/dmd/fail_compilation/fail16600.d b/tests/dmd/fail_compilation/fail16600.d index eb341c64af5..3bd600e507b 100644 --- a/tests/dmd/fail_compilation/fail16600.d +++ b/tests/dmd/fail_compilation/fail16600.d @@ -1,9 +1,9 @@ /* TEST_OUTPUT: --- fail_compilation/fail16600.d(22): Error: `fail16600.S.__ctor` called with argument types `(string) const` matches both: -fail_compilation/fail16600.d(16): `fail16600.S.this(string _param_0)` +fail_compilation/fail16600.d(16): `fail16600.S.this(string __param_0)` and: -fail_compilation/fail16600.d(17): `fail16600.S.this(string _param_0) immutable` +fail_compilation/fail16600.d(17): `fail16600.S.this(string __param_0) immutable` --- */ diff --git a/tests/dmd/fail_compilation/fail17518.d b/tests/dmd/fail_compilation/fail17518.d index 385483c048d..cf2648db40b 100644 --- a/tests/dmd/fail_compilation/fail17518.d +++ b/tests/dmd/fail_compilation/fail17518.d @@ -1,8 +1,8 @@ /* TEST_OUTPUT: --- -fail_compilation/fail17518.d(21): Error: constructor `fail17518.S.this(inout(Correct) _param_0) inout` is not callable using argument types `(Wrong)` -fail_compilation/fail17518.d(21): cannot pass argument `Wrong()` of type `Wrong` to parameter `inout(Correct) _param_0` +fail_compilation/fail17518.d(21): Error: constructor `fail17518.S.this(inout(Correct) __param_0) inout` is not callable using argument types `(Wrong)` +fail_compilation/fail17518.d(21): cannot pass argument `Wrong()` of type `Wrong` to parameter `inout(Correct) __param_0` --- */ diff --git a/tests/dmd/fail_compilation/fail17955.d b/tests/dmd/fail_compilation/fail17955.d index 95eb5cc8c1f..08329194228 100644 --- a/tests/dmd/fail_compilation/fail17955.d +++ b/tests/dmd/fail_compilation/fail17955.d @@ -11,7 +11,7 @@ fail_compilation/fail17955.d(49): instantiated from here: `toRedis!(SysTi fail_compilation/fail17955.d(40): ... (2 instantiations, -v to show) ... fail_compilation/fail17955.d(32): instantiated from here: `indicesOf!(isRedisType, resetCodeExpireTime)` fail_compilation/fail17955.d(67): instantiated from here: `RedisStripped!(User, true)` -fail_compilation/fail17955.d(93): Error: need `this` for `fromISOExtString` of type `pure nothrow @nogc @safe immutable(SimpleTimeZone)(dstring _param_0)` +fail_compilation/fail17955.d(93): Error: need `this` for `fromISOExtString` of type `pure nothrow @nogc @safe immutable(SimpleTimeZone)(dstring __param_0)` fail_compilation/fail17955.d(95): Error: undefined identifier `DateTimeException` fail_compilation/fail17955.d(25): Error: variable `fail17955.isISOExtStringSerializable!(SysTime).isISOExtStringSerializable` - type `void` is inferred from initializer `fromISOExtString("")`, and variables cannot be of type `void` fail_compilation/fail17955.d(54): Error: function `fail17955.toRedis!(SysTime).toRedis` has no `return` statement, but is expected to return a value of type `string` diff --git a/tests/dmd/fail_compilation/fail196.d b/tests/dmd/fail_compilation/fail196.d index 2c7d93fe4e0..53505f496c2 100644 --- a/tests/dmd/fail_compilation/fail196.d +++ b/tests/dmd/fail_compilation/fail196.d @@ -6,15 +6,15 @@ fail_compilation/fail196.d(27): Error: implicit string concatenation is error-pr fail_compilation/fail196.d(27): Use the explicit syntax instead (concatenating literals is `@nogc`): "foo(xxx)" ~ ";\n assert(s == " fail_compilation/fail196.d(28): Error: semicolon needed to end declaration of `s`, instead of `foo` fail_compilation/fail196.d(27): `s` declared here -fail_compilation/fail196.d(28): Error: found `");\n\n s = q"` when expecting `;` following statement -fail_compilation/fail196.d(30): Error: found `";\n assert(s == "` when expecting `;` following statement -fail_compilation/fail196.d(31): Error: found `");\n\n s = q"` when expecting `;` following statement -fail_compilation/fail196.d(33): Error: found `{` when expecting `;` following statement -fail_compilation/fail196.d(33): Error: found `}` when expecting `;` following statement -fail_compilation/fail196.d(34): Error: found `foo` when expecting `;` following statement -fail_compilation/fail196.d(34): Error: found `}` when expecting `;` following statement -fail_compilation/fail196.d(36): Error: found `<` when expecting `;` following statement -fail_compilation/fail196.d(37): Error: found `foo` when expecting `;` following statement +fail_compilation/fail196.d(28): Error: found `");\n\n s = q"` when expecting `;` following statement `foo(xxx)` on line fail_compilation/fail196.d(28) +fail_compilation/fail196.d(30): Error: found `";\n assert(s == "` when expecting `;` following statement `[foo[xxx]]` on line fail_compilation/fail196.d(30) +fail_compilation/fail196.d(31): Error: found `");\n\n s = q"` when expecting `;` following statement `foo[xxx]` on line fail_compilation/fail196.d(31) +fail_compilation/fail196.d(33): Error: found `{` when expecting `;` following statement `foo` on line fail_compilation/fail196.d(33) +fail_compilation/fail196.d(33): Error: found `}` when expecting `;` following statement `xxx` on line fail_compilation/fail196.d(33) +fail_compilation/fail196.d(34): Error: found `foo` when expecting `;` following statement `";\n assert(s == "` on line fail_compilation/fail196.d(33) +fail_compilation/fail196.d(34): Error: found `}` when expecting `;` following statement `xxx` on line fail_compilation/fail196.d(34) +fail_compilation/fail196.d(36): Error: found `<` when expecting `;` following statement `");\n\n s = q" < foo` on line fail_compilation/fail196.d(34) +fail_compilation/fail196.d(37): Error: found `foo` when expecting `;` following statement `xxx >> ";\n assert(s == "` on line fail_compilation/fail196.d(36) fail_compilation/fail196.d(37): Error: found `<` instead of statement fail_compilation/fail196.d(43): Error: unterminated string constant starting at fail_compilation/fail196.d(43) fail_compilation/fail196.d(45): Error: found `End of File` when expecting `}` following compound statement diff --git a/tests/dmd/fail_compilation/fail19948.d b/tests/dmd/fail_compilation/fail19948.d index e8a9e777904..ae67443fea4 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: --- diff --git a/tests/dmd/fail_compilation/fail20609.d b/tests/dmd/fail_compilation/fail20609.d index 05b7c85375a..80a5d461574 100644 --- a/tests/dmd/fail_compilation/fail20609.d +++ b/tests/dmd/fail_compilation/fail20609.d @@ -4,7 +4,7 @@ fail_compilation/fail20609.d(26): Error: none of the overloads of `this` are callable using argument types `(int)` fail_compilation/fail20609.d(23): Candidate is: `fail20609.Foo.this(string[] args)` fail_compilation/fail20609.d(27): Error: none of the overloads of `this` are callable using argument types `(int)` -fail_compilation/fail20609.d(22): Candidates are: `fail20609.Foo.this(Object _param_0)` +fail_compilation/fail20609.d(22): Candidates are: `fail20609.Foo.this(Object __param_0)` fail_compilation/fail20609.d(23): `fail20609.Foo.this(string[] args)` fail_compilation/fail20609.d(37): Error: none of the overloads of `this` are callable using argument types `(int)` fail_compilation/fail20609.d(37): All possible candidates are marked as `deprecated` or `@disable` diff --git a/tests/dmd/fail_compilation/fail22202.d b/tests/dmd/fail_compilation/fail22202.d index 167d3624879..d865fd95553 100644 --- a/tests/dmd/fail_compilation/fail22202.d +++ b/tests/dmd/fail_compilation/fail22202.d @@ -3,7 +3,7 @@ /* TEST_OUTPUT: --- -fail_compilation/fail22202.d(21): Error: function `fail22202.fun(SystemCopy _param_0)` is not callable using argument types `(SystemCopy)` +fail_compilation/fail22202.d(21): Error: function `fail22202.fun(SystemCopy __param_0)` is not callable using argument types `(SystemCopy)` fail_compilation/fail22202.d(21): `inout ref inout(SystemCopy)(ref inout(SystemCopy) other)` copy constructor cannot be called from a `pure @safe nogc` context --- */ diff --git a/tests/dmd/fail_compilation/fail22729.d b/tests/dmd/fail_compilation/fail22729.d new file mode 100644 index 00000000000..38bbfeeed2b --- /dev/null +++ b/tests/dmd/fail_compilation/fail22729.d @@ -0,0 +1,39 @@ +// https://issues.dlang.org/show_bug.cgi?id=22729 + +/* +TEST_OUTPUT: +--- +fail_compilation/fail22729.d(12): Error: field `getChildAtPosition` not allowed in interface +--- +*/ + +interface ContainerFunctionSetI +{ + Tuple!(WidgetI) getChildAtPosition; +} + +interface WidgetI : ContainerFunctionSetI +{ +} + +class Form : WidgetI +{ +} + +template Tuple(Specs) +{ + enum areCompatibleTuples(Tup2)(Tuple tup1, Tup2 tup2) + { + tup1.field == tup2; + } + + struct Tuple + { + Specs field; + + bool opEquals(R)(R) if (areCompatibleTuples!R) + { + } + + } +} diff --git a/tests/dmd/fail_compilation/fail23773.d b/tests/dmd/fail_compilation/fail23773.d new file mode 100644 index 00000000000..e6cdc3e0354 --- /dev/null +++ b/tests/dmd/fail_compilation/fail23773.d @@ -0,0 +1,15 @@ +// https://issues.dlang.org/show_bug.cgi?id=23773 + +/* +TEST_OUTPUT: +--- +fail_compilation/fail23773.d(14): Error: assignment cannot be used as a condition, perhaps `==` was meant? +--- +*/ + +void main() +{ + int i; + int[] arr; + assert(arr.length = i); +} diff --git a/tests/dmd/fail_compilation/fail23822.d b/tests/dmd/fail_compilation/fail23822.d new file mode 100644 index 00000000000..5cdd1fe0503 --- /dev/null +++ b/tests/dmd/fail_compilation/fail23822.d @@ -0,0 +1,22 @@ +// https://issues.dlang.org/show_bug.cgi?id=23822 + +// REQUIRED_ARGS: -de + +/* +TEST_OUTPUT: +--- +fail_compilation/fail23822.d(21): Deprecation: alias `fail23822.S.value` is deprecated +--- +*/ + +alias Alias(alias A) = A; + +struct S +{ + deprecated alias value = Alias!5; +} + +void main() +{ + auto a = S.value; +} diff --git a/tests/dmd/fail_compilation/fail23826.d b/tests/dmd/fail_compilation/fail23826.d new file mode 100644 index 00000000000..3db243a3ce8 --- /dev/null +++ b/tests/dmd/fail_compilation/fail23826.d @@ -0,0 +1,24 @@ +// https://issues.dlang.org/show_bug.cgi?id=23826 + +// REQUIRED_ARGS: -de + +/* +TEST_OUTPUT: +--- +fail_compilation/fail23826.d(23): Deprecation: alias `fail23826.S.value` is deprecated +--- +*/ + +alias Alias(alias A) = A; + +class S +{ + deprecated alias value = Alias!5; +} + +enum identity(alias A) = A; + +void main() +{ + auto a = identity!(S.value); +} diff --git a/tests/dmd/fail_compilation/fail23861.d b/tests/dmd/fail_compilation/fail23861.d new file mode 100644 index 00000000000..23c540799e6 --- /dev/null +++ b/tests/dmd/fail_compilation/fail23861.d @@ -0,0 +1,25 @@ +// https://issues.dlang.org/show_bug.cgi?id=23861 + +/* +TEST_OUTPUT: +--- +fail_compilation/fail23861.d(24): Error: cannot implicitly convert expression `3` of type `int` to `Foo` +--- +*/ + +Foo global; + +struct Foo +{ + ref Foo get() + { + return global; + } + alias get this; +} + +void main() +{ + Foo g; + g = 3; +} diff --git a/tests/dmd/fail_compilation/fail332.d b/tests/dmd/fail_compilation/fail332.d index 91f80464705..77e8cd8eb00 100644 --- a/tests/dmd/fail_compilation/fail332.d +++ b/tests/dmd/fail_compilation/fail332.d @@ -1,14 +1,14 @@ /* TEST_OUTPUT: --- -fail_compilation/fail332.d(22): Error: function `fail332.foo(int _param_0, ...)` is not callable using argument types `()` -fail_compilation/fail332.d(22): missing argument for parameter #1: `int _param_0` -fail_compilation/fail332.d(23): Error: function `fail332.foo(int _param_0, ...)` is not callable using argument types `(typeof(null))` -fail_compilation/fail332.d(23): cannot pass argument `null` of type `typeof(null)` to parameter `int _param_0` -fail_compilation/fail332.d(25): Error: function `fail332.baz(int[] _param_0...)` is not callable using argument types `(string)` -fail_compilation/fail332.d(25): cannot pass argument `""` of type `string` to parameter `int[] _param_0...` -fail_compilation/fail332.d(26): Error: function `fail332.baz(int[] _param_0...)` is not callable using argument types `(int, typeof(null))` -fail_compilation/fail332.d(26): cannot pass argument `null` of type `typeof(null)` to parameter `int[] _param_0...` +fail_compilation/fail332.d(22): Error: function `fail332.foo(int __param_0, ...)` is not callable using argument types `()` +fail_compilation/fail332.d(22): missing argument for parameter #1: `int __param_0` +fail_compilation/fail332.d(23): Error: function `fail332.foo(int __param_0, ...)` is not callable using argument types `(typeof(null))` +fail_compilation/fail332.d(23): cannot pass argument `null` of type `typeof(null)` to parameter `int __param_0` +fail_compilation/fail332.d(25): Error: function `fail332.baz(int[] __param_0...)` is not callable using argument types `(string)` +fail_compilation/fail332.d(25): cannot pass argument `""` of type `string` to parameter `int[] __param_0...` +fail_compilation/fail332.d(26): Error: function `fail332.baz(int[] __param_0...)` is not callable using argument types `(int, typeof(null))` +fail_compilation/fail332.d(26): cannot pass argument `null` of type `typeof(null)` to parameter `int[] __param_0...` --- */ diff --git a/tests/dmd/fail_compilation/fail4375q.d b/tests/dmd/fail_compilation/fail4375q.d index b02fbb169b7..f57e746b6ed 100644 --- a/tests/dmd/fail_compilation/fail4375q.d +++ b/tests/dmd/fail_compilation/fail4375q.d @@ -4,7 +4,7 @@ TEST_OUTPUT: --- fail_compilation/fail4375q.d(17): Warning: else is dangling, add { } after condition at fail_compilation/fail4375q.d(13) -fail_compilation/fail4375q.d(14): Error: `with` expressions must be aggregate types or pointers to them, not `int` +fail_compilation/fail4375q.d(14): Error: `with` expression types must be enums or aggregates or pointers to them, not `int` --- */ diff --git a/tests/dmd/fail_compilation/fail4559.d b/tests/dmd/fail_compilation/fail4559.d index 0101ae98bf1..657c184d787 100644 --- a/tests/dmd/fail_compilation/fail4559.d +++ b/tests/dmd/fail_compilation/fail4559.d @@ -1,10 +1,10 @@ /* -REQUIRED_ARGS: -o- -de +REQUIRED_ARGS: TEST_OUTPUT: --- -fail_compilation/fail4559.d(13): Deprecation: use `{ }` for an empty statement, not `;` -fail_compilation/fail4559.d(19): Deprecation: use `{ }` for an empty statement, not `;` -fail_compilation/fail4559.d(21): Deprecation: use `{ }` for an empty statement, not `;` +fail_compilation/fail4559.d(13): Error: use `{ }` for an empty statement, not `;` +fail_compilation/fail4559.d(19): Error: use `{ }` for an empty statement, not `;` +fail_compilation/fail4559.d(21): Error: use `{ }` for an empty statement, not `;` --- */ diff --git a/tests/dmd/fail_compilation/fail_typeof.d b/tests/dmd/fail_compilation/fail_typeof.d index 392cebd2f73..a3b4d1a1415 100644 --- a/tests/dmd/fail_compilation/fail_typeof.d +++ b/tests/dmd/fail_compilation/fail_typeof.d @@ -1,17 +1,14 @@ /* TEST_OUTPUT: --- -fail_compilation/fail_typeof.d(18): Error: undefined identifier `this` -fail_compilation/fail_typeof.d(23): Error: `this` is not in a class or struct scope -fail_compilation/fail_typeof.d(23): Error: `this` is only defined in non-static member functions, not `fail_typeof` -fail_compilation/fail_typeof.d(28): Error: undefined identifier `super` -fail_compilation/fail_typeof.d(33): Error: `super` is not in a class scope -fail_compilation/fail_typeof.d(33): Error: `super` is only allowed in non-static class member functions -fail_compilation/fail_typeof.d(40): Error: undefined identifier `this`, did you mean `typeof(this)`? -fail_compilation/fail_typeof.d(50): Error: undefined identifier `super` -fail_compilation/fail_typeof.d(55): Error: `super` is not in a class scope -fail_compilation/fail_typeof.d(55): Error: `super` is only allowed in non-static class member functions -fail_compilation/fail_typeof.d(63): Error: undefined identifier `this`, did you mean `typeof(this)`? -fail_compilation/fail_typeof.d(73): Error: undefined identifier `super`, did you mean `typeof(super)`? +fail_compilation/fail_typeof.d(15): Error: undefined identifier `this` +fail_compilation/fail_typeof.d(20): Error: `this` is not in a class or struct scope +fail_compilation/fail_typeof.d(25): Error: undefined identifier `super` +fail_compilation/fail_typeof.d(30): Error: `super` is not in a class scope +fail_compilation/fail_typeof.d(37): Error: undefined identifier `this`, did you mean `typeof(this)`? +fail_compilation/fail_typeof.d(47): Error: undefined identifier `super` +fail_compilation/fail_typeof.d(52): Error: `super` is not in a class scope +fail_compilation/fail_typeof.d(60): Error: undefined identifier `this`, did you mean `typeof(this)`? +fail_compilation/fail_typeof.d(70): Error: undefined identifier `super`, did you mean `typeof(super)`? --- */ diff --git a/tests/dmd/fail_compilation/failcstuff4.i b/tests/dmd/fail_compilation/failcstuff4b.i similarity index 62% rename from tests/dmd/fail_compilation/failcstuff4.i rename to tests/dmd/fail_compilation/failcstuff4b.i index df26647a5c4..66ee7578947 100644 --- a/tests/dmd/fail_compilation/failcstuff4.i +++ b/tests/dmd/fail_compilation/failcstuff4b.i @@ -1,6 +1,6 @@ /* TEST_OUTPUT: --- -fail_compilation/failcstuff4.i(605): Error: invalid flag for line marker directive +fail_compilation/failcstuff4b.i(605): Error: invalid flag for line marker directive --- */ diff --git a/tests/dmd/fail_compilation/failcstuff6.c b/tests/dmd/fail_compilation/failcstuff6.c index af486745444..88c541ca64f 100644 --- a/tests/dmd/fail_compilation/failcstuff6.c +++ b/tests/dmd/fail_compilation/failcstuff6.c @@ -2,11 +2,6 @@ /* TEST_OUTPUT: --- fail_compilation/failcstuff6.c(56): Error: enum member `failcstuff6.test_overflow.boom` initialization with `2147483647+1` causes overflow for type `int` -fail_compilation/failcstuff6.c(105): Error: enum member `failcstuff6.test_enum_fits.firstMinError` enum member value `-2147483649L` does not fit in an `int` -fail_compilation/failcstuff6.c(106): Error: enum member `failcstuff6.test_enum_fits.firstMaxError` enum member value `4294967296L` does not fit in an `int` -fail_compilation/failcstuff6.c(107): Error: enum member `failcstuff6.test_enum_fits.lastMaxError` enum member value `18446744071562067967LU` does not fit in an `int` -fail_compilation/failcstuff6.c(108): Error: enum member `failcstuff6.test_enum_fits.firstBlindSpot` enum member value `18446744071562067968LU` does not fit in an `int` -fail_compilation/failcstuff6.c(109): Error: enum member `failcstuff6.test_enum_fits.lastBlindSpot` enum member value `18446744073709551615LU` does not fit in an `int` --- */ diff --git a/tests/dmd/fail_compilation/gccasm1.c b/tests/dmd/fail_compilation/gccasm1.c new file mode 100644 index 00000000000..3ba12bba057 --- /dev/null +++ b/tests/dmd/fail_compilation/gccasm1.c @@ -0,0 +1,18 @@ +/* TEST_OUTPUT: +--- +fail_compilation/gccasm1.c(12): Error: string literal expected for Assembler Template, not `%` +--- + */ + +#define __fldcw(addr) asm volatile(%0 : : "m" (*(addr))) + +static __inline void +__fnldcw(unsigned short _cw, unsigned short _newcw) +{ + __fldcw(&_newcw); +} + +void main() +{ + __fnldcw(1, 2); +} diff --git a/tests/dmd/fail_compilation/ice10651.d b/tests/dmd/fail_compilation/ice10651.d index 1f87955b959..8b6c7200bb7 100644 --- a/tests/dmd/fail_compilation/ice10651.d +++ b/tests/dmd/fail_compilation/ice10651.d @@ -1,7 +1,9 @@ /* TEST_OUTPUT: --- -fail_compilation/ice10651.d(11): Error: can only throw class objects derived from `Throwable`, not type `int*` +fail_compilation/ice10651.d(13): Error: can only throw class objects derived from `Throwable`, not type `int*` +fail_compilation/ice10651.d(19): Deprecation: cannot throw object of qualified type `immutable(Exception)` +fail_compilation/ice10651.d(20): Deprecation: cannot throw object of qualified type `const(Dummy)` --- */ @@ -10,3 +12,20 @@ void main() alias T = int; throw new T(); // ICE } + +void f() +{ + immutable c = new Exception(""); + if (c) throw c; + throw new const Dummy([]); +} + +class Dummy: Exception +{ + int[] data; + @safe pure nothrow this(immutable int[] data) immutable + { + super("Dummy"); + this.data = data; + } +} diff --git a/tests/dmd/fail_compilation/ice11626.d b/tests/dmd/fail_compilation/ice11626.d index 5dc5d5c1e66..6d347bcdd0e 100644 --- a/tests/dmd/fail_compilation/ice11626.d +++ b/tests/dmd/fail_compilation/ice11626.d @@ -5,4 +5,4 @@ fail_compilation/ice11626.d(8): Error: undefined identifier `Bar` --- */ -void foo(in ref Bar) {} +void foo(const ref Bar) {} diff --git a/tests/dmd/fail_compilation/ice11982.d b/tests/dmd/fail_compilation/ice11982.d index ff5fae491c6..0f2ce413c40 100644 --- a/tests/dmd/fail_compilation/ice11982.d +++ b/tests/dmd/fail_compilation/ice11982.d @@ -1,16 +1,19 @@ /* TEST_OUTPUT: --- -fail_compilation/ice11982.d(16): Error: basic type expected, not `scope` -fail_compilation/ice11982.d(16): Error: found `scope` when expecting `;` following statement -fail_compilation/ice11982.d(16): Error: basic type expected, not `}` -fail_compilation/ice11982.d(16): Error: missing `{ ... }` for function literal -fail_compilation/ice11982.d(16): Error: C style cast illegal, use `cast(funk)function _error_() +fail_compilation/ice11982.d(19): Error: basic type expected, not `scope` +fail_compilation/ice11982.d(19): Error: found `scope` when expecting `;` following statement `new _error_` on line fail_compilation/ice11982.d(19) +fail_compilation/ice11982.d(19): Error: basic type expected, not `}` +fail_compilation/ice11982.d(19): Error: missing `{ ... }` for function literal +fail_compilation/ice11982.d(19): Error: C style cast illegal, use `cast(funk)function _error_() { } ` -fail_compilation/ice11982.d(16): Error: found `}` when expecting `;` following statement -fail_compilation/ice11982.d(17): Error: found `End of File` when expecting `}` following compound statement +fail_compilation/ice11982.d(19): Error: found `}` when expecting `;` following statement `cast(funk)function _error_() +{ +} +` on line fail_compilation/ice11982.d(19) +fail_compilation/ice11982.d(20): Error: found `End of File` when expecting `}` following compound statement --- */ void main() { new scope ( funk ) function } diff --git a/tests/dmd/fail_compilation/ice13225.d b/tests/dmd/fail_compilation/ice13225.d index 6988cd7c18d..abc30eaf4f2 100644 --- a/tests/dmd/fail_compilation/ice13225.d +++ b/tests/dmd/fail_compilation/ice13225.d @@ -1,7 +1,7 @@ /* TEST_OUTPUT: --- -fail_compilation/ice13225.d(12): Error: mixin `ice13225.S.M!(function (S _param_0) pure nothrow @nogc @safe => 0)` does not match template declaration `M(T)` +fail_compilation/ice13225.d(12): Error: mixin `ice13225.S.M!(function (S __param_0) pure nothrow @nogc @safe => 0)` does not match template declaration `M(T)` fail_compilation/ice13225.d(16): Error: undefined identifier `undefined` --- */ diff --git a/tests/dmd/fail_compilation/ice23097.d b/tests/dmd/fail_compilation/ice23097.d index 4fd1f61f828..90cf03f1b48 100644 --- a/tests/dmd/fail_compilation/ice23097.d +++ b/tests/dmd/fail_compilation/ice23097.d @@ -3,7 +3,7 @@ TEST_OUTPUT: --- fail_compilation/ice23097.d(12): Error: undefined identifier `ICE` fail_compilation/ice23097.d(27): Error: template instance `ice23097.ice23097!(S23097)` error instantiating -fail_compilation/ice23097.d(27): Error: function `ice23097.ice23097!(S23097).ice23097(S23097 _param_0)` is not callable using argument types `(S23097)` +fail_compilation/ice23097.d(27): Error: function `ice23097.ice23097!(S23097).ice23097(S23097 __param_0)` is not callable using argument types `(S23097)` fail_compilation/ice23097.d(27): generating a copy constructor for `struct S23097` failed, therefore instances of it are uncopyable --- */ diff --git a/tests/dmd/fail_compilation/ice9540.d b/tests/dmd/fail_compilation/ice9540.d index 5276e83c722..456d3e12f23 100644 --- a/tests/dmd/fail_compilation/ice9540.d +++ b/tests/dmd/fail_compilation/ice9540.d @@ -1,7 +1,7 @@ /* TEST_OUTPUT: --- -fail_compilation/ice9540.d(35): Error: function `ice9540.A.test.AddFront!(this, f).AddFront.dg(int _param_0)` is not callable using argument types `()` +fail_compilation/ice9540.d(35): Error: function `ice9540.A.test.AddFront!(this, f).AddFront.dg(int __param_0)` is not callable using argument types `()` fail_compilation/ice9540.d(35): too few arguments, expected 1, got 0 fail_compilation/ice9540.d(26): Error: template instance `ice9540.A.test.AddFront!(this, f)` error instantiating --- diff --git a/tests/dmd/fail_compilation/imports/import23873.d b/tests/dmd/fail_compilation/imports/import23873.d new file mode 100644 index 00000000000..39334cf62ef --- /dev/null +++ b/tests/dmd/fail_compilation/imports/import23873.d @@ -0,0 +1,2 @@ +static if ; +else auto x diff --git a/tests/dmd/fail_compilation/misc_parser_err_cov1.d b/tests/dmd/fail_compilation/misc_parser_err_cov1.d index 11fddf069d6..d1361440920 100644 --- a/tests/dmd/fail_compilation/misc_parser_err_cov1.d +++ b/tests/dmd/fail_compilation/misc_parser_err_cov1.d @@ -23,7 +23,7 @@ fail_compilation/misc_parser_err_cov1.d(40): Error: semicolon expected following fail_compilation/misc_parser_err_cov1.d(40): Error: identifier or `new` expected following `.`, not `+` 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 statement +fail_compilation/misc_parser_err_cov1.d(42): Error: found `}` when expecting `;` following statement `(__error) + 0` on line fail_compilation/misc_parser_err_cov1.d(41) fail_compilation/misc_parser_err_cov1.d(43): Error: found `End of File` when expecting `}` following compound statement --- */ diff --git a/tests/dmd/fail_compilation/named_arguments_parse.d b/tests/dmd/fail_compilation/named_arguments_parse.d index 19e230ee519..096c499790e 100644 --- a/tests/dmd/fail_compilation/named_arguments_parse.d +++ b/tests/dmd/fail_compilation/named_arguments_parse.d @@ -1,13 +1,13 @@ /** TEST_OUTPUT: --- -fail_compilation/named_arguments_parse.d(10): Error: named arguments not allowed here fail_compilation/named_arguments_parse.d(13): Error: named arguments not allowed here fail_compilation/named_arguments_parse.d(14): Error: named arguments not allowed here --- */ -@(attribute: 3) + +// @(attribute: 3) Currently gives an ugly parse error, will be better when named template arguments are implemented void main() { mixin(thecode: "{}"); diff --git a/tests/dmd/fail_compilation/parseStc.d b/tests/dmd/fail_compilation/parseStc.d index c9c42882438..d13006db0ab 100644 --- a/tests/dmd/fail_compilation/parseStc.d +++ b/tests/dmd/fail_compilation/parseStc.d @@ -3,7 +3,7 @@ TEST_OUTPUT: --- fail_compilation/parseStc.d(12): Error: missing closing `)` after `if (x` fail_compilation/parseStc.d(12): Error: use `{ }` for an empty statement, not `;` -fail_compilation/parseStc.d(12): Error: found `)` when expecting `;` following statement +fail_compilation/parseStc.d(12): Error: found `)` when expecting `;` following statement `1` on line fail_compilation/parseStc.d(12) fail_compilation/parseStc.d(13): Error: redundant attribute `const` --- */ diff --git a/tests/dmd/fail_compilation/previewin.d b/tests/dmd/fail_compilation/previewin.d index ca540930129..d0e97c8bcd3 100644 --- a/tests/dmd/fail_compilation/previewin.d +++ b/tests/dmd/fail_compilation/previewin.d @@ -4,10 +4,10 @@ TEST_OUTPUT: --- fail_compilation/previewin.d(4): Error: function `previewin.takeFunction(void function(in real) f)` is not callable using argument types `(void function(real x) pure nothrow @nogc @safe)` fail_compilation/previewin.d(4): cannot pass argument `__lambda1` of type `void function(real x) pure nothrow @nogc @safe` to parameter `void function(in real) f` -fail_compilation/previewin.d(5): Error: function `previewin.takeFunction(void function(in real) f)` is not callable using argument types `(void function(const(real) x) pure nothrow @nogc @safe)` -fail_compilation/previewin.d(5): cannot pass argument `__lambda2` of type `void function(const(real) x) pure nothrow @nogc @safe` to parameter `void function(in real) f` -fail_compilation/previewin.d(6): Error: function `previewin.takeFunction(void function(in real) f)` is not callable using argument types `(void function(ref const(real) x) pure nothrow @nogc @safe)` -fail_compilation/previewin.d(6): cannot pass argument `__lambda3` of type `void function(ref const(real) x) pure nothrow @nogc @safe` to parameter `void function(in real) f` +fail_compilation/previewin.d(5): Error: function `previewin.takeFunction(void function(in real) f)` is not callable using argument types `(void function(scope const(real) x) pure nothrow @nogc @safe)` +fail_compilation/previewin.d(5): cannot pass argument `__lambda2` of type `void function(scope const(real) x) pure nothrow @nogc @safe` to parameter `void function(in real) f` +fail_compilation/previewin.d(6): Error: function `previewin.takeFunction(void function(in real) f)` is not callable using argument types `(void function(ref scope const(real) x) pure nothrow @nogc @safe)` +fail_compilation/previewin.d(6): cannot pass argument `__lambda3` of type `void function(ref scope const(real) x) pure nothrow @nogc @safe` to parameter `void function(in real) f` fail_compilation/previewin.d(15): Error: scope variable `arg` assigned to global variable `myGlobal` fail_compilation/previewin.d(16): Error: scope variable `arg` assigned to global variable `myGlobal` fail_compilation/previewin.d(17): Error: scope parameter `arg` may not be returned diff --git a/tests/dmd/fail_compilation/retscope2.d b/tests/dmd/fail_compilation/retscope2.d index 829fb6a1970..2e7940f70fe 100644 --- a/tests/dmd/fail_compilation/retscope2.d +++ b/tests/dmd/fail_compilation/retscope2.d @@ -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: 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(614): Error: template instance `retscope2.test600!(int*, int*)` error instantiating --- */ diff --git a/tests/dmd/fail_compilation/retscope6.d b/tests/dmd/fail_compilation/retscope6.d index 5c581d1db71..ddeae81bc23 100644 --- a/tests/dmd/fail_compilation/retscope6.d +++ b/tests/dmd/fail_compilation/retscope6.d @@ -25,7 +25,7 @@ int* test() @safe --- 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(7025): Error: scope variable `__param_2` assigned to `ref` variable `t` with longer lifetime fail_compilation/retscope6.d(7037): Error: template instance `retscope6.S.emplace4!(int*)` error instantiating fail_compilation/retscope6.d(7037): Error: address of variable `i` assigned to `s` with longer lifetime --- diff --git a/tests/dmd/fail_compilation/systemvariables_deprecation.d b/tests/dmd/fail_compilation/systemvariables_deprecation.d index 75dbe2dc1a0..b5115351efe 100644 --- a/tests/dmd/fail_compilation/systemvariables_deprecation.d +++ b/tests/dmd/fail_compilation/systemvariables_deprecation.d @@ -4,7 +4,7 @@ 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 would be `@system` because of: +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 --- */ diff --git a/tests/dmd/fail_compilation/test21025.d b/tests/dmd/fail_compilation/test21025.d new file mode 100644 index 00000000000..40b3a96823d --- /dev/null +++ b/tests/dmd/fail_compilation/test21025.d @@ -0,0 +1,25 @@ +// https://issues.dlang.org/show_bug.cgi?id=21025 +// REQUIRED_ARGS: -preview=dip1021 + +/* +TEST_OUTPUT: +--- +fail_compilation/test21025.d(15): Error: variable `r` cannot be read at compile time +fail_compilation/test21025.d(15): called from here: `binaryFun(r, r)` +fail_compilation/test21025.d(24): Error: none of the overloads of template `test21025.uniq` are callable using argument types `!()(void[])` +fail_compilation/test21025.d(14): Candidate is: `uniq()(int[] r)` +--- +*/ + +void uniq()(int[] r) +if (binaryFun(r, r)) {} + +bool binaryFun(T, U)(T, U) +{ + return true; +} + +void generateStatements() +{ + uniq([]); +} diff --git a/tests/dmd/fail_compilation/test23279.d b/tests/dmd/fail_compilation/test23279.d new file mode 100644 index 00000000000..43f2d44a071 --- /dev/null +++ b/tests/dmd/fail_compilation/test23279.d @@ -0,0 +1,14 @@ +// https://issues.dlang.org/show_bug.cgi?id=23279 + +/* +TEST_OUTPUT: +--- +fail_compilation/test23279.d(13): Error: undefined identifier `Sth` +--- +*/ + +class Tester +{ + enum a = __traits(hasMember, Tester, "setIt"); + void setIt(Sth sth){} +} diff --git a/tests/dmd/fail_compilation/test23715.i b/tests/dmd/fail_compilation/test23715.i new file mode 100644 index 00000000000..5a1a8047ea3 --- /dev/null +++ b/tests/dmd/fail_compilation/test23715.i @@ -0,0 +1,12 @@ +/* TEST_OUTPUT: +--- +fail_compilation/test23715.i(11): Error: `_Thread_local` in block scope must be accompanied with `static` or `extern` +--- +*/ + +// https://issues.dlang.org/show_bug.cgi?id=23715 + +void test2() +{ + _Thread_local int tli; +} diff --git a/tests/dmd/fail_compilation/test23789.c b/tests/dmd/fail_compilation/test23789.c new file mode 100644 index 00000000000..44edc0d98ba --- /dev/null +++ b/tests/dmd/fail_compilation/test23789.c @@ -0,0 +1,14 @@ +/* TEST_OUTPUT: +--- +fail_compilation/test23789.c(101): Error: __decspec(align(3)) must be an integer positive power of 2 and be <= 8,192 +fail_compilation/test23789.c(103): Error: alignment value expected, not `"a"` +--- + */ + +// https://issues.dlang.org/show_bug.cgi?id=23789 + +#line 100 + +struct __declspec(align(3)) S { int a; }; + +struct __declspec(align("a")) T { int a; }; diff --git a/tests/dmd/fail_compilation/test23873.d b/tests/dmd/fail_compilation/test23873.d new file mode 100644 index 00000000000..bb6a71dcc24 --- /dev/null +++ b/tests/dmd/fail_compilation/test23873.d @@ -0,0 +1,14 @@ +// https://issues.dlang.org/show_bug.cgi?id=23873 + +/* +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` +--- +*/ +struct Foo +{ + import imports.import23873; +} diff --git a/tests/dmd/fail_compilation/test23882.d b/tests/dmd/fail_compilation/test23882.d new file mode 100644 index 00000000000..f6b57c4ea04 --- /dev/null +++ b/tests/dmd/fail_compilation/test23882.d @@ -0,0 +1,37 @@ +// https://issues.dlang.org/show_bug.cgi?id=23882 + +/* +TEST_OUTPUT: +--- +fail_compilation/test23882.d(26): Error: `typeof((*YC).S).init` is used as a type +--- +*/ + +struct G(H) +{ + Tuple!(R) S; +} + +struct BB(H) +{ + H* YC; + alias YC this; +} + +struct R +{ + BB!(G!float) CB; + alias CB this; + + this(typeof(CB.S).init); +} + +struct Tuple(Specs) +{ + Specs expand; + + this(Specs values) + { + expand = values; + } +} diff --git a/tests/dmd/fail_compilation/test23905.d b/tests/dmd/fail_compilation/test23905.d new file mode 100644 index 00000000000..5b30fa855e2 --- /dev/null +++ b/tests/dmd/fail_compilation/test23905.d @@ -0,0 +1,25 @@ +// https://issues.dlang.org/show_bug.cgi?id=23905 + +/* +TEST_OUTPUT: +--- +fail_compilation/test23905.d(24): Error: enum `test23905.Foo` is opaque and has no default initializer +--- +*/ + +struct SumType(T) +{ + T storage; + + bool opEquals(Rhs)(Rhs rhs) + if (is(typeof(Rhs.init))) + { + } + +} + +enum Foo; + +void main(){ + SumType!Foo data = Foo.init; +} diff --git a/tests/dmd/fail_compilation/test23982.d b/tests/dmd/fail_compilation/test23982.d new file mode 100644 index 00000000000..f8eee238ed8 --- /dev/null +++ b/tests/dmd/fail_compilation/test23982.d @@ -0,0 +1,36 @@ +/* +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(26): which is not `scope` because of `b = a` +--- +*/ +// https://issues.dlang.org/show_bug.cgi?id=23982 +// Issue 23982 - segfault when printing scope inference failure +@safe: + +struct B() +{ + this(int* a) + { + this.a = a; + } + int* a; +} + +class C() +{ + int* foo2(int* a) + { + auto b = B!()(a); + return b.a; + } +} + +void main() +{ + scope int* a; + C!() c; + c.foo2(a); +} diff --git a/tests/dmd/fail_compilation/testInference.d b/tests/dmd/fail_compilation/testInference.d index c0d5a05d05c..145fc9e8b9d 100644 --- a/tests/dmd/fail_compilation/testInference.d +++ b/tests/dmd/fail_compilation/testInference.d @@ -138,8 +138,13 @@ 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.__lambda2` +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` --- */ +#line 143 auto impure14049() { static int i = 1; return i; } void foo14049(T)(T val) @@ -170,8 +175,10 @@ 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` --- */ +#line 175 int g12422; void foo12422() { ++g12422; } void test12422() pure @@ -184,9 +191,15 @@ 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(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` --- */ + +#line 190 int g13729; void test13729a() pure @@ -229,8 +242,10 @@ 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` --- */ +#line 234 void test20047_impure_function() {} void test20047_pure_function() pure { diff --git a/tests/dmd/fail_compilation/testnothrow.c b/tests/dmd/fail_compilation/testnothrow.c new file mode 100644 index 00000000000..538e2c174bd --- /dev/null +++ b/tests/dmd/fail_compilation/testnothrow.c @@ -0,0 +1,31 @@ +/* TEST_OUTPUT: +--- +fail_compilation/testnothrow.c(105): Error: function `testnothrow.throwing` is not `nothrow` +fail_compilation/testnothrow.c(104): Error: function `testnothrow.mul` may throw but is marked as `nothrow` +fail_compilation/testnothrow.c(111): Error: function `testnothrow.throwing` is not `nothrow` +fail_compilation/testnothrow.c(110): Error: function `testnothrow.add` may throw but is marked as `nothrow` +--- +*/ + +// https://issues.dlang.org/show_bug?id=21938 + +#line 100 + +void throwing() { } + +__attribute__((nothrow)) int mul(int x) +{ + throwing(); + return x * x; +} + +__declspec(nothrow) int add(int x) +{ + throwing(); + return x + x; +} + +int doSquare(int x) +{ + return mul(x) + add(x); +} diff --git a/tests/dmd/fail_compilation/testrvaluecpctor.d b/tests/dmd/fail_compilation/testrvaluecpctor.d index 96511f511e4..50cebabfda4 100644 --- a/tests/dmd/fail_compilation/testrvaluecpctor.d +++ b/tests/dmd/fail_compilation/testrvaluecpctor.d @@ -6,7 +6,7 @@ TEST_OUTPUT: fail_compilation/testrvaluecpctor.d(16): Error: cannot define both an rvalue constructor and a copy constructor for `struct Foo` fail_compilation/testrvaluecpctor.d(24): Template instance `testrvaluecpctor.Foo!int.Foo.__ctor!(immutable(Foo!int), immutable(Foo!int))` creates an rvalue constructor for `struct Foo` fail_compilation/testrvaluecpctor.d(24): Error: none of the overloads of `__ctor` are callable using a `immutable` object -fail_compilation/testrvaluecpctor.d(18): Candidates are: `testrvaluecpctor.Foo!int.Foo.this(ref Foo!int rhs)` +fail_compilation/testrvaluecpctor.d(18): Candidates are: `testrvaluecpctor.Foo!int.Foo.this(ref scope Foo!int rhs)` fail_compilation/testrvaluecpctor.d(16): `__ctor(Rhs, this This)(scope Rhs rhs)` --- */ diff --git a/tests/dmd/fail_compilation/var_func_attr.d b/tests/dmd/fail_compilation/var_func_attr.d new file mode 100644 index 00000000000..8609d29b58c --- /dev/null +++ b/tests/dmd/fail_compilation/var_func_attr.d @@ -0,0 +1,35 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/var_func_attr.d(19): Error: cannot implicitly convert expression `__lambda8` of type `void function() nothrow @nogc @safe` to `void function() pure` +--- +*/ + +// Test the effect of function attributes on variables +// See: +// https://issues.dlang.org/show_bug.cgi?id=7432 +// https://github.com/dlang/dmd/pull/14199 +// Usually it's a no-op, but the attribute can apply to the function/delegate type of the variable +// The current behavior is weird, so this is a test of the current behavior, not necessarily the desired behavior + +// No-op +pure int x; + +// Applies to function type (existing code in dmd and Phobos relies on this) +pure void function() pf = () { + static int g; + g++; +}; + +// Function attributes currently don't apply to inferred types (somewhat surprisingly) +nothrow nf = () { + throw new Exception(""); +}; + +// Neither do they apply to indirections +alias F = void function(); + +pure F pf2 = () { + static int g; + g++; +}; diff --git a/tests/dmd/runnable/betterc.d b/tests/dmd/runnable/betterc.d index d84bf3e3a92..05f0b950bca 100644 --- a/tests/dmd/runnable/betterc.d +++ b/tests/dmd/runnable/betterc.d @@ -48,6 +48,7 @@ extern (C) void main() test18472(); testRuntimeLowerings(); test18457(); + test20737(); } /*******************************************/ @@ -205,3 +206,13 @@ void test18457() } assert(dtor == 1); } + +/**********************************************/ +// https://issues.dlang.org/show_bug.cgi?id=20737 +int tlsVar; + +int test20737() +{ + tlsVar = 123; + return 0; +} diff --git a/tests/dmd/runnable/class_destructors.d b/tests/dmd/runnable/class_destructors.d index b01c4d9e54e..ce126bfc85d 100644 --- a/tests/dmd/runnable/class_destructors.d +++ b/tests/dmd/runnable/class_destructors.d @@ -154,7 +154,13 @@ void testDeleteWithoutCpp() class ThrowingChildD : ChildD { - static immutable ex = new Exception("STOP"); + static Exception ex; + + static this() + { + ex = new Exception("STOP"); + } + ~this() { throw ex; diff --git a/tests/dmd/runnable/cstuff3.i b/tests/dmd/runnable/cstuff3.i index 04a8a95b54a..fd1beb1ae6c 100644 --- a/tests/dmd/runnable/cstuff3.i +++ b/tests/dmd/runnable/cstuff3.i @@ -1,3 +1,5 @@ +// REQUIRED_ARGS: -defaultlib= + # 1 "runnable/extra-files/cstuff3.c" # 1 "" # 1 "" diff --git a/tests/dmd/runnable/debug_info.d b/tests/dmd/runnable/debug_info.d index 60b9c5f90f4..470b408c75e 100644 --- a/tests/dmd/runnable/debug_info.d +++ b/tests/dmd/runnable/debug_info.d @@ -36,8 +36,8 @@ else extern (C) { MachHeader* _dyld_get_image_header(uint image_index); - const(section)* getsectbynamefromheader(in mach_header* mhp, in char* segname, in char* sectname); - const(section_64)* getsectbynamefromheader_64(in mach_header_64* mhp, in char* segname, in char* sectname); + const(section)* getsectbynamefromheader(scope const mach_header* mhp, scope const char* segname, scope const char* sectname); + const(section_64)* getsectbynamefromheader_64(scope const mach_header_64* mhp, scope const char* segname, scope const char* sectname); } const(Section)* getSectByNameFromHeader(MachHeader* mhp, in char* segname, in char* sectname) diff --git a/tests/dmd/runnable/eh2.d b/tests/dmd/runnable/eh2.d index dc285a5dfab..2b469d2f803 100644 --- a/tests/dmd/runnable/eh2.d +++ b/tests/dmd/runnable/eh2.d @@ -24,7 +24,7 @@ class Abc : Throwable { printf("foo 1\n"); x |= 4; - throw this; + throw cast() this; printf("foo 2\n"); x |= 8; } diff --git a/tests/dmd/runnable/extra-files/hello-profile-postscript.sh b/tests/dmd/runnable/extra-files/hello-profile-postscript.sh index 47c18c08955..cb986e0a4fe 100755 --- a/tests/dmd/runnable/extra-files/hello-profile-postscript.sh +++ b/tests/dmd/runnable/extra-files/hello-profile-postscript.sh @@ -3,7 +3,9 @@ source tools/common_funcs.sh # strip out Dmain since it's symbol differs between windows and non-windows -grep -v Dmain ${OUTPUT_BASE}.d.trace.def > ${OUTPUT_BASE}.d.trace.def2 +# strip out _d_arraycatnTX and _d_arraysetlengthT since they are part of the +# lowering of the array concatenation operator +grep -v 'Dmain\|_d_arraycatnTX\|_d_arraysetlengthT' ${OUTPUT_BASE}.d.trace.def > ${OUTPUT_BASE}.d.trace.def2 diff -up --strip-trailing-cr ${EXTRA_FILES}/${TEST_NAME}.d.trace.def ${OUTPUT_BASE}.d.trace.def2 diff --git a/tests/dmd/runnable/imports/imp23014.i b/tests/dmd/runnable/imports/imp23014.i new file mode 100644 index 00000000000..ea51a781765 --- /dev/null +++ b/tests/dmd/runnable/imports/imp23014.i @@ -0,0 +1 @@ +static _Thread_local int tmp; diff --git a/tests/dmd/runnable/imports/imp23402a.c b/tests/dmd/runnable/imports/imp23402a.c new file mode 100644 index 00000000000..53c5fdf1799 --- /dev/null +++ b/tests/dmd/runnable/imports/imp23402a.c @@ -0,0 +1 @@ +#include diff --git a/tests/dmd/runnable/imports/imp23402b.c b/tests/dmd/runnable/imports/imp23402b.c new file mode 100644 index 00000000000..53c5fdf1799 --- /dev/null +++ b/tests/dmd/runnable/imports/imp23402b.c @@ -0,0 +1 @@ +#include diff --git a/tests/dmd/runnable/imports/link11069z.d b/tests/dmd/runnable/imports/link11069z.d index 5987cb4be7b..02301b9d1d2 100644 --- a/tests/dmd/runnable/imports/link11069z.d +++ b/tests/dmd/runnable/imports/link11069z.d @@ -1,7 +1,7 @@ module imports.link11069z; struct Matrix(T, uint _M) { - int opCmp()(auto ref in Matrix b) const + int opCmp()(const auto ref Matrix b) const { return 0; } diff --git a/tests/dmd/runnable/imports/link13415a.d b/tests/dmd/runnable/imports/link13415a.d index de3bbe2ac9b..077671b01c4 100644 --- a/tests/dmd/runnable/imports/link13415a.d +++ b/tests/dmd/runnable/imports/link13415a.d @@ -7,7 +7,7 @@ struct S(alias func) } } -extern(C) int printf(in char*, ...); +extern(C) int printf(const char*, ...); void f(int i = 77) { diff --git a/tests/dmd/runnable/imports/mainx23837.c b/tests/dmd/runnable/imports/mainx23837.c new file mode 100644 index 00000000000..0c446ab6ad1 --- /dev/null +++ b/tests/dmd/runnable/imports/mainx23837.c @@ -0,0 +1,10 @@ +// https://issues.dlang.org/show_bug?id=23837 + +struct stbrp_context +{ + int width; + int height; + int align; + int init_mode; + int heuristic; +}; diff --git a/tests/dmd/runnable/initializer.c b/tests/dmd/runnable/initializer.c index a3a04020e8d..19261dc11d8 100644 --- a/tests/dmd/runnable/initializer.c +++ b/tests/dmd/runnable/initializer.c @@ -1,4 +1,4 @@ -// DISABLED: LDC // FIXME: needs preprocessor for __LINE__ +// DISABLED: LDC // FIXME: needs support for importC special cases /* Test initializers */ diff --git a/tests/dmd/runnable/mangle.d b/tests/dmd/runnable/mangle.d index 7599e0e03c3..6e8f2b28987 100644 --- a/tests/dmd/runnable/mangle.d +++ b/tests/dmd/runnable/mangle.d @@ -571,12 +571,6 @@ void test12231() /***************************************************/ -int test2a(scope int a) { return a; } - -static assert(test2a.mangleof == "_D6mangle6test2aFiZi"); - -/***************************************************/ - class CC { int* p; diff --git a/tests/dmd/runnable/objc_call.d b/tests/dmd/runnable/objc_call.d index 22428a96746..481ee75d841 100644 --- a/tests/dmd/runnable/objc_call.d +++ b/tests/dmd/runnable/objc_call.d @@ -13,12 +13,12 @@ extern class Class extern (Objective-C) extern class NSObject { - NSObject initWithUTF8String(in char* str) @selector("initWithUTF8String:"); + NSObject initWithUTF8String(scope const char* str) @selector("initWithUTF8String:"); void release() @selector("release"); } extern (C) void NSLog(NSObject, ...); -extern (C) Class objc_lookUpClass(in char* name); +extern (C) Class objc_lookUpClass(scope const char* name); void main() { diff --git a/tests/dmd/runnable/objc_objc_msgSend.d b/tests/dmd/runnable/objc_objc_msgSend.d index 587ef79efbe..ef8e5b09dbe 100644 --- a/tests/dmd/runnable/objc_objc_msgSend.d +++ b/tests/dmd/runnable/objc_objc_msgSend.d @@ -4,7 +4,7 @@ import core.attribute : selector; -extern (C) Class objc_lookUpClass(in char* name); +extern (C) Class objc_lookUpClass(scope const char* name); struct Struct { diff --git a/tests/dmd/runnable/objc_protocol_sections.d b/tests/dmd/runnable/objc_protocol_sections.d index 97a655935a4..1382bf2569f 100644 --- a/tests/dmd/runnable/objc_protocol_sections.d +++ b/tests/dmd/runnable/objc_protocol_sections.d @@ -32,8 +32,8 @@ struct objc_method_description } } -SEL sel_registerName(in char* str); -Protocol* objc_getProtocol(in char* name); +SEL sel_registerName(scope const char* str); +Protocol* objc_getProtocol(scope const char* name); objc_method_description protocol_getMethodDescription( Protocol* proto, SEL aSel, bool isRequiredMethod, bool isInstanceMethod ); diff --git a/tests/dmd/runnable/profilegc_stdout.d b/tests/dmd/runnable/profilegc_stdout.d new file mode 100644 index 00000000000..4cf94ed725f --- /dev/null +++ b/tests/dmd/runnable/profilegc_stdout.d @@ -0,0 +1,19 @@ +/* +REQUIRED_ARGS: -profile=gc +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 +--- +*/ + +import core.runtime; + +void main() +{ + // test that stdout output works + profilegc_setlogfilename(""); + + ubyte[] arr = new ubyte[64]; +} diff --git a/tests/dmd/runnable/test22070_2.c b/tests/dmd/runnable/test22070_2.c index 7263202c5a3..f26c70cc6d6 100644 --- a/tests/dmd/runnable/test22070_2.c +++ b/tests/dmd/runnable/test22070_2.c @@ -51,5 +51,23 @@ int main() return 1; if (test4() != '5') return 1; - return 0; + return test23055(); +} + +// https://issues.dlang.org/show_bug?id=23055 + +int *px = (int[1]){0}; + +int fn() +{ + int *p = (int[1]){0}; + *p = 7; + return *p; +} + +_Static_assert(fn() == 7, ""); + +int test23055() +{ + return (fn() != 7); } diff --git a/tests/dmd/runnable/test23014.i b/tests/dmd/runnable/test23014.i new file mode 100644 index 00000000000..a890f0b42f3 --- /dev/null +++ b/tests/dmd/runnable/test23014.i @@ -0,0 +1,7 @@ +/* EXTRA_SOURCES: imports/imp23014.i + * REQUIRED_ARGS: -defaultlib= + */ + +static _Thread_local int tmp; + +int main() { return tmp; } diff --git a/tests/dmd/runnable/test23402.d b/tests/dmd/runnable/test23402.d new file mode 100644 index 00000000000..22fd21a7170 --- /dev/null +++ b/tests/dmd/runnable/test23402.d @@ -0,0 +1,10 @@ +// https://issues.dlang.org/show_bug.cgi?id=23402 + +import imports.imp23402a; +import imports.imp23402b; + +int main() +{ + printf("hello world\n"); + return 0; +} diff --git a/tests/dmd/runnable/test23786.c b/tests/dmd/runnable/test23786.c new file mode 100644 index 00000000000..3cf62063232 --- /dev/null +++ b/tests/dmd/runnable/test23786.c @@ -0,0 +1,66 @@ +// https://issues.dlang.org/show_bug.cgi?id=23768 + +typedef struct { + union { + struct { + int o; + } f; + }; +} T; + +void f(void) { + T data = (T) { + {.f = {.o = 0}} + }; +} + +/***************/ + +typedef struct { + union { + struct { + struct { double o; } f; + }; + }; +} S; + +_Static_assert(sizeof(S) == 8, "1"); + +void test23768() +{ + S data = (S) { + {{.f = {.o = 3}}} + }; + __check(data.f.o == 3); + S s; + s.f.o = 4; + __check(s.f.o == 4); +} + +/**************************/ +// https://issues.dlang.org/show_bug.cgi?id=24026 + +struct A +{ + int type; +}; + +struct E +{ + struct A action; +}; + +void test24026() +{ + struct E entry = {{ .type = 1 }}; + __check(entry.action.type == 1); +} + +/**************************/ + +int main() +{ + test23768(); + test24026(); + return 0; +} diff --git a/tests/dmd/runnable/test23837.d b/tests/dmd/runnable/test23837.d new file mode 100644 index 00000000000..c0c86683ee7 --- /dev/null +++ b/tests/dmd/runnable/test23837.d @@ -0,0 +1,14 @@ +// https://issues.dlang.org/show_bug.cgi?id=23837 + +import imports.mainx23837; + +struct TexturePacker +{ + stbrp_context _context; +} + +int main() +{ + auto res = TexturePacker(); + return 0; +} diff --git a/tests/dmd/runnable/test23959.d b/tests/dmd/runnable/test23959.d new file mode 100644 index 00000000000..32883736d44 --- /dev/null +++ b/tests/dmd/runnable/test23959.d @@ -0,0 +1,30 @@ +// https://issues.dlang.org/show_bug.cgi?id=23959; + +struct ST() +{ + int i; + this(this) {} +} + +alias S = ST!(); + +void poison() +{ + static S g; + auto s = g; +} + +S[1] sa; + +void fun(S[] values...) +{ + sa[] = values; +} + +int main() +{ + fun(S(1)); + assert(sa[0].i); + + return 0; +} diff --git a/tests/dmd/runnable/test42.d b/tests/dmd/runnable/test42.d index 74188e9913f..7b281e17a9a 100644 --- a/tests/dmd/runnable/test42.d +++ b/tests/dmd/runnable/test42.d @@ -2105,7 +2105,7 @@ void test12725() struct Matrix12728(T, uint m, uint n = m, ubyte f = 0) { - void foo(uint r)(auto ref in Matrix12728!(T, n, r) b) + void foo(uint r)(const auto ref Matrix12728!(T, n, r) b) { } } diff --git a/tests/dmd/runnable/testpdb.d b/tests/dmd/runnable/testpdb.d index 578ae68f203..99b4d9c9980 100644 --- a/tests/dmd/runnable/testpdb.d +++ b/tests/dmd/runnable/testpdb.d @@ -10,7 +10,7 @@ import ldc.attributes; void main(string[] args) { // https://issues.dlang.org/show_bug.cgi?id=4014 - // -gf should drag in full definitions of Object, TickDuration and ClockType + // -gf should drag in full definitions of Object, Duration and ClockType version (LDC) { // `Object` has no explicit fields; DMD emits debuginfos about methods which LDC doesn't. @@ -21,7 +21,7 @@ void main(string[] args) { Object o = new Object; } - TickDuration duration; // struct + Duration duration; // struct ClockType ct; // enumerator version (CRuntime_Microsoft) @@ -50,8 +50,8 @@ void main(string[] args) objsym.Release(); } - IDiaSymbol ticksym = searchSymbol(globals, "core.time.TickDuration"); - testSymbolHasChildren(ticksym, "core.time.TickDuration"); + IDiaSymbol ticksym = searchSymbol(globals, "core.time.Duration"); + testSymbolHasChildren(ticksym, "core.time.Duration"); ticksym.Release(); IDiaSymbol ctsym = searchSymbol(globals, "core.time.ClockType"); diff --git a/tests/dmd/runnable/xtest46.d b/tests/dmd/runnable/xtest46.d index e57f52f6578..5017a767693 100644 --- a/tests/dmd/runnable/xtest46.d +++ b/tests/dmd/runnable/xtest46.d @@ -5016,18 +5016,18 @@ void test6763() { int n; - f6763(0); //With D2: Error: function main.f ((ref const const(int) _param_0)) is not callable using argument types (int) + f6763(0); //With D2: Error: function main.f ((ref const const(int) __param_0)) is not callable using argument types (int) c6763(0); r6763(n); static assert(__traits(compiles, r6763(0))); i6763(0); o6763(n); static assert(!__traits(compiles, o6763(0))); // https://issues.dlang.org/show_bug.cgi?id=6755 - static assert(typeof(f6763).stringof == "void(int _param_0)"); - static assert(typeof(c6763).stringof == "void(const(int) _param_0)"); - static assert(typeof(r6763).stringof == "void(ref int _param_0)"); - static assert(typeof(i6763).stringof == "void(in int _param_0)"); - static assert(typeof(o6763).stringof == "void(out int _param_0)"); + static assert(typeof(f6763).stringof == "void(int __param_0)"); + static assert(typeof(c6763).stringof == "void(const(int) __param_0)"); + static assert(typeof(r6763).stringof == "void(ref int __param_0)"); + static assert(typeof(i6763).stringof == "void(in int __param_0)"); + static assert(typeof(o6763).stringof == "void(out int __param_0)"); } /***************************************************/ @@ -5997,7 +5997,7 @@ void test7618(const int x = 1) { int func(ref int x) { return 1; } static assert(!__traits(compiles, func(x))); - // Error: function test.foo.func (ref int _param_0) is not callable using argument types (const(int)) + // Error: function test.foo.func (ref int __param_0) is not callable using argument types (const(int)) int delegate(ref int) dg = (ref int x) => 1; static assert(!__traits(compiles, dg(x))); @@ -6170,14 +6170,6 @@ static assert(!__traits(compiles, foo8220(typeof(0)))); // fail /***************************************************/ -void func8105(in ref int x) { } - -void test8105() -{ -} - -/***************************************************/ - template ParameterTypeTuple159(alias foo) { static if (is(typeof(foo) P == __parameters)) @@ -8300,7 +8292,6 @@ int main() test12503(); test8004(); test8064(); - test8105(); test159(); test12824(); test8283(); diff --git a/tests/dmd/unit/deinitialization.d b/tests/dmd/unit/deinitialization.d index b4cb73a945f..d3458802af9 100644 --- a/tests/dmd/unit/deinitialization.d +++ b/tests/dmd/unit/deinitialization.d @@ -33,7 +33,7 @@ unittest @("Type.deinitialize") unittest { - import dmd.mars : addDefaultVersionIdentifiers; + import dmd.target : addDefaultVersionIdentifiers; import dmd.mtype : Type; import dmd.globals : global; diff --git a/tests/dmd/unit/lexer/diagnostic_reporter.d b/tests/dmd/unit/lexer/diagnostic_reporter.d index e707e99bb76..00ac59af6e1 100644 --- a/tests/dmd/unit/lexer/diagnostic_reporter.d +++ b/tests/dmd/unit/lexer/diagnostic_reporter.d @@ -43,7 +43,7 @@ private void lexUntilEndOfFile(string code) if (!global.errorSink) global.errorSink = new ErrorSinkCompiler; - scope lexer = new Lexer("test", code.ptr, 0, code.length, 0, 0, global.errorSink); + scope lexer = new Lexer("test", code.ptr, 0, code.length, 0, 0, global.errorSink, null); lexer.nextToken; while (lexer.nextToken != TOK.endOfFile) {} diff --git a/tests/dmd/unit/lexer/lexer_dmdlib.d b/tests/dmd/unit/lexer/lexer_dmdlib.d index 4b53bafc9ac..ea7ebb4ab82 100644 --- a/tests/dmd/unit/lexer/lexer_dmdlib.d +++ b/tests/dmd/unit/lexer/lexer_dmdlib.d @@ -170,7 +170,7 @@ unittest TOK.rightCurly, ]; - Lexer lexer = new Lexer(null, code.ptr, 0, code.length, false, false, new ErrorSinkStderr); + Lexer lexer = new Lexer(null, code.ptr, 0, code.length, false, false, new ErrorSinkStderr, null); lexer.nextToken; TOK[] result; @@ -191,7 +191,7 @@ unittest TOK.comment, ]; - Lexer lexer = new Lexer(null, code.ptr, 0, code.length, false, true, new ErrorSinkStderr); + Lexer lexer = new Lexer(null, code.ptr, 0, code.length, false, true, new ErrorSinkStderr, null); lexer.nextToken; TOK[] result; @@ -217,7 +217,7 @@ unittest TOK.reserved, ]; - Lexer lexer = new Lexer(null, code.ptr, 0, code.length, false, false, new ErrorSinkStderr); + Lexer lexer = new Lexer(null, code.ptr, 0, code.length, false, false, new ErrorSinkStderr, null); TOK[] result; @@ -266,7 +266,7 @@ unittest foreach (codeNum, code; codes) { auto fileName = text("file", codeNum, '\0'); - Lexer lexer = new Lexer(fileName.ptr, code.ptr, 0, code.length, false, false, new ErrorSinkCompiler); + Lexer lexer = new Lexer(fileName.ptr, code.ptr, 0, code.length, false, false, new ErrorSinkCompiler, null); // Generate the errors foreach(unused; lexer){} } diff --git a/tests/dmd/unit/lexer/location_offset.d b/tests/dmd/unit/lexer/location_offset.d index c52de839dbe..4a950e866f2 100644 --- a/tests/dmd/unit/lexer/location_offset.d +++ b/tests/dmd/unit/lexer/location_offset.d @@ -17,7 +17,7 @@ unittest { enum code = "token"; - scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, 0, new ErrorSinkStderr); + scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, 0, new ErrorSinkStderr, null); lexer.nextToken; @@ -29,7 +29,7 @@ unittest { enum code = "ignored_token token"; - scope lexer = new Lexer("test.d", code.ptr, 13, code.length - 14, 0, 0, new ErrorSinkStderr); + scope lexer = new Lexer("test.d", code.ptr, 13, code.length - 14, 0, 0, new ErrorSinkStderr, null); lexer.nextToken; @@ -41,7 +41,7 @@ unittest { enum code = "token1 token2 3"; - scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, 0, new ErrorSinkStderr); + scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, 0, new ErrorSinkStderr, null); lexer.nextToken; lexer.nextToken; @@ -55,7 +55,7 @@ unittest { enum code = "token"; - scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, 0, new ErrorSinkStderr); + scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, 0, new ErrorSinkStderr, null); lexer.nextToken; lexer.nextToken; @@ -68,7 +68,7 @@ unittest { enum code = "/* comment */"; - scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, true, new ErrorSinkStderr); + scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, true, new ErrorSinkStderr, null); lexer.nextToken; @@ -81,7 +81,7 @@ unittest { enum code = "// comment"; - scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, true, new ErrorSinkStderr); + scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, true, new ErrorSinkStderr, null); lexer.nextToken; @@ -94,7 +94,7 @@ unittest { enum code = "/+ comment +/"; - scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, true, new ErrorSinkStderr); + scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, true, new ErrorSinkStderr, null); lexer.nextToken; @@ -107,7 +107,7 @@ unittest { enum code = "/* comment */ token"; - scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, 0, new ErrorSinkStderr); + scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, 0, new ErrorSinkStderr, null); lexer.nextToken; @@ -119,7 +119,7 @@ unittest { enum code = "// comment\ntoken"; - scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, 0, new ErrorSinkStderr); + scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, 0, new ErrorSinkStderr, null); lexer.nextToken; @@ -131,7 +131,7 @@ unittest { enum code = "/+ comment +/ token"; - scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, 0, new ErrorSinkStderr); + scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, 0, new ErrorSinkStderr, null); lexer.nextToken; @@ -143,7 +143,7 @@ unittest { enum code = "line\ntoken"; - scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, 0, new ErrorSinkStderr); + scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, 0, new ErrorSinkStderr, null); lexer.nextToken; lexer.nextToken; @@ -156,7 +156,7 @@ unittest { enum code = "line\r\ntoken"; - scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, 0, new ErrorSinkStderr); + scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, 0, new ErrorSinkStderr, null); lexer.nextToken; lexer.nextToken; @@ -169,7 +169,7 @@ unittest { enum code = "line\rtoken"; - scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, 0, new ErrorSinkStderr); + scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, 0, new ErrorSinkStderr, null); lexer.nextToken; lexer.nextToken; @@ -182,7 +182,7 @@ unittest { enum code = "'🍺'"; - scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, 0, new ErrorSinkStderr); + scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, 0, new ErrorSinkStderr, null); lexer.nextToken; @@ -194,7 +194,7 @@ unittest { enum code = `"🍺🍺"`; - scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, 0, new ErrorSinkStderr); + scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, 0, new ErrorSinkStderr, null); lexer.nextToken; @@ -206,7 +206,7 @@ unittest { enum code = "'🍺' token"; - scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, 0, new ErrorSinkStderr); + scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, 0, new ErrorSinkStderr, null); lexer.nextToken; lexer.nextToken; @@ -219,7 +219,7 @@ unittest { enum code = `"🍺🍺" token`; - scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, 0, new ErrorSinkStderr); + scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, 0, new ErrorSinkStderr, null); lexer.nextToken; lexer.nextToken; @@ -542,6 +542,7 @@ enum ignoreTokens __cdecl, __declspec, __stdcall, + __thread, __pragma, __int128, __attribute__, @@ -556,9 +557,9 @@ static foreach (tok; __traits(allMembers, TOK)) @(tests[tok].description) unittest { - const newCode = "first_token " ~ tests[tok].code; + const newCode = "first_token " ~ tests[tok].code ~ '\0'; - scope lexer = new Lexer("test.d", newCode.ptr, 0, newCode.length, 0, 0, new ErrorSinkStderr); + scope lexer = new Lexer("test.d", newCode.ptr, 0, newCode.length, 0, 0, new ErrorSinkStderr, null); lexer.nextToken; lexer.nextToken; diff --git a/tests/driver/fwarn-stack-size.d b/tests/driver/fwarn-stack-size.d index 1ed5184faff..ba028c10a07 100644 --- a/tests/driver/fwarn-stack-size.d +++ b/tests/driver/fwarn-stack-size.d @@ -7,6 +7,12 @@ // RUN: not %ldc -w -c --fwarn-stack-size=200 %s 2>&1 | FileCheck %s +// Test that IR caching does not hide the warning-error in a second compilation run +// RUN: not %ldc -cache=%t-dir -w -c --fwarn-stack-size=200 %s 2>&1 | FileCheck %s +// RUN: not %ldc -cache=%t-dir -w -c --fwarn-stack-size=200 %s 2>&1 | FileCheck %s +// Test that indeed the IR cache does not exist +// RUN: not %prunecache -f %t-dir --max-bytes=1 + module fwarnstacksize; void small_stack() @@ -14,7 +20,7 @@ void small_stack() byte[100] a; } -// CHECK: warning: stack frame size {{.*}} exceeds limit (200) in function {{.*}}14fwarnstacksize9big_stack +// CHECK: warning: {{(:0:0: )?}}stack frame size {{.*}} exceeds limit (200) in function {{.*}}14fwarnstacksize9big_stack void big_stack() { byte[1000] b; diff --git a/tests/driver/riscv_abi4.d b/tests/driver/riscv_abi4.d new file mode 100644 index 00000000000..a5c93bab615 --- /dev/null +++ b/tests/driver/riscv_abi4.d @@ -0,0 +1,6 @@ +// REQUIRES: target_RISCV + +// RUN: %ldc %s -mtriple=riscv64-unknown-linux --gcc=echo > %t && FileCheck %s < %t +// CHECK: -mabi=lp64d -march=rv64gc + +void main() {} diff --git a/tests/driver/riscv_abi5.d b/tests/driver/riscv_abi5.d new file mode 100644 index 00000000000..11cd97c41c5 --- /dev/null +++ b/tests/driver/riscv_abi5.d @@ -0,0 +1,6 @@ +// REQUIRES: target_RISCV + +// RUN: %ldc %s -mtriple=riscv64-unknown-linux -mattr=+m,+a,+f,-d --gcc=echo > %t && FileCheck %s < %t +// CHECK: -mabi=lp64f -march=rv64imaf_zicsr_zifencei + +void main() {} diff --git a/tests/instrument/xray_link.d b/tests/instrument/xray_link.d index 83c498c2747..d05d174ea1e 100644 --- a/tests/instrument/xray_link.d +++ b/tests/instrument/xray_link.d @@ -2,7 +2,7 @@ // fails on macOS with LLVM 11 due to a linker error, see // https://github.com/llvm/llvm-test-suite/commit/2c3c4a6286d453f763c0245c6536ddd368f0db99 -// XFAIL: Darwin && atleast_llvm1100 +// XFAIL: Darwin // RUN: %ldc -fxray-instrument -fxray-instruction-threshold=1 -of=%t%exe %s -vv 2>&1 | FileCheck %s diff --git a/tests/lit.site.cfg.in b/tests/lit.site.cfg.in index 44dac1a1c3c..5d8fdde37fd 100644 --- a/tests/lit.site.cfg.in +++ b/tests/lit.site.cfg.in @@ -17,10 +17,12 @@ OFF = False config.ldc2_bin = "@LDC2_BIN@" config.ldcprofdata_bin = "@LDCPROFDATA_BIN@" config.ldcprunecache_bin = "@LDCPRUNECACHE_BIN@" +config.ldcbuildplugin_bin = "@LDCBUILDPLUGIN_BIN@" config.timetrace2txt_bin = "@TIMETRACE2TXT_BIN@" config.ldc2_bin_dir = "@LDC2_BIN_DIR@" config.ldc2_lib_dir = "@LDC2_LIB_DIR@" config.ldc2_runtime_dir = "@RUNTIME_DIR@" +config.ldc2_source_dir = "@PROJECT_SOURCE_DIR@" config.test_source_root = "@TESTS_IR_DIR@" config.llvm_tools_dir = "@LLVM_TOOLS_DIR@" config.llvm_version = @LDC_LLVM_VER@ @@ -156,6 +158,7 @@ config.substitutions.append( ('%ldc', config.ldc2_bin) ) config.substitutions.append( ('%gnu_make', config.gnu_make_bin) ) config.substitutions.append( ('%profdata', config.ldcprofdata_bin) ) config.substitutions.append( ('%prunecache', config.ldcprunecache_bin) ) +config.substitutions.append( ('%buildplugin', config.ldcbuildplugin_bin + " --ldcSrcDir=" + config.ldc2_source_dir ) ) config.substitutions.append( ('%timetrace2txt', config.timetrace2txt_bin) ) config.substitutions.append( ('%llvm-spirv', os.path.join(config.llvm_tools_dir, 'llvm-spirv')) ) config.substitutions.append( ('%llc', os.path.join(config.llvm_tools_dir, 'llc')) ) @@ -213,14 +216,6 @@ if (platform.system() != 'Windows') and lit.util.which('gdb', config.environment config.substitutions.append( ('%_gdb_dflags', gdb_dflags) ) -# Add substitutions for functionality across different LLVM versions -if config.llvm_version >= 800: - config.substitutions.append( ('%disable_fp_elim', '-frame-pointer=all') ) - config.substitutions.append( ('%enable_fp_elim', '-frame-pointer=none') ) -else: - config.substitutions.append( ('%disable_fp_elim', '-disable-fp-elim') ) - config.substitutions.append( ('%enable_fp_elim', '-disable-fp-elim=false') ) - if 'LD_LIBRARY_PATH' in os.environ: libs = [] for lib_path in [s for s in os.environ['LD_LIBRARY_PATH'].split(':') if s]: diff --git a/tests/plugins/addFuncEntryCall/addFuncEntryCallPass.cpp b/tests/plugins/addFuncEntryCall/addFuncEntryCallPass.cpp index 065b3b086a1..e138526d187 100644 --- a/tests/plugins/addFuncEntryCall/addFuncEntryCallPass.cpp +++ b/tests/plugins/addFuncEntryCall/addFuncEntryCallPass.cpp @@ -37,11 +37,7 @@ bool FuncEntryCallPass::doInitialization(Module &M) { // Add fwd declaration of the `void __test_funcentrycall(void)` function. auto functionType = FunctionType::get(Type::getVoidTy(M.getContext()), false); funcToCallUponEntry = - M.getOrInsertFunction("__test_funcentrycall", functionType) -#if LLVM_VERSION >= 900 - .getCallee() -#endif - ; + M.getOrInsertFunction("__test_funcentrycall", functionType).getCallee(); return true; } @@ -50,14 +46,13 @@ bool FuncEntryCallPass::runOnFunction(Function &F) { // (this includes e.g. `ldc.register_dso`!) llvm::BasicBlock &block = F.getEntryBlock(); IRBuilder<> builder(&block, block.begin()); -#if LLVM_VERSION >= 1100 builder.CreateCall(FunctionCallee(cast(funcToCallUponEntry))); -#else - builder.CreateCall(funcToCallUponEntry); -#endif return true; } + +#if LLVM_VERSION < 1500 // legacy pass manager + static void addFuncEntryCallPass(const PassManagerBuilder &, legacy::PassManagerBase &PM) { PM.add(new FuncEntryCallPass()); @@ -67,10 +62,10 @@ static RegisterStandardPasses RegisterFuncEntryCallPass0(PassManagerBuilder::EP_EnabledOnOptLevel0, addFuncEntryCallPass); +#endif -#if LLVM_VERSION >= 1400 -// Implementation of plugin for the new passmanager +#if LLVM_VERSION >= 1400 // new pass manager #include "llvm/IR/PassManager.h" #include "llvm/Passes/PassBuilder.h" @@ -114,5 +109,4 @@ llvmGetPassPluginInfo() { }; } -#endif - +#endif // LLVM 14+ diff --git a/tests/plugins/basic_sema_plugin.d b/tests/plugins/basic_sema_plugin.d new file mode 100644 index 00000000000..071f1dcf8fd --- /dev/null +++ b/tests/plugins/basic_sema_plugin.d @@ -0,0 +1,23 @@ +// REQUIRES: Plugins + +// RUN: split-file %s %t --leading-lines +// RUN: %buildplugin %t/plugin.d -of=%t/plugin%so --buildDir=%t/build +// RUN: %ldc -wi -c -o- --plugin=%t/plugin%so %t/testcase.d 2>&1 | FileCheck %t/testcase.d + +//--- plugin.d +import dmd.dmodule : Module; +import dmd.errors; +import dmd.location; + +extern(C) void runSemanticAnalysis(Module m) { + if (m.md) { + warning(m.md.loc, "It works!"); + } +} + +//--- testcase.d +// CHECK: testcase.d([[@LINE+1]]): Warning: It works! +module testcase; +int testfunction(int i) { + return i * 2; +} diff --git a/tests/plugins/lit.local.cfg b/tests/plugins/lit.local.cfg index 06c2097c00e..eb5b296e587 100644 --- a/tests/plugins/lit.local.cfg +++ b/tests/plugins/lit.local.cfg @@ -1,8 +1,28 @@ +import lit.formats +import lit.util import os +import sys import platform +import string import re +import subprocess +import glob if (config.plugins_supported): config.available_features.add('Plugins') config.environment['LLVM_CONFIG'] = os.path.join(config.llvm_tools_dir, 'llvm-config') config.environment['LLVM_VERSION'] = str(config.llvm_version) + + # Set feature that tells us that the just-built LDC is ABI compatible with the host D compiler + # For our tets, the required ABI compatibility seems OK since at least LDC 1.30. + # If the compiler is built not by LDC but another compiler, then assume the ABI to be incompatible. + command = [config.ldc2_bin, '--version'] + p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) + text1 = p.stdout.readline() # Ex.: "LDC - the LLVM D compiler (1.33.0-git-716f627)" + text2 = p.stdout.readline() # Ex.: " based on DMD v2.103.1 and LLVM 14.0.0" + text3 = p.stdout.readline() # Ex.: " built with LDC - the LLVM D compiler (1.33.0-beta2)" + host_version = re.compile(' built with LDC.* \(1\.([0-9]+).*\)').match(text3) + if (host_version and int(host_version.group(1)) >= 30): # 30 = LDC 1.30 + config.available_features.add('ABI_compatible_with_host_D') + + diff --git a/tests/plugins/visitor_example.d b/tests/plugins/visitor_example.d new file mode 100644 index 00000000000..ec8af7d2c90 --- /dev/null +++ b/tests/plugins/visitor_example.d @@ -0,0 +1,58 @@ +// REQUIRES: Plugins +// REQUIRES: ABI_compatible_with_host_D + +// RUN: split-file %s %t --leading-lines +// RUN: %buildplugin %t/plugin.d -of=%t/plugin%so --buildDir=%t/build +// RUN: %ldc -wi -c -o- --plugin=%t/plugin%so %t/testcase.d 2>&1 | FileCheck %t/testcase.d + +//--- plugin.d +import dmd.dmodule; +import dmd.errors; +import dmd.location; +import dmd.visitor; +import dmd.declaration; +import dmd.dsymbol; + +extern(C++) class MyVisitor : SemanticTimeTransitiveVisitor { + alias visit = SemanticTimeTransitiveVisitor.visit; + + override void visit(VarDeclaration vd) { + if (vd.aliasTuple) { + vd.aliasTuple.foreachVar((s) { + auto vardecl = s.isVarDeclaration(); + if (vardecl && vardecl.type.needsDestruction()) { + warning(vardecl.loc, "It works!"); + } + }); + } + } +} + +extern(C) void runSemanticAnalysis(Module m) { + scope v = new MyVisitor(); + if (!m.members) + return; + m.members.foreachDsymbol((s) { + s.accept(v); + }); +} + +//--- testcase.d +alias AliasSeq(TList...) = TList; + +int i = 0; +struct A { + ~this() { + i *= 2; + } +} + +void main() { + { + // CHECK: testcase.d([[@LINE+1]]): Warning: + AliasSeq!(A, A) params; + i = 1; + } + + assert(i == 4); +} diff --git a/tests/sanitizers/asan_use_after_scope.d b/tests/sanitizers/asan_use_after_scope.d new file mode 100644 index 00000000000..ff16b164b8c --- /dev/null +++ b/tests/sanitizers/asan_use_after_scope.d @@ -0,0 +1,24 @@ +// REQUIRES: ASan + +// RUN: %ldc -femit-local-var-lifetime -g -fsanitize=address %s -of=%t%exe +// RUN: not %t%exe 2>&1 | FileCheck %s + +// CHECK: ERROR: AddressSanitizer: stack-use-after-scope +// CHECK-NEXT: WRITE of size 4 + +void useAfterScope() { + int* p; + { + int x = 0; + p = &x; // cannot statically disallow this because + *p = 1; // this is a valid use of things + } +// CHECK-NEXT: #0 {{.*}} in {{.*}}asan_use_after_scope.d:[[@LINE+1]] + *p = 5; // but then this can happen... stack use after scope bug! +} + +void main() +{ +// CHECK-NEXT: #1 {{.*}} in {{.*}}asan_use_after_scope.d:[[@LINE+1]] + useAfterScope(); +} diff --git a/tests/sanitizers/asan_use_after_scope_if.d b/tests/sanitizers/asan_use_after_scope_if.d new file mode 100644 index 00000000000..a62c30a89c8 --- /dev/null +++ b/tests/sanitizers/asan_use_after_scope_if.d @@ -0,0 +1,23 @@ +// REQUIRES: ASan + +// RUN: %ldc -femit-local-var-lifetime -g -fsanitize=address %s -of=%t%exe +// RUN: not %t%exe 2>&1 | FileCheck %s + +// CHECK: ERROR: AddressSanitizer: stack-use-after-scope +// CHECK-NEXT: WRITE of size 4 + +void useAfterScope(int xparam) { + int* p; + if (int x = xparam) { + p = &x; // cannot statically disallow this because + *p = 1; // this is a valid use of things + } +// CHECK-NEXT: #0 {{.*}} in {{.*}}asan_use_after_scope_if.d:[[@LINE+1]] + *p = 5; // but then this can happen... stack use after scope bug! +} + +void main() +{ +// CHECK-NEXT: #1 {{.*}} in {{.*}}asan_use_after_scope_if.d:[[@LINE+1]] + useAfterScope(1); +} diff --git a/tests/sanitizers/fuzz_asan.d b/tests/sanitizers/fuzz_asan.d index 1fc05e8ae65..3a1711b1537 100644 --- a/tests/sanitizers/fuzz_asan.d +++ b/tests/sanitizers/fuzz_asan.d @@ -2,9 +2,9 @@ // REQUIRES: Fuzzer, ASan -// See https://github.com/ldc-developers/ldc/issues/2222 for %disable_fp_elim +// 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 %disable_fp_elim %s -of=%t%exe +// RUN: %ldc -g -fsanitize=address,fuzzer -fsanitize-address-use-after-return=never -frame-pointer=all %s -of=%t%exe // RUN: not %t%exe 2>&1 | FileCheck %s bool FuzzMe(ubyte* data, size_t dataSize) diff --git a/tests/sanitizers/lsan_memleak.d b/tests/sanitizers/lsan_memleak.d index 6fc7c16ac35..de9a654a13a 100644 --- a/tests/sanitizers/lsan_memleak.d +++ b/tests/sanitizers/lsan_memleak.d @@ -1,6 +1,6 @@ // Test leak detection with LSan -// REQUIRES: LSan && atleast_llvm1000 +// REQUIRES: LSan // UNSUPPORTED: Windows, FreeBSD diff --git a/tests/sanitizers/msan_uninitialized.d b/tests/sanitizers/msan_uninitialized.d index 407d20f7e8f..f7048d5f60f 100644 --- a/tests/sanitizers/msan_uninitialized.d +++ b/tests/sanitizers/msan_uninitialized.d @@ -8,7 +8,7 @@ // CHECK: MemorySanitizer: use-of-uninitialized-value -// CHECK: Uninitialized value was created by an allocation of {{.*}} in the stack frame of function '_Dmain' +// CHECK: Uninitialized value was created by an allocation of {{.*}} in the stack frame // CHECK-NEXT: #0 {{.* in (_Dmain|D main) .*}}msan_uninitialized.d:[[@LINE+1]] int main() { diff --git a/tests/semantic/dcompute.d b/tests/semantic/dcompute.d index 44247e71f67..1bde310b170 100644 --- a/tests/semantic/dcompute.d +++ b/tests/semantic/dcompute.d @@ -29,7 +29,7 @@ void func() //CHECK: dcompute.d([[@LINE+1]]): Error: {{.*}} interfaces and classes not allowed in `@compute` code C cc; int[] quux; - //CHECK: dcompute.d([[@LINE+1]]): Error: can only call functions from other `@compute` modules in `@compute` code + //CHECK: dcompute.d([[@LINE+1]]): Error: setting `length` in `@compute` code not allowed quux.length = 1; //CHECK: dcompute.d([[@LINE+1]]): Error: can only call functions from other `@compute` modules in `@compute` code quux ~= 42; diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 187c95035dd..eb510f3c940 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -60,3 +60,25 @@ build_d_executable( ${COMPILE_D_MODULES_SEPARATELY} ) install(PROGRAMS ${TIMETRACE2TXT_EXE_FULL} DESTINATION ${CMAKE_INSTALL_PREFIX}/bin) + +############################################################################# +# Only build ldc-build-plugin tool for platforms where plugins are actually enabled. +if(LDC_ENABLE_PLUGINS) + configure_file(${PROJECT_SOURCE_DIR}/tools/ldc-build-plugin.d.in ${PROJECT_BINARY_DIR}/ldc-build-plugin.d @ONLY) + set(LDC_BUILD_PLUGIN_EXE ldc-build-plugin) + set(LDC_BUILD_PLUGIN_EXE ${LDC_BUILD_PLUGIN_EXE} PARENT_SCOPE) # needed for correctly populating lit.site.cfg.in + set(LDC_BUILD_PLUGIN_EXE_NAME ${PROGRAM_PREFIX}${LDC_BUILD_PLUGIN_EXE}${PROGRAM_SUFFIX}) + set(LDC_BUILD_PLUGIN_EXE_FULL ${PROJECT_BINARY_DIR}/bin/${LDC_BUILD_PLUGIN_EXE_NAME}${CMAKE_EXECUTABLE_SUFFIX}) + build_d_executable( + "${LDC_BUILD_PLUGIN_EXE}" + "${LDC_BUILD_PLUGIN_EXE_FULL}" + "${PROJECT_BINARY_DIR}/ldc-build-plugin.d" + "${DFLAGS_BUILD_TYPE}" + "" + "${PROJECT_SOURCE_DIR}/tools/ldc-build-plugin.d.in" + "" + ${COMPILE_D_MODULES_SEPARATELY} + ) + install(PROGRAMS ${LDC_BUILD_PLUGIN_EXE_FULL} DESTINATION ${CMAKE_INSTALL_PREFIX}/bin) +endif() + diff --git a/tools/ldc-build-plugin.d.in b/tools/ldc-build-plugin.d.in new file mode 100644 index 00000000000..84fd25ff357 --- /dev/null +++ b/tools/ldc-build-plugin.d.in @@ -0,0 +1,239 @@ +module ldcBuildRuntime; + +import core.stdc.stdlib : exit; +import std.algorithm; +import std.array; +import std.file; +import std.path; +import std.stdio; + +version (OSX) + version = Darwin; +else version (iOS) + version = Darwin; +else version (TVOS) + version = Darwin; +else version (WatchOS) + version = Darwin; + +struct Config { + string ldcExecutable; + string buildDir; + string ldcSourceDir; + string[] dFlags; + string[] linkerFlags; + bool verbose; + string[] ldcArgs; + string userWorkDir; +} + +version (Windows) enum exeSuffix = ".exe"; +else enum exeSuffix = ""; + +string defaultLdcExecutable; +Config config; + +int main(string[] args) { + enum exeName = "ldc2" ~ exeSuffix; + defaultLdcExecutable = buildPath(thisExePath.dirName, exeName); + config.userWorkDir = getcwd(); + + parseCommandLine(args); + + findLdcExecutable(); + + prepareBuildDir(); + + prepareLdcSource(); + + build(); + + if (config.verbose) + writefln(".: Plugin library built successfully."); + return 0; +} + +void findLdcExecutable() { + if (config.ldcExecutable !is null) { + if (!config.ldcExecutable.exists) { + writefln(".: Error: LDC executable not found: %s", config.ldcExecutable); + exit(1); + } + config.ldcExecutable = config.ldcExecutable.absolutePath; + return; + } + + if (defaultLdcExecutable.exists) { + config.ldcExecutable = defaultLdcExecutable; + return; + } + + writefln(".: Please specify LDC executable via '--ldc='. Aborting.", exeSuffix); + exit(1); +} + +void prepareBuildDir() { + if (config.buildDir is null) + config.buildDir = "ldc-build-plugin.tmp"; + + if (!config.buildDir.exists) { + if (config.verbose) + writefln(".: Creating build directory: %s", config.buildDir); + mkdirRecurse(config.buildDir); + } + + config.buildDir = config.buildDir.absolutePath; +} + +void prepareLdcSource() { + if (config.ldcSourceDir !is null) { + if (!config.ldcSourceDir.exists) { + writefln(".: Error: LDC source directory not found: %s", config.ldcSourceDir); + exit(1); + } + config.ldcSourceDir = config.ldcSourceDir.absolutePath; + return; + } + + const ldcSrc = "ldc-src"; + config.ldcSourceDir = buildPath(config.buildDir, ldcSrc); + if (buildPath(config.ldcSourceDir, "dmd").exists) + return; + + // Download & extract LDC source archive if /ldc-src/dmd doesn't exist yet. + + const wd = WorkingDirScope(config.buildDir); + + auto ldcVersion = "@LDC_VERSION@"; + void removeVersionSuffix(string beginning) { + const suffixIndex = ldcVersion.countUntil(beginning); + if (suffixIndex > 0) + ldcVersion = ldcVersion[0 .. suffixIndex]; + } + removeVersionSuffix("git-"); + removeVersionSuffix("-dirty"); + + import std.format : format; + const localArchiveFile = "ldc-src.zip"; + if (!localArchiveFile.exists) { + const url = "https://github.com/ldc-developers/ldc/releases/download/v%1$s/ldc-%1$s-src.zip".format(ldcVersion); + writefln(".: Downloading LDC source archive: %s", url); + import std.net.curl : download; + download(url, localArchiveFile); + if (getSize(localArchiveFile) < 1_048_576) { + writefln(".: Error: downloaded file is corrupt; has LDC v%s been released?", ldcVersion); + writefln(" You can work around this by manually downloading a src package and moving it to: %s", + buildPath(config.buildDir, localArchiveFile)); + localArchiveFile.remove; + exit(1); + } + } + + extractZipArchive(localArchiveFile, "."); + rename("ldc-%1$s-src".format(ldcVersion), ldcSrc); +} + +void build() { + string[] args = [ + config.ldcExecutable, + "-I" ~ config.ldcSourceDir, + "--d-version=IN_LLVM", + "-J" ~ buildPath(config.ldcSourceDir, "dmd", "res"), + "--shared", + "--defaultlib=", + "--od=" ~ config.buildDir + ]; + + version (Darwin) { + args ~= "-L-Wl,-undefined,dynamic_lookup"; + } + + args ~= config.ldcArgs; + + exec(args); +} + +/*** helpers ***/ + +struct WorkingDirScope { + string originalPath; + this(string path) { originalPath = getcwd(); chdir(path); } + ~this() { chdir(originalPath); } +} + +void exec(string[] command) { + import std.process; + + static string quoteIfNeeded(string arg) { + const r = arg.findAmong(" ;"); + return !r.length ? arg : "'" ~ arg ~ "'"; + } + string flattened = command.map!quoteIfNeeded.join(" "); + if (config.verbose) { + writefln(".: Invoking: %s", flattened); + stdout.flush(); + } + + auto pid = spawnProcess(command, null, std.process.Config.none, config.userWorkDir); + const exitStatus = wait(pid); + + if (exitStatus != 0) { + if (config.verbose) + writeln(".: Error: command failed with status ", exitStatus); + exit(1); + } +} + +void extractZipArchive(string archivePath, string destination) { + import std.zip; + + auto archive = new ZipArchive(std.file.read(archivePath)); + foreach (name, am; archive.directory) { + const destPath = buildNormalizedPath(destination, name); + + const isDir = name.endsWith("/"); + const destDir = isDir ? destPath : destPath.dirName; + mkdirRecurse(destDir); + + if (!isDir) + std.file.write(destPath, archive.expand(am)); + } +} + +void parseCommandLine(string[] args) { + import std.getopt; + + try { + arraySep = ";"; + auto helpInformation = getopt( + args, + std.getopt.config.passThrough, + "ldc", "Path to LDC executable (default: '" ~ defaultLdcExecutable ~ "')", &config.ldcExecutable, + "buildDir", "Path to build directory (default: './ldc-build-plugin.tmp')", &config.buildDir, + "ldcSrcDir", "Path to LDC source directory (if not specified: downloads & extracts source archive into '/ldc-src')", &config.ldcSourceDir, + "dFlags", "Extra LDC flags for the D module (separated by ';')", &config.dFlags, + "verbose|v", "Verbose output (e.g. showing the compile commandline)", &config.verbose, + "linkerFlags", "Extra C linker flags for shared libraries and testrunner executables (separated by ';')", &config.linkerFlags + ); + + // getopt() has removed all consumed args from `args` + // Remaining arguments are interpreted as LDC arguments (e.g. plugin source files and -of=). + config.ldcArgs = args[1 .. $]; + + if (helpInformation.helpWanted) { + defaultGetoptPrinter( + "OVERVIEW: Builds a Semantic Analysis plugin for LDC.\n\n" ~ + "USAGE: ldc-build-plugin [options] sourcefiles... -of=\n\n" ~ + "OPTIONS:\n" ~ + " Unrecognized options are passed through to LDC.", + helpInformation.options + ); + exit(1); + } + } + catch (Exception e) { + writefln("Error processing command line arguments: %s", e.msg); + writeln("Use '--help' for help."); + exit(1); + } +} diff --git a/tools/ldc-profdata/llvm-profdata-10.0.cpp b/tools/ldc-profdata/llvm-profdata-10.0.cpp deleted file mode 100644 index 41e9abb82b1..00000000000 --- a/tools/ldc-profdata/llvm-profdata-10.0.cpp +++ /dev/null @@ -1,1178 +0,0 @@ -//===- llvm-profdata.cpp - LLVM profile data tool -------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// llvm-profdata merges .profdata files. -// -//===----------------------------------------------------------------------===// - -#include "llvm/ADT/SmallSet.h" -#include "llvm/ADT/SmallVector.h" -#include "llvm/ADT/StringRef.h" -#include "llvm/IR/LLVMContext.h" -#include "llvm/ProfileData/InstrProfReader.h" -#include "llvm/ProfileData/InstrProfWriter.h" -#include "llvm/ProfileData/ProfileCommon.h" -#include "llvm/ProfileData/SampleProfReader.h" -#include "llvm/ProfileData/SampleProfWriter.h" -#include "llvm/Support/CommandLine.h" -#include "llvm/Support/Errc.h" -#include "llvm/Support/FileSystem.h" -#include "llvm/Support/Format.h" -#include "llvm/Support/InitLLVM.h" -#include "llvm/Support/MemoryBuffer.h" -#include "llvm/Support/Path.h" -#include "llvm/Support/Threading.h" -#include "llvm/Support/ThreadPool.h" -#include "llvm/Support/WithColor.h" -#include "llvm/Support/raw_ostream.h" -#include - -using namespace llvm; - -enum ProfileFormat { - PF_None = 0, - PF_Text, - PF_Compact_Binary, - PF_Ext_Binary, - PF_GCC, - PF_Binary -}; - -static void warn(Twine Message, std::string Whence = "", - std::string Hint = "") { - WithColor::warning(); - if (!Whence.empty()) - errs() << Whence << ": "; - errs() << Message << "\n"; - if (!Hint.empty()) - WithColor::note() << Hint << "\n"; -} - -static void exitWithError(Twine Message, std::string Whence = "", - std::string Hint = "") { - WithColor::error(); - if (!Whence.empty()) - errs() << Whence << ": "; - errs() << Message << "\n"; - if (!Hint.empty()) - WithColor::note() << Hint << "\n"; - ::exit(1); -} - -static void exitWithError(Error E, StringRef Whence = "") { - if (E.isA()) { - handleAllErrors(std::move(E), [&](const InstrProfError &IPE) { - instrprof_error instrError = IPE.get(); - StringRef Hint = ""; - if (instrError == instrprof_error::unrecognized_format) { - // Hint for common error of forgetting -sample for sample profiles. - Hint = "Perhaps you forgot to use the -sample option?"; - } - exitWithError(IPE.message(), Whence, Hint); - }); - } - - exitWithError(toString(std::move(E)), Whence); -} - -static void exitWithErrorCode(std::error_code EC, StringRef Whence = "") { - exitWithError(EC.message(), Whence); -} - -namespace { -enum ProfileKinds { instr, sample }; -enum FailureMode { failIfAnyAreInvalid, failIfAllAreInvalid }; -} - -static void warnOrExitGivenError(FailureMode FailMode, std::error_code EC, - StringRef Whence = "") { - if (FailMode == failIfAnyAreInvalid) - exitWithErrorCode(EC, Whence); - else - warn(EC.message(), Whence); -} - -static void handleMergeWriterError(Error E, StringRef WhenceFile = "", - StringRef WhenceFunction = "", - bool ShowHint = true) { - if (!WhenceFile.empty()) - errs() << WhenceFile << ": "; - if (!WhenceFunction.empty()) - errs() << WhenceFunction << ": "; - - auto IPE = instrprof_error::success; - E = handleErrors(std::move(E), - [&IPE](std::unique_ptr E) -> Error { - IPE = E->get(); - return Error(std::move(E)); - }); - errs() << toString(std::move(E)) << "\n"; - - if (ShowHint) { - StringRef Hint = ""; - if (IPE != instrprof_error::success) { - switch (IPE) { - case instrprof_error::hash_mismatch: - case instrprof_error::count_mismatch: - case instrprof_error::value_site_count_mismatch: - Hint = "Make sure that all profile data to be merged is generated " - "from the same binary."; - break; - default: - break; - } - } - - if (!Hint.empty()) - errs() << Hint << "\n"; - } -} - -namespace { -/// A remapper from original symbol names to new symbol names based on a file -/// containing a list of mappings from old name to new name. -class SymbolRemapper { - std::unique_ptr File; - DenseMap RemappingTable; - -public: - /// Build a SymbolRemapper from a file containing a list of old/new symbols. - static std::unique_ptr create(StringRef InputFile) { - auto BufOrError = MemoryBuffer::getFileOrSTDIN(InputFile); - if (!BufOrError) - exitWithErrorCode(BufOrError.getError(), InputFile); - - auto Remapper = std::make_unique(); - Remapper->File = std::move(BufOrError.get()); - - for (line_iterator LineIt(*Remapper->File, /*SkipBlanks=*/true, '#'); - !LineIt.is_at_eof(); ++LineIt) { - std::pair Parts = LineIt->split(' '); - if (Parts.first.empty() || Parts.second.empty() || - Parts.second.count(' ')) { - exitWithError("unexpected line in remapping file", - (InputFile + ":" + Twine(LineIt.line_number())).str(), - "expected 'old_symbol new_symbol'"); - } - Remapper->RemappingTable.insert(Parts); - } - return Remapper; - } - - /// Attempt to map the given old symbol into a new symbol. - /// - /// \return The new symbol, or \p Name if no such symbol was found. - StringRef operator()(StringRef Name) { - StringRef New = RemappingTable.lookup(Name); - return New.empty() ? Name : New; - } -}; -} - -struct WeightedFile { - std::string Filename; - uint64_t Weight; -}; -typedef SmallVector WeightedFileVector; - -/// Keep track of merged data and reported errors. -struct WriterContext { - std::mutex Lock; - InstrProfWriter Writer; - std::vector> Errors; - std::mutex &ErrLock; - SmallSet &WriterErrorCodes; - - WriterContext(bool IsSparse, std::mutex &ErrLock, - SmallSet &WriterErrorCodes) - : Lock(), Writer(IsSparse), Errors(), ErrLock(ErrLock), - WriterErrorCodes(WriterErrorCodes) {} -}; - -/// Computer the overlap b/w profile BaseFilename and TestFileName, -/// and store the program level result to Overlap. -static void overlapInput(const std::string &BaseFilename, - const std::string &TestFilename, WriterContext *WC, - OverlapStats &Overlap, - const OverlapFuncFilters &FuncFilter, - raw_fd_ostream &OS, bool IsCS) { - auto ReaderOrErr = InstrProfReader::create(TestFilename); - if (Error E = ReaderOrErr.takeError()) { - // Skip the empty profiles by returning sliently. - instrprof_error IPE = InstrProfError::take(std::move(E)); - if (IPE != instrprof_error::empty_raw_profile) - WC->Errors.emplace_back(make_error(IPE), TestFilename); - return; - } - - auto Reader = std::move(ReaderOrErr.get()); - for (auto &I : *Reader) { - OverlapStats FuncOverlap(OverlapStats::FunctionLevel); - FuncOverlap.setFuncInfo(I.Name, I.Hash); - - WC->Writer.overlapRecord(std::move(I), Overlap, FuncOverlap, FuncFilter); - FuncOverlap.dump(OS); - } -} - -/// Load an input into a writer context. -static void loadInput(const WeightedFile &Input, SymbolRemapper *Remapper, - WriterContext *WC) { - std::unique_lock CtxGuard{WC->Lock}; - - // Copy the filename, because llvm::ThreadPool copied the input "const - // WeightedFile &" by value, making a reference to the filename within it - // invalid outside of this packaged task. - std::string Filename = Input.Filename; - - auto ReaderOrErr = InstrProfReader::create(Input.Filename); - if (Error E = ReaderOrErr.takeError()) { - // Skip the empty profiles by returning sliently. - instrprof_error IPE = InstrProfError::take(std::move(E)); - if (IPE != instrprof_error::empty_raw_profile) - WC->Errors.emplace_back(make_error(IPE), Filename); - return; - } - - auto Reader = std::move(ReaderOrErr.get()); - bool IsIRProfile = Reader->isIRLevelProfile(); - bool HasCSIRProfile = Reader->hasCSIRLevelProfile(); - if (WC->Writer.setIsIRLevelProfile(IsIRProfile, HasCSIRProfile)) { - WC->Errors.emplace_back( - make_error( - "Merge IR generated profile with Clang generated profile.", - std::error_code()), - Filename); - return; - } - - for (auto &I : *Reader) { - if (Remapper) - I.Name = (*Remapper)(I.Name); - const StringRef FuncName = I.Name; - bool Reported = false; - WC->Writer.addRecord(std::move(I), Input.Weight, [&](Error E) { - if (Reported) { - consumeError(std::move(E)); - return; - } - Reported = true; - // Only show hint the first time an error occurs. - instrprof_error IPE = InstrProfError::take(std::move(E)); - std::unique_lock ErrGuard{WC->ErrLock}; - bool firstTime = WC->WriterErrorCodes.insert(IPE).second; - handleMergeWriterError(make_error(IPE), Input.Filename, - FuncName, firstTime); - }); - } - if (Reader->hasError()) - if (Error E = Reader->getError()) - WC->Errors.emplace_back(std::move(E), Filename); -} - -/// Merge the \p Src writer context into \p Dst. -static void mergeWriterContexts(WriterContext *Dst, WriterContext *Src) { - for (auto &ErrorPair : Src->Errors) - Dst->Errors.push_back(std::move(ErrorPair)); - Src->Errors.clear(); - - Dst->Writer.mergeRecordsFromWriter(std::move(Src->Writer), [&](Error E) { - instrprof_error IPE = InstrProfError::take(std::move(E)); - std::unique_lock ErrGuard{Dst->ErrLock}; - bool firstTime = Dst->WriterErrorCodes.insert(IPE).second; - if (firstTime) - warn(toString(make_error(IPE))); - }); -} - -static void mergeInstrProfile(const WeightedFileVector &Inputs, - SymbolRemapper *Remapper, - StringRef OutputFilename, - ProfileFormat OutputFormat, bool OutputSparse, - unsigned NumThreads, FailureMode FailMode) { - if (OutputFilename.compare("-") == 0) - exitWithError("Cannot write indexed profdata format to stdout."); - - if (OutputFormat != PF_Binary && OutputFormat != PF_Compact_Binary && - OutputFormat != PF_Ext_Binary && OutputFormat != PF_Text) - exitWithError("Unknown format is specified."); - - std::mutex ErrorLock; - SmallSet WriterErrorCodes; - - // If NumThreads is not specified, auto-detect a good default. - if (NumThreads == 0) - NumThreads = - std::min(hardware_concurrency(), unsigned((Inputs.size() + 1) / 2)); - - // Initialize the writer contexts. - SmallVector, 4> Contexts; - for (unsigned I = 0; I < NumThreads; ++I) - Contexts.emplace_back(std::make_unique( - OutputSparse, ErrorLock, WriterErrorCodes)); - - if (NumThreads == 1) { - for (const auto &Input : Inputs) - loadInput(Input, Remapper, Contexts[0].get()); - } else { - ThreadPool Pool(NumThreads); - - // Load the inputs in parallel (N/NumThreads serial steps). - unsigned Ctx = 0; - for (const auto &Input : Inputs) { - Pool.async(loadInput, Input, Remapper, Contexts[Ctx].get()); - Ctx = (Ctx + 1) % NumThreads; - } - Pool.wait(); - - // Merge the writer contexts together (~ lg(NumThreads) serial steps). - unsigned Mid = Contexts.size() / 2; - unsigned End = Contexts.size(); - assert(Mid > 0 && "Expected more than one context"); - do { - for (unsigned I = 0; I < Mid; ++I) - Pool.async(mergeWriterContexts, Contexts[I].get(), - Contexts[I + Mid].get()); - Pool.wait(); - if (End & 1) { - Pool.async(mergeWriterContexts, Contexts[0].get(), - Contexts[End - 1].get()); - Pool.wait(); - } - End = Mid; - Mid /= 2; - } while (Mid > 0); - } - - // Handle deferred errors encountered during merging. If the number of errors - // is equal to the number of inputs the merge failed. - unsigned NumErrors = 0; - for (std::unique_ptr &WC : Contexts) { - for (auto &ErrorPair : WC->Errors) { - ++NumErrors; - warn(toString(std::move(ErrorPair.first)), ErrorPair.second); - } - } - if (NumErrors == Inputs.size() || - (NumErrors > 0 && FailMode == failIfAnyAreInvalid)) - exitWithError("No profiles could be merged."); - - std::error_code EC; - raw_fd_ostream Output(OutputFilename.data(), EC, sys::fs::OF_None); - if (EC) - exitWithErrorCode(EC, OutputFilename); - - InstrProfWriter &Writer = Contexts[0]->Writer; - if (OutputFormat == PF_Text) { - if (Error E = Writer.writeText(Output)) - exitWithError(std::move(E)); - } else { - Writer.write(Output); - } -} - -/// Make a copy of the given function samples with all symbol names remapped -/// by the provided symbol remapper. -static sampleprof::FunctionSamples -remapSamples(const sampleprof::FunctionSamples &Samples, - SymbolRemapper &Remapper, sampleprof_error &Error) { - sampleprof::FunctionSamples Result; - Result.setName(Remapper(Samples.getName())); - Result.addTotalSamples(Samples.getTotalSamples()); - Result.addHeadSamples(Samples.getHeadSamples()); - for (const auto &BodySample : Samples.getBodySamples()) { - Result.addBodySamples(BodySample.first.LineOffset, - BodySample.first.Discriminator, - BodySample.second.getSamples()); - for (const auto &Target : BodySample.second.getCallTargets()) { - Result.addCalledTargetSamples(BodySample.first.LineOffset, - BodySample.first.Discriminator, - Remapper(Target.first()), Target.second); - } - } - for (const auto &CallsiteSamples : Samples.getCallsiteSamples()) { - sampleprof::FunctionSamplesMap &Target = - Result.functionSamplesAt(CallsiteSamples.first); - for (const auto &Callsite : CallsiteSamples.second) { - sampleprof::FunctionSamples Remapped = - remapSamples(Callsite.second, Remapper, Error); - MergeResult(Error, Target[Remapped.getName()].merge(Remapped)); - } - } - return Result; -} - -static sampleprof::SampleProfileFormat FormatMap[] = { - sampleprof::SPF_None, - sampleprof::SPF_Text, - sampleprof::SPF_Compact_Binary, - sampleprof::SPF_Ext_Binary, - sampleprof::SPF_GCC, - sampleprof::SPF_Binary}; - -static std::unique_ptr -getInputFileBuf(const StringRef &InputFile) { - if (InputFile == "") - return {}; - - auto BufOrError = MemoryBuffer::getFileOrSTDIN(InputFile); - if (!BufOrError) - exitWithErrorCode(BufOrError.getError(), InputFile); - - return std::move(*BufOrError); -} - -static void populateProfileSymbolList(MemoryBuffer *Buffer, - sampleprof::ProfileSymbolList &PSL) { - if (!Buffer) - return; - - SmallVector SymbolVec; - StringRef Data = Buffer->getBuffer(); - Data.split(SymbolVec, '\n', /*MaxSplit=*/-1, /*KeepEmpty=*/false); - - for (StringRef symbol : SymbolVec) - PSL.add(symbol); -} - -static void handleExtBinaryWriter(sampleprof::SampleProfileWriter &Writer, - ProfileFormat OutputFormat, - MemoryBuffer *Buffer, - sampleprof::ProfileSymbolList &WriterList, - bool CompressAllSections) { - populateProfileSymbolList(Buffer, WriterList); - if (WriterList.size() > 0 && OutputFormat != PF_Ext_Binary) - warn("Profile Symbol list is not empty but the output format is not " - "ExtBinary format. The list will be lost in the output. "); - - Writer.setProfileSymbolList(&WriterList); - - if (CompressAllSections) { - if (OutputFormat != PF_Ext_Binary) { - warn("-compress-all-section is ignored. Specify -extbinary to enable it"); - } else { - auto ExtBinaryWriter = - static_cast(&Writer); - ExtBinaryWriter->setToCompressAllSections(); - } - } -} - -static void mergeSampleProfile(const WeightedFileVector &Inputs, - SymbolRemapper *Remapper, - StringRef OutputFilename, - ProfileFormat OutputFormat, - StringRef ProfileSymbolListFile, - bool CompressAllSections, FailureMode FailMode) { - using namespace sampleprof; - StringMap ProfileMap; - SmallVector, 5> Readers; - LLVMContext Context; - sampleprof::ProfileSymbolList WriterList; - for (const auto &Input : Inputs) { - auto ReaderOrErr = SampleProfileReader::create(Input.Filename, Context); - if (std::error_code EC = ReaderOrErr.getError()) { - warnOrExitGivenError(FailMode, EC, Input.Filename); - continue; - } - - // We need to keep the readers around until after all the files are - // read so that we do not lose the function names stored in each - // reader's memory. The function names are needed to write out the - // merged profile map. - Readers.push_back(std::move(ReaderOrErr.get())); - const auto Reader = Readers.back().get(); - if (std::error_code EC = Reader->read()) { - warnOrExitGivenError(FailMode, EC, Input.Filename); - Readers.pop_back(); - continue; - } - - StringMap &Profiles = Reader->getProfiles(); - for (StringMap::iterator I = Profiles.begin(), - E = Profiles.end(); - I != E; ++I) { - sampleprof_error Result = sampleprof_error::success; - FunctionSamples Remapped = - Remapper ? remapSamples(I->second, *Remapper, Result) - : FunctionSamples(); - FunctionSamples &Samples = Remapper ? Remapped : I->second; - StringRef FName = Samples.getName(); - MergeResult(Result, ProfileMap[FName].merge(Samples, Input.Weight)); - if (Result != sampleprof_error::success) { - std::error_code EC = make_error_code(Result); - handleMergeWriterError(errorCodeToError(EC), Input.Filename, FName); - } - } - - std::unique_ptr ReaderList = - Reader->getProfileSymbolList(); - if (ReaderList) - WriterList.merge(*ReaderList); - } - auto WriterOrErr = - SampleProfileWriter::create(OutputFilename, FormatMap[OutputFormat]); - if (std::error_code EC = WriterOrErr.getError()) - exitWithErrorCode(EC, OutputFilename); - - auto Writer = std::move(WriterOrErr.get()); - // WriterList will have StringRef refering to string in Buffer. - // Make sure Buffer lives as long as WriterList. - auto Buffer = getInputFileBuf(ProfileSymbolListFile); - handleExtBinaryWriter(*Writer, OutputFormat, Buffer.get(), WriterList, - CompressAllSections); - Writer->write(ProfileMap); -} - -static WeightedFile parseWeightedFile(const StringRef &WeightedFilename) { - StringRef WeightStr, FileName; - std::tie(WeightStr, FileName) = WeightedFilename.split(','); - - uint64_t Weight; - if (WeightStr.getAsInteger(10, Weight) || Weight < 1) - exitWithError("Input weight must be a positive integer."); - - return {FileName, Weight}; -} - -static void addWeightedInput(WeightedFileVector &WNI, const WeightedFile &WF) { - StringRef Filename = WF.Filename; - uint64_t Weight = WF.Weight; - - // If it's STDIN just pass it on. - if (Filename == "-") { - WNI.push_back({Filename, Weight}); - return; - } - - llvm::sys::fs::file_status Status; - llvm::sys::fs::status(Filename, Status); - if (!llvm::sys::fs::exists(Status)) - exitWithErrorCode(make_error_code(errc::no_such_file_or_directory), - Filename); - // If it's a source file, collect it. - if (llvm::sys::fs::is_regular_file(Status)) { - WNI.push_back({Filename, Weight}); - return; - } - - if (llvm::sys::fs::is_directory(Status)) { - std::error_code EC; - for (llvm::sys::fs::recursive_directory_iterator F(Filename, EC), E; - F != E && !EC; F.increment(EC)) { - if (llvm::sys::fs::is_regular_file(F->path())) { - addWeightedInput(WNI, {F->path(), Weight}); - } - } - if (EC) - exitWithErrorCode(EC, Filename); - } -} - -static void parseInputFilenamesFile(MemoryBuffer *Buffer, - WeightedFileVector &WFV) { - if (!Buffer) - return; - - SmallVector Entries; - StringRef Data = Buffer->getBuffer(); - Data.split(Entries, '\n', /*MaxSplit=*/-1, /*KeepEmpty=*/false); - for (const StringRef &FileWeightEntry : Entries) { - StringRef SanitizedEntry = FileWeightEntry.trim(" \t\v\f\r"); - // Skip comments. - if (SanitizedEntry.startswith("#")) - continue; - // If there's no comma, it's an unweighted profile. - else if (SanitizedEntry.find(',') == StringRef::npos) - addWeightedInput(WFV, {SanitizedEntry, 1}); - else - addWeightedInput(WFV, parseWeightedFile(SanitizedEntry)); - } -} - -static int merge_main(int argc, const char *argv[]) { - cl::list InputFilenames(cl::Positional, - cl::desc("")); - cl::list WeightedInputFilenames("weighted-input", - cl::desc(",")); - cl::opt InputFilenamesFile( - "input-files", cl::init(""), - cl::desc("Path to file containing newline-separated " - "[,] entries")); - cl::alias InputFilenamesFileA("f", cl::desc("Alias for --input-files"), - cl::aliasopt(InputFilenamesFile)); - cl::opt DumpInputFileList( - "dump-input-file-list", cl::init(false), cl::Hidden, - cl::desc("Dump the list of input files and their weights, then exit")); - cl::opt RemappingFile("remapping-file", cl::value_desc("file"), - cl::desc("Symbol remapping file")); - cl::alias RemappingFileA("r", cl::desc("Alias for --remapping-file"), - cl::aliasopt(RemappingFile)); - cl::opt OutputFilename("output", cl::value_desc("output"), - cl::init("-"), cl::Required, - cl::desc("Output file")); - cl::alias OutputFilenameA("o", cl::desc("Alias for --output"), - cl::aliasopt(OutputFilename)); - cl::opt ProfileKind( - cl::desc("Profile kind:"), cl::init(instr), - cl::values(clEnumVal(instr, "Instrumentation profile (default)"), - clEnumVal(sample, "Sample profile"))); - cl::opt OutputFormat( - cl::desc("Format of output profile"), cl::init(PF_Binary), - cl::values( - clEnumValN(PF_Binary, "binary", "Binary encoding (default)"), - clEnumValN(PF_Compact_Binary, "compbinary", - "Compact binary encoding"), - clEnumValN(PF_Ext_Binary, "extbinary", "Extensible binary encoding"), - clEnumValN(PF_Text, "text", "Text encoding"), - clEnumValN(PF_GCC, "gcc", - "GCC encoding (only meaningful for -sample)"))); - cl::opt FailureMode( - "failure-mode", cl::init(failIfAnyAreInvalid), cl::desc("Failure mode:"), - cl::values(clEnumValN(failIfAnyAreInvalid, "any", - "Fail if any profile is invalid."), - clEnumValN(failIfAllAreInvalid, "all", - "Fail only if all profiles are invalid."))); - cl::opt OutputSparse("sparse", cl::init(false), - cl::desc("Generate a sparse profile (only meaningful for -instr)")); - cl::opt NumThreads( - "num-threads", cl::init(0), - cl::desc("Number of merge threads to use (default: autodetect)")); - cl::alias NumThreadsA("j", cl::desc("Alias for --num-threads"), - cl::aliasopt(NumThreads)); - cl::opt ProfileSymbolListFile( - "prof-sym-list", cl::init(""), - cl::desc("Path to file containing the list of function symbols " - "used to populate profile symbol list")); - cl::opt CompressAllSections( - "compress-all-sections", cl::init(false), cl::Hidden, - cl::desc("Compress all sections when writing the profile (only " - "meaningful for -extbinary)")); - - cl::ParseCommandLineOptions(argc, argv, "LLVM profile data merger\n"); - - WeightedFileVector WeightedInputs; - for (StringRef Filename : InputFilenames) - addWeightedInput(WeightedInputs, {Filename, 1}); - for (StringRef WeightedFilename : WeightedInputFilenames) - addWeightedInput(WeightedInputs, parseWeightedFile(WeightedFilename)); - - // Make sure that the file buffer stays alive for the duration of the - // weighted input vector's lifetime. - auto Buffer = getInputFileBuf(InputFilenamesFile); - parseInputFilenamesFile(Buffer.get(), WeightedInputs); - - if (WeightedInputs.empty()) - exitWithError("No input files specified. See " + - sys::path::filename(argv[0]) + " -help"); - - if (DumpInputFileList) { - for (auto &WF : WeightedInputs) - outs() << WF.Weight << "," << WF.Filename << "\n"; - return 0; - } - - std::unique_ptr Remapper; - if (!RemappingFile.empty()) - Remapper = SymbolRemapper::create(RemappingFile); - - if (ProfileKind == instr) - mergeInstrProfile(WeightedInputs, Remapper.get(), OutputFilename, - OutputFormat, OutputSparse, NumThreads, FailureMode); - else - mergeSampleProfile(WeightedInputs, Remapper.get(), OutputFilename, - OutputFormat, ProfileSymbolListFile, CompressAllSections, - FailureMode); - - return 0; -} - -/// Computer the overlap b/w profile BaseFilename and profile TestFilename. -static void overlapInstrProfile(const std::string &BaseFilename, - const std::string &TestFilename, - const OverlapFuncFilters &FuncFilter, - raw_fd_ostream &OS, bool IsCS) { - std::mutex ErrorLock; - SmallSet WriterErrorCodes; - WriterContext Context(false, ErrorLock, WriterErrorCodes); - WeightedFile WeightedInput{BaseFilename, 1}; - OverlapStats Overlap; - Error E = Overlap.accumulateCounts(BaseFilename, TestFilename, IsCS); - if (E) - exitWithError(std::move(E), "Error in getting profile count sums"); - if (Overlap.Base.CountSum < 1.0f) { - OS << "Sum of edge counts for profile " << BaseFilename << " is 0.\n"; - exit(0); - } - if (Overlap.Test.CountSum < 1.0f) { - OS << "Sum of edge counts for profile " << TestFilename << " is 0.\n"; - exit(0); - } - loadInput(WeightedInput, nullptr, &Context); - overlapInput(BaseFilename, TestFilename, &Context, Overlap, FuncFilter, OS, - IsCS); - Overlap.dump(OS); -} - -static int overlap_main(int argc, const char *argv[]) { - cl::opt BaseFilename(cl::Positional, cl::Required, - cl::desc("")); - cl::opt TestFilename(cl::Positional, cl::Required, - cl::desc("")); - cl::opt Output("output", cl::value_desc("output"), cl::init("-"), - cl::desc("Output file")); - cl::alias OutputA("o", cl::desc("Alias for --output"), cl::aliasopt(Output)); - cl::opt IsCS("cs", cl::init(false), - cl::desc("For context sensitive counts")); - cl::opt ValueCutoff( - "value-cutoff", cl::init(-1), - cl::desc( - "Function level overlap information for every function in test " - "profile with max count value greater then the parameter value")); - cl::opt FuncNameFilter( - "function", - cl::desc("Function level overlap information for matching functions")); - cl::ParseCommandLineOptions(argc, argv, "LLVM profile data overlap tool\n"); - - std::error_code EC; - raw_fd_ostream OS(Output.data(), EC, sys::fs::OF_Text); - if (EC) - exitWithErrorCode(EC, Output); - - overlapInstrProfile(BaseFilename, TestFilename, - OverlapFuncFilters{ValueCutoff, FuncNameFilter}, OS, - IsCS); - - return 0; -} - -typedef struct ValueSitesStats { - ValueSitesStats() - : TotalNumValueSites(0), TotalNumValueSitesWithValueProfile(0), - TotalNumValues(0) {} - uint64_t TotalNumValueSites; - uint64_t TotalNumValueSitesWithValueProfile; - uint64_t TotalNumValues; - std::vector ValueSitesHistogram; -} ValueSitesStats; - -static void traverseAllValueSites(const InstrProfRecord &Func, uint32_t VK, - ValueSitesStats &Stats, raw_fd_ostream &OS, - InstrProfSymtab *Symtab) { - uint32_t NS = Func.getNumValueSites(VK); - Stats.TotalNumValueSites += NS; - for (size_t I = 0; I < NS; ++I) { - uint32_t NV = Func.getNumValueDataForSite(VK, I); - std::unique_ptr VD = Func.getValueForSite(VK, I); - Stats.TotalNumValues += NV; - if (NV) { - Stats.TotalNumValueSitesWithValueProfile++; - if (NV > Stats.ValueSitesHistogram.size()) - Stats.ValueSitesHistogram.resize(NV, 0); - Stats.ValueSitesHistogram[NV - 1]++; - } - - uint64_t SiteSum = 0; - for (uint32_t V = 0; V < NV; V++) - SiteSum += VD[V].Count; - if (SiteSum == 0) - SiteSum = 1; - - for (uint32_t V = 0; V < NV; V++) { - OS << "\t[ " << format("%2u", I) << ", "; - if (Symtab == nullptr) - OS << format("%4" PRIu64, VD[V].Value); - else - OS << Symtab->getFuncName(VD[V].Value); - OS << ", " << format("%10" PRId64, VD[V].Count) << " ] (" - << format("%.2f%%", (VD[V].Count * 100.0 / SiteSum)) << ")\n"; - } - } -} - -static void showValueSitesStats(raw_fd_ostream &OS, uint32_t VK, - ValueSitesStats &Stats) { - OS << " Total number of sites: " << Stats.TotalNumValueSites << "\n"; - OS << " Total number of sites with values: " - << Stats.TotalNumValueSitesWithValueProfile << "\n"; - OS << " Total number of profiled values: " << Stats.TotalNumValues << "\n"; - - OS << " Value sites histogram:\n\tNumTargets, SiteCount\n"; - for (unsigned I = 0; I < Stats.ValueSitesHistogram.size(); I++) { - if (Stats.ValueSitesHistogram[I] > 0) - OS << "\t" << I + 1 << ", " << Stats.ValueSitesHistogram[I] << "\n"; - } -} - -static int showInstrProfile(const std::string &Filename, bool ShowCounts, - uint32_t TopN, bool ShowIndirectCallTargets, - bool ShowMemOPSizes, bool ShowDetailedSummary, - std::vector DetailedSummaryCutoffs, - bool ShowAllFunctions, bool ShowCS, - uint64_t ValueCutoff, bool OnlyListBelow, - const std::string &ShowFunction, bool TextFormat, - raw_fd_ostream &OS) { - auto ReaderOrErr = InstrProfReader::create(Filename); - std::vector Cutoffs = std::move(DetailedSummaryCutoffs); - if (ShowDetailedSummary && Cutoffs.empty()) { - Cutoffs = {800000, 900000, 950000, 990000, 999000, 999900, 999990}; - } - InstrProfSummaryBuilder Builder(std::move(Cutoffs)); - if (Error E = ReaderOrErr.takeError()) - exitWithError(std::move(E), Filename); - - auto Reader = std::move(ReaderOrErr.get()); - bool IsIRInstr = Reader->isIRLevelProfile(); - size_t ShownFunctions = 0; - size_t BelowCutoffFunctions = 0; - int NumVPKind = IPVK_Last - IPVK_First + 1; - std::vector VPStats(NumVPKind); - - auto MinCmp = [](const std::pair &v1, - const std::pair &v2) { - return v1.second > v2.second; - }; - - std::priority_queue, - std::vector>, - decltype(MinCmp)> - HottestFuncs(MinCmp); - - if (!TextFormat && OnlyListBelow) { - OS << "The list of functions with the maximum counter less than " - << ValueCutoff << ":\n"; - } - - // Add marker so that IR-level instrumentation round-trips properly. - if (TextFormat && IsIRInstr) - OS << ":ir\n"; - - for (const auto &Func : *Reader) { - if (Reader->isIRLevelProfile()) { - bool FuncIsCS = NamedInstrProfRecord::hasCSFlagInHash(Func.Hash); - if (FuncIsCS != ShowCS) - continue; - } - bool Show = - ShowAllFunctions || (!ShowFunction.empty() && - Func.Name.find(ShowFunction) != Func.Name.npos); - - bool doTextFormatDump = (Show && TextFormat); - - if (doTextFormatDump) { - InstrProfSymtab &Symtab = Reader->getSymtab(); - InstrProfWriter::writeRecordInText(Func.Name, Func.Hash, Func, Symtab, - OS); - continue; - } - - assert(Func.Counts.size() > 0 && "function missing entry counter"); - Builder.addRecord(Func); - - uint64_t FuncMax = 0; - uint64_t FuncSum = 0; - for (size_t I = 0, E = Func.Counts.size(); I < E; ++I) { - FuncMax = std::max(FuncMax, Func.Counts[I]); - FuncSum += Func.Counts[I]; - } - - if (FuncMax < ValueCutoff) { - ++BelowCutoffFunctions; - if (OnlyListBelow) { - OS << " " << Func.Name << ": (Max = " << FuncMax - << " Sum = " << FuncSum << ")\n"; - } - continue; - } else if (OnlyListBelow) - continue; - - if (TopN) { - if (HottestFuncs.size() == TopN) { - if (HottestFuncs.top().second < FuncMax) { - HottestFuncs.pop(); - HottestFuncs.emplace(std::make_pair(std::string(Func.Name), FuncMax)); - } - } else - HottestFuncs.emplace(std::make_pair(std::string(Func.Name), FuncMax)); - } - - if (Show) { - if (!ShownFunctions) - OS << "Counters:\n"; - - ++ShownFunctions; - - OS << " " << Func.Name << ":\n" - << " Hash: " << format("0x%016" PRIx64, Func.Hash) << "\n" - << " Counters: " << Func.Counts.size() << "\n"; - if (!IsIRInstr) - OS << " Function count: " << Func.Counts[0] << "\n"; - - if (ShowIndirectCallTargets) - OS << " Indirect Call Site Count: " - << Func.getNumValueSites(IPVK_IndirectCallTarget) << "\n"; - - uint32_t NumMemOPCalls = Func.getNumValueSites(IPVK_MemOPSize); - if (ShowMemOPSizes && NumMemOPCalls > 0) - OS << " Number of Memory Intrinsics Calls: " << NumMemOPCalls - << "\n"; - - if (ShowCounts) { - OS << " Block counts: ["; - size_t Start = (IsIRInstr ? 0 : 1); - for (size_t I = Start, E = Func.Counts.size(); I < E; ++I) { - OS << (I == Start ? "" : ", ") << Func.Counts[I]; - } - OS << "]\n"; - } - - if (ShowIndirectCallTargets) { - OS << " Indirect Target Results:\n"; - traverseAllValueSites(Func, IPVK_IndirectCallTarget, - VPStats[IPVK_IndirectCallTarget], OS, - &(Reader->getSymtab())); - } - - if (ShowMemOPSizes && NumMemOPCalls > 0) { - OS << " Memory Intrinsic Size Results:\n"; - traverseAllValueSites(Func, IPVK_MemOPSize, VPStats[IPVK_MemOPSize], OS, - nullptr); - } - } - } - if (Reader->hasError()) - exitWithError(Reader->getError(), Filename); - - if (TextFormat) - return 0; - std::unique_ptr PS(Builder.getSummary()); - OS << "Instrumentation level: " - << (Reader->isIRLevelProfile() ? "IR" : "Front-end") << "\n"; - if (ShowAllFunctions || !ShowFunction.empty()) - OS << "Functions shown: " << ShownFunctions << "\n"; - OS << "Total functions: " << PS->getNumFunctions() << "\n"; - if (ValueCutoff > 0) { - OS << "Number of functions with maximum count (< " << ValueCutoff - << "): " << BelowCutoffFunctions << "\n"; - OS << "Number of functions with maximum count (>= " << ValueCutoff - << "): " << PS->getNumFunctions() - BelowCutoffFunctions << "\n"; - } - OS << "Maximum function count: " << PS->getMaxFunctionCount() << "\n"; - OS << "Maximum internal block count: " << PS->getMaxInternalCount() << "\n"; - - if (TopN) { - std::vector> SortedHottestFuncs; - while (!HottestFuncs.empty()) { - SortedHottestFuncs.emplace_back(HottestFuncs.top()); - HottestFuncs.pop(); - } - OS << "Top " << TopN - << " functions with the largest internal block counts: \n"; - for (auto &hotfunc : llvm::reverse(SortedHottestFuncs)) - OS << " " << hotfunc.first << ", max count = " << hotfunc.second << "\n"; - } - - if (ShownFunctions && ShowIndirectCallTargets) { - OS << "Statistics for indirect call sites profile:\n"; - showValueSitesStats(OS, IPVK_IndirectCallTarget, - VPStats[IPVK_IndirectCallTarget]); - } - - if (ShownFunctions && ShowMemOPSizes) { - OS << "Statistics for memory intrinsic calls sizes profile:\n"; - showValueSitesStats(OS, IPVK_MemOPSize, VPStats[IPVK_MemOPSize]); - } - - if (ShowDetailedSummary) { - OS << "Detailed summary:\n"; - OS << "Total number of blocks: " << PS->getNumCounts() << "\n"; - OS << "Total count: " << PS->getTotalCount() << "\n"; - for (auto Entry : PS->getDetailedSummary()) { - OS << Entry.NumCounts << " blocks with count >= " << Entry.MinCount - << " account for " - << format("%0.6g", (float)Entry.Cutoff / ProfileSummary::Scale * 100) - << " percentage of the total counts.\n"; - } - } - return 0; -} - -static void showSectionInfo(sampleprof::SampleProfileReader *Reader, - raw_fd_ostream &OS) { - if (!Reader->dumpSectionInfo(OS)) { - WithColor::warning() << "-show-sec-info-only is only supported for " - << "sample profile in extbinary format and is " - << "ignored for other formats.\n"; - return; - } -} - -static int showSampleProfile(const std::string &Filename, bool ShowCounts, - bool ShowAllFunctions, - const std::string &ShowFunction, - bool ShowProfileSymbolList, - bool ShowSectionInfoOnly, raw_fd_ostream &OS) { - using namespace sampleprof; - LLVMContext Context; - auto ReaderOrErr = SampleProfileReader::create(Filename, Context); - if (std::error_code EC = ReaderOrErr.getError()) - exitWithErrorCode(EC, Filename); - - auto Reader = std::move(ReaderOrErr.get()); - - if (ShowSectionInfoOnly) { - showSectionInfo(Reader.get(), OS); - return 0; - } - - if (std::error_code EC = Reader->read()) - exitWithErrorCode(EC, Filename); - - if (ShowAllFunctions || ShowFunction.empty()) - Reader->dump(OS); - else - Reader->dumpFunctionProfile(ShowFunction, OS); - - if (ShowProfileSymbolList) { - std::unique_ptr ReaderList = - Reader->getProfileSymbolList(); - ReaderList->dump(OS); - } - - return 0; -} - -static int show_main(int argc, const char *argv[]) { - cl::opt Filename(cl::Positional, cl::Required, - cl::desc("")); - - cl::opt ShowCounts("counts", cl::init(false), - cl::desc("Show counter values for shown functions")); - cl::opt TextFormat( - "text", cl::init(false), - cl::desc("Show instr profile data in text dump format")); - cl::opt ShowIndirectCallTargets( - "ic-targets", cl::init(false), - cl::desc("Show indirect call site target values for shown functions")); - cl::opt ShowMemOPSizes( - "memop-sizes", cl::init(false), - cl::desc("Show the profiled sizes of the memory intrinsic calls " - "for shown functions")); - cl::opt ShowDetailedSummary("detailed-summary", cl::init(false), - cl::desc("Show detailed profile summary")); - cl::list DetailedSummaryCutoffs( - cl::CommaSeparated, "detailed-summary-cutoffs", - cl::desc( - "Cutoff percentages (times 10000) for generating detailed summary"), - cl::value_desc("800000,901000,999999")); - cl::opt ShowAllFunctions("all-functions", cl::init(false), - cl::desc("Details for every function")); - cl::opt ShowCS("showcs", cl::init(false), - cl::desc("Show context sensitive counts")); - cl::opt ShowFunction("function", - cl::desc("Details for matching functions")); - - cl::opt OutputFilename("output", cl::value_desc("output"), - cl::init("-"), cl::desc("Output file")); - cl::alias OutputFilenameA("o", cl::desc("Alias for --output"), - cl::aliasopt(OutputFilename)); - cl::opt ProfileKind( - cl::desc("Profile kind:"), cl::init(instr), - cl::values(clEnumVal(instr, "Instrumentation profile (default)"), - clEnumVal(sample, "Sample profile"))); - cl::opt TopNFunctions( - "topn", cl::init(0), - cl::desc("Show the list of functions with the largest internal counts")); - cl::opt ValueCutoff( - "value-cutoff", cl::init(0), - cl::desc("Set the count value cutoff. Functions with the maximum count " - "less than this value will not be printed out. (Default is 0)")); - cl::opt OnlyListBelow( - "list-below-cutoff", cl::init(false), - cl::desc("Only output names of functions whose max count values are " - "below the cutoff value")); - cl::opt ShowProfileSymbolList( - "show-prof-sym-list", cl::init(false), - cl::desc("Show profile symbol list if it exists in the profile. ")); - cl::opt ShowSectionInfoOnly( - "show-sec-info-only", cl::init(false), - cl::desc("Show the information of each section in the sample profile. " - "The flag is only usable when the sample profile is in " - "extbinary format")); - - cl::ParseCommandLineOptions(argc, argv, "LLVM profile data summary\n"); - - if (OutputFilename.empty()) - OutputFilename = "-"; - - if (!Filename.compare(OutputFilename)) { - errs() << sys::path::filename(argv[0]) - << ": Input file name cannot be the same as the output file name!\n"; - return 1; - } - - std::error_code EC; - raw_fd_ostream OS(OutputFilename.data(), EC, sys::fs::OF_Text); - if (EC) - exitWithErrorCode(EC, OutputFilename); - - if (ShowAllFunctions && !ShowFunction.empty()) - WithColor::warning() << "-function argument ignored: showing all functions\n"; - - if (ProfileKind == instr) - return showInstrProfile(Filename, ShowCounts, TopNFunctions, - ShowIndirectCallTargets, ShowMemOPSizes, - ShowDetailedSummary, DetailedSummaryCutoffs, - ShowAllFunctions, ShowCS, ValueCutoff, - OnlyListBelow, ShowFunction, TextFormat, OS); - else - return showSampleProfile(Filename, ShowCounts, ShowAllFunctions, - ShowFunction, ShowProfileSymbolList, - ShowSectionInfoOnly, OS); -} - -int main(int argc, const char *argv[]) { - InitLLVM X(argc, argv); - - StringRef ProgName(sys::path::filename(argv[0])); - if (argc > 1) { - int (*func)(int, const char *[]) = nullptr; - - if (strcmp(argv[1], "merge") == 0) - func = merge_main; - else if (strcmp(argv[1], "show") == 0) - func = show_main; - else if (strcmp(argv[1], "overlap") == 0) - func = overlap_main; - - if (func) { - std::string Invocation(ProgName.str() + " " + argv[1]); - argv[1] = Invocation.c_str(); - return func(argc - 1, argv + 1); - } - - if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "-help") == 0 || - strcmp(argv[1], "--help") == 0) { - - errs() << "OVERVIEW: LLVM profile data tools\n\n" - << "USAGE: " << ProgName << " [args...]\n" - << "USAGE: " << ProgName << " -help\n\n" - << "See each individual command --help for more details.\n" - << "Available commands: merge, show, overlap\n"; - return 0; - } - } - - if (argc < 2) - errs() << ProgName << ": No command specified!\n"; - else - errs() << ProgName << ": Unknown command!\n"; - - errs() << "USAGE: " << ProgName << " [args...]\n"; - return 1; -} diff --git a/tools/ldc-profdata/llvm-profdata-16.0.cpp b/tools/ldc-profdata/llvm-profdata-16.0.cpp new file mode 100644 index 00000000000..57dfd18076c --- /dev/null +++ b/tools/ldc-profdata/llvm-profdata-16.0.cpp @@ -0,0 +1,3013 @@ +//===- llvm-profdata.cpp - LLVM profile data tool -------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// llvm-profdata merges .profdata files. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/SmallSet.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/Object/Binary.h" +#include "llvm/ProfileData/InstrProfCorrelator.h" +#include "llvm/ProfileData/InstrProfReader.h" +#include "llvm/ProfileData/InstrProfWriter.h" +#include "llvm/ProfileData/MemProf.h" +#include "llvm/ProfileData/ProfileCommon.h" +#include "llvm/ProfileData/RawMemProfReader.h" +#include "llvm/ProfileData/SampleProfReader.h" +#include "llvm/ProfileData/SampleProfWriter.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Discriminator.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/FormattedStream.h" +#include "llvm/Support/InitLLVM.h" +#include "llvm/Support/MD5.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/ThreadPool.h" +#include "llvm/Support/Threading.h" +#include "llvm/Support/WithColor.h" +#include "llvm/Support/raw_ostream.h" +#include +#include +#include +#include + +using namespace llvm; + +// We use this string to indicate that there are +// multiple static functions map to the same name. +const std::string DuplicateNameStr = "----"; + +enum ProfileFormat { + PF_None = 0, + PF_Text, + PF_Compact_Binary, + PF_Ext_Binary, + PF_GCC, + PF_Binary +}; + +enum class ShowFormat { Text, Json, Yaml }; + +static void warn(Twine Message, std::string Whence = "", + std::string Hint = "") { + WithColor::warning(); + if (!Whence.empty()) + errs() << Whence << ": "; + errs() << Message << "\n"; + if (!Hint.empty()) + WithColor::note() << Hint << "\n"; +} + +static void warn(Error E, StringRef Whence = "") { + if (E.isA()) { + handleAllErrors(std::move(E), [&](const InstrProfError &IPE) { + warn(IPE.message(), std::string(Whence), std::string("")); + }); + } +} + +static void exitWithError(Twine Message, std::string Whence = "", + std::string Hint = "") { + WithColor::error(); + if (!Whence.empty()) + errs() << Whence << ": "; + errs() << Message << "\n"; + if (!Hint.empty()) + WithColor::note() << Hint << "\n"; + ::exit(1); +} + +static void exitWithError(Error E, StringRef Whence = "") { + if (E.isA()) { + handleAllErrors(std::move(E), [&](const InstrProfError &IPE) { + instrprof_error instrError = IPE.get(); + StringRef Hint = ""; + if (instrError == instrprof_error::unrecognized_format) { + // Hint in case user missed specifying the profile type. + Hint = "Perhaps you forgot to use the --sample or --memory option?"; + } + exitWithError(IPE.message(), std::string(Whence), std::string(Hint)); + }); + return; + } + + exitWithError(toString(std::move(E)), std::string(Whence)); +} + +static void exitWithErrorCode(std::error_code EC, StringRef Whence = "") { + exitWithError(EC.message(), std::string(Whence)); +} + +namespace { +enum ProfileKinds { instr, sample, memory }; +enum FailureMode { failIfAnyAreInvalid, failIfAllAreInvalid }; +} + +static void warnOrExitGivenError(FailureMode FailMode, std::error_code EC, + StringRef Whence = "") { + if (FailMode == failIfAnyAreInvalid) + exitWithErrorCode(EC, Whence); + else + warn(EC.message(), std::string(Whence)); +} + +static void handleMergeWriterError(Error E, StringRef WhenceFile = "", + StringRef WhenceFunction = "", + bool ShowHint = true) { + if (!WhenceFile.empty()) + errs() << WhenceFile << ": "; + if (!WhenceFunction.empty()) + errs() << WhenceFunction << ": "; + + auto IPE = instrprof_error::success; + E = handleErrors(std::move(E), + [&IPE](std::unique_ptr E) -> Error { + IPE = E->get(); + return Error(std::move(E)); + }); + errs() << toString(std::move(E)) << "\n"; + + if (ShowHint) { + StringRef Hint = ""; + if (IPE != instrprof_error::success) { + switch (IPE) { + case instrprof_error::hash_mismatch: + case instrprof_error::count_mismatch: + case instrprof_error::value_site_count_mismatch: + Hint = "Make sure that all profile data to be merged is generated " + "from the same binary."; + break; + default: + break; + } + } + + if (!Hint.empty()) + errs() << Hint << "\n"; + } +} + +namespace { +/// A remapper from original symbol names to new symbol names based on a file +/// containing a list of mappings from old name to new name. +class SymbolRemapper { + std::unique_ptr File; + DenseMap RemappingTable; + +public: + /// Build a SymbolRemapper from a file containing a list of old/new symbols. + static std::unique_ptr create(StringRef InputFile) { + auto BufOrError = MemoryBuffer::getFileOrSTDIN(InputFile); + if (!BufOrError) + exitWithErrorCode(BufOrError.getError(), InputFile); + + auto Remapper = std::make_unique(); + Remapper->File = std::move(BufOrError.get()); + + for (line_iterator LineIt(*Remapper->File, /*SkipBlanks=*/true, '#'); + !LineIt.is_at_eof(); ++LineIt) { + std::pair Parts = LineIt->split(' '); + if (Parts.first.empty() || Parts.second.empty() || + Parts.second.count(' ')) { + exitWithError("unexpected line in remapping file", + (InputFile + ":" + Twine(LineIt.line_number())).str(), + "expected 'old_symbol new_symbol'"); + } + Remapper->RemappingTable.insert(Parts); + } + return Remapper; + } + + /// Attempt to map the given old symbol into a new symbol. + /// + /// \return The new symbol, or \p Name if no such symbol was found. + StringRef operator()(StringRef Name) { + StringRef New = RemappingTable.lookup(Name); + return New.empty() ? Name : New; + } +}; +} + +struct WeightedFile { + std::string Filename; + uint64_t Weight; +}; +typedef SmallVector WeightedFileVector; + +/// Keep track of merged data and reported errors. +struct WriterContext { + std::mutex Lock; + InstrProfWriter Writer; + std::vector> Errors; + std::mutex &ErrLock; + SmallSet &WriterErrorCodes; + + WriterContext(bool IsSparse, std::mutex &ErrLock, + SmallSet &WriterErrorCodes) + : Writer(IsSparse), ErrLock(ErrLock), WriterErrorCodes(WriterErrorCodes) { + } +}; + +/// Computer the overlap b/w profile BaseFilename and TestFileName, +/// and store the program level result to Overlap. +static void overlapInput(const std::string &BaseFilename, + const std::string &TestFilename, WriterContext *WC, + OverlapStats &Overlap, + const OverlapFuncFilters &FuncFilter, + raw_fd_ostream &OS, bool IsCS) { + auto ReaderOrErr = InstrProfReader::create(TestFilename); + if (Error E = ReaderOrErr.takeError()) { + // Skip the empty profiles by returning sliently. + instrprof_error IPE = InstrProfError::take(std::move(E)); + if (IPE != instrprof_error::empty_raw_profile) + WC->Errors.emplace_back(make_error(IPE), TestFilename); + return; + } + + auto Reader = std::move(ReaderOrErr.get()); + for (auto &I : *Reader) { + OverlapStats FuncOverlap(OverlapStats::FunctionLevel); + FuncOverlap.setFuncInfo(I.Name, I.Hash); + + WC->Writer.overlapRecord(std::move(I), Overlap, FuncOverlap, FuncFilter); + FuncOverlap.dump(OS); + } +} + +/// Load an input into a writer context. +static void loadInput(const WeightedFile &Input, SymbolRemapper *Remapper, + const InstrProfCorrelator *Correlator, + const StringRef ProfiledBinary, WriterContext *WC) { + std::unique_lock CtxGuard{WC->Lock}; + + // Copy the filename, because llvm::ThreadPool copied the input "const + // WeightedFile &" by value, making a reference to the filename within it + // invalid outside of this packaged task. + std::string Filename = Input.Filename; + + using ::llvm::memprof::RawMemProfReader; + if (RawMemProfReader::hasFormat(Input.Filename)) { + auto ReaderOrErr = RawMemProfReader::create(Input.Filename, ProfiledBinary); + if (!ReaderOrErr) { + exitWithError(ReaderOrErr.takeError(), Input.Filename); + } + std::unique_ptr Reader = std::move(ReaderOrErr.get()); + // Check if the profile types can be merged, e.g. clang frontend profiles + // should not be merged with memprof profiles. + if (Error E = WC->Writer.mergeProfileKind(Reader->getProfileKind())) { + consumeError(std::move(E)); + WC->Errors.emplace_back( + make_error( + "Cannot merge MemProf profile with Clang generated profile.", + std::error_code()), + Filename); + return; + } + + auto MemProfError = [&](Error E) { + instrprof_error IPE = InstrProfError::take(std::move(E)); + WC->Errors.emplace_back(make_error(IPE), Filename); + }; + + // Add the frame mappings into the writer context. + const auto &IdToFrame = Reader->getFrameMapping(); + for (const auto &I : IdToFrame) { + bool Succeeded = WC->Writer.addMemProfFrame( + /*Id=*/I.first, /*Frame=*/I.getSecond(), MemProfError); + // If we weren't able to add the frame mappings then it doesn't make sense + // to try to add the records from this profile. + if (!Succeeded) + return; + } + const auto &FunctionProfileData = Reader->getProfileData(); + // Add the memprof records into the writer context. + for (const auto &I : FunctionProfileData) { + WC->Writer.addMemProfRecord(/*Id=*/I.first, /*Record=*/I.second); + } + return; + } + + auto ReaderOrErr = InstrProfReader::create(Input.Filename, Correlator); + if (Error E = ReaderOrErr.takeError()) { + // Skip the empty profiles by returning sliently. + instrprof_error IPE = InstrProfError::take(std::move(E)); + if (IPE != instrprof_error::empty_raw_profile) + WC->Errors.emplace_back(make_error(IPE), Filename); + return; + } + + auto Reader = std::move(ReaderOrErr.get()); + if (Error E = WC->Writer.mergeProfileKind(Reader->getProfileKind())) { + consumeError(std::move(E)); + WC->Errors.emplace_back( + make_error( + "Merge IR generated profile with Clang generated profile.", + std::error_code()), + Filename); + return; + } + + for (auto &I : *Reader) { + if (Remapper) + I.Name = (*Remapper)(I.Name); + const StringRef FuncName = I.Name; + bool Reported = false; + WC->Writer.addRecord(std::move(I), Input.Weight, [&](Error E) { + if (Reported) { + consumeError(std::move(E)); + return; + } + Reported = true; + // Only show hint the first time an error occurs. + instrprof_error IPE = InstrProfError::take(std::move(E)); + std::unique_lock ErrGuard{WC->ErrLock}; + bool firstTime = WC->WriterErrorCodes.insert(IPE).second; + handleMergeWriterError(make_error(IPE), Input.Filename, + FuncName, firstTime); + }); + } + + if (Reader->hasError()) { + if (Error E = Reader->getError()) + WC->Errors.emplace_back(std::move(E), Filename); + } + + std::vector BinaryIds; + if (Error E = Reader->readBinaryIds(BinaryIds)) + WC->Errors.emplace_back(std::move(E), Filename); + WC->Writer.addBinaryIds(BinaryIds); +} + +/// Merge the \p Src writer context into \p Dst. +static void mergeWriterContexts(WriterContext *Dst, WriterContext *Src) { + for (auto &ErrorPair : Src->Errors) + Dst->Errors.push_back(std::move(ErrorPair)); + Src->Errors.clear(); + + if (Error E = Dst->Writer.mergeProfileKind(Src->Writer.getProfileKind())) + exitWithError(std::move(E)); + + Dst->Writer.mergeRecordsFromWriter(std::move(Src->Writer), [&](Error E) { + instrprof_error IPE = InstrProfError::take(std::move(E)); + std::unique_lock ErrGuard{Dst->ErrLock}; + bool firstTime = Dst->WriterErrorCodes.insert(IPE).second; + if (firstTime) + warn(toString(make_error(IPE))); + }); +} + +static void writeInstrProfile(StringRef OutputFilename, + ProfileFormat OutputFormat, + InstrProfWriter &Writer) { + std::error_code EC; + raw_fd_ostream Output(OutputFilename.data(), EC, + OutputFormat == PF_Text ? sys::fs::OF_TextWithCRLF + : sys::fs::OF_None); + if (EC) + exitWithErrorCode(EC, OutputFilename); + + if (OutputFormat == PF_Text) { + if (Error E = Writer.writeText(Output)) + warn(std::move(E)); + } else { + if (Output.is_displayed()) + exitWithError("cannot write a non-text format profile to the terminal"); + if (Error E = Writer.write(Output)) + warn(std::move(E)); + } +} + +static void mergeInstrProfile(const WeightedFileVector &Inputs, + StringRef DebugInfoFilename, + SymbolRemapper *Remapper, + StringRef OutputFilename, + ProfileFormat OutputFormat, bool OutputSparse, + unsigned NumThreads, FailureMode FailMode, + const StringRef ProfiledBinary) { + if (OutputFormat != PF_Binary && OutputFormat != PF_Compact_Binary && + OutputFormat != PF_Ext_Binary && OutputFormat != PF_Text) + exitWithError("unknown format is specified"); + + std::unique_ptr Correlator; + if (!DebugInfoFilename.empty()) { + if (auto Err = + InstrProfCorrelator::get(DebugInfoFilename).moveInto(Correlator)) + exitWithError(std::move(Err), DebugInfoFilename); + if (auto Err = Correlator->correlateProfileData()) + exitWithError(std::move(Err), DebugInfoFilename); + } + + std::mutex ErrorLock; + SmallSet WriterErrorCodes; + + // If NumThreads is not specified, auto-detect a good default. + if (NumThreads == 0) + NumThreads = std::min(hardware_concurrency().compute_thread_count(), + unsigned((Inputs.size() + 1) / 2)); + + // Initialize the writer contexts. + SmallVector, 4> Contexts; + for (unsigned I = 0; I < NumThreads; ++I) + Contexts.emplace_back(std::make_unique( + OutputSparse, ErrorLock, WriterErrorCodes)); + + if (NumThreads == 1) { + for (const auto &Input : Inputs) + loadInput(Input, Remapper, Correlator.get(), ProfiledBinary, + Contexts[0].get()); + } else { + ThreadPool Pool(hardware_concurrency(NumThreads)); + + // Load the inputs in parallel (N/NumThreads serial steps). + unsigned Ctx = 0; + for (const auto &Input : Inputs) { + Pool.async(loadInput, Input, Remapper, Correlator.get(), ProfiledBinary, + Contexts[Ctx].get()); + Ctx = (Ctx + 1) % NumThreads; + } + Pool.wait(); + + // Merge the writer contexts together (~ lg(NumThreads) serial steps). + unsigned Mid = Contexts.size() / 2; + unsigned End = Contexts.size(); + assert(Mid > 0 && "Expected more than one context"); + do { + for (unsigned I = 0; I < Mid; ++I) + Pool.async(mergeWriterContexts, Contexts[I].get(), + Contexts[I + Mid].get()); + Pool.wait(); + if (End & 1) { + Pool.async(mergeWriterContexts, Contexts[0].get(), + Contexts[End - 1].get()); + Pool.wait(); + } + End = Mid; + Mid /= 2; + } while (Mid > 0); + } + + // Handle deferred errors encountered during merging. If the number of errors + // is equal to the number of inputs the merge failed. + unsigned NumErrors = 0; + for (std::unique_ptr &WC : Contexts) { + for (auto &ErrorPair : WC->Errors) { + ++NumErrors; + warn(toString(std::move(ErrorPair.first)), ErrorPair.second); + } + } + if (NumErrors == Inputs.size() || + (NumErrors > 0 && FailMode == failIfAnyAreInvalid)) + exitWithError("no profile can be merged"); + + writeInstrProfile(OutputFilename, OutputFormat, Contexts[0]->Writer); +} + +/// The profile entry for a function in instrumentation profile. +struct InstrProfileEntry { + uint64_t MaxCount = 0; + uint64_t NumEdgeCounters = 0; + float ZeroCounterRatio = 0.0; + InstrProfRecord *ProfRecord; + InstrProfileEntry(InstrProfRecord *Record); + InstrProfileEntry() = default; +}; + +InstrProfileEntry::InstrProfileEntry(InstrProfRecord *Record) { + ProfRecord = Record; + uint64_t CntNum = Record->Counts.size(); + uint64_t ZeroCntNum = 0; + for (size_t I = 0; I < CntNum; ++I) { + MaxCount = std::max(MaxCount, Record->Counts[I]); + ZeroCntNum += !Record->Counts[I]; + } + ZeroCounterRatio = (float)ZeroCntNum / CntNum; + NumEdgeCounters = CntNum; +} + +/// Either set all the counters in the instr profile entry \p IFE to +/// -1 / -2 /in order to drop the profile or scale up the +/// counters in \p IFP to be above hot / cold threshold. We use +/// the ratio of zero counters in the profile of a function to +/// decide the profile is helpful or harmful for performance, +/// and to choose whether to scale up or drop it. +static void updateInstrProfileEntry(InstrProfileEntry &IFE, bool SetToHot, + uint64_t HotInstrThreshold, + uint64_t ColdInstrThreshold, + float ZeroCounterThreshold) { + InstrProfRecord *ProfRecord = IFE.ProfRecord; + if (!IFE.MaxCount || IFE.ZeroCounterRatio > ZeroCounterThreshold) { + // If all or most of the counters of the function are zero, the + // profile is unaccountable and should be dropped. Reset all the + // counters to be -1 / -2 and PGO profile-use will drop the profile. + // All counters being -1 also implies that the function is hot so + // PGO profile-use will also set the entry count metadata to be + // above hot threshold. + // All counters being -2 implies that the function is warm so + // PGO profile-use will also set the entry count metadata to be + // above cold threshold. + auto Kind = + (SetToHot ? InstrProfRecord::PseudoHot : InstrProfRecord::PseudoWarm); + ProfRecord->setPseudoCount(Kind); + return; + } + + // Scale up the MaxCount to be multiple times above hot / cold threshold. + const unsigned MultiplyFactor = 3; + uint64_t Threshold = (SetToHot ? HotInstrThreshold : ColdInstrThreshold); + uint64_t Numerator = Threshold * MultiplyFactor; + + // Make sure Threshold for warm counters is below the HotInstrThreshold. + if (!SetToHot && Threshold >= HotInstrThreshold) { + Threshold = (HotInstrThreshold + ColdInstrThreshold) / 2; + } + + uint64_t Denominator = IFE.MaxCount; + if (Numerator <= Denominator) + return; + ProfRecord->scale(Numerator, Denominator, [&](instrprof_error E) { + warn(toString(make_error(E))); + }); +} + +const uint64_t ColdPercentileIdx = 15; +const uint64_t HotPercentileIdx = 11; + +using sampleprof::FSDiscriminatorPass; + +// Internal options to set FSDiscriminatorPass. Used in merge and show +// commands. +static cl::opt FSDiscriminatorPassOption( + "fs-discriminator-pass", cl::init(PassLast), cl::Hidden, + cl::desc("Zero out the discriminator bits for the FS discrimiantor " + "pass beyond this value. The enum values are defined in " + "Support/Discriminator.h"), + cl::values(clEnumVal(Base, "Use base discriminators only"), + clEnumVal(Pass1, "Use base and pass 1 discriminators"), + clEnumVal(Pass2, "Use base and pass 1-2 discriminators"), + clEnumVal(Pass3, "Use base and pass 1-3 discriminators"), + clEnumVal(PassLast, "Use all discriminator bits (default)"))); + +static unsigned getDiscriminatorMask() { + return getN1Bits(getFSPassBitEnd(FSDiscriminatorPassOption.getValue())); +} + +/// Adjust the instr profile in \p WC based on the sample profile in +/// \p Reader. +static void +adjustInstrProfile(std::unique_ptr &WC, + std::unique_ptr &Reader, + unsigned SupplMinSizeThreshold, float ZeroCounterThreshold, + unsigned InstrProfColdThreshold) { + // Function to its entry in instr profile. + StringMap InstrProfileMap; + StringMap StaticFuncMap; + InstrProfSummaryBuilder IPBuilder(ProfileSummaryBuilder::DefaultCutoffs); + + auto checkSampleProfileHasFUnique = [&Reader]() { + for (const auto &PD : Reader->getProfiles()) { + auto &FContext = PD.first; + if (FContext.toString().find(FunctionSamples::UniqSuffix) != + std::string::npos) { + return true; + } + } + return false; + }; + + bool SampleProfileHasFUnique = checkSampleProfileHasFUnique(); + + auto buildStaticFuncMap = [&StaticFuncMap, + SampleProfileHasFUnique](const StringRef Name) { + std::string Prefixes[] = {".cpp:", "cc:", ".c:", ".hpp:", ".h:"}; + size_t PrefixPos = StringRef::npos; + for (auto &Prefix : Prefixes) { + PrefixPos = Name.find_insensitive(Prefix); + if (PrefixPos == StringRef::npos) + continue; + PrefixPos += Prefix.size(); + break; + } + + if (PrefixPos == StringRef::npos) { + return; + } + + StringRef NewName = Name.drop_front(PrefixPos); + StringRef FName = Name.substr(0, PrefixPos - 1); + if (NewName.size() == 0) { + return; + } + + // This name should have a static linkage. + size_t PostfixPos = NewName.find(FunctionSamples::UniqSuffix); + bool ProfileHasFUnique = (PostfixPos != StringRef::npos); + + // If sample profile and instrumented profile do not agree on symbol + // uniqification. + if (SampleProfileHasFUnique != ProfileHasFUnique) { + // If instrumented profile uses -funique-internal-linakge-symbols, + // we need to trim the name. + if (ProfileHasFUnique) { + NewName = NewName.substr(0, PostfixPos); + } else { + // If sample profile uses -funique-internal-linakge-symbols, + // we build the map. + std::string NStr = + NewName.str() + getUniqueInternalLinkagePostfix(FName); + NewName = StringRef(NStr); + StaticFuncMap[NewName] = Name; + return; + } + } + + if (StaticFuncMap.find(NewName) == StaticFuncMap.end()) { + StaticFuncMap[NewName] = Name; + } else { + StaticFuncMap[NewName] = DuplicateNameStr; + } + }; + + // We need to flatten the SampleFDO profile as the InstrFDO + // profile does not have inlined callsite profiles. + // One caveat is the pre-inlined function -- their samples + // should be collapsed into the caller function. + // Here we do a DFS traversal to get the flatten profile + // info: the sum of entrycount and the max of maxcount. + // Here is the algorithm: + // recursive (FS, root_name) { + // name = FS->getName(); + // get samples for FS; + // if (InstrProf.find(name) { + // root_name = name; + // } else { + // if (name is in static_func map) { + // root_name = static_name; + // } + // } + // update the Map entry for root_name; + // for (subfs: FS) { + // recursive(subfs, root_name); + // } + // } + // + // Here is an example. + // + // SampleProfile: + // foo:12345:1000 + // 1: 1000 + // 2.1: 1000 + // 15: 5000 + // 4: bar:1000 + // 1: 1000 + // 2: goo:3000 + // 1: 3000 + // 8: bar:40000 + // 1: 10000 + // 2: goo:30000 + // 1: 30000 + // + // InstrProfile has two entries: + // foo + // bar.cc:bar + // + // After BuildMaxSampleMap, we should have the following in FlattenSampleMap: + // {"foo", {1000, 5000}} + // {"bar.cc:bar", {11000, 30000}} + // + // foo's has an entry count of 1000, and max body count of 5000. + // bar.cc:bar has an entry count of 11000 (sum two callsites of 1000 and + // 10000), and max count of 30000 (from the callsite in line 8). + // + // Note that goo's count will remain in bar.cc:bar() as it does not have an + // entry in InstrProfile. + DenseMap> FlattenSampleMap; + auto BuildMaxSampleMap = [&FlattenSampleMap, &StaticFuncMap, + &InstrProfileMap](const FunctionSamples &FS, + const StringRef &RootName) { + auto BuildMaxSampleMapImpl = [&](const FunctionSamples &FS, + const StringRef &RootName, + auto &BuildImpl) -> void { + const StringRef &Name = FS.getName(); + const StringRef *NewRootName = &RootName; + uint64_t EntrySample = FS.getHeadSamplesEstimate(); + uint64_t MaxBodySample = FS.getMaxCountInside(/* SkipCallSite*/ true); + + auto It = InstrProfileMap.find(Name); + if (It != InstrProfileMap.end()) { + NewRootName = &Name; + } else { + auto NewName = StaticFuncMap.find(Name); + if (NewName != StaticFuncMap.end()) { + It = InstrProfileMap.find(NewName->second.str()); + if (NewName->second != DuplicateNameStr) { + NewRootName = &NewName->second; + } + } else { + // Here the EntrySample is of an inlined function, so we should not + // update the EntrySample in the map. + EntrySample = 0; + } + } + EntrySample += FlattenSampleMap[*NewRootName].first; + MaxBodySample = + std::max(FlattenSampleMap[*NewRootName].second, MaxBodySample); + FlattenSampleMap[*NewRootName] = + std::make_pair(EntrySample, MaxBodySample); + + for (const auto &C : FS.getCallsiteSamples()) + for (const auto &F : C.second) + BuildImpl(F.second, *NewRootName, BuildImpl); + }; + BuildMaxSampleMapImpl(FS, RootName, BuildMaxSampleMapImpl); + }; + + for (auto &PD : WC->Writer.getProfileData()) { + // Populate IPBuilder. + for (const auto &PDV : PD.getValue()) { + InstrProfRecord Record = PDV.second; + IPBuilder.addRecord(Record); + } + + // If a function has multiple entries in instr profile, skip it. + if (PD.getValue().size() != 1) + continue; + + // Initialize InstrProfileMap. + InstrProfRecord *R = &PD.getValue().begin()->second; + StringRef FullName = PD.getKey(); + InstrProfileMap[FullName] = InstrProfileEntry(R); + buildStaticFuncMap(FullName); + } + + for (auto &PD : Reader->getProfiles()) { + sampleprof::FunctionSamples &FS = PD.second; + BuildMaxSampleMap(FS, FS.getName()); + } + + ProfileSummary InstrPS = *IPBuilder.getSummary(); + ProfileSummary SamplePS = Reader->getSummary(); + + // Compute cold thresholds for instr profile and sample profile. + uint64_t HotSampleThreshold = + ProfileSummaryBuilder::getEntryForPercentile( + SamplePS.getDetailedSummary(), + ProfileSummaryBuilder::DefaultCutoffs[HotPercentileIdx]) + .MinCount; + uint64_t ColdSampleThreshold = + ProfileSummaryBuilder::getEntryForPercentile( + SamplePS.getDetailedSummary(), + ProfileSummaryBuilder::DefaultCutoffs[ColdPercentileIdx]) + .MinCount; + uint64_t HotInstrThreshold = + ProfileSummaryBuilder::getEntryForPercentile( + InstrPS.getDetailedSummary(), + ProfileSummaryBuilder::DefaultCutoffs[HotPercentileIdx]) + .MinCount; + uint64_t ColdInstrThreshold = + InstrProfColdThreshold + ? InstrProfColdThreshold + : ProfileSummaryBuilder::getEntryForPercentile( + InstrPS.getDetailedSummary(), + ProfileSummaryBuilder::DefaultCutoffs[ColdPercentileIdx]) + .MinCount; + + // Find hot/warm functions in sample profile which is cold in instr profile + // and adjust the profiles of those functions in the instr profile. + for (const auto &E : FlattenSampleMap) { + uint64_t SampleMaxCount = std::max(E.second.first, E.second.second); + if (SampleMaxCount < ColdSampleThreshold) + continue; + const StringRef &Name = E.first; + auto It = InstrProfileMap.find(Name); + if (It == InstrProfileMap.end()) { + auto NewName = StaticFuncMap.find(Name); + if (NewName != StaticFuncMap.end()) { + It = InstrProfileMap.find(NewName->second.str()); + if (NewName->second == DuplicateNameStr) { + WithColor::warning() + << "Static function " << Name + << " has multiple promoted names, cannot adjust profile.\n"; + } + } + } + if (It == InstrProfileMap.end() || + It->second.MaxCount > ColdInstrThreshold || + It->second.NumEdgeCounters < SupplMinSizeThreshold) + continue; + bool SetToHot = SampleMaxCount >= HotSampleThreshold; + updateInstrProfileEntry(It->second, SetToHot, HotInstrThreshold, + ColdInstrThreshold, ZeroCounterThreshold); + } +} + +/// The main function to supplement instr profile with sample profile. +/// \Inputs contains the instr profile. \p SampleFilename specifies the +/// sample profile. \p OutputFilename specifies the output profile name. +/// \p OutputFormat specifies the output profile format. \p OutputSparse +/// specifies whether to generate sparse profile. \p SupplMinSizeThreshold +/// specifies the minimal size for the functions whose profile will be +/// adjusted. \p ZeroCounterThreshold is the threshold to check whether +/// a function contains too many zero counters and whether its profile +/// should be dropped. \p InstrProfColdThreshold is the user specified +/// cold threshold which will override the cold threshold got from the +/// instr profile summary. +static void supplementInstrProfile( + const WeightedFileVector &Inputs, StringRef SampleFilename, + StringRef OutputFilename, ProfileFormat OutputFormat, bool OutputSparse, + unsigned SupplMinSizeThreshold, float ZeroCounterThreshold, + unsigned InstrProfColdThreshold) { + if (OutputFilename.compare("-") == 0) + exitWithError("cannot write indexed profdata format to stdout"); + if (Inputs.size() != 1) + exitWithError("expect one input to be an instr profile"); + if (Inputs[0].Weight != 1) + exitWithError("expect instr profile doesn't have weight"); + + StringRef InstrFilename = Inputs[0].Filename; + + // Read sample profile. + LLVMContext Context; + auto ReaderOrErr = sampleprof::SampleProfileReader::create( + SampleFilename.str(), Context, FSDiscriminatorPassOption); + if (std::error_code EC = ReaderOrErr.getError()) + exitWithErrorCode(EC, SampleFilename); + auto Reader = std::move(ReaderOrErr.get()); + if (std::error_code EC = Reader->read()) + exitWithErrorCode(EC, SampleFilename); + + // Read instr profile. + std::mutex ErrorLock; + SmallSet WriterErrorCodes; + auto WC = std::make_unique(OutputSparse, ErrorLock, + WriterErrorCodes); + loadInput(Inputs[0], nullptr, nullptr, /*ProfiledBinary=*/"", WC.get()); + if (WC->Errors.size() > 0) + exitWithError(std::move(WC->Errors[0].first), InstrFilename); + + adjustInstrProfile(WC, Reader, SupplMinSizeThreshold, ZeroCounterThreshold, + InstrProfColdThreshold); + writeInstrProfile(OutputFilename, OutputFormat, WC->Writer); +} + +/// Make a copy of the given function samples with all symbol names remapped +/// by the provided symbol remapper. +static sampleprof::FunctionSamples +remapSamples(const sampleprof::FunctionSamples &Samples, + SymbolRemapper &Remapper, sampleprof_error &Error) { + sampleprof::FunctionSamples Result; + Result.setName(Remapper(Samples.getName())); + Result.addTotalSamples(Samples.getTotalSamples()); + Result.addHeadSamples(Samples.getHeadSamples()); + for (const auto &BodySample : Samples.getBodySamples()) { + uint32_t MaskedDiscriminator = + BodySample.first.Discriminator & getDiscriminatorMask(); + Result.addBodySamples(BodySample.first.LineOffset, MaskedDiscriminator, + BodySample.second.getSamples()); + for (const auto &Target : BodySample.second.getCallTargets()) { + Result.addCalledTargetSamples(BodySample.first.LineOffset, + MaskedDiscriminator, + Remapper(Target.first()), Target.second); + } + } + for (const auto &CallsiteSamples : Samples.getCallsiteSamples()) { + sampleprof::FunctionSamplesMap &Target = + Result.functionSamplesAt(CallsiteSamples.first); + for (const auto &Callsite : CallsiteSamples.second) { + sampleprof::FunctionSamples Remapped = + remapSamples(Callsite.second, Remapper, Error); + MergeResult(Error, + Target[std::string(Remapped.getName())].merge(Remapped)); + } + } + return Result; +} + +static sampleprof::SampleProfileFormat FormatMap[] = { + sampleprof::SPF_None, + sampleprof::SPF_Text, + sampleprof::SPF_Compact_Binary, + sampleprof::SPF_Ext_Binary, + sampleprof::SPF_GCC, + sampleprof::SPF_Binary}; + +static std::unique_ptr +getInputFileBuf(const StringRef &InputFile) { + if (InputFile == "") + return {}; + + auto BufOrError = MemoryBuffer::getFileOrSTDIN(InputFile); + if (!BufOrError) + exitWithErrorCode(BufOrError.getError(), InputFile); + + return std::move(*BufOrError); +} + +static void populateProfileSymbolList(MemoryBuffer *Buffer, + sampleprof::ProfileSymbolList &PSL) { + if (!Buffer) + return; + + SmallVector SymbolVec; + StringRef Data = Buffer->getBuffer(); + Data.split(SymbolVec, '\n', /*MaxSplit=*/-1, /*KeepEmpty=*/false); + + for (StringRef SymbolStr : SymbolVec) + PSL.add(SymbolStr.trim()); +} + +static void handleExtBinaryWriter(sampleprof::SampleProfileWriter &Writer, + ProfileFormat OutputFormat, + MemoryBuffer *Buffer, + sampleprof::ProfileSymbolList &WriterList, + bool CompressAllSections, bool UseMD5, + bool GenPartialProfile) { + populateProfileSymbolList(Buffer, WriterList); + if (WriterList.size() > 0 && OutputFormat != PF_Ext_Binary) + warn("Profile Symbol list is not empty but the output format is not " + "ExtBinary format. The list will be lost in the output. "); + + Writer.setProfileSymbolList(&WriterList); + + if (CompressAllSections) { + if (OutputFormat != PF_Ext_Binary) + warn("-compress-all-section is ignored. Specify -extbinary to enable it"); + else + Writer.setToCompressAllSections(); + } + if (UseMD5) { + if (OutputFormat != PF_Ext_Binary) + warn("-use-md5 is ignored. Specify -extbinary to enable it"); + else + Writer.setUseMD5(); + } + if (GenPartialProfile) { + if (OutputFormat != PF_Ext_Binary) + warn("-gen-partial-profile is ignored. Specify -extbinary to enable it"); + else + Writer.setPartialProfile(); + } +} + +static void +mergeSampleProfile(const WeightedFileVector &Inputs, SymbolRemapper *Remapper, + StringRef OutputFilename, ProfileFormat OutputFormat, + StringRef ProfileSymbolListFile, bool CompressAllSections, + bool UseMD5, bool GenPartialProfile, bool GenCSNestedProfile, + bool SampleMergeColdContext, bool SampleTrimColdContext, + bool SampleColdContextFrameDepth, FailureMode FailMode, + bool DropProfileSymbolList) { + using namespace sampleprof; + SampleProfileMap ProfileMap; + SmallVector, 5> Readers; + LLVMContext Context; + sampleprof::ProfileSymbolList WriterList; + std::optional ProfileIsProbeBased; + std::optional ProfileIsCS; + for (const auto &Input : Inputs) { + auto ReaderOrErr = SampleProfileReader::create(Input.Filename, Context, + FSDiscriminatorPassOption); + if (std::error_code EC = ReaderOrErr.getError()) { + warnOrExitGivenError(FailMode, EC, Input.Filename); + continue; + } + + // We need to keep the readers around until after all the files are + // read so that we do not lose the function names stored in each + // reader's memory. The function names are needed to write out the + // merged profile map. + Readers.push_back(std::move(ReaderOrErr.get())); + const auto Reader = Readers.back().get(); + if (std::error_code EC = Reader->read()) { + warnOrExitGivenError(FailMode, EC, Input.Filename); + Readers.pop_back(); + continue; + } + + SampleProfileMap &Profiles = Reader->getProfiles(); + if (ProfileIsProbeBased && + ProfileIsProbeBased != FunctionSamples::ProfileIsProbeBased) + exitWithError( + "cannot merge probe-based profile with non-probe-based profile"); + ProfileIsProbeBased = FunctionSamples::ProfileIsProbeBased; + if (ProfileIsCS && ProfileIsCS != FunctionSamples::ProfileIsCS) + exitWithError("cannot merge CS profile with non-CS profile"); + ProfileIsCS = FunctionSamples::ProfileIsCS; + for (SampleProfileMap::iterator I = Profiles.begin(), E = Profiles.end(); + I != E; ++I) { + sampleprof_error Result = sampleprof_error::success; + FunctionSamples Remapped = + Remapper ? remapSamples(I->second, *Remapper, Result) + : FunctionSamples(); + FunctionSamples &Samples = Remapper ? Remapped : I->second; + SampleContext FContext = Samples.getContext(); + MergeResult(Result, ProfileMap[FContext].merge(Samples, Input.Weight)); + if (Result != sampleprof_error::success) { + std::error_code EC = make_error_code(Result); + handleMergeWriterError(errorCodeToError(EC), Input.Filename, + FContext.toString()); + } + } + + if (!DropProfileSymbolList) { + std::unique_ptr ReaderList = + Reader->getProfileSymbolList(); + if (ReaderList) + WriterList.merge(*ReaderList); + } + } + + if (ProfileIsCS && (SampleMergeColdContext || SampleTrimColdContext)) { + // Use threshold calculated from profile summary unless specified. + SampleProfileSummaryBuilder Builder(ProfileSummaryBuilder::DefaultCutoffs); + auto Summary = Builder.computeSummaryForProfiles(ProfileMap); + uint64_t SampleProfColdThreshold = + ProfileSummaryBuilder::getColdCountThreshold( + (Summary->getDetailedSummary())); + + // Trim and merge cold context profile using cold threshold above; + SampleContextTrimmer(ProfileMap) + .trimAndMergeColdContextProfiles( + SampleProfColdThreshold, SampleTrimColdContext, + SampleMergeColdContext, SampleColdContextFrameDepth, false); + } + + if (ProfileIsCS && GenCSNestedProfile) { + CSProfileConverter CSConverter(ProfileMap); + CSConverter.convertProfiles(); + ProfileIsCS = FunctionSamples::ProfileIsCS = false; + } + + auto WriterOrErr = + SampleProfileWriter::create(OutputFilename, FormatMap[OutputFormat]); + if (std::error_code EC = WriterOrErr.getError()) + exitWithErrorCode(EC, OutputFilename); + + auto Writer = std::move(WriterOrErr.get()); + // WriterList will have StringRef refering to string in Buffer. + // Make sure Buffer lives as long as WriterList. + auto Buffer = getInputFileBuf(ProfileSymbolListFile); + handleExtBinaryWriter(*Writer, OutputFormat, Buffer.get(), WriterList, + CompressAllSections, UseMD5, GenPartialProfile); + if (std::error_code EC = Writer->write(ProfileMap)) + exitWithErrorCode(std::move(EC)); +} + +static WeightedFile parseWeightedFile(const StringRef &WeightedFilename) { + StringRef WeightStr, FileName; + std::tie(WeightStr, FileName) = WeightedFilename.split(','); + + uint64_t Weight; + if (WeightStr.getAsInteger(10, Weight) || Weight < 1) + exitWithError("input weight must be a positive integer"); + + return {std::string(FileName), Weight}; +} + +static void addWeightedInput(WeightedFileVector &WNI, const WeightedFile &WF) { + StringRef Filename = WF.Filename; + uint64_t Weight = WF.Weight; + + // If it's STDIN just pass it on. + if (Filename == "-") { + WNI.push_back({std::string(Filename), Weight}); + return; + } + + llvm::sys::fs::file_status Status; + llvm::sys::fs::status(Filename, Status); + if (!llvm::sys::fs::exists(Status)) + exitWithErrorCode(make_error_code(errc::no_such_file_or_directory), + Filename); + // If it's a source file, collect it. + if (llvm::sys::fs::is_regular_file(Status)) { + WNI.push_back({std::string(Filename), Weight}); + return; + } + + if (llvm::sys::fs::is_directory(Status)) { + std::error_code EC; + for (llvm::sys::fs::recursive_directory_iterator F(Filename, EC), E; + F != E && !EC; F.increment(EC)) { + if (llvm::sys::fs::is_regular_file(F->path())) { + addWeightedInput(WNI, {F->path(), Weight}); + } + } + if (EC) + exitWithErrorCode(EC, Filename); + } +} + +static void parseInputFilenamesFile(MemoryBuffer *Buffer, + WeightedFileVector &WFV) { + if (!Buffer) + return; + + SmallVector Entries; + StringRef Data = Buffer->getBuffer(); + Data.split(Entries, '\n', /*MaxSplit=*/-1, /*KeepEmpty=*/false); + for (const StringRef &FileWeightEntry : Entries) { + StringRef SanitizedEntry = FileWeightEntry.trim(" \t\v\f\r"); + // Skip comments. + if (SanitizedEntry.startswith("#")) + continue; + // If there's no comma, it's an unweighted profile. + else if (!SanitizedEntry.contains(',')) + addWeightedInput(WFV, {std::string(SanitizedEntry), 1}); + else + addWeightedInput(WFV, parseWeightedFile(SanitizedEntry)); + } +} + +static int merge_main(int argc, const char *argv[]) { + cl::list InputFilenames(cl::Positional, + cl::desc("")); + cl::list WeightedInputFilenames("weighted-input", + cl::desc(",")); + cl::opt InputFilenamesFile( + "input-files", cl::init(""), + cl::desc("Path to file containing newline-separated " + "[,] entries")); + cl::alias InputFilenamesFileA("f", cl::desc("Alias for --input-files"), + cl::aliasopt(InputFilenamesFile)); + cl::opt DumpInputFileList( + "dump-input-file-list", cl::init(false), cl::Hidden, + cl::desc("Dump the list of input files and their weights, then exit")); + cl::opt RemappingFile("remapping-file", cl::value_desc("file"), + cl::desc("Symbol remapping file")); + cl::alias RemappingFileA("r", cl::desc("Alias for --remapping-file"), + cl::aliasopt(RemappingFile)); + cl::opt OutputFilename("output", cl::value_desc("output"), + cl::init("-"), cl::desc("Output file")); + cl::alias OutputFilenameA("o", cl::desc("Alias for --output"), + cl::aliasopt(OutputFilename)); + cl::opt ProfileKind( + cl::desc("Profile kind:"), cl::init(instr), + cl::values(clEnumVal(instr, "Instrumentation profile (default)"), + clEnumVal(sample, "Sample profile"))); + cl::opt OutputFormat( + cl::desc("Format of output profile"), cl::init(PF_Binary), + cl::values( + clEnumValN(PF_Binary, "binary", "Binary encoding (default)"), + clEnumValN(PF_Compact_Binary, "compbinary", + "Compact binary encoding"), + clEnumValN(PF_Ext_Binary, "extbinary", "Extensible binary encoding"), + clEnumValN(PF_Text, "text", "Text encoding"), + clEnumValN(PF_GCC, "gcc", + "GCC encoding (only meaningful for -sample)"))); + cl::opt FailureMode( + "failure-mode", cl::init(failIfAnyAreInvalid), cl::desc("Failure mode:"), + cl::values(clEnumValN(failIfAnyAreInvalid, "any", + "Fail if any profile is invalid."), + clEnumValN(failIfAllAreInvalid, "all", + "Fail only if all profiles are invalid."))); + cl::opt OutputSparse("sparse", cl::init(false), + cl::desc("Generate a sparse profile (only meaningful for -instr)")); + cl::opt NumThreads( + "num-threads", cl::init(0), + cl::desc("Number of merge threads to use (default: autodetect)")); + cl::alias NumThreadsA("j", cl::desc("Alias for --num-threads"), + cl::aliasopt(NumThreads)); + cl::opt ProfileSymbolListFile( + "prof-sym-list", cl::init(""), + cl::desc("Path to file containing the list of function symbols " + "used to populate profile symbol list")); + cl::opt CompressAllSections( + "compress-all-sections", cl::init(false), cl::Hidden, + cl::desc("Compress all sections when writing the profile (only " + "meaningful for -extbinary)")); + cl::opt UseMD5( + "use-md5", cl::init(false), cl::Hidden, + cl::desc("Choose to use MD5 to represent string in name table (only " + "meaningful for -extbinary)")); + cl::opt SampleMergeColdContext( + "sample-merge-cold-context", cl::init(false), cl::Hidden, + cl::desc( + "Merge context sample profiles whose count is below cold threshold")); + cl::opt SampleTrimColdContext( + "sample-trim-cold-context", cl::init(false), cl::Hidden, + cl::desc( + "Trim context sample profiles whose count is below cold threshold")); + cl::opt SampleColdContextFrameDepth( + "sample-frame-depth-for-cold-context", cl::init(1), + cl::desc("Keep the last K frames while merging cold profile. 1 means the " + "context-less base profile")); + cl::opt GenPartialProfile( + "gen-partial-profile", cl::init(false), cl::Hidden, + cl::desc("Generate a partial profile (only meaningful for -extbinary)")); + cl::opt SupplInstrWithSample( + "supplement-instr-with-sample", cl::init(""), cl::Hidden, + cl::desc("Supplement an instr profile with sample profile, to correct " + "the profile unrepresentativeness issue. The sample " + "profile is the input of the flag. Output will be in instr " + "format (The flag only works with -instr)")); + cl::opt ZeroCounterThreshold( + "zero-counter-threshold", cl::init(0.7), cl::Hidden, + cl::desc("For the function which is cold in instr profile but hot in " + "sample profile, if the ratio of the number of zero counters " + "divided by the total number of counters is above the " + "threshold, the profile of the function will be regarded as " + "being harmful for performance and will be dropped.")); + cl::opt SupplMinSizeThreshold( + "suppl-min-size-threshold", cl::init(10), cl::Hidden, + cl::desc("If the size of a function is smaller than the threshold, " + "assume it can be inlined by PGO early inliner and it won't " + "be adjusted based on sample profile.")); + cl::opt InstrProfColdThreshold( + "instr-prof-cold-threshold", cl::init(0), cl::Hidden, + cl::desc("User specified cold threshold for instr profile which will " + "override the cold threshold got from profile summary. ")); + cl::opt GenCSNestedProfile( + "gen-cs-nested-profile", cl::Hidden, cl::init(false), + cl::desc("Generate nested function profiles for CSSPGO")); + cl::opt DebugInfoFilename( + "debug-info", cl::init(""), + cl::desc("Use the provided debug info to correlate the raw profile.")); + cl::opt ProfiledBinary( + "profiled-binary", cl::init(""), + cl::desc("Path to binary from which the profile was collected.")); + cl::opt DropProfileSymbolList( + "drop-profile-symbol-list", cl::init(false), cl::Hidden, + cl::desc("Drop the profile symbol list when merging AutoFDO profiles " + "(only meaningful for -sample)")); + + cl::ParseCommandLineOptions(argc, argv, "LLVM profile data merger\n"); + + WeightedFileVector WeightedInputs; + for (StringRef Filename : InputFilenames) + addWeightedInput(WeightedInputs, {std::string(Filename), 1}); + for (StringRef WeightedFilename : WeightedInputFilenames) + addWeightedInput(WeightedInputs, parseWeightedFile(WeightedFilename)); + + // Make sure that the file buffer stays alive for the duration of the + // weighted input vector's lifetime. + auto Buffer = getInputFileBuf(InputFilenamesFile); + parseInputFilenamesFile(Buffer.get(), WeightedInputs); + + if (WeightedInputs.empty()) + exitWithError("no input files specified. See " + + sys::path::filename(argv[0]) + " -help"); + + if (DumpInputFileList) { + for (auto &WF : WeightedInputs) + outs() << WF.Weight << "," << WF.Filename << "\n"; + return 0; + } + + std::unique_ptr Remapper; + if (!RemappingFile.empty()) + Remapper = SymbolRemapper::create(RemappingFile); + + if (!SupplInstrWithSample.empty()) { + if (ProfileKind != instr) + exitWithError( + "-supplement-instr-with-sample can only work with -instr. "); + + supplementInstrProfile(WeightedInputs, SupplInstrWithSample, OutputFilename, + OutputFormat, OutputSparse, SupplMinSizeThreshold, + ZeroCounterThreshold, InstrProfColdThreshold); + return 0; + } + + if (ProfileKind == instr) + mergeInstrProfile(WeightedInputs, DebugInfoFilename, Remapper.get(), + OutputFilename, OutputFormat, OutputSparse, NumThreads, + FailureMode, ProfiledBinary); + else + mergeSampleProfile( + WeightedInputs, Remapper.get(), OutputFilename, OutputFormat, + ProfileSymbolListFile, CompressAllSections, UseMD5, GenPartialProfile, + GenCSNestedProfile, SampleMergeColdContext, SampleTrimColdContext, + SampleColdContextFrameDepth, FailureMode, DropProfileSymbolList); + return 0; +} + +/// Computer the overlap b/w profile BaseFilename and profile TestFilename. +static void overlapInstrProfile(const std::string &BaseFilename, + const std::string &TestFilename, + const OverlapFuncFilters &FuncFilter, + raw_fd_ostream &OS, bool IsCS) { + std::mutex ErrorLock; + SmallSet WriterErrorCodes; + WriterContext Context(false, ErrorLock, WriterErrorCodes); + WeightedFile WeightedInput{BaseFilename, 1}; + OverlapStats Overlap; + Error E = Overlap.accumulateCounts(BaseFilename, TestFilename, IsCS); + if (E) + exitWithError(std::move(E), "error in getting profile count sums"); + if (Overlap.Base.CountSum < 1.0f) { + OS << "Sum of edge counts for profile " << BaseFilename << " is 0.\n"; + exit(0); + } + if (Overlap.Test.CountSum < 1.0f) { + OS << "Sum of edge counts for profile " << TestFilename << " is 0.\n"; + exit(0); + } + loadInput(WeightedInput, nullptr, nullptr, /*ProfiledBinary=*/"", &Context); + overlapInput(BaseFilename, TestFilename, &Context, Overlap, FuncFilter, OS, + IsCS); + Overlap.dump(OS); +} + +namespace { +struct SampleOverlapStats { + SampleContext BaseName; + SampleContext TestName; + // Number of overlap units + uint64_t OverlapCount; + // Total samples of overlap units + uint64_t OverlapSample; + // Number of and total samples of units that only present in base or test + // profile + uint64_t BaseUniqueCount; + uint64_t BaseUniqueSample; + uint64_t TestUniqueCount; + uint64_t TestUniqueSample; + // Number of units and total samples in base or test profile + uint64_t BaseCount; + uint64_t BaseSample; + uint64_t TestCount; + uint64_t TestSample; + // Number of and total samples of units that present in at least one profile + uint64_t UnionCount; + uint64_t UnionSample; + // Weighted similarity + double Similarity; + // For SampleOverlapStats instances representing functions, weights of the + // function in base and test profiles + double BaseWeight; + double TestWeight; + + SampleOverlapStats() + : OverlapCount(0), OverlapSample(0), BaseUniqueCount(0), + BaseUniqueSample(0), TestUniqueCount(0), TestUniqueSample(0), + BaseCount(0), BaseSample(0), TestCount(0), TestSample(0), UnionCount(0), + UnionSample(0), Similarity(0.0), BaseWeight(0.0), TestWeight(0.0) {} +}; +} // end anonymous namespace + +namespace { +struct FuncSampleStats { + uint64_t SampleSum; + uint64_t MaxSample; + uint64_t HotBlockCount; + FuncSampleStats() : SampleSum(0), MaxSample(0), HotBlockCount(0) {} + FuncSampleStats(uint64_t SampleSum, uint64_t MaxSample, + uint64_t HotBlockCount) + : SampleSum(SampleSum), MaxSample(MaxSample), + HotBlockCount(HotBlockCount) {} +}; +} // end anonymous namespace + +namespace { +enum MatchStatus { MS_Match, MS_FirstUnique, MS_SecondUnique, MS_None }; + +// Class for updating merging steps for two sorted maps. The class should be +// instantiated with a map iterator type. +template class MatchStep { +public: + MatchStep() = delete; + + MatchStep(T FirstIter, T FirstEnd, T SecondIter, T SecondEnd) + : FirstIter(FirstIter), FirstEnd(FirstEnd), SecondIter(SecondIter), + SecondEnd(SecondEnd), Status(MS_None) {} + + bool areBothFinished() const { + return (FirstIter == FirstEnd && SecondIter == SecondEnd); + } + + bool isFirstFinished() const { return FirstIter == FirstEnd; } + + bool isSecondFinished() const { return SecondIter == SecondEnd; } + + /// Advance one step based on the previous match status unless the previous + /// status is MS_None. Then update Status based on the comparison between two + /// container iterators at the current step. If the previous status is + /// MS_None, it means two iterators are at the beginning and no comparison has + /// been made, so we simply update Status without advancing the iterators. + void updateOneStep(); + + T getFirstIter() const { return FirstIter; } + + T getSecondIter() const { return SecondIter; } + + MatchStatus getMatchStatus() const { return Status; } + +private: + // Current iterator and end iterator of the first container. + T FirstIter; + T FirstEnd; + // Current iterator and end iterator of the second container. + T SecondIter; + T SecondEnd; + // Match status of the current step. + MatchStatus Status; +}; +} // end anonymous namespace + +template void MatchStep::updateOneStep() { + switch (Status) { + case MS_Match: + ++FirstIter; + ++SecondIter; + break; + case MS_FirstUnique: + ++FirstIter; + break; + case MS_SecondUnique: + ++SecondIter; + break; + case MS_None: + break; + } + + // Update Status according to iterators at the current step. + if (areBothFinished()) + return; + if (FirstIter != FirstEnd && + (SecondIter == SecondEnd || FirstIter->first < SecondIter->first)) + Status = MS_FirstUnique; + else if (SecondIter != SecondEnd && + (FirstIter == FirstEnd || SecondIter->first < FirstIter->first)) + Status = MS_SecondUnique; + else + Status = MS_Match; +} + +// Return the sum of line/block samples, the max line/block sample, and the +// number of line/block samples above the given threshold in a function +// including its inlinees. +static void getFuncSampleStats(const sampleprof::FunctionSamples &Func, + FuncSampleStats &FuncStats, + uint64_t HotThreshold) { + for (const auto &L : Func.getBodySamples()) { + uint64_t Sample = L.second.getSamples(); + FuncStats.SampleSum += Sample; + FuncStats.MaxSample = std::max(FuncStats.MaxSample, Sample); + if (Sample >= HotThreshold) + ++FuncStats.HotBlockCount; + } + + for (const auto &C : Func.getCallsiteSamples()) { + for (const auto &F : C.second) + getFuncSampleStats(F.second, FuncStats, HotThreshold); + } +} + +/// Predicate that determines if a function is hot with a given threshold. We +/// keep it separate from its callsites for possible extension in the future. +static bool isFunctionHot(const FuncSampleStats &FuncStats, + uint64_t HotThreshold) { + // We intentionally compare the maximum sample count in a function with the + // HotThreshold to get an approximate determination on hot functions. + return (FuncStats.MaxSample >= HotThreshold); +} + +namespace { +class SampleOverlapAggregator { +public: + SampleOverlapAggregator(const std::string &BaseFilename, + const std::string &TestFilename, + double LowSimilarityThreshold, double Epsilon, + const OverlapFuncFilters &FuncFilter) + : BaseFilename(BaseFilename), TestFilename(TestFilename), + LowSimilarityThreshold(LowSimilarityThreshold), Epsilon(Epsilon), + FuncFilter(FuncFilter) {} + + /// Detect 0-sample input profile and report to output stream. This interface + /// should be called after loadProfiles(). + bool detectZeroSampleProfile(raw_fd_ostream &OS) const; + + /// Write out function-level similarity statistics for functions specified by + /// options --function, --value-cutoff, and --similarity-cutoff. + void dumpFuncSimilarity(raw_fd_ostream &OS) const; + + /// Write out program-level similarity and overlap statistics. + void dumpProgramSummary(raw_fd_ostream &OS) const; + + /// Write out hot-function and hot-block statistics for base_profile, + /// test_profile, and their overlap. For both cases, the overlap HO is + /// calculated as follows: + /// Given the number of functions (or blocks) that are hot in both profiles + /// HCommon and the number of functions (or blocks) that are hot in at + /// least one profile HUnion, HO = HCommon / HUnion. + void dumpHotFuncAndBlockOverlap(raw_fd_ostream &OS) const; + + /// This function tries matching functions in base and test profiles. For each + /// pair of matched functions, it aggregates the function-level + /// similarity into a profile-level similarity. It also dump function-level + /// similarity information of functions specified by --function, + /// --value-cutoff, and --similarity-cutoff options. The program-level + /// similarity PS is computed as follows: + /// Given function-level similarity FS(A) for all function A, the + /// weight of function A in base profile WB(A), and the weight of function + /// A in test profile WT(A), compute PS(base_profile, test_profile) = + /// sum_A(FS(A) * avg(WB(A), WT(A))) ranging in [0.0f to 1.0f] with 0.0 + /// meaning no-overlap. + void computeSampleProfileOverlap(raw_fd_ostream &OS); + + /// Initialize ProfOverlap with the sum of samples in base and test + /// profiles. This function also computes and keeps the sum of samples and + /// max sample counts of each function in BaseStats and TestStats for later + /// use to avoid re-computations. + void initializeSampleProfileOverlap(); + + /// Load profiles specified by BaseFilename and TestFilename. + std::error_code loadProfiles(); + + using FuncSampleStatsMap = + std::unordered_map; + +private: + SampleOverlapStats ProfOverlap; + SampleOverlapStats HotFuncOverlap; + SampleOverlapStats HotBlockOverlap; + std::string BaseFilename; + std::string TestFilename; + std::unique_ptr BaseReader; + std::unique_ptr TestReader; + // BaseStats and TestStats hold FuncSampleStats for each function, with + // function name as the key. + FuncSampleStatsMap BaseStats; + FuncSampleStatsMap TestStats; + // Low similarity threshold in floating point number + double LowSimilarityThreshold; + // Block samples above BaseHotThreshold or TestHotThreshold are considered hot + // for tracking hot blocks. + uint64_t BaseHotThreshold; + uint64_t TestHotThreshold; + // A small threshold used to round the results of floating point accumulations + // to resolve imprecision. + const double Epsilon; + std::multimap> + FuncSimilarityDump; + // FuncFilter carries specifications in options --value-cutoff and + // --function. + OverlapFuncFilters FuncFilter; + // Column offsets for printing the function-level details table. + static const unsigned int TestWeightCol = 15; + static const unsigned int SimilarityCol = 30; + static const unsigned int OverlapCol = 43; + static const unsigned int BaseUniqueCol = 53; + static const unsigned int TestUniqueCol = 67; + static const unsigned int BaseSampleCol = 81; + static const unsigned int TestSampleCol = 96; + static const unsigned int FuncNameCol = 111; + + /// Return a similarity of two line/block sample counters in the same + /// function in base and test profiles. The line/block-similarity BS(i) is + /// computed as follows: + /// For an offsets i, given the sample count at i in base profile BB(i), + /// the sample count at i in test profile BT(i), the sum of sample counts + /// in this function in base profile SB, and the sum of sample counts in + /// this function in test profile ST, compute BS(i) = 1.0 - fabs(BB(i)/SB - + /// BT(i)/ST), ranging in [0.0f to 1.0f] with 0.0 meaning no-overlap. + double computeBlockSimilarity(uint64_t BaseSample, uint64_t TestSample, + const SampleOverlapStats &FuncOverlap) const; + + void updateHotBlockOverlap(uint64_t BaseSample, uint64_t TestSample, + uint64_t HotBlockCount); + + void getHotFunctions(const FuncSampleStatsMap &ProfStats, + FuncSampleStatsMap &HotFunc, + uint64_t HotThreshold) const; + + void computeHotFuncOverlap(); + + /// This function updates statistics in FuncOverlap, HotBlockOverlap, and + /// Difference for two sample units in a matched function according to the + /// given match status. + void updateOverlapStatsForFunction(uint64_t BaseSample, uint64_t TestSample, + uint64_t HotBlockCount, + SampleOverlapStats &FuncOverlap, + double &Difference, MatchStatus Status); + + /// This function updates statistics in FuncOverlap, HotBlockOverlap, and + /// Difference for unmatched callees that only present in one profile in a + /// matched caller function. + void updateForUnmatchedCallee(const sampleprof::FunctionSamples &Func, + SampleOverlapStats &FuncOverlap, + double &Difference, MatchStatus Status); + + /// This function updates sample overlap statistics of an overlap function in + /// base and test profile. It also calculates a function-internal similarity + /// FIS as follows: + /// For offsets i that have samples in at least one profile in this + /// function A, given BS(i) returned by computeBlockSimilarity(), compute + /// FIS(A) = (2.0 - sum_i(1.0 - BS(i))) / 2, ranging in [0.0f to 1.0f] with + /// 0.0 meaning no overlap. + double computeSampleFunctionInternalOverlap( + const sampleprof::FunctionSamples &BaseFunc, + const sampleprof::FunctionSamples &TestFunc, + SampleOverlapStats &FuncOverlap); + + /// Function-level similarity (FS) is a weighted value over function internal + /// similarity (FIS). This function computes a function's FS from its FIS by + /// applying the weight. + double weightForFuncSimilarity(double FuncSimilarity, uint64_t BaseFuncSample, + uint64_t TestFuncSample) const; + + /// The function-level similarity FS(A) for a function A is computed as + /// follows: + /// Compute a function-internal similarity FIS(A) by + /// computeSampleFunctionInternalOverlap(). Then, with the weight of + /// function A in base profile WB(A), and the weight of function A in test + /// profile WT(A), compute FS(A) = FIS(A) * (1.0 - fabs(WB(A) - WT(A))) + /// ranging in [0.0f to 1.0f] with 0.0 meaning no overlap. + double + computeSampleFunctionOverlap(const sampleprof::FunctionSamples *BaseFunc, + const sampleprof::FunctionSamples *TestFunc, + SampleOverlapStats *FuncOverlap, + uint64_t BaseFuncSample, + uint64_t TestFuncSample); + + /// Profile-level similarity (PS) is a weighted aggregate over function-level + /// similarities (FS). This method weights the FS value by the function + /// weights in the base and test profiles for the aggregation. + double weightByImportance(double FuncSimilarity, uint64_t BaseFuncSample, + uint64_t TestFuncSample) const; +}; +} // end anonymous namespace + +bool SampleOverlapAggregator::detectZeroSampleProfile( + raw_fd_ostream &OS) const { + bool HaveZeroSample = false; + if (ProfOverlap.BaseSample == 0) { + OS << "Sum of sample counts for profile " << BaseFilename << " is 0.\n"; + HaveZeroSample = true; + } + if (ProfOverlap.TestSample == 0) { + OS << "Sum of sample counts for profile " << TestFilename << " is 0.\n"; + HaveZeroSample = true; + } + return HaveZeroSample; +} + +double SampleOverlapAggregator::computeBlockSimilarity( + uint64_t BaseSample, uint64_t TestSample, + const SampleOverlapStats &FuncOverlap) const { + double BaseFrac = 0.0; + double TestFrac = 0.0; + if (FuncOverlap.BaseSample > 0) + BaseFrac = static_cast(BaseSample) / FuncOverlap.BaseSample; + if (FuncOverlap.TestSample > 0) + TestFrac = static_cast(TestSample) / FuncOverlap.TestSample; + return 1.0 - std::fabs(BaseFrac - TestFrac); +} + +void SampleOverlapAggregator::updateHotBlockOverlap(uint64_t BaseSample, + uint64_t TestSample, + uint64_t HotBlockCount) { + bool IsBaseHot = (BaseSample >= BaseHotThreshold); + bool IsTestHot = (TestSample >= TestHotThreshold); + if (!IsBaseHot && !IsTestHot) + return; + + HotBlockOverlap.UnionCount += HotBlockCount; + if (IsBaseHot) + HotBlockOverlap.BaseCount += HotBlockCount; + if (IsTestHot) + HotBlockOverlap.TestCount += HotBlockCount; + if (IsBaseHot && IsTestHot) + HotBlockOverlap.OverlapCount += HotBlockCount; +} + +void SampleOverlapAggregator::getHotFunctions( + const FuncSampleStatsMap &ProfStats, FuncSampleStatsMap &HotFunc, + uint64_t HotThreshold) const { + for (const auto &F : ProfStats) { + if (isFunctionHot(F.second, HotThreshold)) + HotFunc.emplace(F.first, F.second); + } +} + +void SampleOverlapAggregator::computeHotFuncOverlap() { + FuncSampleStatsMap BaseHotFunc; + getHotFunctions(BaseStats, BaseHotFunc, BaseHotThreshold); + HotFuncOverlap.BaseCount = BaseHotFunc.size(); + + FuncSampleStatsMap TestHotFunc; + getHotFunctions(TestStats, TestHotFunc, TestHotThreshold); + HotFuncOverlap.TestCount = TestHotFunc.size(); + HotFuncOverlap.UnionCount = HotFuncOverlap.TestCount; + + for (const auto &F : BaseHotFunc) { + if (TestHotFunc.count(F.first)) + ++HotFuncOverlap.OverlapCount; + else + ++HotFuncOverlap.UnionCount; + } +} + +void SampleOverlapAggregator::updateOverlapStatsForFunction( + uint64_t BaseSample, uint64_t TestSample, uint64_t HotBlockCount, + SampleOverlapStats &FuncOverlap, double &Difference, MatchStatus Status) { + assert(Status != MS_None && + "Match status should be updated before updating overlap statistics"); + if (Status == MS_FirstUnique) { + TestSample = 0; + FuncOverlap.BaseUniqueSample += BaseSample; + } else if (Status == MS_SecondUnique) { + BaseSample = 0; + FuncOverlap.TestUniqueSample += TestSample; + } else { + ++FuncOverlap.OverlapCount; + } + + FuncOverlap.UnionSample += std::max(BaseSample, TestSample); + FuncOverlap.OverlapSample += std::min(BaseSample, TestSample); + Difference += + 1.0 - computeBlockSimilarity(BaseSample, TestSample, FuncOverlap); + updateHotBlockOverlap(BaseSample, TestSample, HotBlockCount); +} + +void SampleOverlapAggregator::updateForUnmatchedCallee( + const sampleprof::FunctionSamples &Func, SampleOverlapStats &FuncOverlap, + double &Difference, MatchStatus Status) { + assert((Status == MS_FirstUnique || Status == MS_SecondUnique) && + "Status must be either of the two unmatched cases"); + FuncSampleStats FuncStats; + if (Status == MS_FirstUnique) { + getFuncSampleStats(Func, FuncStats, BaseHotThreshold); + updateOverlapStatsForFunction(FuncStats.SampleSum, 0, + FuncStats.HotBlockCount, FuncOverlap, + Difference, Status); + } else { + getFuncSampleStats(Func, FuncStats, TestHotThreshold); + updateOverlapStatsForFunction(0, FuncStats.SampleSum, + FuncStats.HotBlockCount, FuncOverlap, + Difference, Status); + } +} + +double SampleOverlapAggregator::computeSampleFunctionInternalOverlap( + const sampleprof::FunctionSamples &BaseFunc, + const sampleprof::FunctionSamples &TestFunc, + SampleOverlapStats &FuncOverlap) { + + using namespace sampleprof; + + double Difference = 0; + + // Accumulate Difference for regular line/block samples in the function. + // We match them through sort-merge join algorithm because + // FunctionSamples::getBodySamples() returns a map of sample counters ordered + // by their offsets. + MatchStep BlockIterStep( + BaseFunc.getBodySamples().cbegin(), BaseFunc.getBodySamples().cend(), + TestFunc.getBodySamples().cbegin(), TestFunc.getBodySamples().cend()); + BlockIterStep.updateOneStep(); + while (!BlockIterStep.areBothFinished()) { + uint64_t BaseSample = + BlockIterStep.isFirstFinished() + ? 0 + : BlockIterStep.getFirstIter()->second.getSamples(); + uint64_t TestSample = + BlockIterStep.isSecondFinished() + ? 0 + : BlockIterStep.getSecondIter()->second.getSamples(); + updateOverlapStatsForFunction(BaseSample, TestSample, 1, FuncOverlap, + Difference, BlockIterStep.getMatchStatus()); + + BlockIterStep.updateOneStep(); + } + + // Accumulate Difference for callsite lines in the function. We match + // them through sort-merge algorithm because + // FunctionSamples::getCallsiteSamples() returns a map of callsite records + // ordered by their offsets. + MatchStep CallsiteIterStep( + BaseFunc.getCallsiteSamples().cbegin(), + BaseFunc.getCallsiteSamples().cend(), + TestFunc.getCallsiteSamples().cbegin(), + TestFunc.getCallsiteSamples().cend()); + CallsiteIterStep.updateOneStep(); + while (!CallsiteIterStep.areBothFinished()) { + MatchStatus CallsiteStepStatus = CallsiteIterStep.getMatchStatus(); + assert(CallsiteStepStatus != MS_None && + "Match status should be updated before entering loop body"); + + if (CallsiteStepStatus != MS_Match) { + auto Callsite = (CallsiteStepStatus == MS_FirstUnique) + ? CallsiteIterStep.getFirstIter() + : CallsiteIterStep.getSecondIter(); + for (const auto &F : Callsite->second) + updateForUnmatchedCallee(F.second, FuncOverlap, Difference, + CallsiteStepStatus); + } else { + // There may be multiple inlinees at the same offset, so we need to try + // matching all of them. This match is implemented through sort-merge + // algorithm because callsite records at the same offset are ordered by + // function names. + MatchStep CalleeIterStep( + CallsiteIterStep.getFirstIter()->second.cbegin(), + CallsiteIterStep.getFirstIter()->second.cend(), + CallsiteIterStep.getSecondIter()->second.cbegin(), + CallsiteIterStep.getSecondIter()->second.cend()); + CalleeIterStep.updateOneStep(); + while (!CalleeIterStep.areBothFinished()) { + MatchStatus CalleeStepStatus = CalleeIterStep.getMatchStatus(); + if (CalleeStepStatus != MS_Match) { + auto Callee = (CalleeStepStatus == MS_FirstUnique) + ? CalleeIterStep.getFirstIter() + : CalleeIterStep.getSecondIter(); + updateForUnmatchedCallee(Callee->second, FuncOverlap, Difference, + CalleeStepStatus); + } else { + // An inlined function can contain other inlinees inside, so compute + // the Difference recursively. + Difference += 2.0 - 2 * computeSampleFunctionInternalOverlap( + CalleeIterStep.getFirstIter()->second, + CalleeIterStep.getSecondIter()->second, + FuncOverlap); + } + CalleeIterStep.updateOneStep(); + } + } + CallsiteIterStep.updateOneStep(); + } + + // Difference reflects the total differences of line/block samples in this + // function and ranges in [0.0f to 2.0f]. Take (2.0 - Difference) / 2 to + // reflect the similarity between function profiles in [0.0f to 1.0f]. + return (2.0 - Difference) / 2; +} + +double SampleOverlapAggregator::weightForFuncSimilarity( + double FuncInternalSimilarity, uint64_t BaseFuncSample, + uint64_t TestFuncSample) const { + // Compute the weight as the distance between the function weights in two + // profiles. + double BaseFrac = 0.0; + double TestFrac = 0.0; + assert(ProfOverlap.BaseSample > 0 && + "Total samples in base profile should be greater than 0"); + BaseFrac = static_cast(BaseFuncSample) / ProfOverlap.BaseSample; + assert(ProfOverlap.TestSample > 0 && + "Total samples in test profile should be greater than 0"); + TestFrac = static_cast(TestFuncSample) / ProfOverlap.TestSample; + double WeightDistance = std::fabs(BaseFrac - TestFrac); + + // Take WeightDistance into the similarity. + return FuncInternalSimilarity * (1 - WeightDistance); +} + +double +SampleOverlapAggregator::weightByImportance(double FuncSimilarity, + uint64_t BaseFuncSample, + uint64_t TestFuncSample) const { + + double BaseFrac = 0.0; + double TestFrac = 0.0; + assert(ProfOverlap.BaseSample > 0 && + "Total samples in base profile should be greater than 0"); + BaseFrac = static_cast(BaseFuncSample) / ProfOverlap.BaseSample / 2.0; + assert(ProfOverlap.TestSample > 0 && + "Total samples in test profile should be greater than 0"); + TestFrac = static_cast(TestFuncSample) / ProfOverlap.TestSample / 2.0; + return FuncSimilarity * (BaseFrac + TestFrac); +} + +double SampleOverlapAggregator::computeSampleFunctionOverlap( + const sampleprof::FunctionSamples *BaseFunc, + const sampleprof::FunctionSamples *TestFunc, + SampleOverlapStats *FuncOverlap, uint64_t BaseFuncSample, + uint64_t TestFuncSample) { + // Default function internal similarity before weighted, meaning two functions + // has no overlap. + const double DefaultFuncInternalSimilarity = 0; + double FuncSimilarity; + double FuncInternalSimilarity; + + // If BaseFunc or TestFunc is nullptr, it means the functions do not overlap. + // In this case, we use DefaultFuncInternalSimilarity as the function internal + // similarity. + if (!BaseFunc || !TestFunc) { + FuncInternalSimilarity = DefaultFuncInternalSimilarity; + } else { + assert(FuncOverlap != nullptr && + "FuncOverlap should be provided in this case"); + FuncInternalSimilarity = computeSampleFunctionInternalOverlap( + *BaseFunc, *TestFunc, *FuncOverlap); + // Now, FuncInternalSimilarity may be a little less than 0 due to + // imprecision of floating point accumulations. Make it zero if the + // difference is below Epsilon. + FuncInternalSimilarity = (std::fabs(FuncInternalSimilarity - 0) < Epsilon) + ? 0 + : FuncInternalSimilarity; + } + FuncSimilarity = weightForFuncSimilarity(FuncInternalSimilarity, + BaseFuncSample, TestFuncSample); + return FuncSimilarity; +} + +void SampleOverlapAggregator::computeSampleProfileOverlap(raw_fd_ostream &OS) { + using namespace sampleprof; + + std::unordered_map + BaseFuncProf; + const auto &BaseProfiles = BaseReader->getProfiles(); + for (const auto &BaseFunc : BaseProfiles) { + BaseFuncProf.emplace(BaseFunc.second.getContext(), &(BaseFunc.second)); + } + ProfOverlap.UnionCount = BaseFuncProf.size(); + + const auto &TestProfiles = TestReader->getProfiles(); + for (const auto &TestFunc : TestProfiles) { + SampleOverlapStats FuncOverlap; + FuncOverlap.TestName = TestFunc.second.getContext(); + assert(TestStats.count(FuncOverlap.TestName) && + "TestStats should have records for all functions in test profile " + "except inlinees"); + FuncOverlap.TestSample = TestStats[FuncOverlap.TestName].SampleSum; + + bool Matched = false; + const auto Match = BaseFuncProf.find(FuncOverlap.TestName); + if (Match == BaseFuncProf.end()) { + const FuncSampleStats &FuncStats = TestStats[FuncOverlap.TestName]; + ++ProfOverlap.TestUniqueCount; + ProfOverlap.TestUniqueSample += FuncStats.SampleSum; + FuncOverlap.TestUniqueSample = FuncStats.SampleSum; + + updateHotBlockOverlap(0, FuncStats.SampleSum, FuncStats.HotBlockCount); + + double FuncSimilarity = computeSampleFunctionOverlap( + nullptr, nullptr, nullptr, 0, FuncStats.SampleSum); + ProfOverlap.Similarity += + weightByImportance(FuncSimilarity, 0, FuncStats.SampleSum); + + ++ProfOverlap.UnionCount; + ProfOverlap.UnionSample += FuncStats.SampleSum; + } else { + ++ProfOverlap.OverlapCount; + + // Two functions match with each other. Compute function-level overlap and + // aggregate them into profile-level overlap. + FuncOverlap.BaseName = Match->second->getContext(); + assert(BaseStats.count(FuncOverlap.BaseName) && + "BaseStats should have records for all functions in base profile " + "except inlinees"); + FuncOverlap.BaseSample = BaseStats[FuncOverlap.BaseName].SampleSum; + + FuncOverlap.Similarity = computeSampleFunctionOverlap( + Match->second, &TestFunc.second, &FuncOverlap, FuncOverlap.BaseSample, + FuncOverlap.TestSample); + ProfOverlap.Similarity += + weightByImportance(FuncOverlap.Similarity, FuncOverlap.BaseSample, + FuncOverlap.TestSample); + ProfOverlap.OverlapSample += FuncOverlap.OverlapSample; + ProfOverlap.UnionSample += FuncOverlap.UnionSample; + + // Accumulate the percentage of base unique and test unique samples into + // ProfOverlap. + ProfOverlap.BaseUniqueSample += FuncOverlap.BaseUniqueSample; + ProfOverlap.TestUniqueSample += FuncOverlap.TestUniqueSample; + + // Remove matched base functions for later reporting functions not found + // in test profile. + BaseFuncProf.erase(Match); + Matched = true; + } + + // Print function-level similarity information if specified by options. + assert(TestStats.count(FuncOverlap.TestName) && + "TestStats should have records for all functions in test profile " + "except inlinees"); + if (TestStats[FuncOverlap.TestName].MaxSample >= FuncFilter.ValueCutoff || + (Matched && FuncOverlap.Similarity < LowSimilarityThreshold) || + (Matched && !FuncFilter.NameFilter.empty() && + FuncOverlap.BaseName.toString().find(FuncFilter.NameFilter) != + std::string::npos)) { + assert(ProfOverlap.BaseSample > 0 && + "Total samples in base profile should be greater than 0"); + FuncOverlap.BaseWeight = + static_cast(FuncOverlap.BaseSample) / ProfOverlap.BaseSample; + assert(ProfOverlap.TestSample > 0 && + "Total samples in test profile should be greater than 0"); + FuncOverlap.TestWeight = + static_cast(FuncOverlap.TestSample) / ProfOverlap.TestSample; + FuncSimilarityDump.emplace(FuncOverlap.BaseWeight, FuncOverlap); + } + } + + // Traverse through functions in base profile but not in test profile. + for (const auto &F : BaseFuncProf) { + assert(BaseStats.count(F.second->getContext()) && + "BaseStats should have records for all functions in base profile " + "except inlinees"); + const FuncSampleStats &FuncStats = BaseStats[F.second->getContext()]; + ++ProfOverlap.BaseUniqueCount; + ProfOverlap.BaseUniqueSample += FuncStats.SampleSum; + + updateHotBlockOverlap(FuncStats.SampleSum, 0, FuncStats.HotBlockCount); + + double FuncSimilarity = computeSampleFunctionOverlap( + nullptr, nullptr, nullptr, FuncStats.SampleSum, 0); + ProfOverlap.Similarity += + weightByImportance(FuncSimilarity, FuncStats.SampleSum, 0); + + ProfOverlap.UnionSample += FuncStats.SampleSum; + } + + // Now, ProfSimilarity may be a little greater than 1 due to imprecision + // of floating point accumulations. Make it 1.0 if the difference is below + // Epsilon. + ProfOverlap.Similarity = (std::fabs(ProfOverlap.Similarity - 1) < Epsilon) + ? 1 + : ProfOverlap.Similarity; + + computeHotFuncOverlap(); +} + +void SampleOverlapAggregator::initializeSampleProfileOverlap() { + const auto &BaseProf = BaseReader->getProfiles(); + for (const auto &I : BaseProf) { + ++ProfOverlap.BaseCount; + FuncSampleStats FuncStats; + getFuncSampleStats(I.second, FuncStats, BaseHotThreshold); + ProfOverlap.BaseSample += FuncStats.SampleSum; + BaseStats.emplace(I.second.getContext(), FuncStats); + } + + const auto &TestProf = TestReader->getProfiles(); + for (const auto &I : TestProf) { + ++ProfOverlap.TestCount; + FuncSampleStats FuncStats; + getFuncSampleStats(I.second, FuncStats, TestHotThreshold); + ProfOverlap.TestSample += FuncStats.SampleSum; + TestStats.emplace(I.second.getContext(), FuncStats); + } + + ProfOverlap.BaseName = StringRef(BaseFilename); + ProfOverlap.TestName = StringRef(TestFilename); +} + +void SampleOverlapAggregator::dumpFuncSimilarity(raw_fd_ostream &OS) const { + using namespace sampleprof; + + if (FuncSimilarityDump.empty()) + return; + + formatted_raw_ostream FOS(OS); + FOS << "Function-level details:\n"; + FOS << "Base weight"; + FOS.PadToColumn(TestWeightCol); + FOS << "Test weight"; + FOS.PadToColumn(SimilarityCol); + FOS << "Similarity"; + FOS.PadToColumn(OverlapCol); + FOS << "Overlap"; + FOS.PadToColumn(BaseUniqueCol); + FOS << "Base unique"; + FOS.PadToColumn(TestUniqueCol); + FOS << "Test unique"; + FOS.PadToColumn(BaseSampleCol); + FOS << "Base samples"; + FOS.PadToColumn(TestSampleCol); + FOS << "Test samples"; + FOS.PadToColumn(FuncNameCol); + FOS << "Function name\n"; + for (const auto &F : FuncSimilarityDump) { + double OverlapPercent = + F.second.UnionSample > 0 + ? static_cast(F.second.OverlapSample) / F.second.UnionSample + : 0; + double BaseUniquePercent = + F.second.BaseSample > 0 + ? static_cast(F.second.BaseUniqueSample) / + F.second.BaseSample + : 0; + double TestUniquePercent = + F.second.TestSample > 0 + ? static_cast(F.second.TestUniqueSample) / + F.second.TestSample + : 0; + + FOS << format("%.2f%%", F.second.BaseWeight * 100); + FOS.PadToColumn(TestWeightCol); + FOS << format("%.2f%%", F.second.TestWeight * 100); + FOS.PadToColumn(SimilarityCol); + FOS << format("%.2f%%", F.second.Similarity * 100); + FOS.PadToColumn(OverlapCol); + FOS << format("%.2f%%", OverlapPercent * 100); + FOS.PadToColumn(BaseUniqueCol); + FOS << format("%.2f%%", BaseUniquePercent * 100); + FOS.PadToColumn(TestUniqueCol); + FOS << format("%.2f%%", TestUniquePercent * 100); + FOS.PadToColumn(BaseSampleCol); + FOS << F.second.BaseSample; + FOS.PadToColumn(TestSampleCol); + FOS << F.second.TestSample; + FOS.PadToColumn(FuncNameCol); + FOS << F.second.TestName.toString() << "\n"; + } +} + +void SampleOverlapAggregator::dumpProgramSummary(raw_fd_ostream &OS) const { + OS << "Profile overlap infomation for base_profile: " + << ProfOverlap.BaseName.toString() + << " and test_profile: " << ProfOverlap.TestName.toString() + << "\nProgram level:\n"; + + OS << " Whole program profile similarity: " + << format("%.3f%%", ProfOverlap.Similarity * 100) << "\n"; + + assert(ProfOverlap.UnionSample > 0 && + "Total samples in two profile should be greater than 0"); + double OverlapPercent = + static_cast(ProfOverlap.OverlapSample) / ProfOverlap.UnionSample; + assert(ProfOverlap.BaseSample > 0 && + "Total samples in base profile should be greater than 0"); + double BaseUniquePercent = static_cast(ProfOverlap.BaseUniqueSample) / + ProfOverlap.BaseSample; + assert(ProfOverlap.TestSample > 0 && + "Total samples in test profile should be greater than 0"); + double TestUniquePercent = static_cast(ProfOverlap.TestUniqueSample) / + ProfOverlap.TestSample; + + OS << " Whole program sample overlap: " + << format("%.3f%%", OverlapPercent * 100) << "\n"; + OS << " percentage of samples unique in base profile: " + << format("%.3f%%", BaseUniquePercent * 100) << "\n"; + OS << " percentage of samples unique in test profile: " + << format("%.3f%%", TestUniquePercent * 100) << "\n"; + OS << " total samples in base profile: " << ProfOverlap.BaseSample << "\n" + << " total samples in test profile: " << ProfOverlap.TestSample << "\n"; + + assert(ProfOverlap.UnionCount > 0 && + "There should be at least one function in two input profiles"); + double FuncOverlapPercent = + static_cast(ProfOverlap.OverlapCount) / ProfOverlap.UnionCount; + OS << " Function overlap: " << format("%.3f%%", FuncOverlapPercent * 100) + << "\n"; + OS << " overlap functions: " << ProfOverlap.OverlapCount << "\n"; + OS << " functions unique in base profile: " << ProfOverlap.BaseUniqueCount + << "\n"; + OS << " functions unique in test profile: " << ProfOverlap.TestUniqueCount + << "\n"; +} + +void SampleOverlapAggregator::dumpHotFuncAndBlockOverlap( + raw_fd_ostream &OS) const { + assert(HotFuncOverlap.UnionCount > 0 && + "There should be at least one hot function in two input profiles"); + OS << " Hot-function overlap: " + << format("%.3f%%", static_cast(HotFuncOverlap.OverlapCount) / + HotFuncOverlap.UnionCount * 100) + << "\n"; + OS << " overlap hot functions: " << HotFuncOverlap.OverlapCount << "\n"; + OS << " hot functions unique in base profile: " + << HotFuncOverlap.BaseCount - HotFuncOverlap.OverlapCount << "\n"; + OS << " hot functions unique in test profile: " + << HotFuncOverlap.TestCount - HotFuncOverlap.OverlapCount << "\n"; + + assert(HotBlockOverlap.UnionCount > 0 && + "There should be at least one hot block in two input profiles"); + OS << " Hot-block overlap: " + << format("%.3f%%", static_cast(HotBlockOverlap.OverlapCount) / + HotBlockOverlap.UnionCount * 100) + << "\n"; + OS << " overlap hot blocks: " << HotBlockOverlap.OverlapCount << "\n"; + OS << " hot blocks unique in base profile: " + << HotBlockOverlap.BaseCount - HotBlockOverlap.OverlapCount << "\n"; + OS << " hot blocks unique in test profile: " + << HotBlockOverlap.TestCount - HotBlockOverlap.OverlapCount << "\n"; +} + +std::error_code SampleOverlapAggregator::loadProfiles() { + using namespace sampleprof; + + LLVMContext Context; + auto BaseReaderOrErr = SampleProfileReader::create(BaseFilename, Context, + FSDiscriminatorPassOption); + if (std::error_code EC = BaseReaderOrErr.getError()) + exitWithErrorCode(EC, BaseFilename); + + auto TestReaderOrErr = SampleProfileReader::create(TestFilename, Context, + FSDiscriminatorPassOption); + if (std::error_code EC = TestReaderOrErr.getError()) + exitWithErrorCode(EC, TestFilename); + + BaseReader = std::move(BaseReaderOrErr.get()); + TestReader = std::move(TestReaderOrErr.get()); + + if (std::error_code EC = BaseReader->read()) + exitWithErrorCode(EC, BaseFilename); + if (std::error_code EC = TestReader->read()) + exitWithErrorCode(EC, TestFilename); + if (BaseReader->profileIsProbeBased() != TestReader->profileIsProbeBased()) + exitWithError( + "cannot compare probe-based profile with non-probe-based profile"); + if (BaseReader->profileIsCS() != TestReader->profileIsCS()) + exitWithError("cannot compare CS profile with non-CS profile"); + + // Load BaseHotThreshold and TestHotThreshold as 99-percentile threshold in + // profile summary. + ProfileSummary &BasePS = BaseReader->getSummary(); + ProfileSummary &TestPS = TestReader->getSummary(); + BaseHotThreshold = + ProfileSummaryBuilder::getHotCountThreshold(BasePS.getDetailedSummary()); + TestHotThreshold = + ProfileSummaryBuilder::getHotCountThreshold(TestPS.getDetailedSummary()); + + return std::error_code(); +} + +void overlapSampleProfile(const std::string &BaseFilename, + const std::string &TestFilename, + const OverlapFuncFilters &FuncFilter, + uint64_t SimilarityCutoff, raw_fd_ostream &OS) { + using namespace sampleprof; + + // We use 0.000005 to initialize OverlapAggr.Epsilon because the final metrics + // report 2--3 places after decimal point in percentage numbers. + SampleOverlapAggregator OverlapAggr( + BaseFilename, TestFilename, + static_cast(SimilarityCutoff) / 1000000, 0.000005, FuncFilter); + if (std::error_code EC = OverlapAggr.loadProfiles()) + exitWithErrorCode(EC); + + OverlapAggr.initializeSampleProfileOverlap(); + if (OverlapAggr.detectZeroSampleProfile(OS)) + return; + + OverlapAggr.computeSampleProfileOverlap(OS); + + OverlapAggr.dumpProgramSummary(OS); + OverlapAggr.dumpHotFuncAndBlockOverlap(OS); + OverlapAggr.dumpFuncSimilarity(OS); +} + +static int overlap_main(int argc, const char *argv[]) { + cl::opt BaseFilename(cl::Positional, cl::Required, + cl::desc("")); + cl::opt TestFilename(cl::Positional, cl::Required, + cl::desc("")); + cl::opt Output("output", cl::value_desc("output"), cl::init("-"), + cl::desc("Output file")); + cl::alias OutputA("o", cl::desc("Alias for --output"), cl::aliasopt(Output)); + cl::opt IsCS( + "cs", cl::init(false), + cl::desc("For context sensitive PGO counts. Does not work with CSSPGO.")); + cl::opt ValueCutoff( + "value-cutoff", cl::init(-1), + cl::desc( + "Function level overlap information for every function (with calling " + "context for csspgo) in test " + "profile with max count value greater then the parameter value")); + cl::opt FuncNameFilter( + "function", + cl::desc("Function level overlap information for matching functions. For " + "CSSPGO this takes a a function name with calling context")); + cl::opt SimilarityCutoff( + "similarity-cutoff", cl::init(0), + cl::desc("For sample profiles, list function names (with calling context " + "for csspgo) for overlapped functions " + "with similarities below the cutoff (percentage times 10000).")); + cl::opt ProfileKind( + cl::desc("Profile kind:"), cl::init(instr), + cl::values(clEnumVal(instr, "Instrumentation profile (default)"), + clEnumVal(sample, "Sample profile"))); + cl::ParseCommandLineOptions(argc, argv, "LLVM profile data overlap tool\n"); + + std::error_code EC; + raw_fd_ostream OS(Output.data(), EC, sys::fs::OF_TextWithCRLF); + if (EC) + exitWithErrorCode(EC, Output); + + if (ProfileKind == instr) + overlapInstrProfile(BaseFilename, TestFilename, + OverlapFuncFilters{ValueCutoff, FuncNameFilter}, OS, + IsCS); + else + overlapSampleProfile(BaseFilename, TestFilename, + OverlapFuncFilters{ValueCutoff, FuncNameFilter}, + SimilarityCutoff, OS); + + return 0; +} + +namespace { +struct ValueSitesStats { + ValueSitesStats() + : TotalNumValueSites(0), TotalNumValueSitesWithValueProfile(0), + TotalNumValues(0) {} + uint64_t TotalNumValueSites; + uint64_t TotalNumValueSitesWithValueProfile; + uint64_t TotalNumValues; + std::vector ValueSitesHistogram; +}; +} // namespace + +static void traverseAllValueSites(const InstrProfRecord &Func, uint32_t VK, + ValueSitesStats &Stats, raw_fd_ostream &OS, + InstrProfSymtab *Symtab) { + uint32_t NS = Func.getNumValueSites(VK); + Stats.TotalNumValueSites += NS; + for (size_t I = 0; I < NS; ++I) { + uint32_t NV = Func.getNumValueDataForSite(VK, I); + std::unique_ptr VD = Func.getValueForSite(VK, I); + Stats.TotalNumValues += NV; + if (NV) { + Stats.TotalNumValueSitesWithValueProfile++; + if (NV > Stats.ValueSitesHistogram.size()) + Stats.ValueSitesHistogram.resize(NV, 0); + Stats.ValueSitesHistogram[NV - 1]++; + } + + uint64_t SiteSum = 0; + for (uint32_t V = 0; V < NV; V++) + SiteSum += VD[V].Count; + if (SiteSum == 0) + SiteSum = 1; + + for (uint32_t V = 0; V < NV; V++) { + OS << "\t[ " << format("%2u", I) << ", "; + if (Symtab == nullptr) + OS << format("%4" PRIu64, VD[V].Value); + else + OS << Symtab->getFuncName(VD[V].Value); + OS << ", " << format("%10" PRId64, VD[V].Count) << " ] (" + << format("%.2f%%", (VD[V].Count * 100.0 / SiteSum)) << ")\n"; + } + } +} + +static void showValueSitesStats(raw_fd_ostream &OS, uint32_t VK, + ValueSitesStats &Stats) { + OS << " Total number of sites: " << Stats.TotalNumValueSites << "\n"; + OS << " Total number of sites with values: " + << Stats.TotalNumValueSitesWithValueProfile << "\n"; + OS << " Total number of profiled values: " << Stats.TotalNumValues << "\n"; + + OS << " Value sites histogram:\n\tNumTargets, SiteCount\n"; + for (unsigned I = 0; I < Stats.ValueSitesHistogram.size(); I++) { + if (Stats.ValueSitesHistogram[I] > 0) + OS << "\t" << I + 1 << ", " << Stats.ValueSitesHistogram[I] << "\n"; + } +} + +static int showInstrProfile(const std::string &Filename, bool ShowCounts, + uint32_t TopN, bool ShowIndirectCallTargets, + bool ShowMemOPSizes, bool ShowDetailedSummary, + std::vector DetailedSummaryCutoffs, + bool ShowAllFunctions, bool ShowCS, + uint64_t ValueCutoff, bool OnlyListBelow, + const std::string &ShowFunction, bool TextFormat, + bool ShowBinaryIds, bool ShowCovered, + bool ShowProfileVersion, ShowFormat SFormat, + raw_fd_ostream &OS) { + if (SFormat == ShowFormat::Json) + exitWithError("JSON output is not supported for instr profiles"); + if (SFormat == ShowFormat::Yaml) + exitWithError("YAML output is not supported for instr profiles"); + auto ReaderOrErr = InstrProfReader::create(Filename); + std::vector Cutoffs = std::move(DetailedSummaryCutoffs); + if (ShowDetailedSummary && Cutoffs.empty()) { + Cutoffs = ProfileSummaryBuilder::DefaultCutoffs; + } + InstrProfSummaryBuilder Builder(std::move(Cutoffs)); + if (Error E = ReaderOrErr.takeError()) + exitWithError(std::move(E), Filename); + + auto Reader = std::move(ReaderOrErr.get()); + bool IsIRInstr = Reader->isIRLevelProfile(); + size_t ShownFunctions = 0; + size_t BelowCutoffFunctions = 0; + int NumVPKind = IPVK_Last - IPVK_First + 1; + std::vector VPStats(NumVPKind); + + auto MinCmp = [](const std::pair &v1, + const std::pair &v2) { + return v1.second > v2.second; + }; + + std::priority_queue, + std::vector>, + decltype(MinCmp)> + HottestFuncs(MinCmp); + + if (!TextFormat && OnlyListBelow) { + OS << "The list of functions with the maximum counter less than " + << ValueCutoff << ":\n"; + } + + // Add marker so that IR-level instrumentation round-trips properly. + if (TextFormat && IsIRInstr) + OS << ":ir\n"; + + for (const auto &Func : *Reader) { + if (Reader->isIRLevelProfile()) { + bool FuncIsCS = NamedInstrProfRecord::hasCSFlagInHash(Func.Hash); + if (FuncIsCS != ShowCS) + continue; + } + bool Show = ShowAllFunctions || + (!ShowFunction.empty() && Func.Name.contains(ShowFunction)); + + bool doTextFormatDump = (Show && TextFormat); + + if (doTextFormatDump) { + InstrProfSymtab &Symtab = Reader->getSymtab(); + InstrProfWriter::writeRecordInText(Func.Name, Func.Hash, Func, Symtab, + OS); + continue; + } + + assert(Func.Counts.size() > 0 && "function missing entry counter"); + Builder.addRecord(Func); + + if (ShowCovered) { + if (llvm::any_of(Func.Counts, [](uint64_t C) { return C; })) + OS << Func.Name << "\n"; + continue; + } + + uint64_t FuncMax = 0; + uint64_t FuncSum = 0; + + auto PseudoKind = Func.getCountPseudoKind(); + if (PseudoKind != InstrProfRecord::NotPseudo) { + if (Show) { + if (!ShownFunctions) + OS << "Counters:\n"; + ++ShownFunctions; + OS << " " << Func.Name << ":\n" + << " Hash: " << format("0x%016" PRIx64, Func.Hash) << "\n" + << " Counters: " << Func.Counts.size(); + if (PseudoKind == InstrProfRecord::PseudoHot) + OS << " \n"; + else if (PseudoKind == InstrProfRecord::PseudoWarm) + OS << " \n"; + else + llvm_unreachable("Unknown PseudoKind"); + } + continue; + } + + for (size_t I = 0, E = Func.Counts.size(); I < E; ++I) { + FuncMax = std::max(FuncMax, Func.Counts[I]); + FuncSum += Func.Counts[I]; + } + + if (FuncMax < ValueCutoff) { + ++BelowCutoffFunctions; + if (OnlyListBelow) { + OS << " " << Func.Name << ": (Max = " << FuncMax + << " Sum = " << FuncSum << ")\n"; + } + continue; + } else if (OnlyListBelow) + continue; + + if (TopN) { + if (HottestFuncs.size() == TopN) { + if (HottestFuncs.top().second < FuncMax) { + HottestFuncs.pop(); + HottestFuncs.emplace(std::make_pair(std::string(Func.Name), FuncMax)); + } + } else + HottestFuncs.emplace(std::make_pair(std::string(Func.Name), FuncMax)); + } + + if (Show) { + if (!ShownFunctions) + OS << "Counters:\n"; + + ++ShownFunctions; + + OS << " " << Func.Name << ":\n" + << " Hash: " << format("0x%016" PRIx64, Func.Hash) << "\n" + << " Counters: " << Func.Counts.size() << "\n"; + if (!IsIRInstr) + OS << " Function count: " << Func.Counts[0] << "\n"; + + if (ShowIndirectCallTargets) + OS << " Indirect Call Site Count: " + << Func.getNumValueSites(IPVK_IndirectCallTarget) << "\n"; + + uint32_t NumMemOPCalls = Func.getNumValueSites(IPVK_MemOPSize); + if (ShowMemOPSizes && NumMemOPCalls > 0) + OS << " Number of Memory Intrinsics Calls: " << NumMemOPCalls + << "\n"; + + if (ShowCounts) { + OS << " Block counts: ["; + size_t Start = (IsIRInstr ? 0 : 1); + for (size_t I = Start, E = Func.Counts.size(); I < E; ++I) { + OS << (I == Start ? "" : ", ") << Func.Counts[I]; + } + OS << "]\n"; + } + + if (ShowIndirectCallTargets) { + OS << " Indirect Target Results:\n"; + traverseAllValueSites(Func, IPVK_IndirectCallTarget, + VPStats[IPVK_IndirectCallTarget], OS, + &(Reader->getSymtab())); + } + + if (ShowMemOPSizes && NumMemOPCalls > 0) { + OS << " Memory Intrinsic Size Results:\n"; + traverseAllValueSites(Func, IPVK_MemOPSize, VPStats[IPVK_MemOPSize], OS, + nullptr); + } + } + } + if (Reader->hasError()) + exitWithError(Reader->getError(), Filename); + + if (TextFormat || ShowCovered) + return 0; + std::unique_ptr PS(Builder.getSummary()); + bool IsIR = Reader->isIRLevelProfile(); + OS << "Instrumentation level: " << (IsIR ? "IR" : "Front-end"); + if (IsIR) + OS << " entry_first = " << Reader->instrEntryBBEnabled(); + OS << "\n"; + if (ShowAllFunctions || !ShowFunction.empty()) + OS << "Functions shown: " << ShownFunctions << "\n"; + OS << "Total functions: " << PS->getNumFunctions() << "\n"; + if (ValueCutoff > 0) { + OS << "Number of functions with maximum count (< " << ValueCutoff + << "): " << BelowCutoffFunctions << "\n"; + OS << "Number of functions with maximum count (>= " << ValueCutoff + << "): " << PS->getNumFunctions() - BelowCutoffFunctions << "\n"; + } + OS << "Maximum function count: " << PS->getMaxFunctionCount() << "\n"; + OS << "Maximum internal block count: " << PS->getMaxInternalCount() << "\n"; + + if (TopN) { + std::vector> SortedHottestFuncs; + while (!HottestFuncs.empty()) { + SortedHottestFuncs.emplace_back(HottestFuncs.top()); + HottestFuncs.pop(); + } + OS << "Top " << TopN + << " functions with the largest internal block counts: \n"; + for (auto &hotfunc : llvm::reverse(SortedHottestFuncs)) + OS << " " << hotfunc.first << ", max count = " << hotfunc.second << "\n"; + } + + if (ShownFunctions && ShowIndirectCallTargets) { + OS << "Statistics for indirect call sites profile:\n"; + showValueSitesStats(OS, IPVK_IndirectCallTarget, + VPStats[IPVK_IndirectCallTarget]); + } + + if (ShownFunctions && ShowMemOPSizes) { + OS << "Statistics for memory intrinsic calls sizes profile:\n"; + showValueSitesStats(OS, IPVK_MemOPSize, VPStats[IPVK_MemOPSize]); + } + + if (ShowDetailedSummary) { + OS << "Total number of blocks: " << PS->getNumCounts() << "\n"; + OS << "Total count: " << PS->getTotalCount() << "\n"; + PS->printDetailedSummary(OS); + } + + if (ShowBinaryIds) + if (Error E = Reader->printBinaryIds(OS)) + exitWithError(std::move(E), Filename); + + if (ShowProfileVersion) + OS << "Profile version: " << Reader->getVersion() << "\n"; + return 0; +} + +static void showSectionInfo(sampleprof::SampleProfileReader *Reader, + raw_fd_ostream &OS) { + if (!Reader->dumpSectionInfo(OS)) { + WithColor::warning() << "-show-sec-info-only is only supported for " + << "sample profile in extbinary format and is " + << "ignored for other formats.\n"; + return; + } +} + +namespace { +struct HotFuncInfo { + std::string FuncName; + uint64_t TotalCount; + double TotalCountPercent; + uint64_t MaxCount; + uint64_t EntryCount; + + HotFuncInfo() + : TotalCount(0), TotalCountPercent(0.0f), MaxCount(0), EntryCount(0) {} + + HotFuncInfo(StringRef FN, uint64_t TS, double TSP, uint64_t MS, uint64_t ES) + : FuncName(FN.begin(), FN.end()), TotalCount(TS), TotalCountPercent(TSP), + MaxCount(MS), EntryCount(ES) {} +}; +} // namespace + +// Print out detailed information about hot functions in PrintValues vector. +// Users specify titles and offset of every columns through ColumnTitle and +// ColumnOffset. The size of ColumnTitle and ColumnOffset need to be the same +// and at least 4. Besides, users can optionally give a HotFuncMetric string to +// print out or let it be an empty string. +static void dumpHotFunctionList(const std::vector &ColumnTitle, + const std::vector &ColumnOffset, + const std::vector &PrintValues, + uint64_t HotFuncCount, uint64_t TotalFuncCount, + uint64_t HotProfCount, uint64_t TotalProfCount, + const std::string &HotFuncMetric, + uint32_t TopNFunctions, raw_fd_ostream &OS) { + assert(ColumnOffset.size() == ColumnTitle.size() && + "ColumnOffset and ColumnTitle should have the same size"); + assert(ColumnTitle.size() >= 4 && + "ColumnTitle should have at least 4 elements"); + assert(TotalFuncCount > 0 && + "There should be at least one function in the profile"); + double TotalProfPercent = 0; + if (TotalProfCount > 0) + TotalProfPercent = static_cast(HotProfCount) / TotalProfCount * 100; + + formatted_raw_ostream FOS(OS); + FOS << HotFuncCount << " out of " << TotalFuncCount + << " functions with profile (" + << format("%.2f%%", + (static_cast(HotFuncCount) / TotalFuncCount * 100)) + << ") are considered hot functions"; + if (!HotFuncMetric.empty()) + FOS << " (" << HotFuncMetric << ")"; + FOS << ".\n"; + FOS << HotProfCount << " out of " << TotalProfCount << " profile counts (" + << format("%.2f%%", TotalProfPercent) << ") are from hot functions.\n"; + + for (size_t I = 0; I < ColumnTitle.size(); ++I) { + FOS.PadToColumn(ColumnOffset[I]); + FOS << ColumnTitle[I]; + } + FOS << "\n"; + + uint32_t Count = 0; + for (const auto &R : PrintValues) { + if (TopNFunctions && (Count++ == TopNFunctions)) + break; + FOS.PadToColumn(ColumnOffset[0]); + FOS << R.TotalCount << " (" << format("%.2f%%", R.TotalCountPercent) << ")"; + FOS.PadToColumn(ColumnOffset[1]); + FOS << R.MaxCount; + FOS.PadToColumn(ColumnOffset[2]); + FOS << R.EntryCount; + FOS.PadToColumn(ColumnOffset[3]); + FOS << R.FuncName << "\n"; + } +} + +static int showHotFunctionList(const sampleprof::SampleProfileMap &Profiles, + ProfileSummary &PS, uint32_t TopN, + raw_fd_ostream &OS) { + using namespace sampleprof; + + const uint32_t HotFuncCutoff = 990000; + auto &SummaryVector = PS.getDetailedSummary(); + uint64_t MinCountThreshold = 0; + for (const ProfileSummaryEntry &SummaryEntry : SummaryVector) { + if (SummaryEntry.Cutoff == HotFuncCutoff) { + MinCountThreshold = SummaryEntry.MinCount; + break; + } + } + + // Traverse all functions in the profile and keep only hot functions. + // The following loop also calculates the sum of total samples of all + // functions. + std::multimap, + std::greater> + HotFunc; + uint64_t ProfileTotalSample = 0; + uint64_t HotFuncSample = 0; + uint64_t HotFuncCount = 0; + + for (const auto &I : Profiles) { + FuncSampleStats FuncStats; + const FunctionSamples &FuncProf = I.second; + ProfileTotalSample += FuncProf.getTotalSamples(); + getFuncSampleStats(FuncProf, FuncStats, MinCountThreshold); + + if (isFunctionHot(FuncStats, MinCountThreshold)) { + HotFunc.emplace(FuncProf.getTotalSamples(), + std::make_pair(&(I.second), FuncStats.MaxSample)); + HotFuncSample += FuncProf.getTotalSamples(); + ++HotFuncCount; + } + } + + std::vector ColumnTitle{"Total sample (%)", "Max sample", + "Entry sample", "Function name"}; + std::vector ColumnOffset{0, 24, 42, 58}; + std::string Metric = + std::string("max sample >= ") + std::to_string(MinCountThreshold); + std::vector PrintValues; + for (const auto &FuncPair : HotFunc) { + const FunctionSamples &Func = *FuncPair.second.first; + double TotalSamplePercent = + (ProfileTotalSample > 0) + ? (Func.getTotalSamples() * 100.0) / ProfileTotalSample + : 0; + PrintValues.emplace_back( + HotFuncInfo(Func.getContext().toString(), Func.getTotalSamples(), + TotalSamplePercent, FuncPair.second.second, + Func.getHeadSamplesEstimate())); + } + dumpHotFunctionList(ColumnTitle, ColumnOffset, PrintValues, HotFuncCount, + Profiles.size(), HotFuncSample, ProfileTotalSample, + Metric, TopN, OS); + + return 0; +} + +static int showSampleProfile(const std::string &Filename, bool ShowCounts, + uint32_t TopN, bool ShowAllFunctions, + bool ShowDetailedSummary, + const std::string &ShowFunction, + bool ShowProfileSymbolList, + bool ShowSectionInfoOnly, bool ShowHotFuncList, + ShowFormat SFormat, raw_fd_ostream &OS) { + if (SFormat == ShowFormat::Yaml) + exitWithError("YAML output is not supported for sample profiles"); + using namespace sampleprof; + LLVMContext Context; + auto ReaderOrErr = + SampleProfileReader::create(Filename, Context, FSDiscriminatorPassOption); + if (std::error_code EC = ReaderOrErr.getError()) + exitWithErrorCode(EC, Filename); + + auto Reader = std::move(ReaderOrErr.get()); + if (ShowSectionInfoOnly) { + showSectionInfo(Reader.get(), OS); + return 0; + } + + if (std::error_code EC = Reader->read()) + exitWithErrorCode(EC, Filename); + + if (ShowAllFunctions || ShowFunction.empty()) { + if (SFormat == ShowFormat::Json) + Reader->dumpJson(OS); + else + Reader->dump(OS); + } else { + if (SFormat == ShowFormat::Json) + exitWithError( + "the JSON format is supported only when all functions are to " + "be printed"); + + // TODO: parse context string to support filtering by contexts. + Reader->dumpFunctionProfile(StringRef(ShowFunction), OS); + } + + if (ShowProfileSymbolList) { + std::unique_ptr ReaderList = + Reader->getProfileSymbolList(); + ReaderList->dump(OS); + } + + if (ShowDetailedSummary) { + auto &PS = Reader->getSummary(); + PS.printSummary(OS); + PS.printDetailedSummary(OS); + } + + if (ShowHotFuncList || TopN) + showHotFunctionList(Reader->getProfiles(), Reader->getSummary(), TopN, OS); + + return 0; +} + +static int showMemProfProfile(const std::string &Filename, + const std::string &ProfiledBinary, + ShowFormat SFormat, raw_fd_ostream &OS) { + if (SFormat == ShowFormat::Json) + exitWithError("JSON output is not supported for MemProf"); + auto ReaderOr = llvm::memprof::RawMemProfReader::create( + Filename, ProfiledBinary, /*KeepNames=*/true); + if (Error E = ReaderOr.takeError()) + // Since the error can be related to the profile or the binary we do not + // pass whence. Instead additional context is provided where necessary in + // the error message. + exitWithError(std::move(E), /*Whence*/ ""); + + std::unique_ptr Reader( + ReaderOr.get().release()); + + Reader->printYAML(OS); + return 0; +} + +static int showDebugInfoCorrelation(const std::string &Filename, + bool ShowDetailedSummary, + bool ShowProfileSymbolList, + ShowFormat SFormat, raw_fd_ostream &OS) { + if (SFormat == ShowFormat::Json) + exitWithError("JSON output is not supported for debug info correlation"); + std::unique_ptr Correlator; + if (auto Err = InstrProfCorrelator::get(Filename).moveInto(Correlator)) + exitWithError(std::move(Err), Filename); + if (SFormat == ShowFormat::Yaml) { + if (auto Err = Correlator->dumpYaml(OS)) + exitWithError(std::move(Err), Filename); + return 0; + } + + if (auto Err = Correlator->correlateProfileData()) + exitWithError(std::move(Err), Filename); + + InstrProfSymtab Symtab; + if (auto Err = Symtab.create( + StringRef(Correlator->getNamesPointer(), Correlator->getNamesSize()))) + exitWithError(std::move(Err), Filename); + + if (ShowProfileSymbolList) + Symtab.dumpNames(OS); + // TODO: Read "Profile Data Type" from debug info to compute and show how many + // counters the section holds. + if (ShowDetailedSummary) + OS << "Counters section size: 0x" + << Twine::utohexstr(Correlator->getCountersSectionSize()) << " bytes\n"; + OS << "Found " << Correlator->getDataSize() << " functions\n"; + + return 0; +} + +static int show_main(int argc, const char *argv[]) { + cl::opt Filename(cl::Positional, cl::desc("")); + + cl::opt ShowCounts("counts", cl::init(false), + cl::desc("Show counter values for shown functions")); + cl::opt SFormat( + "show-format", cl::init(ShowFormat::Text), + cl::desc("Emit output in the selected format if supported"), + cl::values(clEnumValN(ShowFormat::Text, "text", + "emit normal text output (default)"), + clEnumValN(ShowFormat::Json, "json", "emit JSON"), + clEnumValN(ShowFormat::Yaml, "yaml", "emit YAML"))); + // TODO: Consider replacing this with `--show-format=text-encoding`. + cl::opt TextFormat( + "text", cl::init(false), + cl::desc("Show instr profile data in text dump format")); + cl::opt JsonFormat( + "json", cl::desc("Show sample profile data in the JSON format " + "(deprecated, please use --show-format=json)")); + cl::opt ShowIndirectCallTargets( + "ic-targets", cl::init(false), + cl::desc("Show indirect call site target values for shown functions")); + cl::opt ShowMemOPSizes( + "memop-sizes", cl::init(false), + cl::desc("Show the profiled sizes of the memory intrinsic calls " + "for shown functions")); + cl::opt ShowDetailedSummary("detailed-summary", cl::init(false), + cl::desc("Show detailed profile summary")); + cl::list DetailedSummaryCutoffs( + cl::CommaSeparated, "detailed-summary-cutoffs", + cl::desc( + "Cutoff percentages (times 10000) for generating detailed summary"), + cl::value_desc("800000,901000,999999")); + cl::opt ShowHotFuncList( + "hot-func-list", cl::init(false), + cl::desc("Show profile summary of a list of hot functions")); + cl::opt ShowAllFunctions("all-functions", cl::init(false), + cl::desc("Details for every function")); + cl::opt ShowCS("showcs", cl::init(false), + cl::desc("Show context sensitive counts")); + cl::opt ShowFunction("function", + cl::desc("Details for matching functions")); + + cl::opt OutputFilename("output", cl::value_desc("output"), + cl::init("-"), cl::desc("Output file")); + cl::alias OutputFilenameA("o", cl::desc("Alias for --output"), + cl::aliasopt(OutputFilename)); + cl::opt ProfileKind( + cl::desc("Profile kind:"), cl::init(instr), + cl::values(clEnumVal(instr, "Instrumentation profile (default)"), + clEnumVal(sample, "Sample profile"), + clEnumVal(memory, "MemProf memory access profile"))); + cl::opt TopNFunctions( + "topn", cl::init(0), + cl::desc("Show the list of functions with the largest internal counts")); + cl::opt ValueCutoff( + "value-cutoff", cl::init(0), + cl::desc("Set the count value cutoff. Functions with the maximum count " + "less than this value will not be printed out. (Default is 0)")); + cl::opt OnlyListBelow( + "list-below-cutoff", cl::init(false), + cl::desc("Only output names of functions whose max count values are " + "below the cutoff value")); + cl::opt ShowProfileSymbolList( + "show-prof-sym-list", cl::init(false), + cl::desc("Show profile symbol list if it exists in the profile. ")); + cl::opt ShowSectionInfoOnly( + "show-sec-info-only", cl::init(false), + cl::desc("Show the information of each section in the sample profile. " + "The flag is only usable when the sample profile is in " + "extbinary format")); + cl::opt ShowBinaryIds("binary-ids", cl::init(false), + cl::desc("Show binary ids in the profile. ")); + cl::opt DebugInfoFilename( + "debug-info", cl::init(""), + cl::desc("Read and extract profile metadata from debug info and show " + "the functions it found.")); + cl::opt ShowCovered( + "covered", cl::init(false), + cl::desc("Show only the functions that have been executed.")); + cl::opt ProfiledBinary( + "profiled-binary", cl::init(""), + cl::desc("Path to binary from which the profile was collected.")); + cl::opt ShowProfileVersion("profile-version", cl::init(false), + cl::desc("Show profile version. ")); + cl::ParseCommandLineOptions(argc, argv, "LLVM profile data summary\n"); + + if (Filename.empty() && DebugInfoFilename.empty()) + exitWithError( + "the positional argument '' is required unless '--" + + DebugInfoFilename.ArgStr + "' is provided"); + + if (Filename == OutputFilename) { + errs() << sys::path::filename(argv[0]) + << ": Input file name cannot be the same as the output file name!\n"; + return 1; + } + if (JsonFormat) + SFormat = ShowFormat::Json; + + std::error_code EC; + raw_fd_ostream OS(OutputFilename.data(), EC, sys::fs::OF_TextWithCRLF); + if (EC) + exitWithErrorCode(EC, OutputFilename); + + if (ShowAllFunctions && !ShowFunction.empty()) + WithColor::warning() << "-function argument ignored: showing all functions\n"; + + if (!DebugInfoFilename.empty()) + return showDebugInfoCorrelation(DebugInfoFilename, ShowDetailedSummary, + ShowProfileSymbolList, SFormat, OS); + + if (ProfileKind == instr) + return showInstrProfile( + Filename, ShowCounts, TopNFunctions, ShowIndirectCallTargets, + ShowMemOPSizes, ShowDetailedSummary, DetailedSummaryCutoffs, + ShowAllFunctions, ShowCS, ValueCutoff, OnlyListBelow, ShowFunction, + TextFormat, ShowBinaryIds, ShowCovered, ShowProfileVersion, SFormat, + OS); + if (ProfileKind == sample) + return showSampleProfile(Filename, ShowCounts, TopNFunctions, + ShowAllFunctions, ShowDetailedSummary, + ShowFunction, ShowProfileSymbolList, + ShowSectionInfoOnly, ShowHotFuncList, SFormat, OS); + return showMemProfProfile(Filename, ProfiledBinary, SFormat, OS); +} + +int main(int argc, const char *argv[]) { + InitLLVM X(argc, argv); + + StringRef ProgName(sys::path::filename(argv[0])); + if (argc > 1) { + int (*func)(int, const char *[]) = nullptr; + + if (strcmp(argv[1], "merge") == 0) + func = merge_main; + else if (strcmp(argv[1], "show") == 0) + func = show_main; + else if (strcmp(argv[1], "overlap") == 0) + func = overlap_main; + + if (func) { + std::string Invocation(ProgName.str() + " " + argv[1]); + argv[1] = Invocation.c_str(); + return func(argc - 1, argv + 1); + } + + if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "-help") == 0 || + strcmp(argv[1], "--help") == 0) { + + errs() << "OVERVIEW: LLVM profile data tools\n\n" + << "USAGE: " << ProgName << " [args...]\n" + << "USAGE: " << ProgName << " -help\n\n" + << "See each individual command --help for more details.\n" + << "Available commands: merge, show, overlap\n"; + return 0; + } + } + + if (argc < 2) + errs() << ProgName << ": No command specified!\n"; + else + errs() << ProgName << ": Unknown command!\n"; + + errs() << "USAGE: " << ProgName << " [args...]\n"; + return 1; +} diff --git a/tools/ldc-profdata/llvm-profdata-9.0.cpp b/tools/ldc-profdata/llvm-profdata-9.0.cpp deleted file mode 100644 index 16d3ebe3fcb..00000000000 --- a/tools/ldc-profdata/llvm-profdata-9.0.cpp +++ /dev/null @@ -1,1087 +0,0 @@ -//===- llvm-profdata.cpp - LLVM profile data tool -------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// llvm-profdata merges .profdata files. -// -//===----------------------------------------------------------------------===// - -#include "llvm/ADT/SmallSet.h" -#include "llvm/ADT/SmallVector.h" -#include "llvm/ADT/StringRef.h" -#include "llvm/IR/LLVMContext.h" -#include "llvm/ProfileData/InstrProfReader.h" -#include "llvm/ProfileData/InstrProfWriter.h" -#include "llvm/ProfileData/ProfileCommon.h" -#include "llvm/ProfileData/SampleProfReader.h" -#include "llvm/ProfileData/SampleProfWriter.h" -#include "llvm/Support/CommandLine.h" -#include "llvm/Support/Errc.h" -#include "llvm/Support/FileSystem.h" -#include "llvm/Support/Format.h" -#include "llvm/Support/InitLLVM.h" -#include "llvm/Support/MemoryBuffer.h" -#include "llvm/Support/Path.h" -#include "llvm/Support/ThreadPool.h" -#include "llvm/Support/WithColor.h" -#include "llvm/Support/raw_ostream.h" -#include - -using namespace llvm; - -enum ProfileFormat { - PF_None = 0, - PF_Text, - PF_Compact_Binary, - PF_GCC, - PF_Binary -}; - -static void warn(Twine Message, std::string Whence = "", - std::string Hint = "") { - WithColor::warning(); - if (!Whence.empty()) - errs() << Whence << ": "; - errs() << Message << "\n"; - if (!Hint.empty()) - WithColor::note() << Hint << "\n"; -} - -static void exitWithError(Twine Message, std::string Whence = "", - std::string Hint = "") { - WithColor::error(); - if (!Whence.empty()) - errs() << Whence << ": "; - errs() << Message << "\n"; - if (!Hint.empty()) - WithColor::note() << Hint << "\n"; - ::exit(1); -} - -static void exitWithError(Error E, StringRef Whence = "") { - if (E.isA()) { - handleAllErrors(std::move(E), [&](const InstrProfError &IPE) { - instrprof_error instrError = IPE.get(); - StringRef Hint = ""; - if (instrError == instrprof_error::unrecognized_format) { - // Hint for common error of forgetting -sample for sample profiles. - Hint = "Perhaps you forgot to use the -sample option?"; - } - exitWithError(IPE.message(), Whence, Hint); - }); - } - - exitWithError(toString(std::move(E)), Whence); -} - -static void exitWithErrorCode(std::error_code EC, StringRef Whence = "") { - exitWithError(EC.message(), Whence); -} - -namespace { -enum ProfileKinds { instr, sample }; -} - -static void handleMergeWriterError(Error E, StringRef WhenceFile = "", - StringRef WhenceFunction = "", - bool ShowHint = true) { - if (!WhenceFile.empty()) - errs() << WhenceFile << ": "; - if (!WhenceFunction.empty()) - errs() << WhenceFunction << ": "; - - auto IPE = instrprof_error::success; - E = handleErrors(std::move(E), - [&IPE](std::unique_ptr E) -> Error { - IPE = E->get(); - return Error(std::move(E)); - }); - errs() << toString(std::move(E)) << "\n"; - - if (ShowHint) { - StringRef Hint = ""; - if (IPE != instrprof_error::success) { - switch (IPE) { - case instrprof_error::hash_mismatch: - case instrprof_error::count_mismatch: - case instrprof_error::value_site_count_mismatch: - Hint = "Make sure that all profile data to be merged is generated " - "from the same binary."; - break; - default: - break; - } - } - - if (!Hint.empty()) - errs() << Hint << "\n"; - } -} - -namespace { -/// A remapper from original symbol names to new symbol names based on a file -/// containing a list of mappings from old name to new name. -class SymbolRemapper { - std::unique_ptr File; - DenseMap RemappingTable; - -public: - /// Build a SymbolRemapper from a file containing a list of old/new symbols. - static std::unique_ptr create(StringRef InputFile) { - auto BufOrError = MemoryBuffer::getFileOrSTDIN(InputFile); - if (!BufOrError) - exitWithErrorCode(BufOrError.getError(), InputFile); - - auto Remapper = llvm::make_unique(); - Remapper->File = std::move(BufOrError.get()); - - for (line_iterator LineIt(*Remapper->File, /*SkipBlanks=*/true, '#'); - !LineIt.is_at_eof(); ++LineIt) { - std::pair Parts = LineIt->split(' '); - if (Parts.first.empty() || Parts.second.empty() || - Parts.second.count(' ')) { - exitWithError("unexpected line in remapping file", - (InputFile + ":" + Twine(LineIt.line_number())).str(), - "expected 'old_symbol new_symbol'"); - } - Remapper->RemappingTable.insert(Parts); - } - return Remapper; - } - - /// Attempt to map the given old symbol into a new symbol. - /// - /// \return The new symbol, or \p Name if no such symbol was found. - StringRef operator()(StringRef Name) { - StringRef New = RemappingTable.lookup(Name); - return New.empty() ? Name : New; - } -}; -} - -struct WeightedFile { - std::string Filename; - uint64_t Weight; -}; -typedef SmallVector WeightedFileVector; - -/// Keep track of merged data and reported errors. -struct WriterContext { - std::mutex Lock; - InstrProfWriter Writer; - Error Err; - std::string ErrWhence; - std::mutex &ErrLock; - SmallSet &WriterErrorCodes; - - WriterContext(bool IsSparse, std::mutex &ErrLock, - SmallSet &WriterErrorCodes) - : Lock(), Writer(IsSparse), Err(Error::success()), ErrWhence(""), - ErrLock(ErrLock), WriterErrorCodes(WriterErrorCodes) {} -}; - -/// Determine whether an error is fatal for profile merging. -static bool isFatalError(instrprof_error IPE) { - switch (IPE) { - default: - return true; - case instrprof_error::success: - case instrprof_error::eof: - case instrprof_error::unknown_function: - case instrprof_error::hash_mismatch: - case instrprof_error::count_mismatch: - case instrprof_error::counter_overflow: - case instrprof_error::value_site_count_mismatch: - return false; - } -} - -/// Computer the overlap b/w profile BaseFilename and TestFileName, -/// and store the program level result to Overlap. -static void overlapInput(const std::string &BaseFilename, - const std::string &TestFilename, WriterContext *WC, - OverlapStats &Overlap, - const OverlapFuncFilters &FuncFilter, - raw_fd_ostream &OS, bool IsCS) { - auto ReaderOrErr = InstrProfReader::create(TestFilename); - if (Error E = ReaderOrErr.takeError()) { - // Skip the empty profiles by returning sliently. - instrprof_error IPE = InstrProfError::take(std::move(E)); - if (IPE != instrprof_error::empty_raw_profile) - WC->Err = make_error(IPE); - return; - } - - auto Reader = std::move(ReaderOrErr.get()); - for (auto &I : *Reader) { - OverlapStats FuncOverlap(OverlapStats::FunctionLevel); - FuncOverlap.setFuncInfo(I.Name, I.Hash); - - WC->Writer.overlapRecord(std::move(I), Overlap, FuncOverlap, FuncFilter); - FuncOverlap.dump(OS); - } -} - -/// Load an input into a writer context. -static void loadInput(const WeightedFile &Input, SymbolRemapper *Remapper, - WriterContext *WC) { - std::unique_lock CtxGuard{WC->Lock}; - - // If there's a pending hard error, don't do more work. - if (WC->Err) - return; - - // Copy the filename, because llvm::ThreadPool copied the input "const - // WeightedFile &" by value, making a reference to the filename within it - // invalid outside of this packaged task. - WC->ErrWhence = Input.Filename; - - auto ReaderOrErr = InstrProfReader::create(Input.Filename); - if (Error E = ReaderOrErr.takeError()) { - // Skip the empty profiles by returning sliently. - instrprof_error IPE = InstrProfError::take(std::move(E)); - if (IPE != instrprof_error::empty_raw_profile) - WC->Err = make_error(IPE); - return; - } - - auto Reader = std::move(ReaderOrErr.get()); - bool IsIRProfile = Reader->isIRLevelProfile(); - bool HasCSIRProfile = Reader->hasCSIRLevelProfile(); - if (WC->Writer.setIsIRLevelProfile(IsIRProfile, HasCSIRProfile)) { - WC->Err = make_error( - "Merge IR generated profile with Clang generated profile.", - std::error_code()); - return; - } - - for (auto &I : *Reader) { - if (Remapper) - I.Name = (*Remapper)(I.Name); - const StringRef FuncName = I.Name; - bool Reported = false; - WC->Writer.addRecord(std::move(I), Input.Weight, [&](Error E) { - if (Reported) { - consumeError(std::move(E)); - return; - } - Reported = true; - // Only show hint the first time an error occurs. - instrprof_error IPE = InstrProfError::take(std::move(E)); - std::unique_lock ErrGuard{WC->ErrLock}; - bool firstTime = WC->WriterErrorCodes.insert(IPE).second; - handleMergeWriterError(make_error(IPE), Input.Filename, - FuncName, firstTime); - }); - } - if (Reader->hasError()) { - if (Error E = Reader->getError()) { - instrprof_error IPE = InstrProfError::take(std::move(E)); - if (isFatalError(IPE)) - WC->Err = make_error(IPE); - } - } -} - -/// Merge the \p Src writer context into \p Dst. -static void mergeWriterContexts(WriterContext *Dst, WriterContext *Src) { - // If we've already seen a hard error, continuing with the merge would - // clobber it. - if (Dst->Err || Src->Err) - return; - - bool Reported = false; - Dst->Writer.mergeRecordsFromWriter(std::move(Src->Writer), [&](Error E) { - if (Reported) { - consumeError(std::move(E)); - return; - } - Reported = true; - Dst->Err = std::move(E); - }); -} - -static void mergeInstrProfile(const WeightedFileVector &Inputs, - SymbolRemapper *Remapper, - StringRef OutputFilename, - ProfileFormat OutputFormat, bool OutputSparse, - unsigned NumThreads) { - if (OutputFilename.compare("-") == 0) - exitWithError("Cannot write indexed profdata format to stdout."); - - if (OutputFormat != PF_Binary && OutputFormat != PF_Compact_Binary && - OutputFormat != PF_Text) - exitWithError("Unknown format is specified."); - - std::mutex ErrorLock; - SmallSet WriterErrorCodes; - - // If NumThreads is not specified, auto-detect a good default. - if (NumThreads == 0) - NumThreads = - std::min(hardware_concurrency(), unsigned((Inputs.size() + 1) / 2)); - - // Initialize the writer contexts. - SmallVector, 4> Contexts; - for (unsigned I = 0; I < NumThreads; ++I) - Contexts.emplace_back(llvm::make_unique( - OutputSparse, ErrorLock, WriterErrorCodes)); - - if (NumThreads == 1) { - for (const auto &Input : Inputs) - loadInput(Input, Remapper, Contexts[0].get()); - } else { - ThreadPool Pool(NumThreads); - - // Load the inputs in parallel (N/NumThreads serial steps). - unsigned Ctx = 0; - for (const auto &Input : Inputs) { - Pool.async(loadInput, Input, Remapper, Contexts[Ctx].get()); - Ctx = (Ctx + 1) % NumThreads; - } - Pool.wait(); - - // Merge the writer contexts together (~ lg(NumThreads) serial steps). - unsigned Mid = Contexts.size() / 2; - unsigned End = Contexts.size(); - assert(Mid > 0 && "Expected more than one context"); - do { - for (unsigned I = 0; I < Mid; ++I) - Pool.async(mergeWriterContexts, Contexts[I].get(), - Contexts[I + Mid].get()); - Pool.wait(); - if (End & 1) { - Pool.async(mergeWriterContexts, Contexts[0].get(), - Contexts[End - 1].get()); - Pool.wait(); - } - End = Mid; - Mid /= 2; - } while (Mid > 0); - } - - // Handle deferred hard errors encountered during merging. - for (std::unique_ptr &WC : Contexts) { - if (!WC->Err) - continue; - if (!WC->Err.isA()) - exitWithError(std::move(WC->Err), WC->ErrWhence); - - instrprof_error IPE = InstrProfError::take(std::move(WC->Err)); - if (isFatalError(IPE)) - exitWithError(make_error(IPE), WC->ErrWhence); - else - warn(toString(make_error(IPE)), - WC->ErrWhence); - } - - std::error_code EC; - raw_fd_ostream Output(OutputFilename.data(), EC, sys::fs::F_None); - if (EC) - exitWithErrorCode(EC, OutputFilename); - - InstrProfWriter &Writer = Contexts[0]->Writer; - if (OutputFormat == PF_Text) { - if (Error E = Writer.writeText(Output)) - exitWithError(std::move(E)); - } else { - Writer.write(Output); - } -} - -/// Make a copy of the given function samples with all symbol names remapped -/// by the provided symbol remapper. -static sampleprof::FunctionSamples -remapSamples(const sampleprof::FunctionSamples &Samples, - SymbolRemapper &Remapper, sampleprof_error &Error) { - sampleprof::FunctionSamples Result; - Result.setName(Remapper(Samples.getName())); - Result.addTotalSamples(Samples.getTotalSamples()); - Result.addHeadSamples(Samples.getHeadSamples()); - for (const auto &BodySample : Samples.getBodySamples()) { - Result.addBodySamples(BodySample.first.LineOffset, - BodySample.first.Discriminator, - BodySample.second.getSamples()); - for (const auto &Target : BodySample.second.getCallTargets()) { - Result.addCalledTargetSamples(BodySample.first.LineOffset, - BodySample.first.Discriminator, - Remapper(Target.first()), Target.second); - } - } - for (const auto &CallsiteSamples : Samples.getCallsiteSamples()) { - sampleprof::FunctionSamplesMap &Target = - Result.functionSamplesAt(CallsiteSamples.first); - for (const auto &Callsite : CallsiteSamples.second) { - sampleprof::FunctionSamples Remapped = - remapSamples(Callsite.second, Remapper, Error); - MergeResult(Error, Target[Remapped.getName()].merge(Remapped)); - } - } - return Result; -} - -static sampleprof::SampleProfileFormat FormatMap[] = { - sampleprof::SPF_None, sampleprof::SPF_Text, sampleprof::SPF_Compact_Binary, - sampleprof::SPF_GCC, sampleprof::SPF_Binary}; - -static void mergeSampleProfile(const WeightedFileVector &Inputs, - SymbolRemapper *Remapper, - StringRef OutputFilename, - ProfileFormat OutputFormat) { - using namespace sampleprof; - StringMap ProfileMap; - SmallVector, 5> Readers; - LLVMContext Context; - for (const auto &Input : Inputs) { - auto ReaderOrErr = SampleProfileReader::create(Input.Filename, Context); - if (std::error_code EC = ReaderOrErr.getError()) - exitWithErrorCode(EC, Input.Filename); - - // We need to keep the readers around until after all the files are - // read so that we do not lose the function names stored in each - // reader's memory. The function names are needed to write out the - // merged profile map. - Readers.push_back(std::move(ReaderOrErr.get())); - const auto Reader = Readers.back().get(); - if (std::error_code EC = Reader->read()) - exitWithErrorCode(EC, Input.Filename); - - StringMap &Profiles = Reader->getProfiles(); - for (StringMap::iterator I = Profiles.begin(), - E = Profiles.end(); - I != E; ++I) { - sampleprof_error Result = sampleprof_error::success; - FunctionSamples Remapped = - Remapper ? remapSamples(I->second, *Remapper, Result) - : FunctionSamples(); - FunctionSamples &Samples = Remapper ? Remapped : I->second; - StringRef FName = Samples.getName(); - MergeResult(Result, ProfileMap[FName].merge(Samples, Input.Weight)); - if (Result != sampleprof_error::success) { - std::error_code EC = make_error_code(Result); - handleMergeWriterError(errorCodeToError(EC), Input.Filename, FName); - } - } - } - auto WriterOrErr = - SampleProfileWriter::create(OutputFilename, FormatMap[OutputFormat]); - if (std::error_code EC = WriterOrErr.getError()) - exitWithErrorCode(EC, OutputFilename); - - auto Writer = std::move(WriterOrErr.get()); - Writer->write(ProfileMap); -} - -static WeightedFile parseWeightedFile(const StringRef &WeightedFilename) { - StringRef WeightStr, FileName; - std::tie(WeightStr, FileName) = WeightedFilename.split(','); - - uint64_t Weight; - if (WeightStr.getAsInteger(10, Weight) || Weight < 1) - exitWithError("Input weight must be a positive integer."); - - return {FileName, Weight}; -} - -static std::unique_ptr -getInputFilenamesFileBuf(const StringRef &InputFilenamesFile) { - if (InputFilenamesFile == "") - return {}; - - auto BufOrError = MemoryBuffer::getFileOrSTDIN(InputFilenamesFile); - if (!BufOrError) - exitWithErrorCode(BufOrError.getError(), InputFilenamesFile); - - return std::move(*BufOrError); -} - -static void addWeightedInput(WeightedFileVector &WNI, const WeightedFile &WF) { - StringRef Filename = WF.Filename; - uint64_t Weight = WF.Weight; - - // If it's STDIN just pass it on. - if (Filename == "-") { - WNI.push_back({Filename, Weight}); - return; - } - - llvm::sys::fs::file_status Status; - llvm::sys::fs::status(Filename, Status); - if (!llvm::sys::fs::exists(Status)) - exitWithErrorCode(make_error_code(errc::no_such_file_or_directory), - Filename); - // If it's a source file, collect it. - if (llvm::sys::fs::is_regular_file(Status)) { - WNI.push_back({Filename, Weight}); - return; - } - - if (llvm::sys::fs::is_directory(Status)) { - std::error_code EC; - for (llvm::sys::fs::recursive_directory_iterator F(Filename, EC), E; - F != E && !EC; F.increment(EC)) { - if (llvm::sys::fs::is_regular_file(F->path())) { - addWeightedInput(WNI, {F->path(), Weight}); - } - } - if (EC) - exitWithErrorCode(EC, Filename); - } -} - -static void parseInputFilenamesFile(MemoryBuffer *Buffer, - WeightedFileVector &WFV) { - if (!Buffer) - return; - - SmallVector Entries; - StringRef Data = Buffer->getBuffer(); - Data.split(Entries, '\n', /*MaxSplit=*/-1, /*KeepEmpty=*/false); - for (const StringRef &FileWeightEntry : Entries) { - StringRef SanitizedEntry = FileWeightEntry.trim(" \t\v\f\r"); - // Skip comments. - if (SanitizedEntry.startswith("#")) - continue; - // If there's no comma, it's an unweighted profile. - else if (SanitizedEntry.find(',') == StringRef::npos) - addWeightedInput(WFV, {SanitizedEntry, 1}); - else - addWeightedInput(WFV, parseWeightedFile(SanitizedEntry)); - } -} - -static int merge_main(int argc, const char *argv[]) { - cl::list InputFilenames(cl::Positional, - cl::desc("")); - cl::list WeightedInputFilenames("weighted-input", - cl::desc(",")); - cl::opt InputFilenamesFile( - "input-files", cl::init(""), - cl::desc("Path to file containing newline-separated " - "[,] entries")); - cl::alias InputFilenamesFileA("f", cl::desc("Alias for --input-files"), - cl::aliasopt(InputFilenamesFile)); - cl::opt DumpInputFileList( - "dump-input-file-list", cl::init(false), cl::Hidden, - cl::desc("Dump the list of input files and their weights, then exit")); - cl::opt RemappingFile("remapping-file", cl::value_desc("file"), - cl::desc("Symbol remapping file")); - cl::alias RemappingFileA("r", cl::desc("Alias for --remapping-file"), - cl::aliasopt(RemappingFile)); - cl::opt OutputFilename("output", cl::value_desc("output"), - cl::init("-"), cl::Required, - cl::desc("Output file")); - cl::alias OutputFilenameA("o", cl::desc("Alias for --output"), - cl::aliasopt(OutputFilename)); - cl::opt ProfileKind( - cl::desc("Profile kind:"), cl::init(instr), - cl::values(clEnumVal(instr, "Instrumentation profile (default)"), - clEnumVal(sample, "Sample profile"))); - cl::opt OutputFormat( - cl::desc("Format of output profile"), cl::init(PF_Binary), - cl::values(clEnumValN(PF_Binary, "binary", "Binary encoding (default)"), - clEnumValN(PF_Compact_Binary, "compbinary", - "Compact binary encoding"), - clEnumValN(PF_Text, "text", "Text encoding"), - clEnumValN(PF_GCC, "gcc", - "GCC encoding (only meaningful for -sample)"))); - cl::opt OutputSparse("sparse", cl::init(false), - cl::desc("Generate a sparse profile (only meaningful for -instr)")); - cl::opt NumThreads( - "num-threads", cl::init(0), - cl::desc("Number of merge threads to use (default: autodetect)")); - cl::alias NumThreadsA("j", cl::desc("Alias for --num-threads"), - cl::aliasopt(NumThreads)); - - cl::ParseCommandLineOptions(argc, argv, "LLVM profile data merger\n"); - - WeightedFileVector WeightedInputs; - for (StringRef Filename : InputFilenames) - addWeightedInput(WeightedInputs, {Filename, 1}); - for (StringRef WeightedFilename : WeightedInputFilenames) - addWeightedInput(WeightedInputs, parseWeightedFile(WeightedFilename)); - - // Make sure that the file buffer stays alive for the duration of the - // weighted input vector's lifetime. - auto Buffer = getInputFilenamesFileBuf(InputFilenamesFile); - parseInputFilenamesFile(Buffer.get(), WeightedInputs); - - if (WeightedInputs.empty()) - exitWithError("No input files specified. See " + - sys::path::filename(argv[0]) + " -help"); - - if (DumpInputFileList) { - for (auto &WF : WeightedInputs) - outs() << WF.Weight << "," << WF.Filename << "\n"; - return 0; - } - - std::unique_ptr Remapper; - if (!RemappingFile.empty()) - Remapper = SymbolRemapper::create(RemappingFile); - - if (ProfileKind == instr) - mergeInstrProfile(WeightedInputs, Remapper.get(), OutputFilename, - OutputFormat, OutputSparse, NumThreads); - else - mergeSampleProfile(WeightedInputs, Remapper.get(), OutputFilename, - OutputFormat); - - return 0; -} - -/// Computer the overlap b/w profile BaseFilename and profile TestFilename. -static void overlapInstrProfile(const std::string &BaseFilename, - const std::string &TestFilename, - const OverlapFuncFilters &FuncFilter, - raw_fd_ostream &OS, bool IsCS) { - std::mutex ErrorLock; - SmallSet WriterErrorCodes; - WriterContext Context(false, ErrorLock, WriterErrorCodes); - WeightedFile WeightedInput{BaseFilename, 1}; - OverlapStats Overlap; - Error E = Overlap.accumuateCounts(BaseFilename, TestFilename, IsCS); - if (E) - exitWithError(std::move(E), "Error in getting profile count sums"); - if (Overlap.Base.CountSum < 1.0f) { - OS << "Sum of edge counts for profile " << BaseFilename << " is 0.\n"; - exit(0); - } - if (Overlap.Test.CountSum < 1.0f) { - OS << "Sum of edge counts for profile " << TestFilename << " is 0.\n"; - exit(0); - } - loadInput(WeightedInput, nullptr, &Context); - overlapInput(BaseFilename, TestFilename, &Context, Overlap, FuncFilter, OS, - IsCS); - Overlap.dump(OS); -} - -static int overlap_main(int argc, const char *argv[]) { - cl::opt BaseFilename(cl::Positional, cl::Required, - cl::desc("")); - cl::opt TestFilename(cl::Positional, cl::Required, - cl::desc("")); - cl::opt Output("output", cl::value_desc("output"), cl::init("-"), - cl::desc("Output file")); - cl::alias OutputA("o", cl::desc("Alias for --output"), cl::aliasopt(Output)); - cl::opt IsCS("cs", cl::init(false), - cl::desc("For context sensitive counts")); - cl::opt ValueCutoff( - "value-cutoff", cl::init(-1), - cl::desc( - "Function level overlap information for every function in test " - "profile with max count value greater then the parameter value")); - cl::opt FuncNameFilter( - "function", - cl::desc("Function level overlap information for matching functions")); - cl::ParseCommandLineOptions(argc, argv, "LLVM profile data overlap tool\n"); - - std::error_code EC; - raw_fd_ostream OS(Output.data(), EC, sys::fs::F_Text); - if (EC) - exitWithErrorCode(EC, Output); - - overlapInstrProfile(BaseFilename, TestFilename, - OverlapFuncFilters{ValueCutoff, FuncNameFilter}, OS, - IsCS); - - return 0; -} - -typedef struct ValueSitesStats { - ValueSitesStats() - : TotalNumValueSites(0), TotalNumValueSitesWithValueProfile(0), - TotalNumValues(0) {} - uint64_t TotalNumValueSites; - uint64_t TotalNumValueSitesWithValueProfile; - uint64_t TotalNumValues; - std::vector ValueSitesHistogram; -} ValueSitesStats; - -static void traverseAllValueSites(const InstrProfRecord &Func, uint32_t VK, - ValueSitesStats &Stats, raw_fd_ostream &OS, - InstrProfSymtab *Symtab) { - uint32_t NS = Func.getNumValueSites(VK); - Stats.TotalNumValueSites += NS; - for (size_t I = 0; I < NS; ++I) { - uint32_t NV = Func.getNumValueDataForSite(VK, I); - std::unique_ptr VD = Func.getValueForSite(VK, I); - Stats.TotalNumValues += NV; - if (NV) { - Stats.TotalNumValueSitesWithValueProfile++; - if (NV > Stats.ValueSitesHistogram.size()) - Stats.ValueSitesHistogram.resize(NV, 0); - Stats.ValueSitesHistogram[NV - 1]++; - } - - uint64_t SiteSum = 0; - for (uint32_t V = 0; V < NV; V++) - SiteSum += VD[V].Count; - if (SiteSum == 0) - SiteSum = 1; - - for (uint32_t V = 0; V < NV; V++) { - OS << "\t[ " << format("%2u", I) << ", "; - if (Symtab == nullptr) - OS << format("%4" PRIu64, VD[V].Value); - else - OS << Symtab->getFuncName(VD[V].Value); - OS << ", " << format("%10" PRId64, VD[V].Count) << " ] (" - << format("%.2f%%", (VD[V].Count * 100.0 / SiteSum)) << ")\n"; - } - } -} - -static void showValueSitesStats(raw_fd_ostream &OS, uint32_t VK, - ValueSitesStats &Stats) { - OS << " Total number of sites: " << Stats.TotalNumValueSites << "\n"; - OS << " Total number of sites with values: " - << Stats.TotalNumValueSitesWithValueProfile << "\n"; - OS << " Total number of profiled values: " << Stats.TotalNumValues << "\n"; - - OS << " Value sites histogram:\n\tNumTargets, SiteCount\n"; - for (unsigned I = 0; I < Stats.ValueSitesHistogram.size(); I++) { - if (Stats.ValueSitesHistogram[I] > 0) - OS << "\t" << I + 1 << ", " << Stats.ValueSitesHistogram[I] << "\n"; - } -} - -static int showInstrProfile(const std::string &Filename, bool ShowCounts, - uint32_t TopN, bool ShowIndirectCallTargets, - bool ShowMemOPSizes, bool ShowDetailedSummary, - std::vector DetailedSummaryCutoffs, - bool ShowAllFunctions, bool ShowCS, - uint64_t ValueCutoff, bool OnlyListBelow, - const std::string &ShowFunction, bool TextFormat, - raw_fd_ostream &OS) { - auto ReaderOrErr = InstrProfReader::create(Filename); - std::vector Cutoffs = std::move(DetailedSummaryCutoffs); - if (ShowDetailedSummary && Cutoffs.empty()) { - Cutoffs = {800000, 900000, 950000, 990000, 999000, 999900, 999990}; - } - InstrProfSummaryBuilder Builder(std::move(Cutoffs)); - if (Error E = ReaderOrErr.takeError()) - exitWithError(std::move(E), Filename); - - auto Reader = std::move(ReaderOrErr.get()); - bool IsIRInstr = Reader->isIRLevelProfile(); - size_t ShownFunctions = 0; - size_t BelowCutoffFunctions = 0; - int NumVPKind = IPVK_Last - IPVK_First + 1; - std::vector VPStats(NumVPKind); - - auto MinCmp = [](const std::pair &v1, - const std::pair &v2) { - return v1.second > v2.second; - }; - - std::priority_queue, - std::vector>, - decltype(MinCmp)> - HottestFuncs(MinCmp); - - if (!TextFormat && OnlyListBelow) { - OS << "The list of functions with the maximum counter less than " - << ValueCutoff << ":\n"; - } - - // Add marker so that IR-level instrumentation round-trips properly. - if (TextFormat && IsIRInstr) - OS << ":ir\n"; - - for (const auto &Func : *Reader) { - if (Reader->isIRLevelProfile()) { - bool FuncIsCS = NamedInstrProfRecord::hasCSFlagInHash(Func.Hash); - if (FuncIsCS != ShowCS) - continue; - } - bool Show = - ShowAllFunctions || (!ShowFunction.empty() && - Func.Name.find(ShowFunction) != Func.Name.npos); - - bool doTextFormatDump = (Show && TextFormat); - - if (doTextFormatDump) { - InstrProfSymtab &Symtab = Reader->getSymtab(); - InstrProfWriter::writeRecordInText(Func.Name, Func.Hash, Func, Symtab, - OS); - continue; - } - - assert(Func.Counts.size() > 0 && "function missing entry counter"); - Builder.addRecord(Func); - - uint64_t FuncMax = 0; - uint64_t FuncSum = 0; - for (size_t I = 0, E = Func.Counts.size(); I < E; ++I) { - FuncMax = std::max(FuncMax, Func.Counts[I]); - FuncSum += Func.Counts[I]; - } - - if (FuncMax < ValueCutoff) { - ++BelowCutoffFunctions; - if (OnlyListBelow) { - OS << " " << Func.Name << ": (Max = " << FuncMax - << " Sum = " << FuncSum << ")\n"; - } - continue; - } else if (OnlyListBelow) - continue; - - if (TopN) { - if (HottestFuncs.size() == TopN) { - if (HottestFuncs.top().second < FuncMax) { - HottestFuncs.pop(); - HottestFuncs.emplace(std::make_pair(std::string(Func.Name), FuncMax)); - } - } else - HottestFuncs.emplace(std::make_pair(std::string(Func.Name), FuncMax)); - } - - if (Show) { - if (!ShownFunctions) - OS << "Counters:\n"; - - ++ShownFunctions; - - OS << " " << Func.Name << ":\n" - << " Hash: " << format("0x%016" PRIx64, Func.Hash) << "\n" - << " Counters: " << Func.Counts.size() << "\n"; - if (!IsIRInstr) - OS << " Function count: " << Func.Counts[0] << "\n"; - - if (ShowIndirectCallTargets) - OS << " Indirect Call Site Count: " - << Func.getNumValueSites(IPVK_IndirectCallTarget) << "\n"; - - uint32_t NumMemOPCalls = Func.getNumValueSites(IPVK_MemOPSize); - if (ShowMemOPSizes && NumMemOPCalls > 0) - OS << " Number of Memory Intrinsics Calls: " << NumMemOPCalls - << "\n"; - - if (ShowCounts) { - OS << " Block counts: ["; - size_t Start = (IsIRInstr ? 0 : 1); - for (size_t I = Start, E = Func.Counts.size(); I < E; ++I) { - OS << (I == Start ? "" : ", ") << Func.Counts[I]; - } - OS << "]\n"; - } - - if (ShowIndirectCallTargets) { - OS << " Indirect Target Results:\n"; - traverseAllValueSites(Func, IPVK_IndirectCallTarget, - VPStats[IPVK_IndirectCallTarget], OS, - &(Reader->getSymtab())); - } - - if (ShowMemOPSizes && NumMemOPCalls > 0) { - OS << " Memory Intrinsic Size Results:\n"; - traverseAllValueSites(Func, IPVK_MemOPSize, VPStats[IPVK_MemOPSize], OS, - nullptr); - } - } - } - if (Reader->hasError()) - exitWithError(Reader->getError(), Filename); - - if (TextFormat) - return 0; - std::unique_ptr PS(Builder.getSummary()); - OS << "Instrumentation level: " - << (Reader->isIRLevelProfile() ? "IR" : "Front-end") << "\n"; - if (ShowAllFunctions || !ShowFunction.empty()) - OS << "Functions shown: " << ShownFunctions << "\n"; - OS << "Total functions: " << PS->getNumFunctions() << "\n"; - if (ValueCutoff > 0) { - OS << "Number of functions with maximum count (< " << ValueCutoff - << "): " << BelowCutoffFunctions << "\n"; - OS << "Number of functions with maximum count (>= " << ValueCutoff - << "): " << PS->getNumFunctions() - BelowCutoffFunctions << "\n"; - } - OS << "Maximum function count: " << PS->getMaxFunctionCount() << "\n"; - OS << "Maximum internal block count: " << PS->getMaxInternalCount() << "\n"; - - if (TopN) { - std::vector> SortedHottestFuncs; - while (!HottestFuncs.empty()) { - SortedHottestFuncs.emplace_back(HottestFuncs.top()); - HottestFuncs.pop(); - } - OS << "Top " << TopN - << " functions with the largest internal block counts: \n"; - for (auto &hotfunc : llvm::reverse(SortedHottestFuncs)) - OS << " " << hotfunc.first << ", max count = " << hotfunc.second << "\n"; - } - - if (ShownFunctions && ShowIndirectCallTargets) { - OS << "Statistics for indirect call sites profile:\n"; - showValueSitesStats(OS, IPVK_IndirectCallTarget, - VPStats[IPVK_IndirectCallTarget]); - } - - if (ShownFunctions && ShowMemOPSizes) { - OS << "Statistics for memory intrinsic calls sizes profile:\n"; - showValueSitesStats(OS, IPVK_MemOPSize, VPStats[IPVK_MemOPSize]); - } - - if (ShowDetailedSummary) { - OS << "Detailed summary:\n"; - OS << "Total number of blocks: " << PS->getNumCounts() << "\n"; - OS << "Total count: " << PS->getTotalCount() << "\n"; - for (auto Entry : PS->getDetailedSummary()) { - OS << Entry.NumCounts << " blocks with count >= " << Entry.MinCount - << " account for " - << format("%0.6g", (float)Entry.Cutoff / ProfileSummary::Scale * 100) - << " percentage of the total counts.\n"; - } - } - return 0; -} - -static int showSampleProfile(const std::string &Filename, bool ShowCounts, - bool ShowAllFunctions, - const std::string &ShowFunction, - raw_fd_ostream &OS) { - using namespace sampleprof; - LLVMContext Context; - auto ReaderOrErr = SampleProfileReader::create(Filename, Context); - if (std::error_code EC = ReaderOrErr.getError()) - exitWithErrorCode(EC, Filename); - - auto Reader = std::move(ReaderOrErr.get()); - if (std::error_code EC = Reader->read()) - exitWithErrorCode(EC, Filename); - - if (ShowAllFunctions || ShowFunction.empty()) - Reader->dump(OS); - else - Reader->dumpFunctionProfile(ShowFunction, OS); - - return 0; -} - -static int show_main(int argc, const char *argv[]) { - cl::opt Filename(cl::Positional, cl::Required, - cl::desc("")); - - cl::opt ShowCounts("counts", cl::init(false), - cl::desc("Show counter values for shown functions")); - cl::opt TextFormat( - "text", cl::init(false), - cl::desc("Show instr profile data in text dump format")); - cl::opt ShowIndirectCallTargets( - "ic-targets", cl::init(false), - cl::desc("Show indirect call site target values for shown functions")); - cl::opt ShowMemOPSizes( - "memop-sizes", cl::init(false), - cl::desc("Show the profiled sizes of the memory intrinsic calls " - "for shown functions")); - cl::opt ShowDetailedSummary("detailed-summary", cl::init(false), - cl::desc("Show detailed profile summary")); - cl::list DetailedSummaryCutoffs( - cl::CommaSeparated, "detailed-summary-cutoffs", - cl::desc( - "Cutoff percentages (times 10000) for generating detailed summary"), - cl::value_desc("800000,901000,999999")); - cl::opt ShowAllFunctions("all-functions", cl::init(false), - cl::desc("Details for every function")); - cl::opt ShowCS("showcs", cl::init(false), - cl::desc("Show context sensitive counts")); - cl::opt ShowFunction("function", - cl::desc("Details for matching functions")); - - cl::opt OutputFilename("output", cl::value_desc("output"), - cl::init("-"), cl::desc("Output file")); - cl::alias OutputFilenameA("o", cl::desc("Alias for --output"), - cl::aliasopt(OutputFilename)); - cl::opt ProfileKind( - cl::desc("Profile kind:"), cl::init(instr), - cl::values(clEnumVal(instr, "Instrumentation profile (default)"), - clEnumVal(sample, "Sample profile"))); - cl::opt TopNFunctions( - "topn", cl::init(0), - cl::desc("Show the list of functions with the largest internal counts")); - cl::opt ValueCutoff( - "value-cutoff", cl::init(0), - cl::desc("Set the count value cutoff. Functions with the maximum count " - "less than this value will not be printed out. (Default is 0)")); - cl::opt OnlyListBelow( - "list-below-cutoff", cl::init(false), - cl::desc("Only output names of functions whose max count values are " - "below the cutoff value")); - cl::ParseCommandLineOptions(argc, argv, "LLVM profile data summary\n"); - - if (OutputFilename.empty()) - OutputFilename = "-"; - - if (!Filename.compare(OutputFilename)) { - errs() << sys::path::filename(argv[0]) - << ": Input file name cannot be the same as the output file name!\n"; - return 1; - } - - std::error_code EC; - raw_fd_ostream OS(OutputFilename.data(), EC, sys::fs::F_Text); - if (EC) - exitWithErrorCode(EC, OutputFilename); - - if (ShowAllFunctions && !ShowFunction.empty()) - WithColor::warning() << "-function argument ignored: showing all functions\n"; - - if (ProfileKind == instr) - return showInstrProfile(Filename, ShowCounts, TopNFunctions, - ShowIndirectCallTargets, ShowMemOPSizes, - ShowDetailedSummary, DetailedSummaryCutoffs, - ShowAllFunctions, ShowCS, ValueCutoff, - OnlyListBelow, ShowFunction, TextFormat, OS); - else - return showSampleProfile(Filename, ShowCounts, ShowAllFunctions, - ShowFunction, OS); -} - -int main(int argc, const char *argv[]) { - InitLLVM X(argc, argv); - - StringRef ProgName(sys::path::filename(argv[0])); - if (argc > 1) { - int (*func)(int, const char *[]) = nullptr; - - if (strcmp(argv[1], "merge") == 0) - func = merge_main; - else if (strcmp(argv[1], "show") == 0) - func = show_main; - else if (strcmp(argv[1], "overlap") == 0) - func = overlap_main; - - if (func) { - std::string Invocation(ProgName.str() + " " + argv[1]); - argv[1] = Invocation.c_str(); - return func(argc - 1, argv + 1); - } - - if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "-help") == 0 || - strcmp(argv[1], "--help") == 0) { - - errs() << "OVERVIEW: LLVM profile data tools\n\n" - << "USAGE: " << ProgName << " [args...]\n" - << "USAGE: " << ProgName << " -help\n\n" - << "See each individual command --help for more details.\n" - << "Available commands: merge, show, overlap\n"; - return 0; - } - } - - if (argc < 2) - errs() << ProgName << ": No command specified!\n"; - else - errs() << ProgName << ": Unknown command!\n"; - - errs() << "USAGE: " << ProgName << " [args...]\n"; - return 1; -} diff --git a/utils/FileCheck-10.cpp b/utils/FileCheck-16.cpp similarity index 52% rename from utils/FileCheck-10.cpp rename to utils/FileCheck-16.cpp index 6f5791354ec..6657a1aff39 100644 --- a/utils/FileCheck-10.cpp +++ b/utils/FileCheck-16.cpp @@ -15,13 +15,16 @@ // //===----------------------------------------------------------------------===// +#include "llvm/FileCheck/FileCheck.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/InitLLVM.h" +#include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Process.h" +#include "llvm/Support/SourceMgr.h" #include "llvm/Support/WithColor.h" #include "llvm/Support/raw_ostream.h" -#include "llvm/Support/FileCheck.h" #include +#include using namespace llvm; static cl::extrahelp FileCheckOptsEnv( @@ -44,6 +47,14 @@ static cl::alias CheckPrefixesAlias( cl::desc( "Alias for -check-prefix permitting multiple comma separated values")); +static cl::list CommentPrefixes( + "comment-prefixes", cl::CommaSeparated, cl::Hidden, + cl::desc("Comma-separated list of comment prefixes to use from check file\n" + "(defaults to 'COM,RUN'). Please avoid using this feature in\n" + "LLVM's LIT-based test suites, which should be easier to\n" + "maintain if they all follow a consistent comment style. This\n" + "feature is meant for non-LIT test suites using FileCheck.")); + static cl::opt NoCanonicalizeWhiteSpace( "strict-whitespace", cl::desc("Do not treat all horizontal whitespace as equivalent")); @@ -69,6 +80,10 @@ static cl::opt AllowEmptyInput( cl::desc("Allow the input file to be empty. This is useful when making\n" "checks that some error message does not occur, for example.")); +static cl::opt AllowUnusedPrefixes( + "allow-unused-prefixes", + cl::desc("Allow prefixes to be specified but not appear in the test.")); + static cl::opt MatchFullLines( "match-full-lines", cl::init(false), cl::desc("Require all positive matches to cover an entire input line.\n" @@ -89,29 +104,19 @@ static cl::opt AllowDeprecatedDagOverlap( "non-overlapping CHECK-DAG implementation.\n")); static cl::opt Verbose( - "v", cl::init(false), + "v", cl::desc("Print directive pattern matches, or add them to the input dump\n" "if enabled.\n")); static cl::opt VerboseVerbose( - "vv", cl::init(false), + "vv", cl::desc("Print information helpful in diagnosing internal FileCheck\n" "issues, or add it to the input dump if enabled. Implies\n" "-v.\n")); -static const char * DumpInputEnv = "FILECHECK_DUMP_INPUT_ON_FAILURE"; - -static cl::opt DumpInputOnFailure( - "dump-input-on-failure", - cl::init(std::getenv(DumpInputEnv) && *std::getenv(DumpInputEnv)), - cl::desc("Dump original input to stderr before failing.\n" - "The value can be also controlled using\n" - "FILECHECK_DUMP_INPUT_ON_FAILURE environment variable.\n" - "This option is deprecated in favor of -dump-input=fail.\n")); // The order of DumpInputValue members affects their precedence, as documented // for -dump-input below. enum DumpInputValue { - DumpInputDefault, DumpInputNever, DumpInputFail, DumpInputAlways, @@ -123,14 +128,47 @@ static cl::list DumpInputs( cl::desc("Dump input to stderr, adding annotations representing\n" "currently enabled diagnostics. When there are multiple\n" "occurrences of this option, the that appears earliest\n" - "in the list below has precedence.\n"), + "in the list below has precedence. The default is 'fail'.\n"), cl::value_desc("mode"), - cl::values(clEnumValN(DumpInputHelp, "help", - "Explain dump format and quit"), + cl::values(clEnumValN(DumpInputHelp, "help", "Explain input dump and quit"), clEnumValN(DumpInputAlways, "always", "Always dump input"), clEnumValN(DumpInputFail, "fail", "Dump input on failure"), clEnumValN(DumpInputNever, "never", "Never dump input"))); +// The order of DumpInputFilterValue members affects their precedence, as +// documented for -dump-input-filter below. +enum DumpInputFilterValue { + DumpInputFilterError, + DumpInputFilterAnnotation, + DumpInputFilterAnnotationFull, + DumpInputFilterAll +}; + +static cl::list DumpInputFilters( + "dump-input-filter", + cl::desc("In the dump requested by -dump-input, print only input lines of\n" + "kind plus any context specified by -dump-input-context.\n" + "When there are multiple occurrences of this option, the \n" + "that appears earliest in the list below has precedence. The\n" + "default is 'error' when -dump-input=fail, and it's 'all' when\n" + "-dump-input=always.\n"), + cl::values(clEnumValN(DumpInputFilterAll, "all", "All input lines"), + clEnumValN(DumpInputFilterAnnotationFull, "annotation-full", + "Input lines with annotations"), + clEnumValN(DumpInputFilterAnnotation, "annotation", + "Input lines with starting points of annotations"), + clEnumValN(DumpInputFilterError, "error", + "Input lines with starting points of error " + "annotations"))); + +static cl::list DumpInputContexts( + "dump-input-context", cl::value_desc("N"), + cl::desc("In the dump requested by -dump-input, print input lines\n" + "before and input lines after any lines specified by\n" + "-dump-input-filter. When there are multiple occurrences of\n" + "this option, the largest specified has precedence. The\n" + "default is 5.\n")); + typedef cl::list::const_iterator prefix_iterator; @@ -153,10 +191,15 @@ struct MarkerStyle { raw_ostream::Colors Color; /// A note to follow the marker, or empty string if none. std::string Note; + /// Does this marker indicate inclusion by -dump-input-filter=error? + bool FiltersAsError; MarkerStyle() {} MarkerStyle(char Lead, raw_ostream::Colors Color, - const std::string &Note = "") - : Lead(Lead), Color(Color), Note(Note) {} + const std::string &Note = "", bool FiltersAsError = false) + : Lead(Lead), Color(Color), Note(Note), FiltersAsError(FiltersAsError) { + assert((!FiltersAsError || !Note.empty()) && + "expected error diagnostic to have note"); + } }; static MarkerStyle GetMarker(FileCheckDiag::MatchType MatchTy) { @@ -164,43 +207,76 @@ static MarkerStyle GetMarker(FileCheckDiag::MatchType MatchTy) { case FileCheckDiag::MatchFoundAndExpected: return MarkerStyle('^', raw_ostream::GREEN); case FileCheckDiag::MatchFoundButExcluded: - return MarkerStyle('!', raw_ostream::RED, "error: no match expected"); + return MarkerStyle('!', raw_ostream::RED, "error: no match expected", + /*FiltersAsError=*/true); case FileCheckDiag::MatchFoundButWrongLine: - return MarkerStyle('!', raw_ostream::RED, "error: match on wrong line"); + return MarkerStyle('!', raw_ostream::RED, "error: match on wrong line", + /*FiltersAsError=*/true); case FileCheckDiag::MatchFoundButDiscarded: return MarkerStyle('!', raw_ostream::CYAN, "discard: overlaps earlier match"); + case FileCheckDiag::MatchFoundErrorNote: + // Note should always be overridden within the FileCheckDiag. + return MarkerStyle('!', raw_ostream::RED, + "error: unknown error after match", + /*FiltersAsError=*/true); case FileCheckDiag::MatchNoneAndExcluded: return MarkerStyle('X', raw_ostream::GREEN); case FileCheckDiag::MatchNoneButExpected: - return MarkerStyle('X', raw_ostream::RED, "error: no match found"); + return MarkerStyle('X', raw_ostream::RED, "error: no match found", + /*FiltersAsError=*/true); + case FileCheckDiag::MatchNoneForInvalidPattern: + return MarkerStyle('X', raw_ostream::RED, + "error: match failed for invalid pattern", + /*FiltersAsError=*/true); case FileCheckDiag::MatchFuzzy: - return MarkerStyle('?', raw_ostream::MAGENTA, "possible intended match"); + return MarkerStyle('?', raw_ostream::MAGENTA, "possible intended match", + /*FiltersAsError=*/true); } llvm_unreachable_internal("unexpected match type"); } static void DumpInputAnnotationHelp(raw_ostream &OS) { OS << "The following description was requested by -dump-input=help to\n" - << "explain the input annotations printed by -dump-input=always and\n" - << "-dump-input=fail:\n\n"; + << "explain the input dump printed by FileCheck.\n" + << "\n" + << "Related command-line options:\n" + << "\n" + << " - -dump-input= enables or disables the input dump\n" + << " - -dump-input-filter= filters the input lines\n" + << " - -dump-input-context= adjusts the context of filtered lines\n" + << " - -v and -vv add more annotations\n" + << " - -color forces colors to be enabled both in the dump and below\n" + << " - -help documents the above options in more detail\n" + << "\n" + << "These options can also be set via FILECHECK_OPTS. For example, for\n" + << "maximum debugging output on failures:\n" + << "\n" + << " $ FILECHECK_OPTS='-dump-input-filter=all -vv -color' ninja check\n" + << "\n" + << "Input dump annotation format:\n" + << "\n"; // Labels for input lines. OS << " - "; WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "L:"; - OS << " labels line number L of the input file\n"; + OS << " labels line number L of the input file\n" + << " An extra space is added after each input line to represent" + << " the\n" + << " newline character\n"; // Labels for annotation lines. OS << " - "; WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "T:L"; - OS << " labels the only match result for a pattern of type T from " - << "line L of\n" - << " the check file\n"; + OS << " labels the only match result for either (1) a pattern of type T" + << " from\n" + << " line L of the check file if L is an integer or (2) the" + << " I-th implicit\n" + << " pattern if L is \"imp\" followed by an integer " + << "I (index origin one)\n"; OS << " - "; WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "T:L'N"; - OS << " labels the Nth match result for a pattern of type T from line " - << "L of\n" - << " the check file\n"; + OS << " labels the Nth match result for such a pattern\n"; // Markers on annotation lines. OS << " - "; @@ -223,6 +299,12 @@ static void DumpInputAnnotationHelp(raw_ostream &OS) { WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "?"; OS << " marks fuzzy match when no match is found\n"; + // Elided lines. + OS << " - "; + WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "..."; + OS << " indicates elided input lines and annotations, as specified by\n" + << " -dump-input-filter and -dump-input-context\n"; + // Colors. OS << " - colors "; WithColor(OS, raw_ostream::GREEN, true) << "success"; @@ -234,25 +316,23 @@ static void DumpInputAnnotationHelp(raw_ostream &OS) { WithColor(OS, raw_ostream::CYAN, true, false) << "discarded match"; OS << ", "; WithColor(OS, raw_ostream::CYAN, true, true) << "unmatched input"; - OS << "\n\n" - << "If you are not seeing color above or in input dumps, try: -color\n"; + OS << "\n"; } /// An annotation for a single input line. struct InputAnnotation { - /// The check file line (one-origin indexing) where the directive that - /// produced this annotation is located. - unsigned CheckLine; - /// The index of the match result for this check. - unsigned CheckDiagIndex; + /// The index of the match result across all checks + unsigned DiagIndex; /// The label for this annotation. std::string Label; + /// Is this the initial fragment of a diagnostic that has been broken across + /// multiple lines? + bool IsFirstLine; /// What input line (one-origin indexing) this annotation marks. This might - /// be different from the starting line of the original diagnostic if this is - /// a non-initial fragment of a diagnostic that has been broken across - /// multiple lines. + /// be different from the starting line of the original diagnostic if + /// !IsFirstLine. unsigned InputLine; - /// The column range (one-origin indexing, open end) in which to to mark the + /// The column range (one-origin indexing, open end) in which to mark the /// input line. If InputEndCol is UINT_MAX, treat it as the last column /// before the newline. unsigned InputStartCol, InputEndCol; @@ -263,7 +343,7 @@ struct InputAnnotation { }; /// Get an abbreviation for the check type. -std::string GetCheckTypeAbbreviation(Check::FileCheckType Ty) { +static std::string GetCheckTypeAbbreviation(Check::FileCheckType Ty) { switch (Ty) { case Check::CheckPlain: if (Ty.getCount() > 1) @@ -281,56 +361,90 @@ std::string GetCheckTypeAbbreviation(Check::FileCheckType Ty) { return "label"; case Check::CheckEmpty: return "empty"; + case Check::CheckComment: + return "com"; case Check::CheckEOF: return "eof"; case Check::CheckBadNot: return "bad-not"; case Check::CheckBadCount: return "bad-count"; + case Check::CheckMisspelled: + return "misspelled"; case Check::CheckNone: llvm_unreachable("invalid FileCheckType"); } llvm_unreachable("unknown FileCheckType"); } -static void BuildInputAnnotations(const std::vector &Diags, - std::vector &Annotations, - unsigned &LabelWidth) { - // How many diagnostics has the current check seen so far? - unsigned CheckDiagCount = 0; +static void +BuildInputAnnotations(const SourceMgr &SM, unsigned CheckFileBufferID, + const std::pair &ImpPatBufferIDRange, + const std::vector &Diags, + std::vector &Annotations, + unsigned &LabelWidth) { + struct CompareSMLoc { + bool operator()(const SMLoc &LHS, const SMLoc &RHS) const { + return LHS.getPointer() < RHS.getPointer(); + } + }; + // How many diagnostics does each pattern have? + std::map DiagCountPerPattern; + for (auto Diag : Diags) + ++DiagCountPerPattern[Diag.CheckLoc]; + // How many diagnostics have we seen so far per pattern? + std::map DiagIndexPerPattern; + // How many total diagnostics have we seen so far? + unsigned DiagIndex = 0; // What's the widest label? LabelWidth = 0; for (auto DiagItr = Diags.begin(), DiagEnd = Diags.end(); DiagItr != DiagEnd; ++DiagItr) { InputAnnotation A; + A.DiagIndex = DiagIndex++; // Build label, which uniquely identifies this check result. - A.CheckLine = DiagItr->CheckLine; + unsigned CheckBufferID = SM.FindBufferContainingLoc(DiagItr->CheckLoc); + auto CheckLineAndCol = + SM.getLineAndColumn(DiagItr->CheckLoc, CheckBufferID); llvm::raw_string_ostream Label(A.Label); - Label << GetCheckTypeAbbreviation(DiagItr->CheckTy) << ":" - << DiagItr->CheckLine; - A.CheckDiagIndex = UINT_MAX; - auto DiagNext = std::next(DiagItr); - if (DiagNext != DiagEnd && DiagItr->CheckTy == DiagNext->CheckTy && - DiagItr->CheckLine == DiagNext->CheckLine) - A.CheckDiagIndex = CheckDiagCount++; - else if (CheckDiagCount) { - A.CheckDiagIndex = CheckDiagCount; - CheckDiagCount = 0; - } - if (A.CheckDiagIndex != UINT_MAX) - Label << "'" << A.CheckDiagIndex; + Label << GetCheckTypeAbbreviation(DiagItr->CheckTy) << ":"; + if (CheckBufferID == CheckFileBufferID) + Label << CheckLineAndCol.first; + else if (ImpPatBufferIDRange.first <= CheckBufferID && + CheckBufferID < ImpPatBufferIDRange.second) + Label << "imp" << (CheckBufferID - ImpPatBufferIDRange.first + 1); else - A.CheckDiagIndex = 0; - Label.flush(); + llvm_unreachable("expected diagnostic's check location to be either in " + "the check file or for an implicit pattern"); + if (DiagCountPerPattern[DiagItr->CheckLoc] > 1) + Label << "'" << DiagIndexPerPattern[DiagItr->CheckLoc]++; LabelWidth = std::max((std::string::size_type)LabelWidth, A.Label.size()); A.Marker = GetMarker(DiagItr->MatchTy); + if (!DiagItr->Note.empty()) { + A.Marker.Note = DiagItr->Note; + // It's less confusing if notes that don't actually have ranges don't have + // markers. For example, a marker for 'with "VAR" equal to "5"' would + // seem to indicate where "VAR" matches, but the location we actually have + // for the marker simply points to the start of the match/search range for + // the full pattern of which the substitution is potentially just one + // component. + if (DiagItr->InputStartLine == DiagItr->InputEndLine && + DiagItr->InputStartCol == DiagItr->InputEndCol) + A.Marker.Lead = ' '; + } + if (DiagItr->MatchTy == FileCheckDiag::MatchFoundErrorNote) { + assert(!DiagItr->Note.empty() && + "expected custom note for MatchFoundErrorNote"); + A.Marker.Note = "error: " + A.Marker.Note; + } A.FoundAndExpectedMatch = DiagItr->MatchTy == FileCheckDiag::MatchFoundAndExpected; // Compute the mark location, and break annotation into multiple // annotations if it spans multiple lines. + A.IsFirstLine = true; A.InputLine = DiagItr->InputStartLine; A.InputStartCol = DiagItr->InputStartCol; if (DiagItr->InputStartLine == DiagItr->InputEndLine) { @@ -352,9 +466,9 @@ static void BuildInputAnnotations(const std::vector &Diags, if (DiagItr->InputEndCol == 1 && L == E) break; InputAnnotation B; - B.CheckLine = A.CheckLine; - B.CheckDiagIndex = A.CheckDiagIndex; + B.DiagIndex = A.DiagIndex; B.Label = A.Label; + B.IsFirstLine = false; B.InputLine = L; B.Marker = A.Marker; B.Marker.Lead = '~'; @@ -371,43 +485,109 @@ static void BuildInputAnnotations(const std::vector &Diags, } } +static unsigned FindInputLineInFilter( + DumpInputFilterValue DumpInputFilter, unsigned CurInputLine, + const std::vector::iterator &AnnotationBeg, + const std::vector::iterator &AnnotationEnd) { + if (DumpInputFilter == DumpInputFilterAll) + return CurInputLine; + for (auto AnnotationItr = AnnotationBeg; AnnotationItr != AnnotationEnd; + ++AnnotationItr) { + switch (DumpInputFilter) { + case DumpInputFilterAll: + llvm_unreachable("unexpected DumpInputFilterAll"); + break; + case DumpInputFilterAnnotationFull: + return AnnotationItr->InputLine; + case DumpInputFilterAnnotation: + if (AnnotationItr->IsFirstLine) + return AnnotationItr->InputLine; + break; + case DumpInputFilterError: + if (AnnotationItr->IsFirstLine && AnnotationItr->Marker.FiltersAsError) + return AnnotationItr->InputLine; + break; + } + } + return UINT_MAX; +} + +/// To OS, print a vertical ellipsis (right-justified at LabelWidth) if it would +/// occupy less lines than ElidedLines, but print ElidedLines otherwise. Either +/// way, clear ElidedLines. Thus, if ElidedLines is empty, do nothing. +static void DumpEllipsisOrElidedLines(raw_ostream &OS, std::string &ElidedLines, + unsigned LabelWidth) { + if (ElidedLines.empty()) + return; + unsigned EllipsisLines = 3; + if (EllipsisLines < StringRef(ElidedLines).count('\n')) { + for (unsigned i = 0; i < EllipsisLines; ++i) { + WithColor(OS, raw_ostream::BLACK, /*Bold=*/true) + << right_justify(".", LabelWidth); + OS << '\n'; + } + } else + OS << ElidedLines; + ElidedLines.clear(); +} + static void DumpAnnotatedInput(raw_ostream &OS, const FileCheckRequest &Req, + DumpInputFilterValue DumpInputFilter, + unsigned DumpInputContext, StringRef InputFileText, std::vector &Annotations, unsigned LabelWidth) { - OS << "Full input was:\n<<<<<<\n"; + OS << "Input was:\n<<<<<<\n"; // Sort annotations. - // - // First, sort in the order of input lines to make it easier to find relevant - // annotations while iterating input lines in the implementation below. - // FileCheck diagnostics are not always reported and recorded in the order of - // input lines due to, for example, CHECK-DAG and CHECK-NOT. - // - // Second, for annotations for the same input line, sort in the order of the - // FileCheck directive's line in the check file (where there's at most one - // directive per line) and then by the index of the match result for that - // directive. The rationale of this choice is that, for any input line, this - // sort establishes a total order of annotations that, with respect to match - // results, is consistent across multiple lines, thus making match results - // easier to track from one line to the next when they span multiple lines. - std::sort(Annotations.begin(), Annotations.end(), - [](const InputAnnotation &A, const InputAnnotation &B) { - if (A.InputLine != B.InputLine) - return A.InputLine < B.InputLine; - if (A.CheckLine != B.CheckLine) - return A.CheckLine < B.CheckLine; - // FIXME: Sometimes CHECK-LABEL reports its match twice with - // other diagnostics in between, and then diag index incrementing - // fails to work properly, and then this assert fails. We should - // suppress one of those diagnostics or do a better job of - // computing this index. For now, we just produce a redundant - // CHECK-LABEL annotation. - // assert(A.CheckDiagIndex != B.CheckDiagIndex && - // "expected diagnostic indices to be unique within a " - // " check line"); - return A.CheckDiagIndex < B.CheckDiagIndex; - }); + llvm::sort(Annotations, + [](const InputAnnotation &A, const InputAnnotation &B) { + // 1. Sort annotations in the order of the input lines. + // + // This makes it easier to find relevant annotations while + // iterating input lines in the implementation below. FileCheck + // does not always produce diagnostics in the order of input + // lines due to, for example, CHECK-DAG and CHECK-NOT. + if (A.InputLine != B.InputLine) + return A.InputLine < B.InputLine; + // 2. Sort annotations in the temporal order FileCheck produced + // their associated diagnostics. + // + // This sort offers several benefits: + // + // A. On a single input line, the order of annotations reflects + // the FileCheck logic for processing directives/patterns. + // This can be helpful in understanding cases in which the + // order of the associated directives/patterns in the check + // file or on the command line either (i) does not match the + // temporal order in which FileCheck looks for matches for the + // directives/patterns (due to, for example, CHECK-LABEL, + // CHECK-NOT, or `--implicit-check-not`) or (ii) does match + // that order but does not match the order of those + // diagnostics along an input line (due to, for example, + // CHECK-DAG). + // + // On the other hand, because our presentation format presents + // input lines in order, there's no clear way to offer the + // same benefit across input lines. For consistency, it might + // then seem worthwhile to have annotations on a single line + // also sorted in input order (that is, by input column). + // However, in practice, this appears to be more confusing + // than helpful. Perhaps it's intuitive to expect annotations + // to be listed in the temporal order in which they were + // produced except in cases the presentation format obviously + // and inherently cannot support it (that is, across input + // lines). + // + // B. When diagnostics' annotations are split among multiple + // input lines, the user must track them from one input line + // to the next. One property of the sort chosen here is that + // it facilitates the user in this regard by ensuring the + // following: when comparing any two input lines, a + // diagnostic's annotations are sorted in the same position + // relative to all other diagnostics' annotations. + return A.DiagIndex < B.DiagIndex; + }); // Compute the width of the label column. const unsigned char *InputFilePtr = InputFileText.bytes_begin(), @@ -427,21 +607,53 @@ static void DumpAnnotatedInput(raw_ostream &OS, const FileCheckRequest &Req, LabelWidth = std::max(LabelWidth, LineNoWidth) + 3; // Print annotated input lines. + unsigned PrevLineInFilter = 0; // 0 means none so far + unsigned NextLineInFilter = 0; // 0 means uncomputed, UINT_MAX means none + std::string ElidedLines; + raw_string_ostream ElidedLinesOS(ElidedLines); + ColorMode TheColorMode = + WithColor(OS).colorsEnabled() ? ColorMode::Enable : ColorMode::Disable; + if (TheColorMode == ColorMode::Enable) + ElidedLinesOS.enable_colors(true); auto AnnotationItr = Annotations.begin(), AnnotationEnd = Annotations.end(); for (unsigned Line = 1; InputFilePtr != InputFileEnd || AnnotationItr != AnnotationEnd; ++Line) { const unsigned char *InputFileLine = InputFilePtr; + // Compute the previous and next line included by the filter. + if (NextLineInFilter < Line) + NextLineInFilter = FindInputLineInFilter(DumpInputFilter, Line, + AnnotationItr, AnnotationEnd); + assert(NextLineInFilter && "expected NextLineInFilter to be computed"); + if (NextLineInFilter == Line) + PrevLineInFilter = Line; + + // Elide this input line and its annotations if it's not within the + // context specified by -dump-input-context of an input line included by + // -dump-input-filter. However, in case the resulting ellipsis would occupy + // more lines than the input lines and annotations it elides, buffer the + // elided lines and annotations so we can print them instead. + raw_ostream *LineOS; + if ((!PrevLineInFilter || PrevLineInFilter + DumpInputContext < Line) && + (NextLineInFilter == UINT_MAX || + Line + DumpInputContext < NextLineInFilter)) + LineOS = &ElidedLinesOS; + else { + LineOS = &OS; + DumpEllipsisOrElidedLines(OS, ElidedLinesOS.str(), LabelWidth); + } + // Print right-aligned line number. - WithColor(OS, raw_ostream::BLACK, true) + WithColor(*LineOS, raw_ostream::BLACK, /*Bold=*/true, /*BF=*/false, + TheColorMode) << format_decimal(Line, LabelWidth) << ": "; // For the case where -v and colors are enabled, find the annotations for // good matches for expected patterns in order to highlight everything // else in the line. There are no such annotations if -v is disabled. std::vector FoundAndExpectedMatches; - if (Req.Verbose && WithColor(OS).colorsEnabled()) { + if (Req.Verbose && TheColorMode == ColorMode::Enable) { for (auto I = AnnotationItr; I != AnnotationEnd && I->InputLine == Line; ++I) { if (I->FoundAndExpectedMatch) @@ -453,7 +665,8 @@ static void DumpAnnotatedInput(raw_ostream &OS, const FileCheckRequest &Req, // expected patterns. bool Newline = false; { - WithColor COS(OS); + WithColor COS(*LineOS, raw_ostream::SAVEDCOLOR, /*Bold=*/false, + /*BG=*/false, TheColorMode); bool InMatch = false; if (Req.Verbose) COS.changeColor(raw_ostream::CYAN, true, true); @@ -470,20 +683,22 @@ static void DumpAnnotatedInput(raw_ostream &OS, const FileCheckRequest &Req, COS.resetColor(); else if (WasInMatch && !InMatch) COS.changeColor(raw_ostream::CYAN, true, true); - if (*InputFilePtr == '\n') + if (*InputFilePtr == '\n') { Newline = true; - else + COS << ' '; + } else COS << *InputFilePtr; ++InputFilePtr; } } - OS << '\n'; - unsigned InputLineWidth = InputFilePtr - InputFileLine - Newline; + *LineOS << '\n'; + unsigned InputLineWidth = InputFilePtr - InputFileLine; // Print any annotations. while (AnnotationItr != AnnotationEnd && AnnotationItr->InputLine == Line) { - WithColor COS(OS, AnnotationItr->Marker.Color, true); + WithColor COS(*LineOS, AnnotationItr->Marker.Color, /*Bold=*/true, + /*BG=*/false, TheColorMode); // The two spaces below are where the ": " appears on input lines. COS << left_justify(AnnotationItr->Label, LabelWidth) << " "; unsigned Col; @@ -508,6 +723,7 @@ static void DumpAnnotatedInput(raw_ostream &OS, const FileCheckRequest &Req, ++AnnotationItr; } } + DumpEllipsisOrElidedLines(OS, ElidedLinesOS.str(), LabelWidth); OS << ">>>>>>\n"; } @@ -520,10 +736,27 @@ int main(int argc, char **argv) { InitLLVM X(argc, argv); cl::ParseCommandLineOptions(argc, argv, /*Overview*/ "", /*Errs*/ nullptr, "FILECHECK_OPTS"); + + // Select -dump-input* values. The -help documentation specifies the default + // value and which value to choose if an option is specified multiple times. + // In the latter case, the general rule of thumb is to choose the value that + // provides the most information. DumpInputValue DumpInput = DumpInputs.empty() - ? DumpInputDefault + ? DumpInputFail : *std::max_element(DumpInputs.begin(), DumpInputs.end()); + DumpInputFilterValue DumpInputFilter; + if (DumpInputFilters.empty()) + DumpInputFilter = DumpInput == DumpInputAlways ? DumpInputFilterAll + : DumpInputFilterError; + else + DumpInputFilter = + *std::max_element(DumpInputFilters.begin(), DumpInputFilters.end()); + unsigned DumpInputContext = DumpInputContexts.empty() + ? 5 + : *std::max_element(DumpInputContexts.begin(), + DumpInputContexts.end()); + if (DumpInput == DumpInputHelp) { DumpInputAnnotationHelp(outs()); return 0; @@ -534,14 +767,14 @@ int main(int argc, char **argv) { } FileCheckRequest Req; - for (auto Prefix : CheckPrefixes) - Req.CheckPrefixes.push_back(Prefix); + append_range(Req.CheckPrefixes, CheckPrefixes); - for (auto CheckNot : ImplicitCheckNot) - Req.ImplicitCheckNot.push_back(CheckNot); + append_range(Req.CommentPrefixes, CommentPrefixes); + + append_range(Req.ImplicitCheckNot, ImplicitCheckNot); bool GlobalDefineError = false; - for (auto G : GlobalDefines) { + for (StringRef G : GlobalDefines) { size_t EqIdx = G.find('='); if (EqIdx == std::string::npos) { errs() << "Missing equal sign in command-line definition '-D" << G @@ -561,6 +794,7 @@ int main(int argc, char **argv) { return 2; Req.AllowEmptyInput = AllowEmptyInput; + Req.AllowUnusedPrefixes = AllowUnusedPrefixes; Req.EnableVarScope = EnableVarScope; Req.AllowDeprecatedDagOverlap = AllowDeprecatedDagOverlap; Req.Verbose = Verbose; @@ -573,12 +807,8 @@ int main(int argc, char **argv) { Req.Verbose = true; FileCheck FC(Req); - if (!FC.ValidateCheckPrefixes()) { - errs() << "Supplied check-prefix is invalid! Prefixes must be unique and " - "start with a letter and contain only alphanumeric characters, " - "hyphens and underscores\n"; + if (!FC.ValidateCheckPrefixes()) return 2; - } Regex PrefixRE = FC.buildCheckPrefixRegex(); std::string REError; @@ -595,7 +825,7 @@ int main(int argc, char **argv) { // Read the expected strings from the check file. ErrorOr> CheckFileOrErr = - MemoryBuffer::getFileOrSTDIN(CheckFilename); + MemoryBuffer::getFileOrSTDIN(CheckFilename, /*IsText=*/true); if (std::error_code EC = CheckFileOrErr.getError()) { errs() << "Could not open check file '" << CheckFilename << "': " << EC.message() << '\n'; @@ -606,16 +836,20 @@ int main(int argc, char **argv) { SmallString<4096> CheckFileBuffer; StringRef CheckFileText = FC.CanonicalizeFile(CheckFile, CheckFileBuffer); - SM.AddNewSourceBuffer(MemoryBuffer::getMemBuffer( - CheckFileText, CheckFile.getBufferIdentifier()), - SMLoc()); + unsigned CheckFileBufferID = + SM.AddNewSourceBuffer(MemoryBuffer::getMemBuffer( + CheckFileText, CheckFile.getBufferIdentifier()), + SMLoc()); - if (FC.readCheckFile(SM, CheckFileText, PrefixRE)) + std::pair ImpPatBufferIDRange; + if (FC.readCheckFile(SM, CheckFileText, PrefixRE, &ImpPatBufferIDRange)) return 2; // Open the file to check and add it to SourceMgr. ErrorOr> InputFileOrErr = - MemoryBuffer::getFileOrSTDIN(InputFilename); + MemoryBuffer::getFileOrSTDIN(InputFilename, /*IsText=*/true); + if (InputFilename == "-") + InputFilename = ""; // Overwrite for improved diagnostic messages if (std::error_code EC = InputFileOrErr.getError()) { errs() << "Could not open input file '" << InputFilename << "': " << EC.message() << '\n'; @@ -636,9 +870,6 @@ int main(int argc, char **argv) { InputFileText, InputFile.getBufferIdentifier()), SMLoc()); - if (DumpInput == DumpInputDefault) - DumpInput = DumpInputOnFailure ? DumpInputFail : DumpInputNever; - std::vector Diags; int ExitCode = FC.checkInput(SM, InputFileText, DumpInput == DumpInputNever ? nullptr : &Diags) @@ -647,17 +878,17 @@ int main(int argc, char **argv) { if (DumpInput == DumpInputAlways || (ExitCode == 1 && DumpInput == DumpInputFail)) { errs() << "\n" - << "Input file: " - << (InputFilename == "-" ? "" : InputFilename.getValue()) - << "\n" + << "Input file: " << InputFilename << "\n" << "Check file: " << CheckFilename << "\n" << "\n" - << "-dump-input=help describes the format of the following dump.\n" + << "-dump-input=help explains the following input dump.\n" << "\n"; std::vector Annotations; unsigned LabelWidth; - BuildInputAnnotations(Diags, Annotations, LabelWidth); - DumpAnnotatedInput(errs(), Req, InputFileText, Annotations, LabelWidth); + BuildInputAnnotations(SM, CheckFileBufferID, ImpPatBufferIDRange, Diags, + Annotations, LabelWidth); + DumpAnnotatedInput(errs(), Req, DumpInputFilter, DumpInputContext, + InputFileText, Annotations, LabelWidth); } return ExitCode; diff --git a/utils/FileCheck-9.cpp b/utils/FileCheck-9.cpp deleted file mode 100644 index c882736d4bf..00000000000 --- a/utils/FileCheck-9.cpp +++ /dev/null @@ -1,651 +0,0 @@ -//===- FileCheck.cpp - Check that File's Contents match what is expected --===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// FileCheck does a line-by line check of a file that validates whether it -// contains the expected content. This is useful for regression tests etc. -// -// This program exits with an exit status of 2 on error, exit status of 0 if -// the file matched the expected contents, and exit status of 1 if it did not -// contain the expected contents. -// -//===----------------------------------------------------------------------===// - -#include "llvm/Support/CommandLine.h" -#include "llvm/Support/InitLLVM.h" -#include "llvm/Support/Process.h" -#include "llvm/Support/WithColor.h" -#include "llvm/Support/raw_ostream.h" -#include "llvm/Support/FileCheck.h" -#include -using namespace llvm; - -static cl::opt - CheckFilename(cl::Positional, cl::desc(""), cl::Optional); - -static cl::opt - InputFilename("input-file", cl::desc("File to check (defaults to stdin)"), - cl::init("-"), cl::value_desc("filename")); - -static cl::list CheckPrefixes( - "check-prefix", - cl::desc("Prefix to use from check file (defaults to 'CHECK')")); -static cl::alias CheckPrefixesAlias( - "check-prefixes", cl::aliasopt(CheckPrefixes), cl::CommaSeparated, - cl::NotHidden, - cl::desc( - "Alias for -check-prefix permitting multiple comma separated values")); - -static cl::opt NoCanonicalizeWhiteSpace( - "strict-whitespace", - cl::desc("Do not treat all horizontal whitespace as equivalent")); - -static cl::list ImplicitCheckNot( - "implicit-check-not", - cl::desc("Add an implicit negative check with this pattern to every\n" - "positive check. This can be used to ensure that no instances of\n" - "this pattern occur which are not matched by a positive pattern"), - cl::value_desc("pattern")); - -static cl::list - GlobalDefines("D", cl::AlwaysPrefix, - cl::desc("Define a variable to be used in capture patterns."), - cl::value_desc("VAR=VALUE")); - -static cl::opt AllowEmptyInput( - "allow-empty", cl::init(false), - cl::desc("Allow the input file to be empty. This is useful when making\n" - "checks that some error message does not occur, for example.")); - -static cl::opt MatchFullLines( - "match-full-lines", cl::init(false), - cl::desc("Require all positive matches to cover an entire input line.\n" - "Allows leading and trailing whitespace if --strict-whitespace\n" - "is not also passed.")); - -static cl::opt EnableVarScope( - "enable-var-scope", cl::init(false), - cl::desc("Enables scope for regex variables. Variables with names that\n" - "do not start with '$' will be reset at the beginning of\n" - "each CHECK-LABEL block.")); - -static cl::opt AllowDeprecatedDagOverlap( - "allow-deprecated-dag-overlap", cl::init(false), - cl::desc("Enable overlapping among matches in a group of consecutive\n" - "CHECK-DAG directives. This option is deprecated and is only\n" - "provided for convenience as old tests are migrated to the new\n" - "non-overlapping CHECK-DAG implementation.\n")); - -static cl::opt Verbose( - "v", cl::init(false), - cl::desc("Print directive pattern matches, or add them to the input dump\n" - "if enabled.\n")); - -static cl::opt VerboseVerbose( - "vv", cl::init(false), - cl::desc("Print information helpful in diagnosing internal FileCheck\n" - "issues, or add it to the input dump if enabled. Implies\n" - "-v.\n")); -static const char * DumpInputEnv = "FILECHECK_DUMP_INPUT_ON_FAILURE"; - -static cl::opt DumpInputOnFailure( - "dump-input-on-failure", cl::init(std::getenv(DumpInputEnv)), - cl::desc("Dump original input to stderr before failing.\n" - "The value can be also controlled using\n" - "FILECHECK_DUMP_INPUT_ON_FAILURE environment variable.\n" - "This option is deprecated in favor of -dump-input=fail.\n")); - -enum DumpInputValue { - DumpInputDefault, - DumpInputHelp, - DumpInputNever, - DumpInputFail, - DumpInputAlways -}; - -static cl::opt DumpInput( - "dump-input", cl::init(DumpInputDefault), - cl::desc("Dump input to stderr, adding annotations representing\n" - " currently enabled diagnostics\n"), - cl::value_desc("mode"), - cl::values(clEnumValN(DumpInputHelp, "help", - "Explain dump format and quit"), - clEnumValN(DumpInputNever, "never", "Never dump input"), - clEnumValN(DumpInputFail, "fail", "Dump input on failure"), - clEnumValN(DumpInputAlways, "always", "Always dump input"))); - -typedef cl::list::const_iterator prefix_iterator; - - - - - - - -static void DumpCommandLine(int argc, char **argv) { - errs() << "FileCheck command line: "; - for (int I = 0; I < argc; I++) - errs() << " " << argv[I]; - errs() << "\n"; -} - -struct MarkerStyle { - /// The starting char (before tildes) for marking the line. - char Lead; - /// What color to use for this annotation. - raw_ostream::Colors Color; - /// A note to follow the marker, or empty string if none. - std::string Note; - MarkerStyle() {} - MarkerStyle(char Lead, raw_ostream::Colors Color, - const std::string &Note = "") - : Lead(Lead), Color(Color), Note(Note) {} -}; - -static MarkerStyle GetMarker(FileCheckDiag::MatchType MatchTy) { - switch (MatchTy) { - case FileCheckDiag::MatchFoundAndExpected: - return MarkerStyle('^', raw_ostream::GREEN); - case FileCheckDiag::MatchFoundButExcluded: - return MarkerStyle('!', raw_ostream::RED, "error: no match expected"); - case FileCheckDiag::MatchFoundButWrongLine: - return MarkerStyle('!', raw_ostream::RED, "error: match on wrong line"); - case FileCheckDiag::MatchFoundButDiscarded: - return MarkerStyle('!', raw_ostream::CYAN, - "discard: overlaps earlier match"); - case FileCheckDiag::MatchNoneAndExcluded: - return MarkerStyle('X', raw_ostream::GREEN); - case FileCheckDiag::MatchNoneButExpected: - return MarkerStyle('X', raw_ostream::RED, "error: no match found"); - case FileCheckDiag::MatchFuzzy: - return MarkerStyle('?', raw_ostream::MAGENTA, "possible intended match"); - } - llvm_unreachable_internal("unexpected match type"); -} - -static void DumpInputAnnotationHelp(raw_ostream &OS) { - OS << "The following description was requested by -dump-input=help to\n" - << "explain the input annotations printed by -dump-input=always and\n" - << "-dump-input=fail:\n\n"; - - // Labels for input lines. - OS << " - "; - WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "L:"; - OS << " labels line number L of the input file\n"; - - // Labels for annotation lines. - OS << " - "; - WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "T:L"; - OS << " labels the only match result for a pattern of type T from " - << "line L of\n" - << " the check file\n"; - OS << " - "; - WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "T:L'N"; - OS << " labels the Nth match result for a pattern of type T from line " - << "L of\n" - << " the check file\n"; - - // Markers on annotation lines. - OS << " - "; - WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "^~~"; - OS << " marks good match (reported if -v)\n" - << " - "; - WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "!~~"; - OS << " marks bad match, such as:\n" - << " - CHECK-NEXT on same line as previous match (error)\n" - << " - CHECK-NOT found (error)\n" - << " - CHECK-DAG overlapping match (discarded, reported if " - << "-vv)\n" - << " - "; - WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "X~~"; - OS << " marks search range when no match is found, such as:\n" - << " - CHECK-NEXT not found (error)\n" - << " - CHECK-NOT not found (success, reported if -vv)\n" - << " - CHECK-DAG not found after discarded matches (error)\n" - << " - "; - WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "?"; - OS << " marks fuzzy match when no match is found\n"; - - // Colors. - OS << " - colors "; - WithColor(OS, raw_ostream::GREEN, true) << "success"; - OS << ", "; - WithColor(OS, raw_ostream::RED, true) << "error"; - OS << ", "; - WithColor(OS, raw_ostream::MAGENTA, true) << "fuzzy match"; - OS << ", "; - WithColor(OS, raw_ostream::CYAN, true, false) << "discarded match"; - OS << ", "; - WithColor(OS, raw_ostream::CYAN, true, true) << "unmatched input"; - OS << "\n\n" - << "If you are not seeing color above or in input dumps, try: -color\n"; -} - -/// An annotation for a single input line. -struct InputAnnotation { - /// The check file line (one-origin indexing) where the directive that - /// produced this annotation is located. - unsigned CheckLine; - /// The index of the match result for this check. - unsigned CheckDiagIndex; - /// The label for this annotation. - std::string Label; - /// What input line (one-origin indexing) this annotation marks. This might - /// be different from the starting line of the original diagnostic if this is - /// a non-initial fragment of a diagnostic that has been broken across - /// multiple lines. - unsigned InputLine; - /// The column range (one-origin indexing, open end) in which to to mark the - /// input line. If InputEndCol is UINT_MAX, treat it as the last column - /// before the newline. - unsigned InputStartCol, InputEndCol; - /// The marker to use. - MarkerStyle Marker; - /// Whether this annotation represents a good match for an expected pattern. - bool FoundAndExpectedMatch; -}; - -/// Get an abbreviation for the check type. -std::string GetCheckTypeAbbreviation(Check::FileCheckType Ty) { - switch (Ty) { - case Check::CheckPlain: - if (Ty.getCount() > 1) - return "count"; - return "check"; - case Check::CheckNext: - return "next"; - case Check::CheckSame: - return "same"; - case Check::CheckNot: - return "not"; - case Check::CheckDAG: - return "dag"; - case Check::CheckLabel: - return "label"; - case Check::CheckEmpty: - return "empty"; - case Check::CheckEOF: - return "eof"; - case Check::CheckBadNot: - return "bad-not"; - case Check::CheckBadCount: - return "bad-count"; - case Check::CheckNone: - llvm_unreachable("invalid FileCheckType"); - } - llvm_unreachable("unknown FileCheckType"); -} - -static void BuildInputAnnotations(const std::vector &Diags, - std::vector &Annotations, - unsigned &LabelWidth) { - // How many diagnostics has the current check seen so far? - unsigned CheckDiagCount = 0; - // What's the widest label? - LabelWidth = 0; - for (auto DiagItr = Diags.begin(), DiagEnd = Diags.end(); DiagItr != DiagEnd; - ++DiagItr) { - InputAnnotation A; - - // Build label, which uniquely identifies this check result. - A.CheckLine = DiagItr->CheckLine; - llvm::raw_string_ostream Label(A.Label); - Label << GetCheckTypeAbbreviation(DiagItr->CheckTy) << ":" - << DiagItr->CheckLine; - A.CheckDiagIndex = UINT_MAX; - auto DiagNext = std::next(DiagItr); - if (DiagNext != DiagEnd && DiagItr->CheckTy == DiagNext->CheckTy && - DiagItr->CheckLine == DiagNext->CheckLine) - A.CheckDiagIndex = CheckDiagCount++; - else if (CheckDiagCount) { - A.CheckDiagIndex = CheckDiagCount; - CheckDiagCount = 0; - } - if (A.CheckDiagIndex != UINT_MAX) - Label << "'" << A.CheckDiagIndex; - else - A.CheckDiagIndex = 0; - Label.flush(); - LabelWidth = std::max((std::string::size_type)LabelWidth, A.Label.size()); - - MarkerStyle Marker = GetMarker(DiagItr->MatchTy); - A.Marker = Marker; - A.FoundAndExpectedMatch = - DiagItr->MatchTy == FileCheckDiag::MatchFoundAndExpected; - - // Compute the mark location, and break annotation into multiple - // annotations if it spans multiple lines. - A.InputLine = DiagItr->InputStartLine; - A.InputStartCol = DiagItr->InputStartCol; - if (DiagItr->InputStartLine == DiagItr->InputEndLine) { - // Sometimes ranges are empty in order to indicate a specific point, but - // that would mean nothing would be marked, so adjust the range to - // include the following character. - A.InputEndCol = - std::max(DiagItr->InputStartCol + 1, DiagItr->InputEndCol); - Annotations.push_back(A); - } else { - assert(DiagItr->InputStartLine < DiagItr->InputEndLine && - "expected input range not to be inverted"); - A.InputEndCol = UINT_MAX; - A.Marker.Note = ""; - Annotations.push_back(A); - for (unsigned L = DiagItr->InputStartLine + 1, E = DiagItr->InputEndLine; - L <= E; ++L) { - // If a range ends before the first column on a line, then it has no - // characters on that line, so there's nothing to render. - if (DiagItr->InputEndCol == 1 && L == E) { - Annotations.back().Marker.Note = Marker.Note; - break; - } - InputAnnotation B; - B.CheckLine = A.CheckLine; - B.CheckDiagIndex = A.CheckDiagIndex; - B.Label = A.Label; - B.InputLine = L; - B.Marker = Marker; - B.Marker.Lead = '~'; - B.InputStartCol = 1; - if (L != E) { - B.InputEndCol = UINT_MAX; - B.Marker.Note = ""; - } else - B.InputEndCol = DiagItr->InputEndCol; - B.FoundAndExpectedMatch = A.FoundAndExpectedMatch; - Annotations.push_back(B); - } - } - } -} - -static void DumpAnnotatedInput(raw_ostream &OS, const FileCheckRequest &Req, - StringRef InputFileText, - std::vector &Annotations, - unsigned LabelWidth) { - OS << "Full input was:\n<<<<<<\n"; - - // Sort annotations. - // - // First, sort in the order of input lines to make it easier to find relevant - // annotations while iterating input lines in the implementation below. - // FileCheck diagnostics are not always reported and recorded in the order of - // input lines due to, for example, CHECK-DAG and CHECK-NOT. - // - // Second, for annotations for the same input line, sort in the order of the - // FileCheck directive's line in the check file (where there's at most one - // directive per line) and then by the index of the match result for that - // directive. The rationale of this choice is that, for any input line, this - // sort establishes a total order of annotations that, with respect to match - // results, is consistent across multiple lines, thus making match results - // easier to track from one line to the next when they span multiple lines. - std::sort(Annotations.begin(), Annotations.end(), - [](const InputAnnotation &A, const InputAnnotation &B) { - if (A.InputLine != B.InputLine) - return A.InputLine < B.InputLine; - if (A.CheckLine != B.CheckLine) - return A.CheckLine < B.CheckLine; - // FIXME: Sometimes CHECK-LABEL reports its match twice with - // other diagnostics in between, and then diag index incrementing - // fails to work properly, and then this assert fails. We should - // suppress one of those diagnostics or do a better job of - // computing this index. For now, we just produce a redundant - // CHECK-LABEL annotation. - // assert(A.CheckDiagIndex != B.CheckDiagIndex && - // "expected diagnostic indices to be unique within a " - // " check line"); - return A.CheckDiagIndex < B.CheckDiagIndex; - }); - - // Compute the width of the label column. - const unsigned char *InputFilePtr = InputFileText.bytes_begin(), - *InputFileEnd = InputFileText.bytes_end(); - unsigned LineCount = InputFileText.count('\n'); - if (InputFileEnd[-1] != '\n') - ++LineCount; - unsigned LineNoWidth = std::log10(LineCount) + 1; - // +3 below adds spaces (1) to the left of the (right-aligned) line numbers - // on input lines and (2) to the right of the (left-aligned) labels on - // annotation lines so that input lines and annotation lines are more - // visually distinct. For example, the spaces on the annotation lines ensure - // that input line numbers and check directive line numbers never align - // horizontally. Those line numbers might not even be for the same file. - // One space would be enough to achieve that, but more makes it even easier - // to see. - LabelWidth = std::max(LabelWidth, LineNoWidth) + 3; - - // Print annotated input lines. - auto AnnotationItr = Annotations.begin(), AnnotationEnd = Annotations.end(); - for (unsigned Line = 1; - InputFilePtr != InputFileEnd || AnnotationItr != AnnotationEnd; - ++Line) { - const unsigned char *InputFileLine = InputFilePtr; - - // Print right-aligned line number. - WithColor(OS, raw_ostream::BLACK, true) - << format_decimal(Line, LabelWidth) << ": "; - - // For the case where -v and colors are enabled, find the annotations for - // good matches for expected patterns in order to highlight everything - // else in the line. There are no such annotations if -v is disabled. - std::vector FoundAndExpectedMatches; - if (Req.Verbose && WithColor(OS).colorsEnabled()) { - for (auto I = AnnotationItr; I != AnnotationEnd && I->InputLine == Line; - ++I) { - if (I->FoundAndExpectedMatch) - FoundAndExpectedMatches.push_back(*I); - } - } - - // Print numbered line with highlighting where there are no matches for - // expected patterns. - bool Newline = false; - { - WithColor COS(OS); - bool InMatch = false; - if (Req.Verbose) - COS.changeColor(raw_ostream::CYAN, true, true); - for (unsigned Col = 1; InputFilePtr != InputFileEnd && !Newline; ++Col) { - bool WasInMatch = InMatch; - InMatch = false; - for (auto M : FoundAndExpectedMatches) { - if (M.InputStartCol <= Col && Col < M.InputEndCol) { - InMatch = true; - break; - } - } - if (!WasInMatch && InMatch) - COS.resetColor(); - else if (WasInMatch && !InMatch) - COS.changeColor(raw_ostream::CYAN, true, true); - if (*InputFilePtr == '\n') - Newline = true; - else - COS << *InputFilePtr; - ++InputFilePtr; - } - } - OS << '\n'; - unsigned InputLineWidth = InputFilePtr - InputFileLine - Newline; - - // Print any annotations. - while (AnnotationItr != AnnotationEnd && - AnnotationItr->InputLine == Line) { - WithColor COS(OS, AnnotationItr->Marker.Color, true); - // The two spaces below are where the ": " appears on input lines. - COS << left_justify(AnnotationItr->Label, LabelWidth) << " "; - unsigned Col; - for (Col = 1; Col < AnnotationItr->InputStartCol; ++Col) - COS << ' '; - COS << AnnotationItr->Marker.Lead; - // If InputEndCol=UINT_MAX, stop at InputLineWidth. - for (++Col; Col < AnnotationItr->InputEndCol && Col <= InputLineWidth; - ++Col) - COS << '~'; - const std::string &Note = AnnotationItr->Marker.Note; - if (!Note.empty()) { - // Put the note at the end of the input line. If we were to instead - // put the note right after the marker, subsequent annotations for the - // same input line might appear to mark this note instead of the input - // line. - for (; Col <= InputLineWidth; ++Col) - COS << ' '; - COS << ' ' << Note; - } - COS << '\n'; - ++AnnotationItr; - } - } - - OS << ">>>>>>\n"; -} - -int main(int argc, char **argv) { - // Enable use of ANSI color codes because FileCheck is using them to - // highlight text. - llvm::sys::Process::UseANSIEscapeCodes(true); - - InitLLVM X(argc, argv); - cl::ParseCommandLineOptions(argc, argv, /*Overview*/ "", /*Errs*/ nullptr, - "FILECHECK_OPTS"); - if (DumpInput == DumpInputHelp) { - DumpInputAnnotationHelp(outs()); - return 0; - } - if (CheckFilename.empty()) { - errs() << " not specified\n"; - return 2; - } - - FileCheckRequest Req; - for (auto Prefix : CheckPrefixes) - Req.CheckPrefixes.push_back(Prefix); - - for (auto CheckNot : ImplicitCheckNot) - Req.ImplicitCheckNot.push_back(CheckNot); - - bool GlobalDefineError = false; - for (auto G : GlobalDefines) { - size_t EqIdx = G.find('='); - if (EqIdx == std::string::npos) { - errs() << "Missing equal sign in command-line definition '-D" << G - << "'\n"; - GlobalDefineError = true; - continue; - } - if (EqIdx == 0) { - errs() << "Missing variable name in command-line definition '-D" << G - << "'\n"; - GlobalDefineError = true; - continue; - } - Req.GlobalDefines.push_back(G); - } - if (GlobalDefineError) - return 2; - - Req.AllowEmptyInput = AllowEmptyInput; - Req.EnableVarScope = EnableVarScope; - Req.AllowDeprecatedDagOverlap = AllowDeprecatedDagOverlap; - Req.Verbose = Verbose; - Req.VerboseVerbose = VerboseVerbose; - Req.NoCanonicalizeWhiteSpace = NoCanonicalizeWhiteSpace; - Req.MatchFullLines = MatchFullLines; - - if (VerboseVerbose) - Req.Verbose = true; - - FileCheck FC(Req); - if (!FC.ValidateCheckPrefixes()) { - errs() << "Supplied check-prefix is invalid! Prefixes must be unique and " - "start with a letter and contain only alphanumeric characters, " - "hyphens and underscores\n"; - return 2; - } - - Regex PrefixRE = FC.buildCheckPrefixRegex(); - std::string REError; - if (!PrefixRE.isValid(REError)) { - errs() << "Unable to combine check-prefix strings into a prefix regular " - "expression! This is likely a bug in FileCheck's verification of " - "the check-prefix strings. Regular expression parsing failed " - "with the following error: " - << REError << "\n"; - return 2; - } - - SourceMgr SM; - - // Read the expected strings from the check file. - ErrorOr> CheckFileOrErr = - MemoryBuffer::getFileOrSTDIN(CheckFilename); - if (std::error_code EC = CheckFileOrErr.getError()) { - errs() << "Could not open check file '" << CheckFilename - << "': " << EC.message() << '\n'; - return 2; - } - MemoryBuffer &CheckFile = *CheckFileOrErr.get(); - - SmallString<4096> CheckFileBuffer; - StringRef CheckFileText = FC.CanonicalizeFile(CheckFile, CheckFileBuffer); - - SM.AddNewSourceBuffer(MemoryBuffer::getMemBuffer( - CheckFileText, CheckFile.getBufferIdentifier()), - SMLoc()); - - std::vector CheckStrings; - if (FC.ReadCheckFile(SM, CheckFileText, PrefixRE, CheckStrings)) - return 2; - - // Open the file to check and add it to SourceMgr. - ErrorOr> InputFileOrErr = - MemoryBuffer::getFileOrSTDIN(InputFilename); - if (std::error_code EC = InputFileOrErr.getError()) { - errs() << "Could not open input file '" << InputFilename - << "': " << EC.message() << '\n'; - return 2; - } - MemoryBuffer &InputFile = *InputFileOrErr.get(); - - if (InputFile.getBufferSize() == 0 && !AllowEmptyInput) { - errs() << "FileCheck error: '" << InputFilename << "' is empty.\n"; - DumpCommandLine(argc, argv); - return 2; - } - - SmallString<4096> InputFileBuffer; - StringRef InputFileText = FC.CanonicalizeFile(InputFile, InputFileBuffer); - - SM.AddNewSourceBuffer(MemoryBuffer::getMemBuffer( - InputFileText, InputFile.getBufferIdentifier()), - SMLoc()); - - if (DumpInput == DumpInputDefault) - DumpInput = DumpInputOnFailure ? DumpInputFail : DumpInputNever; - - std::vector Diags; - int ExitCode = FC.CheckInput(SM, InputFileText, CheckStrings, - DumpInput == DumpInputNever ? nullptr : &Diags) - ? EXIT_SUCCESS - : 1; - if (DumpInput == DumpInputAlways || - (ExitCode == 1 && DumpInput == DumpInputFail)) { - errs() << "\n" - << "Input file: " - << (InputFilename == "-" ? "" : InputFilename.getValue()) - << "\n" - << "Check file: " << CheckFilename << "\n" - << "\n" - << "-dump-input=help describes the format of the following dump.\n" - << "\n"; - std::vector Annotations; - unsigned LabelWidth; - BuildInputAnnotations(Diags, Annotations, LabelWidth); - DumpAnnotatedInput(errs(), Req, InputFileText, Annotations, LabelWidth); - } - - return ExitCode; -} diff --git a/utils/not.cpp b/utils/not.cpp index b434af73315..7ca8d1d8a66 100644 --- a/utils/not.cpp +++ b/utils/not.cpp @@ -43,7 +43,11 @@ int main(int argc, const char **argv) { Argv.reserve(argc); for (int i = 0; i < argc; ++i) Argv.push_back(argv[i]); +#if LDC_LLVM_VER < 1600 auto Env = llvm::None; +#else + auto Env = std::nullopt; +#endif std::string ErrMsg; int Result = sys::ExecuteAndWait(*Program, Argv, Env, {}, 0, 0, &ErrMsg);